qstbak/Questionable/Questionable.Functions/AlliedSocietyQuestFunctions.cs
2025-10-09 07:47:19 +10:00

171 lines
5.1 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using FFXIVClientStructs.FFXIV.Client.Game;
using Microsoft.Extensions.Logging;
using Questionable.Data;
using Questionable.Model;
using Questionable.Model.Questing;
namespace Questionable.Functions;
internal sealed class AlliedSocietyQuestFunctions
{
private sealed class NpcData
{
public required uint IssuerDataId { get; init; }
public required List<QuestInfo> AllQuests { get; init; } = new List<QuestInfo>();
}
private record struct Rng(uint S0, uint S1 = 0u, uint S2 = 0u, uint S3 = 0u)
{
public int Next(int range)
{
uint s = S3;
uint s2 = Transform(S0, S1);
uint s3 = S1;
uint s4 = S2;
S0 = s;
S1 = s2;
S2 = s3;
S3 = s4;
return (int)(S1 % range);
}
private static uint Transform(uint s0, uint s1)
{
uint num = s0 ^ (s0 << 11);
return s1 ^ num ^ ((num ^ (s1 >> 11)) >> 8);
}
}
private readonly ILogger<AlliedSocietyQuestFunctions> _logger;
private readonly Dictionary<EAlliedSociety, List<NpcData>> _questsByAlliedSociety = new Dictionary<EAlliedSociety, List<NpcData>>();
private readonly Dictionary<(uint NpcDataId, byte Seed, bool OutranksAll, bool RankedUp), List<QuestId>> _dailyQuests = new Dictionary<(uint, byte, bool, bool), List<QuestId>>();
public AlliedSocietyQuestFunctions(QuestData questData, ILogger<AlliedSocietyQuestFunctions> logger)
{
_logger = logger;
foreach (EAlliedSociety item in from x in Enum.GetValues<EAlliedSociety>()
where x != EAlliedSociety.None
select x)
{
foreach (KeyValuePair<uint, List<QuestInfo>> item2 in (from x in questData.GetAllByAlliedSociety(item)
where x.IsRepeatable
group x by x.IssuerDataId).ToDictionary((IGrouping<uint, QuestInfo> x) => x.Key, (IGrouping<uint, QuestInfo> x) => (from y in x
orderby y.AlliedSocietyQuestGroup == 3, y.QuestId
select y).ToList()))
{
item2.Deconstruct(out var key, out var value);
uint issuerDataId = key;
List<QuestInfo> allQuests = value;
NpcData npcData = new NpcData
{
IssuerDataId = issuerDataId,
AllQuests = allQuests
};
if (_questsByAlliedSociety.TryGetValue(item, out List<NpcData> value2))
{
value2.Add(npcData);
continue;
}
Dictionary<EAlliedSociety, List<NpcData>> questsByAlliedSociety = _questsByAlliedSociety;
int num = 1;
List<NpcData> list = new List<NpcData>(num);
CollectionsMarshal.SetCount(list, num);
Span<NpcData> span = CollectionsMarshal.AsSpan(list);
int index = 0;
span[index] = npcData;
questsByAlliedSociety[item] = list;
}
}
}
public unsafe List<QuestId> GetAvailableAlliedSocietyQuests(EAlliedSociety alliedSociety)
{
byte rank = QuestManager.Instance()->BeastReputation[(int)(alliedSociety - 1)].Rank;
byte currentRank = (byte)(rank & 0x7F);
if (currentRank == 0)
{
return new List<QuestId>();
}
bool flag = (rank & 0x80) != 0;
byte dailyQuestSeed = QuestManager.Instance()->DailyQuestSeed;
List<QuestId> list = new List<QuestId>();
foreach (NpcData item in _questsByAlliedSociety[alliedSociety])
{
bool flag2 = item.AllQuests.All((QuestInfo x) => currentRank > x.AlliedSocietyRank);
(uint, byte, bool, bool) key = (item.IssuerDataId, dailyQuestSeed, flag2, flag);
if (_dailyQuests.TryGetValue(key, out List<QuestId> value))
{
list.AddRange(value);
continue;
}
List<QuestId> list2 = CalculateAvailableQuests(item.AllQuests, dailyQuestSeed, flag2, currentRank, flag);
_logger.LogInformation("Available for {Tribe} (Seed: {Seed}, Issuer: {IssuerId}): {Quests}", alliedSociety, dailyQuestSeed, item.IssuerDataId, string.Join(", ", list2));
_dailyQuests[key] = list2;
list.AddRange(list2);
}
return list;
}
private static List<QuestId> CalculateAvailableQuests(List<QuestInfo> allQuests, byte seed, bool outranksAll, byte currentRank, bool rankedUp)
{
List<QuestInfo> list = allQuests.Where((QuestInfo q) => IsEligible(q, currentRank, rankedUp)).ToList();
List<QuestInfo> list2 = new List<QuestInfo>();
if (list.Count == 0)
{
return new List<QuestId>();
}
Rng rng = new Rng(seed);
if (outranksAll)
{
int num = 0;
for (int num2 = Math.Min(list.Count, 3); num < num2; num++)
{
int num3 = rng.Next(list.Count);
while (list2.Contains(list[num3]))
{
num3 = (num3 + 1) % list.Count;
}
list2.Add(list[num3]);
}
}
else
{
int num4 = list.FindIndex((QuestInfo q) => q.AlliedSocietyQuestGroup == 3);
if (num4 >= 0)
{
list2.Add(list[num4 + rng.Next(list.Count - num4)]);
}
else
{
num4 = list.Count;
}
int num5 = list2.Count;
for (int num6 = Math.Min(num4, 3); num5 < num6; num5++)
{
int num7 = rng.Next(num4);
while (list2.Contains(list[num7]))
{
num7 = (num7 + 1) % num4;
}
list2.Add(list[num7]);
}
}
return list2.Select((QuestInfo x) => (QuestId)x.QuestId).ToList();
}
private static bool IsEligible(QuestInfo questInfo, byte currentRank, bool rankedUp)
{
if (!rankedUp)
{
return questInfo.AlliedSocietyRank <= currentRank;
}
return questInfo.AlliedSocietyRank == currentRank;
}
}