diff --git a/AtlasNodeMods.cs b/AtlasNodeMods.cs index 128b7b2..0825e67 100644 --- a/AtlasNodeMods.cs +++ b/AtlasNodeMods.cs @@ -1,72 +1,10 @@ using System; using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using ExileCore2.PoEMemory; using ExileCore2.PoEMemory.FilesInMemory; using ExileCore2.Shared.Enums; -using ExileCore2.Shared.Interfaces; -using GameOffsets2.Native; namespace ExileMaps; -[StructLayout(LayoutKind.Explicit, Pack = 1)] -public struct AtlasPinOffsets -{ - [FieldOffset(0x2F0)] public long MapRow; - [FieldOffset(0x310)] public int CoordX; - [FieldOffset(0x314)] public int CoordY; - [FieldOffset(0x318)] public int RollSeed; - [FieldOffset(0x31C)] public byte RollTier; - [FieldOffset(0x31D)] public byte RollSalt; - [FieldOffset(0x31F)] public byte Flags; - [FieldOffset(0x321)] public byte FissureFamily; - [FieldOffset(0x322)] public byte FissureDepth; - [FieldOffset(0x325)] public byte RollIsCleansed; - [FieldOffset(0x350)] public StdVector Stats; - [FieldOffset(0x3A8)] public long TooltipRowContainer; -} - -[StructLayout(LayoutKind.Sequential, Pack = 1)] -public struct AtlasNodeStat -{ - public int Id; - public int Value; -} - -[StructLayout(LayoutKind.Sequential, Pack = 1)] -public struct RolledModRow -{ - public long ModRow; - public long Owner; - public long WeightCount; - public long Weights; - public byte Disabled; -} - -[StructLayout(LayoutKind.Explicit, Pack = 1)] -public struct WorldAreaOffsets -{ - [FieldOffset(0x10D)] public long ModCount; - [FieldOffset(0x115)] public long Mods; - [FieldOffset(0x18B)] public byte EmitsMapMods; -} - -[StructLayout(LayoutKind.Sequential, Pack = 1)] -public struct RitualLineRecord -{ - public int CoordX; - public int CoordY; - public StdVector Mods; -} - -[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x40)] -public struct RitualLineModEntry -{ - [FieldOffset(0x00)] public StdVector Values; - [FieldOffset(0x28)] public long ModRow; -} - public readonly record struct AtlasStatValue(string Key, GameStat Stat, StatType Type, int Value); public sealed class TinyMt32 @@ -169,163 +107,9 @@ public int NextBoundedInclusive(int min, int max) } } -public sealed class AtlasNodeModReader(IMemory memory, FilesContainer files) +public static class AtlasModHelpers { - private const int MaxNodeStats = 128; - private const int MaxRolledModRows = 512; - private const int MaxRolledModWeights = 64; - private const int MaxRitualRecords = 64; - private const int MaxRitualMods = 16; - private const int MaxRitualValues = 16; - private const int MaxMapMods = 32; - private const int MapModRefSize = 16; - private const int FissureMaxDepth = 6; - private const int RolledModRowsHolder = 0x28; - - private const string CleansedModsFile = "Data/Balance/EndgameCleansedMods.dat"; - private const string CorruptionModsFile = "Data/Balance/EndgameCorruptionMods.dat"; - - private const int RitualPageOwner = 0x308; - private const int RitualSession = 0x1B0; - private const int RitualLine = 0x3A48; - - private static readonly string[] FissureFamilies = ["BlackBlood", "Lightless", "OfThePit"]; - - private readonly Dictionary rolledModTables = []; - private readonly TinyMt32 rng = new(); - - public AtlasPinOffsets ReadPin(long address) => memory.Read(address); - - public static int Fingerprint(in AtlasPinOffsets pin, long ritualLineStamp) - { - var roll = HashCode.Combine(pin.RollSeed, pin.RollTier, pin.RollSalt, pin.Flags, - pin.FissureFamily, pin.FissureDepth, pin.RollIsCleansed, pin.MapRow); - return HashCode.Combine(roll, pin.Stats.First, pin.Stats.Last, ritualLineStamp); - } - - public long ReadRitualLineStamp(long line) - { - if (line == 0) - return 0; - - var records = memory.Read(line); - return records.First ^ (records.Last << 1); - } - - public long ReadRitualLine(long atlasPageAddress) - { - if (atlasPageAddress == 0) - return 0; - - long owner = memory.Read(atlasPageAddress + RitualPageOwner); - if (owner == 0) - return 0; - - long session = memory.Read(owner + RitualSession); - return session == 0 ? 0 : memory.Read(session + RitualLine); - } - - public void Collect(in AtlasPinOffsets pin, long ritualLine, List into) - { - ReadNodeStats(pin, into); - ReadFissureMods(pin, into); - ReadRolledMod(pin, into); - ReadUniqueMapMods(pin, into); - ReadRitualMods(ritualLine, pin, into); - } - - private void ReadNodeStats(in AtlasPinOffsets pin, List into) - { - if (!Sane(pin.Stats, Unsafe.SizeOf(), MaxNodeStats)) - return; - - var records = files.Stats?.recordsById; - if (records == null) - return; - - foreach (var entry in memory.ReadStdVector(pin.Stats)) - if (entry.Value != 0 && records.TryGetValue(entry.Id, out var stat) && !string.IsNullOrEmpty(stat.Key)) - into.Add(new AtlasStatValue(stat.Key, stat.MatchingStat, stat.Type, entry.Value)); - } - - private void ReadFissureMods(in AtlasPinOffsets pin, List into) - { - if (pin.FissureFamily < 1 || pin.FissureFamily > FissureFamilies.Length) - return; - - int tiers = Math.Min(pin.FissureDepth + 1, FissureMaxDepth); - for (int tier = 1; tier <= tiers; tier++) { - string key = $"AtlasAbyssCrack{FissureFamilies[pin.FissureFamily - 1]}{tier}"; - if (!files.Mods.records.TryGetValue(key, out var mod)) - continue; - AddModStats(mod, into, rolled: false); - } - } - - private void ReadRolledMod(in AtlasPinOffsets pin, List into) - { - if ((pin.Flags & 4) == 0) - return; - - var rows = RolledModTable(pin.RollIsCleansed != 0 ? CleansedModsFile : CorruptionModsFile); - if (rows.Length == 0) - return; - - rng.InitByArray((uint)pin.RollSeed, (uint)pin.CoordX, (uint)pin.CoordY, pin.RollSalt); - - long modRow = PickRolledMod(rows, pin.RollTier); - if (modRow == 0) - return; - - AddModStats(files.Mods.GetModByAddress(modRow), into, rolled: true); - } - - private long PickRolledMod(RolledModRow[] rows, byte tier) - { - long chosen = 0; - int total = 0; - - foreach (var row in rows) { - if (row.WeightCount <= 0 || row.WeightCount > MaxRolledModWeights || row.Weights == 0) - continue; - - long index = Math.Min(tier, row.WeightCount - 1); - int weight = memory.Read(row.Weights + index * sizeof(int)); - if (weight <= 0) - continue; - - total += weight; - if (rng.NextRange((uint)total) < weight) - chosen = row.ModRow; - } - - return chosen; - } - - private void ReadUniqueMapMods(in AtlasPinOffsets pin, List into) - { - if (pin.MapRow == 0) - return; - - long worldArea = memory.Read(pin.MapRow); - if (worldArea == 0) - return; - - var area = memory.Read(worldArea); - if (area.EmitsMapMods == 0 || area.ModCount <= 0 || area.ModCount > MaxMapMods || area.Mods == 0) - return; - - for (int i = 0; i < area.ModCount; i++) { - long modRow = memory.Read(area.Mods + i * MapModRefSize); - if (modRow == 0) - continue; - - rng.InitByArray((uint)i, (uint)pin.RollSeed, (uint)pin.CoordX, (uint)pin.CoordY, pin.RollSalt); - AddModStats(files.Mods.GetModByAddress(modRow), into, rolled: true); - } - } - - private void AddModStats(ModsDat.ModRecord mod, List into, bool rolled) + public static void AddModStats(ModsDat.ModRecord mod, List into, bool rolled, TinyMt32 rng) { if (mod?.StatNames == null || mod.StatRange == null) return; @@ -342,62 +126,4 @@ private void AddModStats(ModsDat.ModRecord mod, List into, bool into.Add(new AtlasStatValue(stat.Key, stat.MatchingStat, stat.Type, value)); } } - - private RolledModRow[] RolledModTable(string fileName) - { - if (rolledModTables.TryGetValue(fileName, out var cached)) - return cached; - - long handle = files.FindFile(fileName); - if (handle == 0) - return []; - - long rowsHolder = memory.Read(handle + RolledModRowsHolder); - if (rowsHolder == 0) - return []; - - var rows = memory.Read(rowsHolder); - if (!Sane(rows, Unsafe.SizeOf(), MaxRolledModRows)) - return []; - - return rolledModTables[fileName] = memory.ReadStdVector(rows); - } - - private void ReadRitualMods(long line, in AtlasPinOffsets pin, List into) - { - if (line == 0) - return; - - var records = memory.Read(line); - if (!Sane(records, Unsafe.SizeOf(), MaxRitualRecords)) - return; - - foreach (var record in memory.ReadStdVector(records)) { - if (record.CoordX != pin.CoordX || record.CoordY != pin.CoordY) - continue; - if (!Sane(record.Mods, Unsafe.SizeOf(), MaxRitualMods)) - return; - - foreach (var entry in memory.ReadStdVector(record.Mods)) { - if (entry.ModRow == 0) - continue; - - var mod = files.Mods.GetModByAddress(entry.ModRow); - if (mod?.StatNames == null || !Sane(entry.Values, sizeof(int), MaxRitualValues)) - continue; - - int[] values = memory.ReadStdVector(entry.Values); - for (int i = 0; i < values.Length && i < mod.StatNames.Length && mod.StatNames[i] != null; i++) { - var stat = mod.StatNames[i]; - if (!string.IsNullOrEmpty(stat.Key)) - into.Add(new AtlasStatValue(stat.Key, stat.MatchingStat, stat.Type, values[i])); - } - } - - return; - } - } - - private static bool Sane(StdVector vector, int elementSize, int maxElements) => - vector.First != 0 && vector.Last > vector.First && vector.TotalElements(elementSize) <= maxElements; } diff --git a/Cache.cs b/Cache.cs index a311233..c827c1c 100644 --- a/Cache.cs +++ b/Cache.cs @@ -41,7 +41,6 @@ public void RefreshMapCache(bool clearCache = false) lastCurveRecordCount = -1; } - RefreshRitualLine(); SeedAtlasModCatalogue(); long tSnap = Stopwatch.GetTimestamp(); @@ -346,40 +345,23 @@ private void AddNodeContentFromIdentity(AtlasNodeDescription node, Node toNode) private readonly List atlasModOrder = []; private readonly Dictionary> atlasModOwnedStats = []; private bool atlasModDescriptionsUsable; - private AtlasNodeModReader atlasModReader; private StatDescriptionWrapper endgameMapStatDescriptions; private bool endgameMapStatDescriptionsFailed; - private long ritualLineAddress; - private long ritualLineStamp; private bool atlasModsChanged; + private readonly TinyMt32 modRng = new(); private const string EndgameMapStatDescriptionsFile = "Metadata/StatDescriptions/endgame_map_stat_descriptions.csd"; - private AtlasNodeModReader AtlasModReader => - atlasModReader ??= new AtlasNodeModReader(GameController.Memory, GameController.Files); - - private void RefreshRitualLine() - { - try { - ritualLineAddress = AtlasModReader.ReadRitualLine(AtlasPanel?.Address ?? 0); - ritualLineStamp = AtlasModReader.ReadRitualLineStamp(ritualLineAddress); - } catch (Exception e) { - ritualLineAddress = 0; - ritualLineStamp = 0; - DebugSwallow("RefreshRitualLine", e); - } - } - private bool RefreshNodeAtlasMods(AtlasNodeDescription node, Node toNode, bool force = false) { try { var element = node.Element; - long el = element?.Address ?? 0; - if (el == 0) + if (element == null) return false; - var pin = AtlasModReader.ReadPin(el); - int fingerprint = AtlasNodeModReader.Fingerprint(pin, ritualLineStamp); + int fingerprint = HashCode.Combine(element.ModRollSeed, element.ModRollTier, element.ModRollSalt, + element.Flags, element.AbyssFissureFamily, element.AbyssFissureDepth, element.IsCleansed, + element.Area?.Address ?? 0); if (!force && toNode.AtlasModsBuilt && fingerprint == toNode.AtlasModFingerprint) return false; @@ -389,7 +371,63 @@ private bool RefreshNodeAtlasMods(AtlasNodeDescription node, Node toNode, bool f toNode.AtlasMods.Clear(); atlasModRaw.Clear(); - AtlasModReader.Collect(pin, ritualLineAddress, atlasModRaw); + + foreach (var (stat, value) in element.Stats) + if (value != 0) + { + var statRecord = GameController.Files.Stats?.recordsById.TryGetValue((int)stat, out var sr) == true ? sr : null; + if (statRecord != null && !string.IsNullOrEmpty(statRecord.Key)) + atlasModRaw.Add(new AtlasStatValue(statRecord.Key, statRecord.MatchingStat, statRecord.Type, value)); + } + + foreach (var mod in element.AbyssMods) + AtlasModHelpers.AddModStats(mod, atlasModRaw, rolled: false, modRng); + + if (element.Flags[2]) + { + modRng.InitByArray((uint)element.ModRollSeed, (uint)element.Coordinate.X, (uint)element.Coordinate.Y, element.ModRollSalt); + + int total = 0; + ModsDat.ModRecord chosen = null; + + var rows = element.IsCleansed + ? GameController.Files.EndgameCleansedMods.EntriesList.Select(x => (x.Mod, x.Weights)) + : GameController.Files.EndgameCorruptionMods.EntriesList.Select(x => (x.Mod, x.Weights)); + foreach (var row in rows) + { + if (row.Weights == null || row.Weights.Count == 0) continue; + int idx = Math.Min(element.ModRollTier, row.Weights.Count - 1); + int weight = row.Weights[idx]; + if (weight <= 0) continue; + total += weight; + if (modRng.NextRange((uint)total) < weight) chosen = row.Mod; + } + + if (chosen != null) + AtlasModHelpers.AddModStats(chosen, atlasModRaw, rolled: true, modRng); + } + + var area = element.Area; + if (area != null) + { + foreach (var mod in area.MapMods) + { + modRng.InitByArray(0, (uint)element.ModRollSeed, (uint)element.Coordinate.X, (uint)element.Coordinate.Y, element.ModRollSalt); + AtlasModHelpers.AddModStats(mod, atlasModRaw, rolled: true, modRng); + } + } + + var mapMods = GameController.IngameState.ServerData.MapMods; + if (mapMods != null && mapMods.TryGetValue(element.Coordinate, out var ritualModList)) + { + foreach (var itemMod in ritualModList) + { + var mod = itemMod?.ModRecord; + if (mod != null) + AtlasModHelpers.AddModStats(mod, atlasModRaw, rolled: false, modRng); + } + } + AddContentStats(element, atlasModRaw); atlasModTotals.Clear(); diff --git a/Core.cs b/Core.cs index f63704c..902ad9b 100644 --- a/Core.cs +++ b/Core.cs @@ -669,36 +669,6 @@ int Code(Vector2 p) { } } - private const int ElementTextureRef = 0x240; - - private string TextureOf(ExileCore2.PoEMemory.Element element) - { - try { - long addr = element?.Address ?? 0; - if (addr == 0) - return null; - - var mem = GameController.Memory; - long slot = mem.Read(addr + ElementTextureRef); - if (slot == 0) - return null; - - long str = mem.Read(slot + 8); - if (str == 0) - return null; - - string raw = mem.ReadStringU(str, 512); - if (string.IsNullOrEmpty(raw)) - return null; - - int bar = raw.IndexOf('|'); - return bar > 0 ? raw[..bar] : raw; - } catch (Exception e) { - DebugSwallow("TextureOf", e); - return null; - } - } - private const string TooltipTexturePrefix = "Art/Textures/Interface/2D/2DArt/UIImages/InGame/AtlasScreen/"; private static bool IsTooltipTexture(string textureName) => textureName != null && textureName.StartsWith(TooltipTexturePrefix) && textureName.Contains("Popup"); @@ -721,10 +691,10 @@ private static bool IsTooltipTexture(string textureName) => private void AddExcludeRectsByTexture(ExileCore2.PoEMemory.Element element, int depth, List target) { - if (element == null || depth > 8 || !element.IsVisible) + if (element == null || depth > 8 || !element.IsVisibleLocal) return; - string texture = TextureOf(element); + string texture = element.TextureNameRaw; if (texture != null) { if (ExcludeTextureSubtrees.Contains(texture)) @@ -784,9 +754,9 @@ private void UpdateScreenBounds() mapTooltipVisible = false; foreach (var tooltip in UI.WorldMap.Children) { - if (tooltip == null || !tooltip.IsVisible) + if (tooltip == null || !tooltip.IsVisibleLocal) continue; - if (!IsTooltipTexture(TextureOf(tooltip))) + if (!IsTooltipTexture(tooltip.TextureNameRaw)) continue; RectangleF mapTooltip = tooltip.GetClientRect(); diff --git a/Features.cs b/Features.cs index e58d8e1..01bca6e 100644 --- a/Features.cs +++ b/Features.cs @@ -11,6 +11,7 @@ using ExileCore2; using ExileCore2.PoEMemory.Elements.AtlasElements; using ExileCore2.PoEMemory.Models; +using ExileCore2.Shared.Enums; using GameOffsets2.Native; using ImGuiNET; using RectangleF = ExileCore2.Shared.RectangleF; @@ -1869,8 +1870,6 @@ private void LayoutAtlasButtonDebug() #region Ritual Foretelling - private const int RitualGateOffset = 0x61F; - private class RitualModDef { public string id { get; set; } public string text { get; set; } public string item { get; set; } public int count { get; set; } } private static List ritualMods; @@ -2150,7 +2149,7 @@ private List ForetellRoll(int depth, int index) var panel = AtlasPanel; if (panel == null) return empty; - int seed = RitualSeedRaw(); + int seed = panel.RitualForetoldSeed; int live = RitualLineCount(); if (seed != foretellRollSeed) { @@ -2217,121 +2216,30 @@ private float ForetellWeightOf(List picks) return w; } - private const int RitualAdjBeginOffset = 0x578; - private const int RitualAdjEndOffset = 0x580; - private const int RitualAdjStride = 68; - private const int RitualSeedOffset = 0x624; - private const int RitualLineBeginOffset = 0x648; - private const int RitualLineEndOffset = 0x650; - - private int RitualSeedRaw() - { - try - { - long page = AtlasPanel?.Address ?? 0; - return page == 0 ? 0 : GameController.Memory.Read(page + RitualSeedOffset); - } - catch { return 0; } - } - - private int RitualLineCountRaw() - { - try - { - long page = AtlasPanel?.Address ?? 0; - if (page == 0) return -1; - var mem = GameController.Memory; - long b = mem.Read(page + RitualLineBeginOffset); - long e = mem.Read(page + RitualLineEndOffset); - if (b == 0 || e < b) return -1; - long n = (e - b) / 8; - return n < 0 || n > 64 ? -1 : (int)n; - } - catch { return -1; } - } - private int RitualLineCount() { - int n = RitualLineCountRaw(); - if (n >= 0) return n; try { return AtlasPanel?.SelectedForetoldRitualMaps?.Count ?? 0; } catch { return 0; } } - private List RitualSelectedRaw() - { - int n = RitualLineCountRaw(); - if (n < 0) return null; - try - { - long page = AtlasPanel?.Address ?? 0; - if (page == 0) return null; - var mem = GameController.Memory; - long b = mem.Read(page + RitualLineBeginOffset); - var list = new List(n); - for (int i = 0; i < n; i++) - list.Add(new Vector2i(mem.Read(b + i * 8), mem.Read(b + i * 8 + 4))); - return list; - } - catch { return null; } - } - private static bool SameCoord(Vector2i a, Vector2i b) => a.X == b.X && a.Y == b.Y; private List RitualSelected() { - var raw = RitualSelectedRaw(); - if (raw != null) return raw; - try { return new List(AtlasPanel?.SelectedForetoldRitualMaps ?? new List()); } - catch { return new List(); } + try { return new List(AtlasPanel?.SelectedForetoldRitualMaps ?? []); } + catch { return []; } } - private bool RitualNeighbours(Vector2i coord, List into) + private List RitualNeighbours(Vector2i coord) { - into.Clear(); try { - long page = AtlasPanel?.Address ?? 0; - if (page == 0) return false; - var mem = GameController.Memory; - long b = mem.Read(page + RitualAdjBeginOffset); - long e = mem.Read(page + RitualAdjEndOffset); - if (b == 0 || e < b) return false; - long n = (e - b) / RitualAdjStride; - if (n <= 0 || n > 200000) return false; - - int want = (coord.X << 16) + coord.Y; - long lo = 0, cnt = n; - while (cnt > 0) - { - long half = cnt / 2; - long rec = b + (lo + half) * RitualAdjStride; - int key = (mem.Read(rec) << 16) + mem.Read(rec + 4); - if (key >= want) cnt = half; - else { lo += half + 1; cnt -= half + 1; } - } - if (lo >= n) return false; - - long found = b + lo * RitualAdjStride; - if (mem.Read(found) != coord.X || mem.Read(found + 4) != coord.Y) return false; - for (int i = 0; i < 5; i++) - { - long p = found + 8 + i * 12; - var c = new Vector2i(mem.Read(p), mem.Read(p + 4)); - if (c.X == 0 && c.Y == 0) continue; - into.Add(c); - } - return true; + var set = AtlasPanel?.Points?.FirstOrDefault(p => p.Source == coord); + return set?.Targets ?? []; } - catch { return false; } + catch { return []; } } - private const int RitualSessionOffset = 0x308; - private const int RitualAccountOffset = 0x1B0; - private const int RitualStatsOffset = 0x3A20; - private const int RitualStatsBegin = 1032; - private const int RitualStatsEnd = 1040; - private const int RitualStatStride = 40; private const int RitualMaxLineStat = 26381; private const int RitualSecondModStat = 26382; private const uint RitualSecondModSalt = 0x91DA3AD9u; @@ -2339,27 +2247,8 @@ private bool RitualNeighbours(Vector2i coord, List into) private int RitualStat(int statId) { - try - { - long page = AtlasPanel?.Address ?? 0; - if (page == 0) return 0; - var mem = GameController.Memory; - long a = mem.Read(page + RitualSessionOffset); - if (a == 0) return 0; - long b = mem.Read(a + RitualAccountOffset); - if (b == 0) return 0; - long stats = mem.Read(b + RitualStatsOffset); - if (stats == 0) return 0; - - long begin = mem.Read(stats + RitualStatsBegin); - long end = mem.Read(stats + RitualStatsEnd); - if (begin == 0 || end < begin || end - begin > RitualStatStride * 8192) return 0; - - for (long p = begin; p < end; p += RitualStatStride) - if (mem.Read(p) == statId) return mem.Read(p + 8); - } - catch { } - return 0; + try { return GameController.IngameState.ServerData.AtlasStats?.GetValueOrDefault((GameStat)statId, 0) ?? 0; } + catch { return 0; } } private const long RitualMaxLineCacheMs = 500; @@ -2375,26 +2264,15 @@ private int RitualMaxLine() return ritualMaxLineCached; } - private const int RitualValidBeginOffset = 0x630; - private const int RitualValidEndOffset = 0x638; - private List RitualValidTargets() { - var list = new List(); try { - long page = AtlasPanel?.Address ?? 0; - if (page == 0) return list; - var mem = GameController.Memory; - long b = mem.Read(page + RitualValidBeginOffset); - long e = mem.Read(page + RitualValidEndOffset); - if (b == 0 || e < b || e - b > 8 * 4096) return list; - for (long p = b; p < e; p += 8) - list.Add(new Vector2i(mem.Read(p), mem.Read(p + 4))); + var list = new List(AtlasPanel?.SelectableForetoldRitualMaps ?? []); + list.Sort((p, q) => p.X != q.X ? p.X.CompareTo(q.X) : p.Y.CompareTo(q.Y)); + return list; } - catch { } - list.Sort((p, q) => p.X != q.X ? p.X.CompareTo(q.X) : p.Y.CompareTo(q.Y)); - return list; + catch { return []; } } private HashSet ritualValidNow = new(); @@ -2429,9 +2307,8 @@ private bool RitualSelectable(Vector2i coord) private List RitualCandidates(Vector2i tip, List selected) { - var raw = new List(); + var raw = RitualNeighbours(tip); var result = new List(); - if (!RitualNeighbours(tip, raw)) return result; foreach (var c in raw) { bool taken = false; @@ -2463,11 +2340,9 @@ private bool ReadRitualLineState(out int seed, out int depth, out bool selecting { var panel = AtlasPanel; if (panel == null) return false; - seed = RitualSeedRaw(); + seed = panel.RitualForetoldSeed; depth = RitualLineCount(); - - long page = panel.Address; - if (page != 0) selecting = GameController.Memory.Read(page + RitualGateOffset) != 0; + selecting = panel.IsRitualForetoldSelectionActive; if (Settings.Features.DebugRitualRolls) { @@ -2475,9 +2350,7 @@ private bool ReadRitualLineState(out int seed, out int depth, out bool selecting if (!state.Equals(ritualLineStateLogged)) { ritualLineStateLogged = state; - int engineSeed = 0, engineDepth = -1; - try { engineSeed = panel.RitualForetoldSeed; engineDepth = panel.SelectedForetoldRitualMaps?.Count ?? -1; } catch { } - LogMessage($"RitualLineState seed={seed} depth={depth} selecting={selecting} rawCount={RitualLineCountRaw()} engineSeed={engineSeed} engineDepth={engineDepth} maxLine={RitualMaxLine()} valid={RitualValidTargets().Count}"); + LogMessage($"RitualLineState seed={seed} depth={depth} selecting={selecting} maxLine={RitualMaxLine()} valid={RitualValidTargets().Count}"); } } return true; @@ -2506,7 +2379,6 @@ private System.Drawing.Color ForetellColor(RitualModDef d) return ColorUtils.WithAlphaOf(WeightRampColor(w, minForetellWeight, maxForetellWeight), OverlayText); } - private readonly Dictionary> ritualAdjMemo = new(); private List ritualPlan = new(); private readonly Dictionary ritualPlanSteps = new(); public bool ritualStartSearchPending; @@ -2528,12 +2400,7 @@ private System.Drawing.Color ForetellColor(RitualModDef d) private List RitualCandidatesMemo(Vector2i tip, List selected) { - if (!ritualAdjMemo.TryGetValue(tip, out var raw)) - { - raw = new List(); - RitualNeighbours(tip, raw); - ritualAdjMemo[tip] = raw; - } + var raw = RitualNeighbours(tip); var result = new List(); foreach (var c in raw) { @@ -2599,7 +2466,6 @@ private List RitualPlanFromStart(int seed, Vector2i start) var key = (seed, -1, start, weightsRecalcVersion, RitualStartSteps(), foretellRollOk, Settings.Features.RitualPlanUnlockedOnly); if (key.Equals(ritualPlanKey)) return ritualPlan; ritualPlanKey = key; - ritualAdjMemo.Clear(); var plan = new List(); RitualEvaluateStart(start, RitualStartSteps(), plan); ritualPlan = plan; @@ -2609,7 +2475,6 @@ private List RitualPlanFromStart(int seed, Vector2i start) private void SearchBestRitualStart(int seed, List starts) { int steps = RitualStartSteps(); - ritualAdjMemo.Clear(); var best = new List(); float bestScore = float.NegativeInfinity; @@ -2640,7 +2505,6 @@ private void RefreshRitualPlan(int seed, int depth, Vector2i tip, List var key = (seed, depth, tip, weightsRecalcVersion, steps, foretellRollOk, Settings.Features.RitualPlanUnlockedOnly); if (key.Equals(ritualPlanKey)) return; ritualPlanKey = key; - ritualAdjMemo.Clear(); var plan = new List(); if (steps > 0) RitualBestPath(depth, tip, new List(selected), steps, plan, true); @@ -2700,7 +2564,6 @@ private void BuildRitualStartRows() var starts = RitualValidTargets(); int steps = RitualStartSteps(); - ritualAdjMemo.Clear(); var sw = System.Diagnostics.Stopwatch.StartNew(); var scratch = new List(); diff --git a/Panels.cs b/Panels.cs index 81ed2e1..9530b9e 100644 --- a/Panels.cs +++ b/Panels.cs @@ -64,7 +64,7 @@ private Element AtlasSearchBox() var kids = UI?.WorldMap?.Children; if (kids == null) return null; foreach (var c in kids) - if (TextureOf(c)?.EndsWith(SearchBoxTexture, StringComparison.OrdinalIgnoreCase) == true) { + if (c.TextureNameRaw?.EndsWith(SearchBoxTexture, StringComparison.OrdinalIgnoreCase) == true) { cachedSearchBox = c; break; } diff --git a/Render.cs b/Render.cs index d479a67..07e7e29 100644 --- a/Render.cs +++ b/Render.cs @@ -4,6 +4,7 @@ using System.Drawing; using System.Numerics; using ExileCore2; +using ExileCore2.PoEMemory.Elements.AtlasElements; using GameOffsets2.Native; using RectangleF = ExileCore2.Shared.RectangleF; using ExileMaps.Classes; @@ -1045,14 +1046,6 @@ Vector2 RotatePoint(Vector2 point) Graphics.DrawQuad(textureId, topLeft, topRight, bottomRight, bottomLeft, color); } - private const int PageOwner = 0x310; - private const int OwnerConnections = 0x17E8; - private const int ConnRecords = 984; - private const int ConnCount = 1000; - private const int RecordStride = 184; - private const int RecP0 = 56; - private const int RecSegments = 176; - #endregion #region Connection Curves @@ -1078,29 +1071,13 @@ private void RefreshConnectionCurves(IReadOnlyCollection nodes) try { - long atlas = AtlasPanel?.Address ?? 0; - if (atlas == 0) - return; - - var mem = GameController.Memory; - long owner = mem.Read(atlas + PageOwner); - if (owner == 0) + var curves = AtlasPanel?.ConnectionCurves; + if (curves == null || curves.Count == 0) return; - long wmc = owner + OwnerConnections; - long records = mem.Read(wmc + ConnRecords); - long count = mem.Read(wmc + ConnCount); - - if (records == 0 || count <= 0 || count > 200000) - { - if (Settings.Features.DebugLogging) - LogMessage($"ConnectionCurves: bad read (records={records:X} count={count}), offsets likely stale after a patch"); + if (curves.Count == lastCurveRecordCount && !connectionCurves.IsEmpty) return; - } - - if (count == lastCurveRecordCount && !connectionCurves.IsEmpty) - return; - lastCurveRecordCount = count; + lastCurveRecordCount = curves.Count; var byPos = new Dictionary(nodes.Count); foreach (var n in nodes) @@ -1113,28 +1090,17 @@ private void RefreshConnectionCurves(IReadOnlyCollection nodes) if (byPos.Count == 0) return; - byte[] blob = mem.ReadBytes(records, (int)count * RecordStride); - if (blob == null || blob.Length < count * RecordStride) - return; - const int extra = 1; var built = connectionCurves; int addedThisPass = 0; - for (int i = 0; i < count; i++) + foreach (var curve in curves) { - int o = i * RecordStride; - Vector3 p0 = ReadVec3(blob, o + RecP0); - Vector3 p1 = ReadVec3(blob, o + RecP0 + 16); - Vector3 p2 = ReadVec3(blob, o + RecP0 + 32); - Vector3 p3 = ReadVec3(blob, o + RecP0 + 48); - int segments = BitConverter.ToInt32(blob, o + RecSegments); - - if (segments < 2) + if (curve.Segments < 2) continue; - if (!byPos.TryGetValue(PosKey(p1), out var ca) || !byPos.TryGetValue(PosKey(p2), out var cb)) + if (!byPos.TryGetValue(PosKey(curve.P1), out var ca) || !byPos.TryGetValue(PosKey(curve.P2), out var cb)) continue; var key = EdgeKey(ca, cb); @@ -1142,17 +1108,17 @@ private void RefreshConnectionCurves(IReadOnlyCollection nodes) continue; addedThisPass++; - int steps = Math.Min((segments - 1) * extra, 512); + int steps = Math.Min((curve.Segments - 1) * extra, 512); var pts = new Vector3[steps + 1]; - HermiteCoefficients(p0, p1, p2, p3, out var a, out var b, out var c, out var d); + HermiteCoefficients(curve.P0, curve.P1, curve.P2, curve.P3, out var a, out var b, out var c, out var d); for (int s = 0; s <= steps; s++) { float t = (float)s / steps; pts[s] = ((a * t + b) * t + c) * t + d; } - pts[0] = p1; - pts[steps] = p2; + pts[0] = curve.P1; + pts[steps] = curve.P2; if (key.Item1 != ca) Array.Reverse(pts); @@ -1168,11 +1134,6 @@ private void RefreshConnectionCurves(IReadOnlyCollection nodes) } } - private static Vector3 ReadVec3(byte[] b, int o) => new( - BitConverter.ToSingle(b, o), - BitConverter.ToSingle(b, o + 4), - BitConverter.ToSingle(b, o + 8)); - private static void HermiteCoefficients(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, out Vector3 a, out Vector3 b, out Vector3 c, out Vector3 d) {