Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 82 additions & 2 deletions Arrowgene.Ddon.GameServer/Quests/QuestStateManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,49 @@ public SharedQuestStateManager(PartyGroup party, DdonGameServer server)
this.Server = server;
}

private Dictionary<QuestId, int> BuildMutualExclusionLookup()
{
var lookup = new Dictionary<QuestId, int>();
var groups = Server.GameSettings.GameServerSettings.MutuallyExclusiveWorldQuestGroups;
for (int i = 0; i < groups.Count; i++)
{
foreach (var questId in groups[i])
{
lookup[(QuestId)questId] = i;
}
}
return lookup;
}

private void EnforceMutualExclusion()
{
var lookup = BuildMutualExclusionLookup();
var rolledGroups = new HashSet<int>();

lock (ActiveQuests)
{
foreach (var scheduleIds in RolledInstanceWorldQuests.Values)
{
var toRemove = new List<uint>();
foreach (var scheduleId in scheduleIds)
{
var quest = QuestManager.GetQuestByScheduleId(scheduleId);
if (quest != null && lookup.TryGetValue(quest.QuestId, out int groupIndex))
{
if (!rolledGroups.Add(groupIndex))
{
toRemove.Add(scheduleId);
}
}
}
foreach (var scheduleId in toRemove)
{
scheduleIds.Remove(scheduleId);
}
}
}
}

public override void EnforceInitialPoolEligibility()
{
var settings = Server.GameSettings.GameServerSettings;
Expand All @@ -868,14 +911,23 @@ public override void EnforceInitialPoolEligibility()
{
// Copy the server-wide pool and apply rank filter (no re-roll replacement).
ApplyServerPool(Server.WorldQuestManager.GetCurrentPool());
EnforceMutualExclusion();
return;
}

// InstanceReset mode: re-roll ineligible slots for this party.
if (!settings.WorldQuestFilterByLeaderAreaRank) return;
if (!settings.WorldQuestFilterByLeaderAreaRank)
{
EnforceMutualExclusion();
return;
}

var leaderCharacter = Party.Leader?.Client.Character;
if (leaderCharacter == null) return;
if (leaderCharacter == null)
{
EnforceMutualExclusion();
return;
}

lock (ActiveQuests)
{
Expand All @@ -898,6 +950,8 @@ public override void EnforceInitialPoolEligibility()
}
}
}

EnforceMutualExclusion();
}

/// <summary>
Expand Down Expand Up @@ -949,6 +1003,7 @@ public void OnServerWorldQuestReset(Dictionary<QuestAreaId, HashSet<uint>> serve
}

ApplyServerPool(serverPool);
EnforceMutualExclusion();
SendWorldQuestListNtc();
}

Expand Down Expand Up @@ -986,9 +1041,34 @@ protected override uint GetEffectiveAreaRank(Character character, QuestAreaId ar

protected override Quest RollQuestVariant(QuestId questId)
{
var lookup = BuildMutualExclusionLookup();
if (lookup.TryGetValue(questId, out int groupIndex))
{
var rolledQuestIds = GetRolledQuestIds();
var groups = Server.GameSettings.GameServerSettings.MutuallyExclusiveWorldQuestGroups;
if (groups[groupIndex].Any(gid => (QuestId)gid != questId && rolledQuestIds.Contains((QuestId)gid)))
{
return null;
}
}
return RollEligibleQuestVariant(questId, Party.Leader?.Client.Character);
}

private HashSet<QuestId> GetRolledQuestIds()
{
lock (ActiveQuests)
{
var result = new HashSet<QuestId>();
foreach (var scheduleIds in RolledInstanceWorldQuests.Values)
foreach (var sid in scheduleIds)
{
var quest = QuestManager.GetQuestByScheduleId(sid);
if (quest != null) result.Add(quest.QuestId);
}
return result;
}
}

public override bool CompleteQuestProgress(uint questScheduleId, DbConnection? connectionIn = null)
{
Quest quest = GetQuest(questScheduleId);
Expand Down
29 changes: 29 additions & 0 deletions Arrowgene.Ddon.Server/Settings/GameServerSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,35 @@ public uint AdventureGuideMaxQuestList
}
private const uint _AdventureGuideMaxQuestList = 50;

/// <summary>
/// Groups of mutually exclusive world quest IDs. When rolling world quests for a party,
/// at most one quest from each group will be selected at a time. Use this to prevent
/// conflicting quests from appearing simultaneously. Quest IDs should be specified as
/// uint values (e.g. 21000079).
/// </summary>
[DefaultValue(
"new List<List<uint>>\n" +
"{\n" +
" new List<uint> { 21000079, 21000089 },\n" +
" new List<uint> { 20995009, 21000008 },\n" +
"}"
)]
public List<List<uint>> MutuallyExclusiveWorldQuestGroups
{
set
{
SetSetting("MutuallyExclusiveWorldQuestGroups", value);
}
get
{
return TryGetSetting("MutuallyExclusiveWorldQuestGroups", new List<List<uint>>
{
new List<uint> { 21000079, 21000089 },
new List<uint> { 20995009, 21000008 },

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idk why I have these two grouped together 20995009, 21000008. They don't seem to be related.

});
}
}

/// <summary>
/// Uses the automatic exp calculation system for all enemies instead of just using the
/// ones marked in quest files.
Expand Down
Loading