@@ -860,6 +860,49 @@ public SharedQuestStateManager(PartyGroup party, DdonGameServer server)
860860 this . Server = server ;
861861 }
862862
863+ private Dictionary < QuestId , int > BuildMutualExclusionLookup ( )
864+ {
865+ var lookup = new Dictionary < QuestId , int > ( ) ;
866+ var groups = Server . GameSettings . GameServerSettings . MutuallyExclusiveWorldQuestGroups ;
867+ for ( int i = 0 ; i < groups . Count ; i ++ )
868+ {
869+ foreach ( var questId in groups [ i ] )
870+ {
871+ lookup [ ( QuestId ) questId ] = i ;
872+ }
873+ }
874+ return lookup ;
875+ }
876+
877+ private void EnforceMutualExclusion ( )
878+ {
879+ var lookup = BuildMutualExclusionLookup ( ) ;
880+ var rolledGroups = new HashSet < int > ( ) ;
881+
882+ lock ( ActiveQuests )
883+ {
884+ foreach ( var scheduleIds in RolledInstanceWorldQuests . Values )
885+ {
886+ var toRemove = new List < uint > ( ) ;
887+ foreach ( var scheduleId in scheduleIds )
888+ {
889+ var quest = QuestManager . GetQuestByScheduleId ( scheduleId ) ;
890+ if ( quest != null && lookup . TryGetValue ( quest . QuestId , out int groupIndex ) )
891+ {
892+ if ( ! rolledGroups . Add ( groupIndex ) )
893+ {
894+ toRemove . Add ( scheduleId ) ;
895+ }
896+ }
897+ }
898+ foreach ( var scheduleId in toRemove )
899+ {
900+ scheduleIds . Remove ( scheduleId ) ;
901+ }
902+ }
903+ }
904+ }
905+
863906 public override void EnforceInitialPoolEligibility ( )
864907 {
865908 var settings = Server . GameSettings . GameServerSettings ;
@@ -868,14 +911,23 @@ public override void EnforceInitialPoolEligibility()
868911 {
869912 // Copy the server-wide pool and apply rank filter (no re-roll replacement).
870913 ApplyServerPool ( Server . WorldQuestManager . GetCurrentPool ( ) ) ;
914+ EnforceMutualExclusion ( ) ;
871915 return ;
872916 }
873917
874918 // InstanceReset mode: re-roll ineligible slots for this party.
875- if ( ! settings . WorldQuestFilterByLeaderAreaRank ) return ;
919+ if ( ! settings . WorldQuestFilterByLeaderAreaRank )
920+ {
921+ EnforceMutualExclusion ( ) ;
922+ return ;
923+ }
876924
877925 var leaderCharacter = Party . Leader ? . Client . Character ;
878- if ( leaderCharacter == null ) return ;
926+ if ( leaderCharacter == null )
927+ {
928+ EnforceMutualExclusion ( ) ;
929+ return ;
930+ }
879931
880932 lock ( ActiveQuests )
881933 {
@@ -898,6 +950,8 @@ public override void EnforceInitialPoolEligibility()
898950 }
899951 }
900952 }
953+
954+ EnforceMutualExclusion ( ) ;
901955 }
902956
903957 /// <summary>
@@ -949,6 +1003,7 @@ public void OnServerWorldQuestReset(Dictionary<QuestAreaId, HashSet<uint>> serve
9491003 }
9501004
9511005 ApplyServerPool ( serverPool ) ;
1006+ EnforceMutualExclusion ( ) ;
9521007 SendWorldQuestListNtc ( ) ;
9531008 }
9541009
@@ -986,9 +1041,34 @@ protected override uint GetEffectiveAreaRank(Character character, QuestAreaId ar
9861041
9871042 protected override Quest RollQuestVariant ( QuestId questId )
9881043 {
1044+ var lookup = BuildMutualExclusionLookup ( ) ;
1045+ if ( lookup . TryGetValue ( questId , out int groupIndex ) )
1046+ {
1047+ var rolledQuestIds = GetRolledQuestIds ( ) ;
1048+ var groups = Server . GameSettings . GameServerSettings . MutuallyExclusiveWorldQuestGroups ;
1049+ if ( groups [ groupIndex ] . Any ( gid => ( QuestId ) gid != questId && rolledQuestIds . Contains ( ( QuestId ) gid ) ) )
1050+ {
1051+ return null ;
1052+ }
1053+ }
9891054 return RollEligibleQuestVariant ( questId , Party . Leader ? . Client . Character ) ;
9901055 }
9911056
1057+ private HashSet < QuestId > GetRolledQuestIds ( )
1058+ {
1059+ lock ( ActiveQuests )
1060+ {
1061+ var result = new HashSet < QuestId > ( ) ;
1062+ foreach ( var scheduleIds in RolledInstanceWorldQuests . Values )
1063+ foreach ( var sid in scheduleIds )
1064+ {
1065+ var quest = QuestManager . GetQuestByScheduleId ( sid ) ;
1066+ if ( quest != null ) result . Add ( quest . QuestId ) ;
1067+ }
1068+ return result ;
1069+ }
1070+ }
1071+
9921072 public override bool CompleteQuestProgress ( uint questScheduleId , DbConnection ? connectionIn = null )
9931073 {
9941074 Quest quest = GetQuest ( questScheduleId ) ;
0 commit comments