ddon-server/Arrowgene.Ddon.GameServer/Handler/QuestGetCycleContentsStateListHandler.cs
Paul Campbell 0bd7bcd404 feat: Add Sub Story Quest Handlers
Added handlers and required packets for:
- QuestAddPackageQuestPointHandler
- QuestGetPackageQuestInfoDetailHandler
- QuestGetPackageQuestInfoHandler

Added new quests
- q10300102.csx
- q60300030.csx

Added a new settings file substory.csx to describe the mission
requirements.

Database 56 -> 57

Co-authored-by: @hankhotspur
2026-04-21 23:02:46 -04:00

186 lines
8.5 KiB
C#

using Arrowgene.Ddon.GameServer.Characters;
using Arrowgene.Ddon.GameServer.Dump;
using Arrowgene.Ddon.Server;
using Arrowgene.Ddon.Shared.Entity;
using Arrowgene.Ddon.Shared.Entity.PacketStructure;
using Arrowgene.Ddon.Shared.Entity.Structure;
using Arrowgene.Ddon.Shared.Model;
using Arrowgene.Ddon.Shared.Model.Quest;
using Arrowgene.Logging;
using System.Collections.Generic;
using System.Linq;
namespace Arrowgene.Ddon.GameServer.Handler
{
public class QuestGetCycleContentsStateListHandler : GameRequestPacketHandler<C2SQuestGetCycleContentsStateListReq, S2CQuestGetCycleContentsStateListRes>
{
private static readonly ServerLogger Logger = LogProvider.Logger<ServerLogger>(typeof(QuestGetCycleContentsStateListHandler));
public QuestGetCycleContentsStateListHandler(DdonGameServer server) : base(server)
{
}
public override S2CQuestGetCycleContentsStateListRes Handle(GameClient client, C2SQuestGetCycleContentsStateListReq request)
{
S2CQuestJoinLobbyQuestInfoNtc pcap = EntitySerializer.Get<S2CQuestJoinLobbyQuestInfoNtc>().Read(InGameDump.data_Dump_20B);
S2CQuestJoinLobbyQuestInfoNtc ntc = new S2CQuestJoinLobbyQuestInfoNtc();
ntc.WorldManageQuestOrderList = pcap.WorldManageQuestOrderList; // Recover paths + change vocation
// TODO: Eventually populate all flags based player state for q7* quests
foreach (var quest in ntc.WorldManageQuestOrderList)
{
quest.Param.QuestFlagList.Clear();
quest.Param.QuestLayoutFlagList.Clear();
quest.Param.QuestFlagList = client.Character.GetWorldManageQuestUnlocks((QuestId) quest.Param.QuestId);
quest.Param.QuestLayoutFlagList = client.Character.GetWorldManageLayoutUnlocks((QuestId)quest.Param.QuestId);
}
ntc.QuestDefine = pcap.QuestDefine; // Recover quest log data to be able to accept quests
ntc.QuestDefine.OrderMaxNum = Server.GameSettings.GameServerSettings.QuestOrderMax;
ntc.QuestDefine.RewardBoxMaxNum = Server.GameSettings.GameServerSettings.RewardBoxMax;
// pcap.MainQuestIdList; (this will add back all missing functionality which depends on complete MSQ)
var completedMsq = client.Character.CompletedQuests.Values.Where(x => x.QuestType == QuestType.Main);
foreach (var msq in completedMsq)
{
ntc.MainQuestIdList.Add(new CDataQuestId() { QuestId = (uint)msq.QuestId });
}
var completedTutorials = client.Character.CompletedQuests.Values.Where(x => x.QuestType == QuestType.Tutorial);
foreach (var tut in completedTutorials)
{
ntc.TutorialQuestIdList.Add(new CDataQuestId() { QuestId = (uint)tut.QuestId });
}
// Add special quests not normally part of DDON
foreach (var questId in new List<QuestId>() {QuestId.WorldManageMonsterCaution , QuestId.WorldManageJobTutorial, QuestId.WorldManageDebug})
{
var customWorldManageQuest = QuestManager.GetQuestByQuestId(questId);
ntc.WorldManageQuestOrderList.Add(customWorldManageQuest.ToCDataWorldManageQuestOrderList(0));
}
List<QuestProgress> allQuestsInProgress = new();
List<uint> priorityQuests = new();
List<uint> decayedQuests = new();
Server.Database.ExecuteInTransaction(connection =>
{
allQuestsInProgress = Server.Database.GetQuestProgressByType(client.Character.CommonId, QuestType.All, connection);
priorityQuests = client.Party is not null ? Server.Database.GetPriorityQuestScheduleIds(client.Party.Leader.Client.Character.CommonId, connection) : new();
decayedQuests = Server.LightQuestManager.HandleQuestDecay(client.Character, allQuestsInProgress, priorityQuests, connection).ToList();
});
foreach(var decayedScheduleId in decayedQuests)
{
var quest = QuestManager.GetQuestByScheduleId(decayedScheduleId);
if (quest is null)
{
continue;
}
ntc.ExpiredQuestList.Add(new()
{
QuestId = quest.QuestId,
QuestScheduleId = quest.QuestScheduleId,
});
}
foreach (var questProgress in allQuestsInProgress)
{
var quest = QuestManager.GetQuestByScheduleId(questProgress.QuestScheduleId);
if (quest == null || !quest.IsActive(client))
{
continue;
}
switch (questProgress.QuestType)
{
case QuestType.Tutorial:
var tutorialQuest = quest.ToCDataTutorialQuestOrderList(questProgress.Step);
ntc.TutorialQuestOrderList.Add(tutorialQuest);
break;
case QuestType.WildHunt:
var mobHuntQuest = quest.ToCDataMobHuntQuestOrderList(questProgress.Step);
ntc.MobHuntQuestOrderList.Add(mobHuntQuest);
break;
case QuestType.Light:
var lightQuest = quest.ToCDataLightQuestOrderList(questProgress.Step);
if (lightQuest.Detail.BoardType == 1 && lightQuest.Detail.GetAp == 0)
{
lightQuest.Detail.GetAp = AreaRankManager.GetAreaPointReward(quest);
}
ntc.LightQuestOrderList.Add(lightQuest);
break;
}
}
Dictionary<QuestSubstoryGroupId, Dictionary<uint, List<CDataQuestOrderList>>> substoryGroups = new();
foreach (var questProgress in allQuestsInProgress)
{
if (questProgress.QuestType != QuestType.Substory) continue;
var quest = QuestManager.GetQuestByScheduleId(questProgress.QuestScheduleId);
if (quest == null || !quest.IsActive(client)) continue;
var props = QuestManager.GetSubstoryQuestProperties(Server, quest.QuestId);
if (props.SubstoryGroupId == QuestSubstoryGroupId.Invalid) continue;
if (!substoryGroups.ContainsKey(props.SubstoryGroupId))
substoryGroups[props.SubstoryGroupId] = new();
if (!substoryGroups[props.SubstoryGroupId].ContainsKey(props.SeqNo))
substoryGroups[props.SubstoryGroupId][props.SeqNo] = new();
substoryGroups[props.SubstoryGroupId][props.SeqNo].Add(quest.ToCDataQuestOrderList(questProgress.Step));
}
foreach (var (substoryGroupId, seqData) in substoryGroups)
{
var entry = new CDataSubstoryQuestOrderList() { SubstoryGroupId = substoryGroupId };
foreach (var (seqNo, questList) in seqData)
{
entry.Details.Add(new CDataS2CQuestJoinLobbyQuestInfoNtcUnk0Unk1()
{
SequenceNo = seqNo,
Unk1 = 0,
Unk2 = 0,
Unk3 = [],
Unk4 = false,
QuestList = questList
});
}
ntc.SubstoryQuestOrderList.Add(entry);
}
if (client.Party != null)
{
foreach (var questScheduleId in priorityQuests)
{
var quest = QuestManager.GetQuestByScheduleId(questScheduleId);
if (quest == null || !quest.IsActive(client))
{
continue;
}
ntc.PriorityQuestList.Add(new CDataPriorityQuest()
{
QuestId = (uint)quest.QuestId,
QuestScheduleId = (uint)quest.QuestScheduleId
});
}
}
if (decayedQuests.Count > 0)
{
client.Send(new S2CLobbyChatMsgNotice()
{
Type = LobbyChatMsgType.ManagementAlertC,
Message = "A quest has been canceled because the\ndelivery time period has ended."
});
}
client.Send(ntc);
return new();
}
}
}