diff --git a/Arrowgene.Ddon.Database/Arrowgene.Ddon.Database.csproj b/Arrowgene.Ddon.Database/Arrowgene.Ddon.Database.csproj index 583b34e7c..4e47c9f78 100644 --- a/Arrowgene.Ddon.Database/Arrowgene.Ddon.Database.csproj +++ b/Arrowgene.Ddon.Database/Arrowgene.Ddon.Database.csproj @@ -33,10 +33,6 @@ - - - - diff --git a/Arrowgene.Ddon.Database/Files/Database/Script/migration_equipment_preset.sql b/Arrowgene.Ddon.Database/Files/Database/Script/migration_equipment_preset.sql new file mode 100644 index 000000000..adcb4244b --- /dev/null +++ b/Arrowgene.Ddon.Database/Files/Database/Script/migration_equipment_preset.sql @@ -0,0 +1,20 @@ +CREATE TABLE "ddon_equip_preset" +( + "character_common_id" INTEGER NOT NULL, + "preset_no" INTEGER NOT NULL, + "job_id" INTEGER NOT NULL, + "preset_name" TEXT NOT NULL, + CONSTRAINT "pk_ddon_equip_preset" PRIMARY KEY ("character_common_id", "job_id", "preset_no"), + CONSTRAINT "fk_ddon_equip_preset_character_common_id" FOREIGN KEY ("character_common_id") REFERENCES "ddon_character_common" ("character_common_id") ON DELETE CASCADE +); + +CREATE TABLE "ddon_equip_preset_template" +( + "character_common_id" INTEGER NOT NULL, + "preset_no" INTEGER NOT NULL, + "job_id" INTEGER NOT NULL, + "slot_no" INTEGER NOT NULL, + "item_uid" VARCHAR(8) NOT NULL, + CONSTRAINT "pk_ddon_equip_preset_template" PRIMARY KEY ("character_common_id", "job_id", "preset_no", "slot_no"), + CONSTRAINT "fk_ddon_equip_preset_template_character_common_id" FOREIGN KEY ("character_common_id", "job_id", "preset_no") REFERENCES "ddon_equip_preset" ("character_common_id", "job_id", "preset_no") ON DELETE CASCADE +); diff --git a/Arrowgene.Ddon.Database/Files/Database/Script/schema_sqlite.sql b/Arrowgene.Ddon.Database/Files/Database/Script/schema_sqlite.sql index 002193e35..e02273101 100644 --- a/Arrowgene.Ddon.Database/Files/Database/Script/schema_sqlite.sql +++ b/Arrowgene.Ddon.Database/Files/Database/Script/schema_sqlite.sql @@ -513,7 +513,6 @@ CREATE TABLE IF NOT EXISTS "ddon_contact_list" "requester_favorite" BOOLEAN NOT NULL, "requested_favorite" BOOLEAN NOT NULL, CONSTRAINT "fk_ddon_contact_list_requester_character_id" FOREIGN KEY ("requester_character_id") REFERENCES "ddon_character" ("character_id") ON DELETE CASCADE, - CONSTRAINT "fk_ddon_contact_list_requested_character_id" FOREIGN KEY ("requested_character_id") REFERENCES "ddon_character" ("character_id") ON DELETE CASCADE, CONSTRAINT "uq_ddon_contact_list_requester_character_id_requested_character_id" UNIQUE ("requester_character_id", "requested_character_id") ); @@ -750,3 +749,24 @@ CREATE TABLE IF NOT EXISTS "ddon_clan_membership" CONSTRAINT "fk_ddon_clan_membership_character_id" FOREIGN KEY ("character_id") REFERENCES "ddon_character" ("character_id") ON DELETE CASCADE, CONSTRAINT "fk_ddon_clan_membership_clan_id" FOREIGN KEY ("clan_id") REFERENCES "ddon_clan_param" ("clan_id") ON DELETE CASCADE ); + +CREATE TABLE IF NOT EXISTS "ddon_equip_preset" +( + "character_common_id" INTEGER NOT NULL, + "job_id" INTEGER NOT NULL, + "preset_no" INTEGER NOT NULL, + "preset_name" TEXT NOT NULL, + CONSTRAINT "pk_ddon_equip_preset" PRIMARY KEY ("character_common_id", "job_id", "preset_no"), + CONSTRAINT "fk_ddon_equip_preset_character_common_id" FOREIGN KEY ("character_common_id") REFERENCES "ddon_character_common" ("character_common_id") ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS "ddon_equip_preset_template" +( + "character_common_id" INTEGER NOT NULL, + "job_id" INTEGER NOT NULL, + "preset_no" INTEGER NOT NULL, + "slot_no" INTEGER NOT NULL, + "item_uid" VARCHAR(8) NOT NULL, + CONSTRAINT "pk_ddon_equip_preset_template" PRIMARY KEY ("character_common_id", "job_id", "preset_no", "slot_no"), + CONSTRAINT "fk_ddon_equip_preset_template_character_common_id" FOREIGN KEY ("character_common_id", "job_id", "preset_no") REFERENCES "ddon_equip_preset" ("character_common_id", "job_id", "preset_no") ON DELETE CASCADE +); diff --git a/Arrowgene.Ddon.Database/IDatabase.cs b/Arrowgene.Ddon.Database/IDatabase.cs index ad3d09165..898e77341 100644 --- a/Arrowgene.Ddon.Database/IDatabase.cs +++ b/Arrowgene.Ddon.Database/IDatabase.cs @@ -582,5 +582,15 @@ bool InsertBBMContentTreasure( List GetClanMemberList(uint clanId, DbConnection? connectionIn = null); CDataClanMemberInfo GetClanMember(uint characterId, DbConnection? connectionIn = null); bool UpdateClanMember(CDataClanMemberInfo memberInfo, uint clanId, DbConnection? connectionIn = null); + + // Equipment Preset + bool InsertEquipmentPreset(uint characterCommonId, JobId jobId, uint presetNo, string presetName); + bool UpdateEquipmentPreset(uint characterCommonId, JobId jobId, uint presetNo, string presetName); + List SelectEquipmentPresets(uint characterCommonId, JobId jobId); + bool DeleteEquipmentPreset(uint characterCommonId, JobId jobId, uint presetNo); + + bool InsertEquipmentPresetTemplate(uint characterCommonId, JobId jobId, uint presetNo, uint slotNo, string itemUId); + List SelectEquipmentPresetTemplate(uint characterCommonId, JobId jobId, uint presetNo); + bool DeleteEquipmentPresetTemplate(uint characterCommonId, JobId jobId, uint presetNo); } } diff --git a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbEquipPresetTemplate.cs b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbEquipPresetTemplate.cs new file mode 100644 index 000000000..5eb08776d --- /dev/null +++ b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbEquipPresetTemplate.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using System.ComponentModel.Design; +using System.Data.Common; +using System.Reflection.Metadata.Ecma335; +using System.Xml; +using Arrowgene.Ddon.Shared.Entity.Structure; +using Arrowgene.Ddon.Shared.Model; + +namespace Arrowgene.Ddon.Database.Sql.Core +{ + public abstract partial class DdonSqlDb : SqlDb + where TCon : DbConnection + where TCom : DbCommand + where TReader : DbDataReader + { + protected static readonly string[] EquipmentTmeplateFields = new string[] + { + "character_common_id", "job_id", "preset_no", "slot_no", "item_uid" + }; + + private readonly string SqlInsertEquipmentPresetTemplate = $"INSERT INTO \"ddon_equip_preset_template\" ({BuildQueryField(EquipmentTmeplateFields)}) VALUES ({BuildQueryInsert(EquipmentTmeplateFields)});"; + private static readonly string SqlSelectEquipmentPresetTemplate = $"SELECT {BuildQueryField(EquipmentTmeplateFields)} FROM \"ddon_equip_preset_template\" WHERE \"character_common_id\" = @character_common_id AND \"job_id\" = @job_id AND \"preset_no\" = @preset_no;"; + private static readonly string SqlDeleteEquipmentPresetTemplate = $"DELETE FROM \"ddon_equip_preset_template\" WHERE \"character_common_id\"=@character_common_id AND \"job_id\"=@job_id AND \"preset_no\"=@preset_no;"; + + public bool InsertEquipmentPresetTemplate(uint characterCommonId, JobId jobId, uint presetNo, uint slotNo, string itemUId) + { + using TCon connection = OpenNewConnection(); + return InsertEquipmentPresetTemplate(connection, characterCommonId, jobId, presetNo, slotNo, itemUId); + } + + public bool InsertEquipmentPresetTemplate(TCon conn, uint characterCommonId, JobId jobId, uint presetNo, uint slotNo, string itemUId) + { + return ExecuteNonQuery(conn, SqlInsertEquipmentPresetTemplate, command => + { + AddParameter(command, "character_common_id", characterCommonId); + AddParameter(command, "job_id", (byte)jobId); + AddParameter(command, "preset_no", presetNo); + AddParameter(command, "slot_no", slotNo); + AddParameter(command, "item_uid", itemUId); + }) == 1; + } + + public List SelectEquipmentPresetTemplate(uint characterCommonId, JobId jobId, uint presetNo) + { + using TCon connection = OpenNewConnection(); + return SelectEquipmentPresetTemplate(connection, characterCommonId, jobId, presetNo); + } + + public List SelectEquipmentPresetTemplate(TCon conn, uint characterCommonId, JobId jobId, uint presetNo) + { + var results = new List(); + ExecuteInTransaction(conn => + { + ExecuteReader(conn, SqlSelectEquipmentPresetTemplate, + command => { + AddParameter(command, "character_common_id", characterCommonId); + AddParameter(command, "job_id", (byte)jobId); + AddParameter(command, "preset_no", presetNo); + }, reader => { + while (reader.Read()) + { + results.Add(new CDataPresetEquipInfo() + { + ItemUId = GetString(reader, "item_uid"), + EquipSlotNo = GetByte(reader, "slot_no") + }); + } + }); + }); + return results; + } + + public bool DeleteEquipmentPresetTemplate(uint characterCommonId, JobId jobId, uint presetNo) + { + using TCon connection = OpenNewConnection(); + return DeleteEquipmentPresetTemplate(connection, characterCommonId, jobId, presetNo); + } + + public bool DeleteEquipmentPresetTemplate(TCon conn, uint characterCommonId, JobId jobId, uint presetNo) + { + return ExecuteNonQuery(conn, SqlDeleteEquipmentPresetTemplate, command => + { + AddParameter(command, "character_common_id", characterCommonId); + AddParameter(command, "job_id", (byte)jobId); + AddParameter(command, "preset_no", presetNo); + }) == 1; + } + } +} diff --git a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbEquipmentPreset.cs b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbEquipmentPreset.cs new file mode 100644 index 000000000..26023239b --- /dev/null +++ b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbEquipmentPreset.cs @@ -0,0 +1,106 @@ +using System.Collections.Generic; +using System.ComponentModel.Design; +using System.Data.Common; +using System.Reflection.Metadata.Ecma335; +using System.Xml; +using Arrowgene.Ddon.Shared.Entity.Structure; +using Arrowgene.Ddon.Shared.Model; + +namespace Arrowgene.Ddon.Database.Sql.Core +{ + public abstract partial class DdonSqlDb : SqlDb + where TCon : DbConnection + where TCom : DbCommand + where TReader : DbDataReader + { + protected static readonly string[] EquipmentPresetFields = new string[] + { + "character_common_id", "job_id", "preset_no", "preset_name" + }; + + private readonly string SqlInsertEquipmentPreset = $"INSERT INTO \"ddon_equip_preset\" ({BuildQueryField(EquipmentPresetFields)}) VALUES ({BuildQueryInsert(EquipmentPresetFields)});"; + private static readonly string SqlSelectEquipmentPresets = $"SELECT {BuildQueryField(EquipmentPresetFields)} FROM \"ddon_equip_preset\" WHERE \"character_common_id\" = @character_common_id AND \"job_id\" = @job_id;"; + private static readonly string SqlUpdateEquipmentPreset = $"UPDATE \"ddon_equip_preset\" SET {BuildQueryUpdate(EquipmentPresetFields)} WHERE \"character_common_id\"=@character_common_id AND \"preset_no\"=@preset_no"; + private static readonly string SqlDeleteEquipmentPreset = $"DELETE FROM \"ddon_equip_preset\" WHERE \"character_common_id\"=@character_common_id AND \"job_id\"=@job_id AND \"preset_no\"=@preset_no;"; + + public bool InsertEquipmentPreset(uint characterCommonId, JobId jobId, uint presetNo, string presetName) + { + using TCon connection = OpenNewConnection(); + return InsertEquipmentPreset(connection, characterCommonId, jobId, presetNo, presetName); + } + + public bool InsertEquipmentPreset(TCon conn, uint characterCommonId, JobId jobId, uint presetNo, string presetName) + { + return ExecuteNonQuery(conn, SqlInsertEquipmentPreset, command => + { + AddParameter(command, "character_common_id", characterCommonId); + AddParameter(command, "job_id", (byte)jobId); + AddParameter(command, "preset_no", presetNo); + AddParameter(command, "preset_name", presetName); + }) == 1; + } + + public bool UpdateEquipmentPreset(uint characterCommonId, JobId jobId, uint presetNo, string presetName) + { + using TCon connection = OpenNewConnection(); + return UpdateEquipmentPreset(connection, characterCommonId, jobId, presetNo, presetName); + } + + public bool UpdateEquipmentPreset(TCon conn, uint characterCommonId, JobId jobId, uint presetNo, string presetName) + { + return ExecuteNonQuery(conn, SqlUpdateEquipmentPreset, command => + { + AddParameter(command, "character_common_id", characterCommonId); + AddParameter(command, "job_id", (byte)jobId); + AddParameter(command, "preset_no", presetNo); + AddParameter(command, "preset_name", presetName); + }) == 1; + } + + public List SelectEquipmentPresets(uint characterCommonId, JobId jobId) + { + using TCon connection = OpenNewConnection(); + return SelectEquipmentPresets(connection, characterCommonId, jobId); + } + + public List SelectEquipmentPresets(TCon conn, uint characterCommonId, JobId jobId) + { + var results = new List(); + ExecuteInTransaction(conn => + { + ExecuteReader(conn, SqlSelectEquipmentPresets, + command => { + AddParameter(command, "@character_common_id", characterCommonId); + AddParameter(command, "@job_id", (byte) jobId); + }, reader => { + while (reader.Read()) + { + results.Add(new CDataEquipPreset() + { + Job = (JobId)GetByte(reader, "job_id"), + PresetNo = GetUInt32(reader, "preset_no"), + PresetName = GetString(reader, "preset_name") + }); + } + }); + }); + return results; + } + + public bool DeleteEquipmentPreset(uint characterCommonId, JobId jobId, uint presetNo) + { + using TCon connection = OpenNewConnection(); + return DeleteEquipmentPreset(connection, characterCommonId, jobId, presetNo); + } + + public bool DeleteEquipmentPreset(TCon conn, uint characterCommonId, JobId jobId, uint presetNo) + { + return ExecuteNonQuery(conn, SqlDeleteEquipmentPreset, command => + { + AddParameter(command, "character_common_id", characterCommonId); + AddParameter(command, "job_id", (byte) jobId); + AddParameter(command, "preset_no", presetNo); + }) == 1; + } + } +} diff --git a/Arrowgene.Ddon.GameServer/DdonGameServer.cs b/Arrowgene.Ddon.GameServer/DdonGameServer.cs index 397c0698f..48e150765 100644 --- a/Arrowgene.Ddon.GameServer/DdonGameServer.cs +++ b/Arrowgene.Ddon.GameServer/DdonGameServer.cs @@ -340,6 +340,9 @@ private void LoadPacketHandler() AddHandler(new EquipUpdateHideCharacterLanternHandler(this)); AddHandler(new EquipUpdateHidePawnHeadArmorHandler(this)); AddHandler(new EquipUpdateHidePawnLanternHandler(this)); + AddHandler(new EquipGetEquipPresetListHandler(this)); + AddHandler(new EquipUpdateEquipPresetHandler(this)); + AddHandler(new EquipUpdateEquipPresetNameHandler(this)); AddHandler(new EventStartHandler(this)); AddHandler(new EventEndHandler(this)); diff --git a/Arrowgene.Ddon.GameServer/GameClient.cs b/Arrowgene.Ddon.GameServer/GameClient.cs index 970ee56de..0e2488e05 100644 --- a/Arrowgene.Ddon.GameServer/GameClient.cs +++ b/Arrowgene.Ddon.GameServer/GameClient.cs @@ -53,6 +53,7 @@ public void UpdateIdentity() public InstanceEventDropItemManager InstanceEventDropItemManager { get; } public GameMode GameMode { get; set; } + public uint EquipPresetIndex { get; set; } // TODO: Place somewhere else more sensible public uint LastWarpPointId { get; set; } diff --git a/Arrowgene.Ddon.GameServer/Handler/EquipGetEquipPresetListHandler.cs b/Arrowgene.Ddon.GameServer/Handler/EquipGetEquipPresetListHandler.cs new file mode 100644 index 000000000..8400a7884 --- /dev/null +++ b/Arrowgene.Ddon.GameServer/Handler/EquipGetEquipPresetListHandler.cs @@ -0,0 +1,64 @@ + +using Arrowgene.Ddon.GameServer.Characters; +using Arrowgene.Ddon.Server; +using Arrowgene.Ddon.Shared.Entity.PacketStructure; +using Arrowgene.Ddon.Shared.Entity.Structure; +using Arrowgene.Ddon.Shared.Model; +using Arrowgene.Logging; +using System.Drawing; +using System.Linq; + +namespace Arrowgene.Ddon.GameServer.Handler +{ + public class EquipGetEquipPresetListHandler : GameRequestPacketHandler + { + private static readonly ServerLogger Logger = LogProvider.Logger(typeof(EquipGetEquipPresetListHandler)); + + public EquipGetEquipPresetListHandler(DdonGameServer server) : base(server) + { + } + + public override S2CEquipGetEquipPresetListRes Handle(GameClient client, C2SEquipGetEquipPresetListReq request) + { + var results = new S2CEquipGetEquipPresetListRes(); + + CharacterCommon characterCommon; + if (client.EquipPresetIndex == 0) + { + characterCommon = client.Character; + } + else + { + characterCommon = client.Character.PawnBySlotNo((byte)client.EquipPresetIndex); + } + + foreach (var presetInfo in Server.Database.SelectEquipmentPresets(characterCommon.CommonId, characterCommon.Job)) + { + var presetItems = Server.Database.SelectEquipmentPresetTemplate(characterCommon.CommonId, presetInfo.Job, presetInfo.PresetNo); + foreach (var presetItem in presetItems) + { + var matches = client.Character.Storage.FindItemByUIdInStorage(ItemManager.AllItemStorages, presetItem.ItemUId); + if (matches == null) + { + // Item was deleted or sold? + continue; + } + + var storageType = matches.Item1; + var item = matches.Item2.Item2; + + presetItem.ItemId = item.ItemId; + presetItem.Color = item.Color; + presetItem.PlusValue = item.PlusValue; + presetItem.EquipElementParamList = item.EquipElementParamList; + + presetInfo.PresetEquipInfoList.Add(presetItem); + } + + results.EquipPresetList.Add(presetInfo); + } + + return results; + } + } +} diff --git a/Arrowgene.Ddon.GameServer/Handler/EquipUpdateEquipPresetHandler.cs b/Arrowgene.Ddon.GameServer/Handler/EquipUpdateEquipPresetHandler.cs new file mode 100644 index 000000000..edaa83405 --- /dev/null +++ b/Arrowgene.Ddon.GameServer/Handler/EquipUpdateEquipPresetHandler.cs @@ -0,0 +1,50 @@ +using Arrowgene.Ddon.Server; +using Arrowgene.Ddon.Shared.Entity.PacketStructure; +using Arrowgene.Ddon.Shared.Entity.Structure; +using Arrowgene.Ddon.Shared.Model; +using Arrowgene.Logging; +using System.Collections.Generic; + +namespace Arrowgene.Ddon.GameServer.Handler +{ + public class EquipUpdateEquipPresetHandler : GameRequestPacketHandler + { + private static readonly ServerLogger Logger = LogProvider.Logger(typeof(EquipUpdateEquipPresetHandler)); + + public EquipUpdateEquipPresetHandler(DdonGameServer server) : base(server) + { + } + + public override S2CEquipUpdateEquipPresetRes Handle(GameClient client, C2SEquipUpdateEquipPresetReq request) + { + var result = new S2CEquipUpdateEquipPresetRes(); + + CharacterCommon characterCommon; + if (client.EquipPresetIndex == 0) + { + characterCommon = client.Character; + } + else + { + characterCommon = client.Character.PawnBySlotNo((byte) request.PawnId); + } + + result.EquipPreset.PresetName = request.PresetName; + result.EquipPreset.PresetNo = request.PresetNo; + result.EquipPreset.PresetEquipInfoList = characterCommon.Equipment.AsCDataPresetEquipInfo((EquipType) request.Type); + result.EquipPreset.Job = characterCommon.Job; + + // TODO: Make transaction + Server.Database.DeleteEquipmentPreset(characterCommon.CommonId, characterCommon.Job, request.PresetNo); + Server.Database.InsertEquipmentPreset(characterCommon.CommonId, characterCommon.Job, request.PresetNo, request.PresetName); + + foreach (var item in result.EquipPreset.PresetEquipInfoList) + { + Server.Database.InsertEquipmentPresetTemplate(characterCommon.CommonId, characterCommon.Job, request.PresetNo, item.EquipSlotNo, item.ItemUId); + } + + return result; + } + } +} + diff --git a/Arrowgene.Ddon.GameServer/Handler/EquipUpdateEquipPresetNameHandler.cs b/Arrowgene.Ddon.GameServer/Handler/EquipUpdateEquipPresetNameHandler.cs new file mode 100644 index 000000000..b228684cd --- /dev/null +++ b/Arrowgene.Ddon.GameServer/Handler/EquipUpdateEquipPresetNameHandler.cs @@ -0,0 +1,37 @@ +using Arrowgene.Ddon.Server; +using Arrowgene.Ddon.Shared.Entity.PacketStructure; +using Arrowgene.Ddon.Shared.Entity.Structure; +using Arrowgene.Ddon.Shared.Model; +using Arrowgene.Logging; +using System.Collections.Generic; +using System.Linq; + +namespace Arrowgene.Ddon.GameServer.Handler +{ + public class EquipUpdateEquipPresetNameHandler : GameRequestPacketHandler + { + private static readonly ServerLogger Logger = LogProvider.Logger(typeof(EquipUpdateEquipPresetNameHandler)); + + public EquipUpdateEquipPresetNameHandler(DdonGameServer server) : base(server) + { + } + + public override S2CEquipUpdateEquipPresetNameRes Handle(GameClient client, C2SEquipUpdateEquipPresetNameReq request) + { + CharacterCommon characterCommon; + if (client.EquipPresetIndex == 0) + { + characterCommon = client.Character; + } + else + { + characterCommon = client.Character.PawnBySlotNo((byte)client.EquipPresetIndex); + } + + var result = Server.Database.SelectEquipmentPresets(characterCommon.CommonId, characterCommon.Job).Where(x => x.PresetNo == request.PresetNo).FirstOrDefault(); + Server.Database.UpdateEquipmentPreset(characterCommon.CommonId, result.Job, result.PresetNo, request.PresetName); + return new S2CEquipUpdateEquipPresetNameRes(); + } + } +} + diff --git a/Arrowgene.Ddon.GameServer/Handler/PawnGetMypawnDataHandler.cs b/Arrowgene.Ddon.GameServer/Handler/PawnGetMypawnDataHandler.cs index 2eda945bc..0386ed132 100644 --- a/Arrowgene.Ddon.GameServer/Handler/PawnGetMypawnDataHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/PawnGetMypawnDataHandler.cs @@ -26,6 +26,7 @@ public override void Handle(GameClient client, StructurePacket().Read(new byte[] {0x0, 0x20, 0xB8, 0xF8, 0x0, 0xDB, 0x3B, 0xCF, 0x0, 0x20, 0xB8, 0xF8, 0x0, 0x5, 0x44, 0x69, 0x61, 0x6E, 0x61, 0x0, 0x6, 0x53, 0x65, 0x65, 0x6C, 0x69, 0x78, 0x0, 0x3, 0x53, 0x3B, 0x52, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xB8, 0xC0, 0xC1}); pcap33.CharacterId = client.Character.CharacterId; diff --git a/Arrowgene.Ddon.GameServer/Handler/ProfileGetMyCharacterProfileHandler.cs b/Arrowgene.Ddon.GameServer/Handler/ProfileGetMyCharacterProfileHandler.cs index 48f28b67b..fdb2abafa 100644 --- a/Arrowgene.Ddon.GameServer/Handler/ProfileGetMyCharacterProfileHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/ProfileGetMyCharacterProfileHandler.cs @@ -23,6 +23,8 @@ public ProfileGetMyCharacterProfileHandler(DdonGameServer server) : base(server) public override void Handle(GameClient client, StructurePacket packet) { + client.EquipPresetIndex = 0; + S2CProfileGetMyCharacterProfileRes Result = new S2CProfileGetMyCharacterProfileRes(); Result.OrbStatusList = _OrbUnlockManager.GetOrbPageStatus(client.Character); Result.AbilityCostMax = _CharacterManager.GetMaxAugmentAllocation(client.Character); diff --git a/Arrowgene.Ddon.Shared/Entity/EntitySerializer.cs b/Arrowgene.Ddon.Shared/Entity/EntitySerializer.cs index b3c040e41..ff2df6eba 100644 --- a/Arrowgene.Ddon.Shared/Entity/EntitySerializer.cs +++ b/Arrowgene.Ddon.Shared/Entity/EntitySerializer.cs @@ -129,6 +129,7 @@ static EntitySerializer() Create(new S2CCraftCraftRankUpNtc.Serializer()); Create(new CDataCurrentEquipInfo.Serializer()); Create(new CDataEquipSlot.Serializer()); + Create(new CDataEquipPreset.Serializer()); Create(new CDataDeliveredItem.Serializer()); Create(new CDataDeliveredItemRecord.Serializer()); Create(new CDataDeliveryItem.Serializer()); @@ -278,6 +279,8 @@ static EntitySerializer() Create(new CDataPlayPointDataSerializer()); Create(new CDataPresetAbilityParam.Serializer()); + Create(new CDataPresetEquipInfo.Serializer()); + Create(new CDatapresetEquipUnk0.Serializer()); Create(new CDataPriorityQuest.Serializer()); Create(new CDataPriorityQuestSetting.Serializer()); @@ -522,6 +525,9 @@ static EntitySerializer() Create(new C2SEquipUpdateHideCharacterLanternReq.Serializer()); Create(new C2SEquipUpdateHidePawnHeadArmorReq.Serializer()); Create(new C2SEquipUpdateHidePawnLanternReq.Serializer()); + Create(new C2SEquipGetEquipPresetListReq.Serializer()); + Create(new C2SEquipUpdateEquipPresetReq.Serializer()); + Create(new C2SEquipUpdateEquipPresetNameReq.Serializer()); Create(new C2SEntryBoardEntryBoardListReq.Serializer()); Create(new C2SEntryBoardEntryBoardItemCreateReq.Serializer()); @@ -1020,6 +1026,10 @@ static EntitySerializer() Create(new S2CEquipUpdateHideCharacterLanternRes.Serializer()); Create(new S2CEquipUpdateHidePawnHeadArmorRes.Serializer()); Create(new S2CEquipUpdateHidePawnLanternRes.Serializer()); + Create(new S2CEquipGetEquipPresetListRes.Serializer()); + Create(new S2CEquipUpdateEquipPresetRes.Serializer()); + Create(new S2CEquipUpdateEquipPresetNameRes.Serializer()); + Create(new S2CExtendEquipSlotNtc.Serializer()); Create(new S2CGpGetGpRes.Serializer()); Create(new S2CGpGetGpDetailRes.Serializer()); diff --git a/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SEquipGetEquipPresetListReq.cs b/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SEquipGetEquipPresetListReq.cs new file mode 100644 index 000000000..55c026aca --- /dev/null +++ b/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SEquipGetEquipPresetListReq.cs @@ -0,0 +1,23 @@ +using Arrowgene.Buffers; +using Arrowgene.Ddon.Shared.Network; + +namespace Arrowgene.Ddon.Shared.Entity.PacketStructure +{ + public class C2SEquipGetEquipPresetListReq : IPacketStructure + { + public PacketId Id => PacketId.C2S_EQUIP_GET_EQUIP_PRESET_LIST_REQ; + + public class Serializer : PacketEntitySerializer + { + public override void Write(IBuffer buffer, C2SEquipGetEquipPresetListReq obj) + { + } + + public override C2SEquipGetEquipPresetListReq Read(IBuffer buffer) + { + C2SEquipGetEquipPresetListReq obj = new C2SEquipGetEquipPresetListReq(); + return obj; + } + } + } +} diff --git a/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SEquipUpdateEquipPresetNameReq.cs b/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SEquipUpdateEquipPresetNameReq.cs new file mode 100644 index 000000000..6916f9d9e --- /dev/null +++ b/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SEquipUpdateEquipPresetNameReq.cs @@ -0,0 +1,34 @@ +using Arrowgene.Buffers; +using Arrowgene.Ddon.Shared.Network; + +namespace Arrowgene.Ddon.Shared.Entity.PacketStructure +{ + public class C2SEquipUpdateEquipPresetNameReq : IPacketStructure + { + public PacketId Id => PacketId.C2S_EQUIP_UPDATE_EQUIP_PRESET_NAME_REQ; + + public C2SEquipUpdateEquipPresetNameReq() + { + } + + public uint PresetNo { get; set; } + public string PresetName { get; set; } + + public class Serializer : PacketEntitySerializer + { + public override void Write(IBuffer buffer, C2SEquipUpdateEquipPresetNameReq obj) + { + WriteUInt32(buffer, obj.PresetNo); + WriteMtString(buffer, obj.PresetName); + } + + public override C2SEquipUpdateEquipPresetNameReq Read(IBuffer buffer) + { + C2SEquipUpdateEquipPresetNameReq obj = new C2SEquipUpdateEquipPresetNameReq(); + obj.PresetNo = ReadUInt32(buffer); + obj.PresetName = ReadMtString(buffer); + return obj; + } + } + } +} diff --git a/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SEquipUpdateEquipPresetReq.cs b/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SEquipUpdateEquipPresetReq.cs new file mode 100644 index 000000000..1db4e09e5 --- /dev/null +++ b/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SEquipUpdateEquipPresetReq.cs @@ -0,0 +1,41 @@ +using Arrowgene.Buffers; +using Arrowgene.Ddon.Shared.Network; + +namespace Arrowgene.Ddon.Shared.Entity.PacketStructure +{ + public class C2SEquipUpdateEquipPresetReq : IPacketStructure + { + public C2SEquipUpdateEquipPresetReq() + { + PresetName = string.Empty; + } + + public PacketId Id => PacketId.C2S_EQUIP_UPDATE_EQUIP_PRESET_REQ; + + public uint PresetNo { get; set; } + public uint PawnId { get; set; } + public uint Type { get; set; } + public string PresetName { get; set; } + + public class Serializer : PacketEntitySerializer + { + public override void Write(IBuffer buffer, C2SEquipUpdateEquipPresetReq obj) + { + WriteUInt32(buffer, obj.PresetNo); + WriteUInt32(buffer, obj.PawnId); + WriteUInt32(buffer, obj.Type); + WriteMtString(buffer, obj.PresetName); + } + + public override C2SEquipUpdateEquipPresetReq Read(IBuffer buffer) + { + C2SEquipUpdateEquipPresetReq obj = new C2SEquipUpdateEquipPresetReq(); + obj.PresetNo = ReadUInt32(buffer); + obj.PawnId = ReadUInt32(buffer); + obj.Type = ReadUInt32(buffer); + obj.PresetName = ReadMtString(buffer); + return obj; + } + } + } +} diff --git a/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CEquipGetEquipPresetListRes.cs b/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CEquipGetEquipPresetListRes.cs new file mode 100644 index 000000000..b39013e8d --- /dev/null +++ b/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CEquipGetEquipPresetListRes.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using Arrowgene.Buffers; +using Arrowgene.Ddon.Shared.Entity.Structure; +using Arrowgene.Ddon.Shared.Network; + +namespace Arrowgene.Ddon.Shared.Entity.PacketStructure +{ + public class S2CEquipGetEquipPresetListRes : ServerResponse + { + public override PacketId Id => PacketId.S2C_EQUIP_GET_EQUIP_PRESET_LIST_RES; + + public S2CEquipGetEquipPresetListRes() + { + EquipPresetList = new List(); + } + + public List EquipPresetList; + + public class Serializer : PacketEntitySerializer + { + public override void Write(IBuffer buffer, S2CEquipGetEquipPresetListRes obj) + { + WriteServerResponse(buffer, obj); + WriteEntityList(buffer, obj.EquipPresetList); + } + + public override S2CEquipGetEquipPresetListRes Read(IBuffer buffer) + { + S2CEquipGetEquipPresetListRes obj = new S2CEquipGetEquipPresetListRes(); + ReadServerResponse(buffer, obj); + obj.EquipPresetList = ReadEntityList(buffer); + return obj; + } + } + } +} diff --git a/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CEquipUpdateEquipPresetNameRes.cs b/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CEquipUpdateEquipPresetNameRes.cs new file mode 100644 index 000000000..78d199d16 --- /dev/null +++ b/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CEquipUpdateEquipPresetNameRes.cs @@ -0,0 +1,30 @@ +using Arrowgene.Buffers; +using Arrowgene.Ddon.Shared.Entity.Structure; +using Arrowgene.Ddon.Shared.Network; + +namespace Arrowgene.Ddon.Shared.Entity.PacketStructure +{ + public class S2CEquipUpdateEquipPresetNameRes : ServerResponse + { + public override PacketId Id => PacketId.S2C_EQUIP_UPDATE_EQUIP_PRESET_NAME_RES; + + public S2CEquipUpdateEquipPresetNameRes() + { + } + + public class Serializer : PacketEntitySerializer + { + public override void Write(IBuffer buffer, S2CEquipUpdateEquipPresetNameRes obj) + { + WriteServerResponse(buffer, obj); + } + + public override S2CEquipUpdateEquipPresetNameRes Read(IBuffer buffer) + { + S2CEquipUpdateEquipPresetNameRes obj = new S2CEquipUpdateEquipPresetNameRes(); + ReadServerResponse(buffer, obj); + return obj; + } + } + } +} diff --git a/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CEquipUpdateEquipPresetRes.cs b/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CEquipUpdateEquipPresetRes.cs new file mode 100644 index 000000000..2a0819d38 --- /dev/null +++ b/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CEquipUpdateEquipPresetRes.cs @@ -0,0 +1,35 @@ +using Arrowgene.Buffers; +using Arrowgene.Ddon.Shared.Entity.Structure; +using Arrowgene.Ddon.Shared.Network; + +namespace Arrowgene.Ddon.Shared.Entity.PacketStructure +{ + public class S2CEquipUpdateEquipPresetRes : ServerResponse + { + public override PacketId Id => PacketId.S2C_EQUIP_UPDATE_EQUIP_PRESET_RES; + + public S2CEquipUpdateEquipPresetRes() + { + EquipPreset = new CDataEquipPreset(); + } + + public CDataEquipPreset EquipPreset { get; set; } + + public class Serializer : PacketEntitySerializer + { + public override void Write(IBuffer buffer, S2CEquipUpdateEquipPresetRes obj) + { + WriteServerResponse(buffer, obj); + WriteEntity(buffer, obj.EquipPreset); + } + + public override S2CEquipUpdateEquipPresetRes Read(IBuffer buffer) + { + S2CEquipUpdateEquipPresetRes obj = new S2CEquipUpdateEquipPresetRes(); + ReadServerResponse(buffer, obj); + obj.EquipPreset = ReadEntity(buffer); + return obj; + } + } + } +} diff --git a/Arrowgene.Ddon.Shared/Entity/Structure/CDataEquipPreset.cs b/Arrowgene.Ddon.Shared/Entity/Structure/CDataEquipPreset.cs new file mode 100644 index 000000000..2f1505823 --- /dev/null +++ b/Arrowgene.Ddon.Shared/Entity/Structure/CDataEquipPreset.cs @@ -0,0 +1,45 @@ +using Arrowgene.Buffers; +using Arrowgene.Ddon.Shared.Model; +using System.Collections.Generic; + +namespace Arrowgene.Ddon.Shared.Entity.Structure +{ + public class CDataEquipPreset + { + public CDataEquipPreset() + { + PresetName = string.Empty; + PresetEquipInfoList = new List(); + PresetJobItemList = new List(); + } + + public uint PresetNo { get; set; } + public string PresetName { get; set; } + public JobId Job { get; set; } + public List PresetEquipInfoList { get; set; } + public List PresetJobItemList { get; set; } + + public class Serializer : EntitySerializer + { + public override void Write(IBuffer buffer, CDataEquipPreset obj) + { + WriteUInt32(buffer, obj.PresetNo); + WriteMtString(buffer, obj.PresetName); + WriteByte(buffer, (byte) obj.Job); + WriteEntityList(buffer, obj.PresetEquipInfoList); + WriteEntityList(buffer, obj.PresetJobItemList); + } + + public override CDataEquipPreset Read(IBuffer buffer) + { + CDataEquipPreset obj = new CDataEquipPreset(); + obj.PresetNo = ReadUInt32(buffer); + obj.PresetName = ReadMtString(buffer); + obj.Job = (JobId)ReadByte(buffer); + obj.PresetEquipInfoList = ReadEntityList(buffer); + obj.PresetJobItemList = ReadEntityList(buffer); + return obj; + } + } + } +} diff --git a/Arrowgene.Ddon.Shared/Entity/Structure/CDataPresetEquipInfo.cs b/Arrowgene.Ddon.Shared/Entity/Structure/CDataPresetEquipInfo.cs new file mode 100644 index 000000000..d96e5ede3 --- /dev/null +++ b/Arrowgene.Ddon.Shared/Entity/Structure/CDataPresetEquipInfo.cs @@ -0,0 +1,52 @@ +using Arrowgene.Buffers; +using Arrowgene.Ddon.Shared.Model; +using System.Collections.Generic; + +namespace Arrowgene.Ddon.Shared.Entity.Structure +{ + public class CDataPresetEquipInfo + { + public CDataPresetEquipInfo() + { + ItemUId = string.Empty; + EquipElementParamList = new List(); + Unk0List = new List(); + } + + public string ItemUId { get; set; } + public uint ItemId { get; set; } + public byte EquipSlotNo { get; set; } + public byte Color { get; set; } + public List EquipElementParamList { get; set; } + public List Unk0List { get; set; } // Emblem? + public byte PlusValue { get; set; } + + + public class Serializer : EntitySerializer + { + public override void Write(IBuffer buffer, CDataPresetEquipInfo obj) + { + WriteMtString(buffer, obj.ItemUId); + WriteUInt32(buffer, obj.ItemId); + WriteByte(buffer, obj.EquipSlotNo); + WriteByte(buffer, obj.Color); + WriteEntityList(buffer, obj.EquipElementParamList); + WriteEntityList(buffer, obj.Unk0List); + WriteByte(buffer, obj.PlusValue); + } + + public override CDataPresetEquipInfo Read(IBuffer buffer) + { + CDataPresetEquipInfo obj = new CDataPresetEquipInfo(); + obj.ItemUId = ReadMtString(buffer); + obj.ItemId = ReadUInt32(buffer); + obj.EquipSlotNo = ReadByte(buffer); + obj.Color = ReadByte(buffer); + obj.EquipElementParamList = ReadEntityList(buffer); + obj.Unk0List = ReadEntityList(buffer); + obj.PlusValue = ReadByte(buffer); + return obj; + } + } + } +} diff --git a/Arrowgene.Ddon.Shared/Entity/Structure/CDatapresetEquipUnk0.cs b/Arrowgene.Ddon.Shared/Entity/Structure/CDatapresetEquipUnk0.cs new file mode 100644 index 000000000..c14584add --- /dev/null +++ b/Arrowgene.Ddon.Shared/Entity/Structure/CDatapresetEquipUnk0.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using Arrowgene.Buffers; + +namespace Arrowgene.Ddon.Shared.Entity.Structure +{ + public class CDatapresetEquipUnk0 + { + public CDatapresetEquipUnk0() + { + } + + public byte Unk0 { get; set; } + public byte Unk1 { get; set; } + public ushort Unk2 { get; set; } + + public class Serializer : EntitySerializer + { + public override void Write(IBuffer buffer, CDatapresetEquipUnk0 obj) + { + WriteByte(buffer, obj.Unk0); + WriteByte(buffer, obj.Unk1); + WriteUInt16(buffer, obj.Unk2); + } + + public override CDatapresetEquipUnk0 Read(IBuffer buffer) + { + CDatapresetEquipUnk0 obj = new CDatapresetEquipUnk0(); + obj.Unk0 = ReadByte(buffer); + obj.Unk1 = ReadByte(buffer); + obj.Unk2 = ReadUInt16(buffer); + return obj; + } + } + } +} diff --git a/Arrowgene.Ddon.Shared/Model/Storages.cs b/Arrowgene.Ddon.Shared/Model/Storages.cs index 176f8f5e3..b0a363f5c 100644 --- a/Arrowgene.Ddon.Shared/Model/Storages.cs +++ b/Arrowgene.Ddon.Shared/Model/Storages.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Security.Cryptography; using Arrowgene.Ddon.Shared.Entity.PacketStructure; using Arrowgene.Ddon.Shared.Entity.Structure; @@ -326,7 +327,24 @@ public List AsCDataCharacterEquipInfo(EquipType equipTy }) .ToList(); } - + + public List AsCDataPresetEquipInfo(EquipType equipType) + { + return GetItems(equipType) + .Select((x, index) => new { item = x, slot = (byte)(index + 1) }) + .Where(tuple => tuple.item != null) + .Select(tuple => new CDataPresetEquipInfo() + { + ItemId = tuple!.item.ItemId, + ItemUId = tuple!.item.UId, + EquipSlotNo = tuple!.slot, + Color = tuple!.item.Color, + PlusValue = tuple!.item.PlusValue, + EquipElementParamList = tuple!.item.EquipElementParamList + }) + .ToList(); + } + private int calculateEquipTypeOffset(EquipType equipType) { return equipType == EquipType.Performance ? 0 : EquipmentTemplate.TOTAL_EQUIP_SLOTS; diff --git a/Arrowgene.Ddon.Test/Database/DatabaseMigratorTest.cs b/Arrowgene.Ddon.Test/Database/DatabaseMigratorTest.cs index 8ed7cf237..d513fb7c5 100644 --- a/Arrowgene.Ddon.Test/Database/DatabaseMigratorTest.cs +++ b/Arrowgene.Ddon.Test/Database/DatabaseMigratorTest.cs @@ -393,6 +393,8 @@ public bool UpdateRentalPawnSlot(uint characterId, uint num) public List SelectRandomPlayerPawns(uint limit = 100) { return new List(); } public List SelectRandomPlayerPawns(DbConnection connection, uint limit = 100) { return new List(); } public uint GetPawnOwnerCharacterId(uint pawnId) { return 0; } + public List SelectRegisteredPawns(Character searchingCharacter, CDataPawnSearchParameter searchParams) { return new List(); } + public List SelectRegisteredPawns(DbConnection conn, Character searchingCharacter, CDataPawnSearchParameter searchParams) { return new List(); } public CDataCharacterSearchParam SelectCharacterNameById(uint characterId) { return new CDataCharacterSearchParam(); } public CDataCharacterSearchParam SelectCharacterNameById(DbConnection connection, uint characterId) { return new CDataCharacterSearchParam(); } @@ -408,6 +410,14 @@ public bool UpdateRentalPawnSlot(uint characterId, uint num) public CDataClanMemberInfo GetClanMember(uint characterId, DbConnection? connectionIn = null) { return new(); } public bool UpdateClanMember(CDataClanMemberInfo memberInfo, uint clanId, DbConnection? connectionIn = null) { return true; } + public bool InsertEquipmentPreset(uint characterCommonId, JobId jobId, uint presetNo, string presetName) { return true; } + public bool UpdateEquipmentPreset(uint characterCommonId, JobId jobId, uint presetNo, string presetName) { return true; } + public List SelectEquipmentPresets(uint characterCommonId, JobId jobId) { return new List(); } + public bool DeleteEquipmentPreset(uint characterCommonId, JobId jobId, uint presetNo) { return true; } + public bool InsertEquipmentPresetTemplate(uint characterCommonId, JobId jobId, uint presetNo, uint slotNo, string itemUId) { return true; } + public List SelectEquipmentPresetTemplate(uint characterCommonId, JobId jobId, uint presetNo) { return new(); } + public bool DeleteEquipmentPresetTemplate(uint characterCommonId, JobId jobId, uint presetNo) { return true; } + public void AddParameter(DbCommand command, string name, object? value, DbType type) { } public void AddParameter(DbCommand command, string name, string value) { } public void AddParameter(DbCommand command, string name, Int32 value) { } @@ -430,8 +440,6 @@ public void AddParameter(DbCommand command, string name, bool value) { } public string GetString(DbDataReader reader, string column) { return ""; } public bool GetBoolean(DbDataReader reader, string column) { return false; } public byte[] GetBytes(DbDataReader reader, string column, int size) { return null; } - public List SelectRegisteredPawns(Character searchingCharacter, CDataPawnSearchParameter searchParams) { return new List(); } - public List SelectRegisteredPawns(DbConnection conn, Character searchingCharacter, CDataPawnSearchParameter searchParams) { return new List(); } } class MockMigrationStrategy : IMigrationStrategy diff --git a/docs/quests/quests.md b/docs/quests/quests.md index 3b59b06d4..87569524e 100644 --- a/docs/quests/quests.md +++ b/docs/quests/quests.md @@ -4,6 +4,34 @@ This document describes currently implemented quest information and known issues If you are interested in modifying or creating new quests, see the [generic quest state machine document](generic_quest_state_machine.md) for the details. +## Quest Types + +- Main story quests + - Quest IDs for S1 q00000001 to q00000030 + - Quest IDs for S2 q00020010 to q00020250 + - Quest IDs for S2 q00030010 to q00030440 +- Set Quests (World Quests) + - Quest IDs are q2xxxxxx +- Light Quests (Personal Quests from board) + - Quest IDs are q4xxxxxxx +- Extreme Missions + - Quest IDs are q5xxxxxxx +- Tutorial Quests (Personal Quests from NPCs) + - Quest IDs are q6xxxxxx +- World Manage Quest + - q7xxxxxx +- War Missions + - Quest IDs are q9xxxxxx + - Earlier quests look like grand missions +- Wild Hunt + - Quest IDs are q15xxxxxx +- Substory + - Quest IDs are q10xxxxxx + +- Unknown + - q3xxxxxxx (looks like more board quests?) + - q8xxxxxxx (cooking/crafting?) + ## What works in the current implementation - Currently only [Season 1.0 MSQ](#season-10), [Season 1.1 MSQ](#season-11) and a limited number of [World Quests](#world-quests) are activated.