From 445ae1387c8d28c61aa17ce27c7cee3ba657013c Mon Sep 17 00:00:00 2001 From: Boki Date: Thu, 5 Mar 2026 11:26:30 -0500 Subject: [PATCH] quests and queststate work --- .gitignore | 3 +- components.json | 1 + entities.json | 103 + offsets.json | 21 +- profiles/_assignments.json | 4 +- profiles/dudemoko_Default.json | 166 ++ profiles/terdsare_Default.json | 166 ++ src/Automata.Ui/ViewModels/MemoryViewModel.cs | 431 ++-- src/Automata.Ui/ViewModels/RobotoViewModel.cs | 14 +- src/Automata.Ui/Views/MainWindow.axaml | 21 + src/Roboto.Core/GameState.cs | 2 + src/Roboto.Core/UiQuestInfo.cs | 12 + src/Roboto.Data/GameDataCache.cs | 13 + src/Roboto.Data/MemoryPoller.cs | 49 +- src/Roboto.Input/SendInputController.cs | 300 +++ .../Diagnostics/MemoryDiagnostics.cs | 1858 +++++++++++++++++ src/Roboto.Memory/GameMemoryReader.cs | 23 +- src/Roboto.Memory/GameOffsets.cs | 69 +- src/Roboto.Memory/Objects/AreaInstance.cs | 45 +- src/Roboto.Memory/Objects/InGameState.cs | 7 + src/Roboto.Memory/Objects/QuestFlags.cs | 70 +- src/Roboto.Memory/Objects/UIElements.cs | 528 +++++ .../Snapshots/GameStateSnapshot.cs | 12 + .../Snapshots/QuestLinkedEntry.cs | 24 + .../Snapshots/QuestStateEntry.cs | 13 + src/Roboto.Memory/Snapshots/UIElementNode.cs | 17 + src/Roboto.Memory/Snapshots/UiQuestEntry.cs | 22 + 27 files changed, 3815 insertions(+), 179 deletions(-) create mode 100644 profiles/dudemoko_Default.json create mode 100644 profiles/terdsare_Default.json create mode 100644 src/Roboto.Core/UiQuestInfo.cs create mode 100644 src/Roboto.Input/SendInputController.cs create mode 100644 src/Roboto.Memory/Objects/UIElements.cs create mode 100644 src/Roboto.Memory/Snapshots/QuestLinkedEntry.cs create mode 100644 src/Roboto.Memory/Snapshots/QuestStateEntry.cs create mode 100644 src/Roboto.Memory/Snapshots/UIElementNode.cs create mode 100644 src/Roboto.Memory/Snapshots/UiQuestEntry.cs diff --git a/.gitignore b/.gitignore index 7cf5e46..f93fd7b 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,5 @@ tools/python-detect/models/ nul # Extras -lib/extras \ No newline at end of file +lib/extras +lib/ExileCore-master \ No newline at end of file diff --git a/components.json b/components.json index e62d2d5..845e076 100644 --- a/components.json +++ b/components.json @@ -9,6 +9,7 @@ "ControlZone", "CritterAI", "DiesAfterTime", + "F", "Functions", "GlobalAudioParamEvents", "HideoutDoodad", diff --git a/entities.json b/entities.json index aa69ea4..9a01522 100644 --- a/entities.json +++ b/entities.json @@ -11,20 +11,35 @@ "Metadata/Chests/EzomyteChest_02", "Metadata/Chests/EzomyteChest_05", "Metadata/Chests/EzomyteChest_06", + "Metadata/Chests/EzomyteStatueGreen1", + "Metadata/Chests/EzomyteStatueGreen2", + "Metadata/Chests/EzomyteStatueGreen3", + "Metadata/Chests/EzomyteStatueGreen4", + "Metadata/Chests/EzomyteStatueGreen5", + "Metadata/Chests/EzomyteStatueGreen7", + "Metadata/Chests/GallowsTutorialChest2", "Metadata/Chests/LeagueIncursion/EncounterChest", + "Metadata/Chests/MossyBoulder1", + "Metadata/Chests/MossyBoulder2", "Metadata/Chests/MossyChest11", + "Metadata/Chests/MossyChest11MagicAndRare", "Metadata/Chests/MossyChest13", "Metadata/Chests/MossyChest14", + "Metadata/Chests/MossyChest14MagicAndRare", + "Metadata/Chests/MossyChest17", "Metadata/Chests/MossyChest20", "Metadata/Chests/MossyChest21", "Metadata/Chests/MossyChest26", "Metadata/Chests/MuddyChest1", + "Metadata/Chests/SirenEggs/SirenEgg_02", "Metadata/Critters/BloodWorm/BloodWormBrown", "Metadata/Critters/Chicken/Chicken_kingsmarch", "Metadata/Critters/Crow/Crow", "Metadata/Critters/Ferret/Ferret", "Metadata/Critters/Hedgehog/HedgehogSlow", + "Metadata/Critters/Spider/NurseryWebSpider", "Metadata/Critters/Weta/Basic", + "Metadata/Effects/BeamEffect", "Metadata/Effects/Effect", "Metadata/Effects/Microtransactions/Town_Portals/PersonSplitPortal/_PersonSplitPortalPrespawnDummy", "Metadata/Effects/Microtransactions/Town_Portals/PersonSplitPortal/_PersonSplitPortalPrespawnDummyMarble", @@ -32,19 +47,37 @@ "Metadata/Effects/Microtransactions/foot_prints/harvest02/footprints_harvest", "Metadata/Effects/PermanentEffect", "Metadata/Effects/ServerEffect", + "Metadata/Effects/SleepableBeamEffect", + "Metadata/Effects/Spells/ground_effects/VisibleServerGroundEffect", "Metadata/Effects/Spells/monsters_effects/Act1_FOUR/CarrionCrone/IceSpike", + "Metadata/Effects/Spells/monsters_effects/Act1_FOUR/MudBurrower/mudburrower_chasm", + "Metadata/Effects/Spells/monsters_effects/Act1_FOUR/MudBurrower/mudburrower_chasm_body", "Metadata/Effects/Spells/sandstorm_swipe/sandstorm_swipe_storm", "Metadata/MiscellaneousObjects/AreaTransitionBlockage", "Metadata/MiscellaneousObjects/AreaTransitionDoodad", "Metadata/MiscellaneousObjects/AreaTransition_Animate", + "Metadata/MiscellaneousObjects/BossTargetMarkerSerialized", + "Metadata/MiscellaneousObjects/BossTargetMarkerSerialized2", + "Metadata/MiscellaneousObjects/CameraZoom/MinorZoomIn", + "Metadata/MiscellaneousObjects/CameraZoom/TreeOfSouls", "Metadata/MiscellaneousObjects/Checkpoint", + "Metadata/MiscellaneousObjects/CheckpointTutorial", "Metadata/MiscellaneousObjects/Doodad", "Metadata/MiscellaneousObjects/DoodadInvisible", "Metadata/MiscellaneousObjects/DoodadNoBlocking", + "Metadata/MiscellaneousObjects/Environment/EnvLineEnd", + "Metadata/MiscellaneousObjects/Environment/EnvLineStart", "Metadata/MiscellaneousObjects/Environment/NonDefaultFlows/FlowBlocking_20_1", + "Metadata/MiscellaneousObjects/Environment/NonDefaultFlows/FlowBlocking_5_1", + "Metadata/MiscellaneousObjects/Environment/NonDefaultFlows/FlowBlocking_6_1", + "Metadata/MiscellaneousObjects/Environment/NonDefaultFlows/FlowBlocking_8_1", "Metadata/MiscellaneousObjects/Environment/NonDefaultFlows/FlowSink_4.75_1", "Metadata/MiscellaneousObjects/Environment/NonDefaultFlows/FlowSink_6_4", + "Metadata/MiscellaneousObjects/Environment/NonDefaultFlows/FlowSink_8_8", "Metadata/MiscellaneousObjects/Environment/NonDefaultFlows/FlowSource_4.75_1", + "Metadata/MiscellaneousObjects/Environment/NonDefaultFlows/FlowSource_6_4", + "Metadata/MiscellaneousObjects/Environment/NonDefaultFlows/FlowSource_6_6", + "Metadata/MiscellaneousObjects/Environment/NonDefaultFlows/FlowSource_8_8", "Metadata/MiscellaneousObjects/GuildStash", "Metadata/MiscellaneousObjects/HealingWell", "Metadata/MiscellaneousObjects/LeagueIncursionNew/IncursionPedestalCrystal_1", @@ -58,19 +91,46 @@ "Metadata/MiscellaneousObjects/ReviveIcon", "Metadata/MiscellaneousObjects/ServerDoodadHidden", "Metadata/MiscellaneousObjects/Stash", + "Metadata/MiscellaneousObjects/TargetMarker1", + "Metadata/MiscellaneousObjects/TutorialArrow", "Metadata/MiscellaneousObjects/Waypoint", "Metadata/MiscellaneousObjects/WorldItem", "Metadata/Monsters/BansheeRemake/WitchHut/Objects/AmbushLocation", "Metadata/Monsters/BansheeRemake/WitchHutBanshee", + "Metadata/Monsters/CarnivorousPlantEater/OldForest/BossRoomMinimapIcon", + "Metadata/Monsters/CarnivorousPlantEater/OldForest/CarnivorousPlantEaterOldForest_", + "Metadata/Monsters/Daemon/FungalBurstDaemon", + "Metadata/Monsters/FungusZombie/FungalBurstMushrooms/FungalBurstSpawner", "Metadata/Monsters/FungusZombie/FungusZombieLarge", "Metadata/Monsters/FungusZombie/FungusZombieMedium", "Metadata/Monsters/Hags/Objects/BossRoomMinimapIcon", "Metadata/Monsters/Hags/UrchinHag1", "Metadata/Monsters/Hags/UrchinHagBoss", + "Metadata/Monsters/HuhuGrub/CinematicHuhuGrub", "Metadata/Monsters/HuhuGrub/HuhuGrubLarvaeEmerge1", + "Metadata/Monsters/HuhuGrub/HuhuGrubLarvaeEmergeSummoned1_", "Metadata/Monsters/HuhuGrub/HuhuGrubLarvaeRanged1", "Metadata/Monsters/InvisibleFire/MDCarrionCroneWave", + "Metadata/Monsters/MonsterMods/GroundOnDeath/ShockedGroundDaemonParent", "Metadata/Monsters/MonsterMods/OnDeathColdExplosionParent", + "Metadata/Monsters/MudBurrower/Arena_Blocker", + "Metadata/Monsters/MudBurrower/Arena_Blocker_Visual", + "Metadata/Monsters/MudBurrower/MudBurrowerBodyBoss", + "Metadata/Monsters/MudBurrower/MudBurrowerBodyDaemon", + "Metadata/Monsters/MudBurrower/MudBurrowerHeadBoss", + "Metadata/Monsters/MudBurrower/MudBurrowerTailBoss_", + "Metadata/Monsters/MudBurrower/Objects/BossRoomMinimapIcon", + "Metadata/Monsters/MudGolem/MudGolemWallAnimated", + "Metadata/Monsters/MudGolem/MudGolemWet1", + "Metadata/Monsters/MudGolem/MudGolemWetEncased1", + "Metadata/Monsters/NPC/DogTrader_", + "Metadata/Monsters/QuillCrab/QuillCrab", + "Metadata/Monsters/QuillCrab/QuillCrabBig", + "Metadata/Monsters/Skeletons/RetchSkeletonOneHandSword", + "Metadata/Monsters/Skeletons/RetchSkeletonOneHandSwordShield", + "Metadata/Monsters/SnakeFlowerMan/BloomSerpentEmerge1", + "Metadata/Monsters/SwollenMiller/Objects/BossRoomMinimapIcon", + "Metadata/Monsters/SwollenMiller/SwollenMillerBoss", "Metadata/Monsters/Urchins/MeleeUrchin1", "Metadata/Monsters/Urchins/SlingUrchin1", "Metadata/Monsters/Werewolves/WerewolfPack1", @@ -84,6 +144,14 @@ "Metadata/Monsters/Zombies/Lumberjack/LumberingDrownedDryOneHandAxePhysics__", "Metadata/Monsters/Zombies/Lumberjack/LumberingDrownedDryUnarmed", "Metadata/Monsters/Zombies/Lumberjack/LumberingDrownedDryUnarmedPhysics", + "Metadata/Monsters/Zombies/Lumberjack/LumberingDrownedOneHandAxe", + "Metadata/Monsters/Zombies/Lumberjack/LumberingDrownedOneHandAxeHighAggroRangeMiller", + "Metadata/Monsters/Zombies/Lumberjack/LumberingDrownedOneHandAxePhysics", + "Metadata/Monsters/Zombies/Lumberjack/LumberingDrownedOneHandAxePhysicsHighAggroRangeMiller", + "Metadata/Monsters/Zombies/Lumberjack/LumberingDrownedUnarmed", + "Metadata/Monsters/Zombies/Lumberjack/LumberingDrownedUnarmedHighAggroRangeMiller", + "Metadata/Monsters/Zombies/Lumberjack/LumberingDrownedUnarmedPhysics", + "Metadata/Monsters/Zombies/Lumberjack/LumberingDrownedUnarmedPhysicsHighAggroRangeMiller", "Metadata/NPC/Four_Act1/ClearfellPosting1", "Metadata/NPC/Four_Act1/ClearfellPosting3", "Metadata/NPC/Four_Act1/DogTrader_Entrance", @@ -96,6 +164,9 @@ "Metadata/NPC/Four_Act1/HoodedMentor", "Metadata/NPC/Four_Act1/HoodedMentorAfterIronCount", "Metadata/NPC/Four_Act1/HoodedMentorInjured", + "Metadata/NPC/Four_Act1/HoodedMentorOldForest", + "Metadata/NPC/Four_Act1/HoodedMentorOldForestInTree", + "Metadata/NPC/Four_Act1/OldForestGlyph1", "Metadata/NPC/Four_Act1/Renly", "Metadata/NPC/Four_Act1/RenlyAfterIronCount", "Metadata/NPC/Four_Act1/RenlyIntro", @@ -103,6 +174,8 @@ "Metadata/NPC/Four_Act1/UnaAfterHealHoodedMentor", "Metadata/NPC/Four_Act1/UnaAfterIronCount", "Metadata/NPC/Four_Act1/UnaHoodedOneInjured", + "Metadata/NPC/Four_Act1/UnaTreeSummon", + "Metadata/NPC/Four_Act1/UnaTreeSummonKneeling", "Metadata/NPC/League/Incursion/AlvaIncursionWild", "Metadata/Pet/AzmeriStag/AzmeriStag", "Metadata/Pet/BabyBossesHumans/BabyBrutus/BabyBrutus", @@ -128,20 +201,48 @@ "Metadata/Pet/ScavengerBat/ScavengerBat", "Metadata/Pet/WayfinderWolf/WayfinderWolf", "Metadata/Projectiles/CarrionCroneIceSpear", + "Metadata/Projectiles/Fireball", "Metadata/Projectiles/HagBossIceShard", + "Metadata/Projectiles/HuhuGrubLarvaeMortar", "Metadata/Projectiles/IceSpear", + "Metadata/Projectiles/MudBurrowerAcidMortarSmall", + "Metadata/Projectiles/MudBurrowerBloodProj", + "Metadata/Projectiles/MudBurrowerGoopMortar", + "Metadata/Projectiles/MudBurrowerGoopProjectile", + "Metadata/Projectiles/QuillCrabShrapnel", + "Metadata/Projectiles/QuillCrabSpike", "Metadata/Projectiles/SlingUrchinProjectile", "Metadata/Projectiles/Spark", "Metadata/Projectiles/Twister", + "Metadata/QuestObjects/Four_Act1/TreeOfSoulsRoots", "Metadata/Terrain/Doodads/Gallows/ClearfellBull1", "Metadata/Terrain/Doodads/Gallows/ClearfellBull1_CountKilled", "Metadata/Terrain/Doodads/Gallows/ClearfellBull2", "Metadata/Terrain/Doodads/Gallows/ClearfellBull2_CountKilled", + "Metadata/Terrain/Gallows/Act1/1_1/Objects/EncampmentSpikeLadder", + "Metadata/Terrain/Gallows/Act1/1_1/Objects/TutorialBlocker1", + "Metadata/Terrain/Gallows/Act1/1_1/Objects/TutorialBlocker2", + "Metadata/Terrain/Gallows/Act1/1_1/Objects/TutorialBlocker3", + "Metadata/Terrain/Gallows/Act1/1_1/Objects/TutorialBlocker4", + "Metadata/Terrain/Gallows/Act1/1_1/Objects/TutorialNPCZombie", "Metadata/Terrain/Gallows/Act1/1_2/Objects/CampsiteChest", "Metadata/Terrain/Gallows/Act1/1_2/Objects/CampsiteController", "Metadata/Terrain/Gallows/Act1/1_2/Objects/RuleSet", + "Metadata/Terrain/Gallows/Act1/1_3/Objects/MudGolemSpawners/MudGolemSpawnerBase01_02", + "Metadata/Terrain/Gallows/Act1/1_3/Objects/MudGolemSpawners/MudGolemSpawnerBase01_06", + "Metadata/Terrain/Gallows/Act1/1_3/Objects/MudGolemSpawners/MudGolemSpawnerBase02_011", + "Metadata/Terrain/Gallows/Act1/1_3/Objects/MudGolemSpawners/MudGolemSpawnerBase02_03", + "Metadata/Terrain/Gallows/Act1/1_3/Objects/SecretRoomMinimapIcon", + "Metadata/Terrain/Gallows/Act1/1_4/Objects/AreaTransition_4a", + "Metadata/Terrain/Gallows/Act1/1_4/Objects/BossRootDoor", "Metadata/Terrain/Gallows/Act1/1_4/Objects/HagCauldron", + "Metadata/Terrain/Gallows/Act1/1_4/Objects/HoodedMentorController", "Metadata/Terrain/Gallows/Act1/1_4/Objects/SecretRoomMinimapIcon", + "Metadata/Terrain/Gallows/Act1/1_4/Objects/SummonAlly", + "Metadata/Terrain/Gallows/Act1/1_4/Objects/TreeOfSoulsBodies", + "Metadata/Terrain/Gallows/Act1/1_4/Objects/TreeOfSoulsNailStake1", + "Metadata/Terrain/Gallows/Act1/1_4/Objects/TreeOfSoulsNailStake2", + "Metadata/Terrain/Gallows/Act1/1_4/Objects/TreeOfSoulsNailStake3", "Metadata/Terrain/Gallows/Act1/1_4/Objects/WitchHutTitle", "Metadata/Terrain/Gallows/Act1/1_town_ExileEncampment/Objects/Act1_finished_LightController", "Metadata/Terrain/Gallows/Act1/1_town_ExileEncampment/Objects/CraftingBenchEzomyte", @@ -149,12 +250,14 @@ "Metadata/Terrain/Gallows/Act1/1_town_ExileEncampment/Objects/CraftingBench_EnableRendering", "Metadata/Terrain/Gallows/Act1/1_town_ExileEncampment/Objects/VisitedAct2_DisableRendering", "Metadata/Terrain/Gallows/Act1/1_town_ExileEncampment/Objects/VisitedAct2_EnableRendering", + "Metadata/Terrain/Tools/AudioTools/G1_1/TownEntrance", "Metadata/Terrain/Tools/AudioTools/G1_2/BurrowEntrance", "Metadata/Terrain/Tools/AudioTools/G1_2/ForestEntrance", "Metadata/Terrain/Tools/AudioTools/G1_2/HagArena", "Metadata/Terrain/Tools/AudioTools/G1_2/RiverRapidsMedium", "Metadata/Terrain/Tools/AudioTools/G1_3/TunnelA", "Metadata/Terrain/Tools/AudioTools/G1_4/WitchHutIndoorAudio", + "Metadata/Terrain/Tools/AudioTools/G1_5/OldForestEntrance", "Metadata/Terrain/Tools/AudioTools/G1_Town/FurnaceFireAudio", "Metadata/Terrain/Tools/AudioTools/G1_Town/InsideWaterMillAudio" ] \ No newline at end of file diff --git a/offsets.json b/offsets.json index 2c1be35..97d1e2b 100644 --- a/offsets.json +++ b/offsets.json @@ -50,10 +50,17 @@ "QuestCompanionObjPtrOffset": "0x10", "QuestTrackedMarker": "0x43020000", "QuestObjEncounterStateOffset": 8, - "QuestDatRowSize": "0x77", + "QuestDatRowSize": "0x68", "QuestDatNameOffset": 0, + "QuestDatOrderOffset": "0x10", + "QuestDatTextOffset": "0x34", + "QuestDatMessageOffset": "0x3D", "QuestDatInternalIdOffset": "0x6B", "QuestDatActOffset": "0x73", + "QuestStateObjectOffset": "0x900", + "QuestStateVectorOffset": "0x240", + "QuestStateEntrySize": 12, + "QuestStateMaxEntries": "0x100", "ComponentListOffset": "0x10", "EntityHeaderOffset": 8, "ComponentLookupOffset": "0x28", @@ -94,5 +101,15 @@ "UiElementVisibleBit": 11, "UiElementSizeOffset": "0x288", "UiElementTextOffset": "0x448", - "UiElementScanRange": "0x1000" + "UiElementScanRange": "0x1000", + "QuestLinkedListOffset": "0x358", + "QuestLinkedListNodeSize": "0x28", + "QuestNodeQuestPtrOffset": "0x10", + "QuestNodeStateIdOffset": "0x20", + "QuestObjNamePtrOffset": 0, + "QuestLinkedListMaxNodes": "0x100", + "TrackedQuestPanelChildIndex": 6, + "TrackedQuestPanelSubChildIndex": 1, + "TrackedQuestLinkedListOffset": "0x318", + "QuestStateObjTextOffset": "0x34" } diff --git a/profiles/_assignments.json b/profiles/_assignments.json index 62b7268..c1f9fba 100644 --- a/profiles/_assignments.json +++ b/profiles/_assignments.json @@ -1,3 +1,5 @@ { - "GooGoGaaGa": "GooGoGaaGa_Default_Copy" + "GooGoGaaGa": "GooGoGaaGa_Default_Copy", + "terdsare": "terdsare_Default", + "dudemoko": "dudemoko_Default" } \ No newline at end of file diff --git a/profiles/dudemoko_Default.json b/profiles/dudemoko_Default.json new file mode 100644 index 0000000..d338f17 --- /dev/null +++ b/profiles/dudemoko_Default.json @@ -0,0 +1,166 @@ +{ + "Name": "dudemoko_Default", + "CreatedAt": "2026-03-05T05:44:54.8014147Z", + "LastModified": "2026-03-05T05:44:54.8014154Z", + "Flasks": { + "LifeFlaskThreshold": 50, + "ManaFlaskThreshold": 50, + "FlaskCooldownMs": 4000, + "LifeFlaskScanCode": 2, + "ManaFlaskScanCode": 3 + }, + "Combat": { + "GlobalCooldownMs": 500, + "AttackRange": 600, + "SafeRange": 400, + "KiteEnabled": false, + "KiteRange": 300, + "KiteDelayMs": 200 + }, + "Skills": [ + { + "SlotIndex": 0, + "Label": "LMB", + "SkillName": null, + "InputType": "LeftClick", + "ScanCode": 0, + "Priority": 0, + "IsEnabled": true, + "CooldownMs": 300, + "RangeMin": 0, + "RangeMax": 600, + "TargetSelection": "Nearest", + "RequiresTarget": true, + "IsAura": false, + "IsMovementSkill": false, + "MinMonstersInRange": 1, + "MaintainPressed": false + }, + { + "SlotIndex": 1, + "Label": "RMB", + "SkillName": null, + "InputType": "RightClick", + "ScanCode": 0, + "Priority": 1, + "IsEnabled": true, + "CooldownMs": 300, + "RangeMin": 0, + "RangeMax": 600, + "TargetSelection": "Nearest", + "RequiresTarget": true, + "IsAura": false, + "IsMovementSkill": false, + "MinMonstersInRange": 1, + "MaintainPressed": false + }, + { + "SlotIndex": 2, + "Label": "MMB", + "SkillName": null, + "InputType": "MiddleClick", + "ScanCode": 0, + "Priority": 2, + "IsEnabled": true, + "CooldownMs": 300, + "RangeMin": 0, + "RangeMax": 600, + "TargetSelection": "Nearest", + "RequiresTarget": true, + "IsAura": false, + "IsMovementSkill": false, + "MinMonstersInRange": 1, + "MaintainPressed": false + }, + { + "SlotIndex": 3, + "Label": "Q", + "SkillName": null, + "InputType": "KeyPress", + "ScanCode": 16, + "Priority": 3, + "IsEnabled": true, + "CooldownMs": 300, + "RangeMin": 0, + "RangeMax": 600, + "TargetSelection": "Nearest", + "RequiresTarget": true, + "IsAura": false, + "IsMovementSkill": false, + "MinMonstersInRange": 1, + "MaintainPressed": false + }, + { + "SlotIndex": 4, + "Label": "E", + "SkillName": null, + "InputType": "KeyPress", + "ScanCode": 18, + "Priority": 4, + "IsEnabled": true, + "CooldownMs": 300, + "RangeMin": 0, + "RangeMax": 600, + "TargetSelection": "Nearest", + "RequiresTarget": true, + "IsAura": false, + "IsMovementSkill": false, + "MinMonstersInRange": 1, + "MaintainPressed": false + }, + { + "SlotIndex": 5, + "Label": "R", + "SkillName": null, + "InputType": "KeyPress", + "ScanCode": 19, + "Priority": 5, + "IsEnabled": true, + "CooldownMs": 300, + "RangeMin": 0, + "RangeMax": 600, + "TargetSelection": "Nearest", + "RequiresTarget": true, + "IsAura": false, + "IsMovementSkill": false, + "MinMonstersInRange": 1, + "MaintainPressed": false + }, + { + "SlotIndex": 6, + "Label": "T", + "SkillName": null, + "InputType": "KeyPress", + "ScanCode": 20, + "Priority": 6, + "IsEnabled": true, + "CooldownMs": 300, + "RangeMin": 0, + "RangeMax": 600, + "TargetSelection": "Nearest", + "RequiresTarget": true, + "IsAura": false, + "IsMovementSkill": false, + "MinMonstersInRange": 1, + "MaintainPressed": false + }, + { + "SlotIndex": 7, + "Label": "F", + "SkillName": null, + "InputType": "KeyPress", + "ScanCode": 33, + "Priority": 7, + "IsEnabled": true, + "CooldownMs": 300, + "RangeMin": 0, + "RangeMax": 600, + "TargetSelection": "Nearest", + "RequiresTarget": true, + "IsAura": false, + "IsMovementSkill": false, + "MinMonstersInRange": 1, + "MaintainPressed": false + } + ] +} \ No newline at end of file diff --git a/profiles/terdsare_Default.json b/profiles/terdsare_Default.json new file mode 100644 index 0000000..a759145 --- /dev/null +++ b/profiles/terdsare_Default.json @@ -0,0 +1,166 @@ +{ + "Name": "terdsare_Default", + "CreatedAt": "2026-03-05T04:11:41.4102481Z", + "LastModified": "2026-03-05T04:11:41.4102483Z", + "Flasks": { + "LifeFlaskThreshold": 50, + "ManaFlaskThreshold": 50, + "FlaskCooldownMs": 4000, + "LifeFlaskScanCode": 2, + "ManaFlaskScanCode": 3 + }, + "Combat": { + "GlobalCooldownMs": 500, + "AttackRange": 600, + "SafeRange": 400, + "KiteEnabled": false, + "KiteRange": 300, + "KiteDelayMs": 200 + }, + "Skills": [ + { + "SlotIndex": 0, + "Label": "LMB", + "SkillName": null, + "InputType": "LeftClick", + "ScanCode": 0, + "Priority": 0, + "IsEnabled": true, + "CooldownMs": 300, + "RangeMin": 0, + "RangeMax": 600, + "TargetSelection": "Nearest", + "RequiresTarget": true, + "IsAura": false, + "IsMovementSkill": false, + "MinMonstersInRange": 1, + "MaintainPressed": false + }, + { + "SlotIndex": 1, + "Label": "RMB", + "SkillName": null, + "InputType": "RightClick", + "ScanCode": 0, + "Priority": 1, + "IsEnabled": true, + "CooldownMs": 300, + "RangeMin": 0, + "RangeMax": 600, + "TargetSelection": "Nearest", + "RequiresTarget": true, + "IsAura": false, + "IsMovementSkill": false, + "MinMonstersInRange": 1, + "MaintainPressed": false + }, + { + "SlotIndex": 2, + "Label": "MMB", + "SkillName": null, + "InputType": "MiddleClick", + "ScanCode": 0, + "Priority": 2, + "IsEnabled": true, + "CooldownMs": 300, + "RangeMin": 0, + "RangeMax": 600, + "TargetSelection": "Nearest", + "RequiresTarget": true, + "IsAura": false, + "IsMovementSkill": false, + "MinMonstersInRange": 1, + "MaintainPressed": false + }, + { + "SlotIndex": 3, + "Label": "Q", + "SkillName": null, + "InputType": "KeyPress", + "ScanCode": 16, + "Priority": 3, + "IsEnabled": true, + "CooldownMs": 300, + "RangeMin": 0, + "RangeMax": 600, + "TargetSelection": "Nearest", + "RequiresTarget": true, + "IsAura": false, + "IsMovementSkill": false, + "MinMonstersInRange": 1, + "MaintainPressed": false + }, + { + "SlotIndex": 4, + "Label": "E", + "SkillName": null, + "InputType": "KeyPress", + "ScanCode": 18, + "Priority": 4, + "IsEnabled": true, + "CooldownMs": 300, + "RangeMin": 0, + "RangeMax": 600, + "TargetSelection": "Nearest", + "RequiresTarget": true, + "IsAura": false, + "IsMovementSkill": false, + "MinMonstersInRange": 1, + "MaintainPressed": false + }, + { + "SlotIndex": 5, + "Label": "R", + "SkillName": null, + "InputType": "KeyPress", + "ScanCode": 19, + "Priority": 5, + "IsEnabled": true, + "CooldownMs": 300, + "RangeMin": 0, + "RangeMax": 600, + "TargetSelection": "Nearest", + "RequiresTarget": true, + "IsAura": false, + "IsMovementSkill": false, + "MinMonstersInRange": 1, + "MaintainPressed": false + }, + { + "SlotIndex": 6, + "Label": "T", + "SkillName": null, + "InputType": "KeyPress", + "ScanCode": 20, + "Priority": 6, + "IsEnabled": true, + "CooldownMs": 300, + "RangeMin": 0, + "RangeMax": 600, + "TargetSelection": "Nearest", + "RequiresTarget": true, + "IsAura": false, + "IsMovementSkill": false, + "MinMonstersInRange": 1, + "MaintainPressed": false + }, + { + "SlotIndex": 7, + "Label": "F", + "SkillName": null, + "InputType": "KeyPress", + "ScanCode": 33, + "Priority": 7, + "IsEnabled": true, + "CooldownMs": 300, + "RangeMin": 0, + "RangeMax": 600, + "TargetSelection": "Nearest", + "RequiresTarget": true, + "IsAura": false, + "IsMovementSkill": false, + "MinMonstersInRange": 1, + "MaintainPressed": false + } + ] +} \ No newline at end of file diff --git a/src/Automata.Ui/ViewModels/MemoryViewModel.cs b/src/Automata.Ui/ViewModels/MemoryViewModel.cs index 417ea0b..dbfba36 100644 --- a/src/Automata.Ui/ViewModels/MemoryViewModel.cs +++ b/src/Automata.Ui/ViewModels/MemoryViewModel.cs @@ -16,7 +16,10 @@ public partial class MemoryNodeViewModel : ObservableObject [ObservableProperty] private string _name; [ObservableProperty] private string _value = ""; [ObservableProperty] private string _valueColor = "#484f58"; - [ObservableProperty] private bool _isExpanded = true; + [ObservableProperty] private bool _isExpanded; + + /// Optional back-reference to a UIElementNode for lazy child population. + public UIElementNode? UiElement { get; set; } public ObservableCollection Children { get; } = []; @@ -93,8 +96,6 @@ public partial class MemoryViewModel : ObservableObject private MemoryNodeViewModel? _currentStateNode; private MemoryNodeViewModel? _isLoadingNode; private MemoryNodeViewModel? _escapeStateNode; - private MemoryNodeViewModel? _activeStatesNode; - private MemoryNodeViewModel? _statesNode; private MemoryNodeViewModel? _terrainCells; private MemoryNodeViewModel? _terrainGrid; private MemoryNodeViewModel? _terrainWalkable; @@ -102,7 +103,6 @@ public partial class MemoryViewModel : ObservableObject private MemoryNodeViewModel? _entityTypesNode; private MemoryNodeViewModel? _entityListNode; private MemoryNodeViewModel? _skillsNode; - private MemoryNodeViewModel? _questsNode; private MemoryNodeViewModel? _areaRawName; private MemoryNodeViewModel? _areaDisplayName; private MemoryNodeViewModel? _areaAct; @@ -110,6 +110,8 @@ public partial class MemoryViewModel : ObservableObject private MemoryNodeViewModel? _areaHasWaypoint; private MemoryNodeViewModel? _areaMonsterLevel; private MemoryNodeViewModel? _worldAreaId; + private MemoryNodeViewModel? _uiElementsNode; + private MemoryNodeViewModel? _questLinkedListNode; partial void OnIsEnabledChanged(bool value) { @@ -178,8 +180,6 @@ public partial class MemoryViewModel : ObservableObject _currentStateNode = new MemoryNodeViewModel("Current State:"); _isLoadingNode = new MemoryNodeViewModel("Loading:"); _escapeStateNode = new MemoryNodeViewModel("Escape:"); - _activeStatesNode = new MemoryNodeViewModel("Controller") { IsExpanded = true }; - _statesNode = new MemoryNodeViewModel("State Slots") { IsExpanded = true }; gameState.Children.Add(_gsPattern); gameState.Children.Add(_gsBase); gameState.Children.Add(_gsController); @@ -188,8 +188,6 @@ public partial class MemoryViewModel : ObservableObject gameState.Children.Add(_currentStateNode); gameState.Children.Add(_isLoadingNode); gameState.Children.Add(_escapeStateNode); - gameState.Children.Add(_activeStatesNode); - gameState.Children.Add(_statesNode); // InGameState children var inGameStateGroup = new MemoryNodeViewModel("InGameState"); @@ -215,14 +213,12 @@ public partial class MemoryViewModel : ObservableObject _playerLife = new MemoryNodeViewModel("Life:") { Value = "?", ValueColor = "#484f58" }; _playerMana = new MemoryNodeViewModel("Mana:") { Value = "?", ValueColor = "#484f58" }; _playerEs = new MemoryNodeViewModel("ES:") { Value = "?", ValueColor = "#484f58" }; - _skillsNode = new MemoryNodeViewModel("Skills") { IsExpanded = false }; - _questsNode = new MemoryNodeViewModel("Quests") { IsExpanded = false }; + _skillsNode = new MemoryNodeViewModel("Skills"); player.Children.Add(_playerPos); player.Children.Add(_playerLife); player.Children.Add(_playerMana); player.Children.Add(_playerEs); player.Children.Add(_skillsNode); - player.Children.Add(_questsNode); // Entities var entitiesGroup = new MemoryNodeViewModel("Entities"); @@ -259,11 +255,19 @@ public partial class MemoryViewModel : ObservableObject areaTemplateGroup.Children.Add(_areaMonsterLevel); areaTemplateGroup.Children.Add(_worldAreaId); + // Quest Linked Lists (all quests + tracked merged from GameUi) + _questLinkedListNode = new MemoryNodeViewModel("Quests"); + + // UIElements tree + _uiElementsNode = new MemoryNodeViewModel("UIElements") { IsExpanded = false }; + inGameStateGroup.Children.Add(areaInstanceGroup); inGameStateGroup.Children.Add(areaTemplateGroup); inGameStateGroup.Children.Add(player); inGameStateGroup.Children.Add(entitiesGroup); inGameStateGroup.Children.Add(terrain); + inGameStateGroup.Children.Add(_questLinkedListNode); + inGameStateGroup.Children.Add(_uiElementsNode); RootNodes.Add(process); RootNodes.Add(gameState); @@ -334,103 +338,6 @@ public partial class MemoryViewModel : ObservableObject _isLoadingNode!.Set(snap.IsLoading ? "Loading..." : "Ready", !snap.IsLoading); _escapeStateNode!.Set(snap.IsEscapeOpen ? "Open" : "Closed", !snap.IsEscapeOpen); - // Controller dump — show qwords before state slots to find active state offset - if (_activeStatesNode is not null) - { - var pre = snap.ControllerPreSlots; - _activeStatesNode.Value = pre.Length > 0 - ? $"controller+0x00..0x{pre.Length * 8:X} ({pre.Length} qwords)" - : "no data"; - _activeStatesNode.ValueColor = "#8b949e"; - - while (_activeStatesNode.Children.Count > pre.Length) - _activeStatesNode.Children.RemoveAt(_activeStatesNode.Children.Count - 1); - - for (var i = 0; i < pre.Length; i++) - { - var (off, val, match, changed, derefInfo) = pre[i]; - var label = $"+0x{off:X2}:"; - var changeTag = changed ? " [CHANGED]" : ""; - var derefTag = derefInfo != null ? $" ({derefInfo})" : ""; - var display = val == 0 - ? "0" - : match != null - ? $"0x{val:X} ← {match}{changeTag}" - : $"0x{val:X}{derefTag}{changeTag}"; - var color = changed ? "#f85149" - : match != null ? "#3fb950" - : derefInfo != null && derefInfo.Contains('→') && derefInfo.Contains("State") ? "#d29922" // yellow for indirect state match - : val == 0 ? "#484f58" - : "#8b949e"; - - if (i < _activeStatesNode.Children.Count) - { - _activeStatesNode.Children[i].Name = label; - _activeStatesNode.Children[i].Value = display; - _activeStatesNode.Children[i].ValueColor = color; - } - else - { - var node = new MemoryNodeViewModel(label) { Value = display, ValueColor = color }; - _activeStatesNode.Children.Add(node); - } - } - } - - // State Slots — show pointer + int32 at +0x08 for each state slot - if (_statesNode is not null && snap.StateSlots.Length > 0) - { - var slots = snap.StateSlots; - var needed = slots.Length; - - while (_statesNode.Children.Count > needed) - _statesNode.Children.RemoveAt(_statesNode.Children.Count - 1); - - for (var i = 0; i < needed; i++) - { - var ptr = slots[i]; - var stateName = i < GameMemoryReader.StateNames.Length ? GameMemoryReader.StateNames[i] : $"State{i}"; - var label = $"[{i}] {stateName}:"; - string val; - string color; - - if (ptr == 0) - { - val = "null"; - color = "#484f58"; - } - else - { - var int32Val = snap.StateSlotValues?.Length > i ? snap.StateSlotValues[i] : 0; - var isActive = snap.ActiveStates.Contains(ptr); - var activeTag = isActive ? " [ACTIVE]" : ""; - val = $"0x{ptr:X} [+0x08]={int32Val}{activeTag}"; - - // Green if current state, cyan if active, default gray - if (i < (int)GameStateType.GameNotLoaded && (GameStateType)i == snap.CurrentGameState) - color = "#3fb950"; // green — current state - else if (isActive) - color = "#58a6ff"; // blue — active but not current - else - color = "#8b949e"; // gray — inactive - } - - if (i < _statesNode.Children.Count) - { - _statesNode.Children[i].Name = label; - _statesNode.Children[i].Set(val, true); - _statesNode.Children[i].ValueColor = color; - } - else - { - var node = new MemoryNodeViewModel(label); - node.Set(val, true); - node.ValueColor = color; - _statesNode.Children.Add(node); - } - } - } - // Status text — show resolved current state if (snap.Attached) { @@ -593,48 +500,106 @@ public partial class MemoryViewModel : ObservableObject } } - // Quest states with rich info from companion vector - if (_questsNode is not null) + // Quest Linked Lists (all quests + tracked merged from GameUi) + if (_questLinkedListNode is not null) { - if (snap.QuestFlags is { Count: > 0 }) + if (snap.QuestLinkedList is { Count: > 0 }) { - var named = snap.QuestFlags.Count(q => q.QuestName is not null); - _questsNode.Value = $"{snap.QuestFlags.Count} quest states ({named} named)"; - _questsNode.ValueColor = "#3fb950"; + var active = snap.QuestLinkedList.Count(q => q.StateId > 0); + var tracked = snap.QuestLinkedList.Count(q => q.IsTracked); + _questLinkedListNode.Value = $"{snap.QuestLinkedList.Count} total, {active} active, {tracked} tracked"; + _questLinkedListNode.ValueColor = "#3fb950"; - while (_questsNode.Children.Count > snap.QuestFlags.Count) - _questsNode.Children.RemoveAt(_questsNode.Children.Count - 1); - - for (var i = 0; i < snap.QuestFlags.Count; i++) + // Only update children if expanded + if (_questLinkedListNode.IsExpanded) { - var q = snap.QuestFlags[i]; - var trackedPrefix = q.IsTracked ? "[T] " : ""; - var stateLabel = q.StateId switch { 1 => "locked", 2 => "started", _ => $"s{q.StateId}" }; - var label = $"{trackedPrefix}{q.QuestName ?? (q.QuestStateIndex > 0 ? $"#{q.QuestStateIndex}" : $"[{i}]")}"; - var value = q.InternalId is not null - ? $"idx={q.QuestStateIndex} {stateLabel} id={q.InternalId}" - : $"idx={q.QuestStateIndex} {stateLabel}"; + var sorted = snap.QuestLinkedList + .Where(q => q.StateId > 0) + .OrderByDescending(q => q.IsTracked) + .ThenByDescending(q => q.StateId > 0) + .ThenBy(q => q.Act) + .ThenBy(q => q.DisplayName) + .ToList(); - var color = q.IsTracked ? "#58a6ff" : q.StateId == 2 ? "#8b949e" : "#484f58"; + while (_questLinkedListNode.Children.Count > sorted.Count) + _questLinkedListNode.Children.RemoveAt(_questLinkedListNode.Children.Count - 1); - if (i < _questsNode.Children.Count) + for (var i = 0; i < sorted.Count; i++) { - _questsNode.Children[i].Name = label; - _questsNode.Children[i].Value = value; - _questsNode.Children[i].ValueColor = color; - } - else - { - var node = new MemoryNodeViewModel(label) { Value = value, ValueColor = color }; - _questsNode.Children.Add(node); + var q = sorted[i]; + var prefix = q.IsTracked ? "[T] " : ""; + var stateLabel = $"step {q.StateId}"; + var label = $"{prefix}{q.DisplayName ?? q.InternalId ?? $"[{i}]"}"; + var value = $"Act{q.Act} {stateLabel}"; + + var color = q.IsTracked ? "#58a6ff" : "#d29922"; + + MemoryNodeViewModel node; + if (i < _questLinkedListNode.Children.Count) + { + node = _questLinkedListNode.Children[i]; + node.Name = label; + node.Value = value; + node.ValueColor = color; + } + else + { + node = new MemoryNodeViewModel(label) { Value = value, ValueColor = color }; + _questLinkedListNode.Children.Add(node); + } + + // Populate detail children when quest node is expanded + if (node.IsExpanded) + { + var details = new List<(string key, string val)> + { + ("InternalId:", q.InternalId ?? "—"), + ("DisplayName:", q.DisplayName ?? "—"), + ("Act:", q.Act.ToString()), + ("StateId:", q.StateId.ToString()), + ("IsTracked:", q.IsTracked.ToString()), + }; + if (q.ObjectiveText is not null) + details.Add(("Objective:", q.ObjectiveText)); + if (q.QuestDatPtr != 0) + details.Add(("QuestDatPtr:", $"0x{q.QuestDatPtr:X}")); + + while (node.Children.Count > details.Count) + node.Children.RemoveAt(node.Children.Count - 1); + + for (var j = 0; j < details.Count; j++) + { + var (key, val) = details[j]; + if (j < node.Children.Count) + { + node.Children[j].Name = key; + node.Children[j].Value = val; + node.Children[j].ValueColor = "#8b949e"; + } + else + node.Children.Add(new MemoryNodeViewModel(key) { Value = val, ValueColor = "#8b949e" }); + } + } + else + { + // Seed placeholder so expand arrow shows + if (node.Children.Count == 0) + node.Children.Add(new MemoryNodeViewModel("...") { ValueColor = "#484f58" }); + } } } + else + { + // Seed placeholder so expand arrow shows + if (_questLinkedListNode.Children.Count == 0) + _questLinkedListNode.Children.Add(new MemoryNodeViewModel("...") { ValueColor = "#484f58" }); + } } else { - _questsNode.Value = "—"; - _questsNode.ValueColor = "#484f58"; - _questsNode.Children.Clear(); + _questLinkedListNode.Value = "—"; + _questLinkedListNode.ValueColor = "#484f58"; + _questLinkedListNode.Children.Clear(); } } @@ -688,9 +653,101 @@ public partial class MemoryViewModel : ObservableObject _terrainWalkable!.Set("?", false); } + // UIElements tree + UpdateUiElementsTree(snap.GameUiPtr); + UpdateMinimap(snap); } + private void UpdateUiElementsTree(nint gameUiPtr) + { + if (_uiElementsNode is null) return; + + var uiElements = _reader?.UIElements; + if (gameUiPtr == 0 || uiElements is null) + { + _uiElementsNode.Value = "—"; + _uiElementsNode.ValueColor = "#484f58"; + _uiElementsNode.Children.Clear(); + return; + } + + _uiElementsNode.Value = $"GameUi 0x{gameUiPtr:X}"; + _uiElementsNode.ValueColor = "#3fb950"; + _uiElementsNode.UiElement = new UIElementNode { Address = gameUiPtr, ChildCount = 1 }; + + // Read and sync children for all expanded nodes + SyncUiNodeLazy(_uiElementsNode, uiElements); + } + + /// + /// Recursively syncs UI element tree nodes. For expanded nodes, reads children + /// from live memory and recurses. For collapsed nodes with children, ensures + /// a placeholder exists so the expand arrow is visible. + /// + private static void SyncUiNodeLazy(MemoryNodeViewModel vm, Roboto.Memory.Objects.UIElements uiElements) + { + var uiEl = vm.UiElement; + if (uiEl is null || uiEl.Address == 0) return; + + if (!vm.IsExpanded) + { + // Collapsed — just ensure placeholder for expand arrow + if (uiEl.ChildCount > 0 && vm.Children.Count == 0) + vm.Children.Add(new MemoryNodeViewModel("...") { Value = "", ValueColor = "#484f58", IsExpanded = false }); + return; + } + + // Expanded — read children from live memory + var children = uiElements.ReadChildren(uiEl.Address); + if (children is null || children.Count == 0) + { + // Read failed or no children — show error hint + vm.Children.Clear(); + if (uiEl.ChildCount > 0) + vm.Children.Add(new MemoryNodeViewModel("(read failed)") { Value = $"ChildCount={uiEl.ChildCount}", ValueColor = "#f85149", IsExpanded = false }); + return; + } + + // Remove placeholder + if (vm.Children.Count == 1 && vm.Children[0].UiElement is null) + vm.Children.Clear(); + + // Trim excess + while (vm.Children.Count > children.Count) + vm.Children.RemoveAt(vm.Children.Count - 1); + + for (var i = 0; i < children.Count; i++) + { + var child = children[i]; + var label = child.StringId ?? $"[{i}]"; + var visTag = child.IsVisible ? "" : " [hidden]"; + var childTag = child.ChildCount > 0 ? $" ({child.ChildCount} ch)" : ""; + var textTag = child.Text is not null ? $" \"{child.Text}\"" : ""; + var value = $"0x{child.Address:X}{visTag}{childTag}{textTag}"; + var color = child.IsVisible ? "#3fb950" : "#484f58"; + + MemoryNodeViewModel childVm; + if (i < vm.Children.Count) + { + childVm = vm.Children[i]; + childVm.Name = label; + childVm.Value = value; + childVm.ValueColor = color; + childVm.UiElement = child; + } + else + { + childVm = new MemoryNodeViewModel(label) + { Value = value, ValueColor = color, IsExpanded = false, UiElement = child }; + vm.Children.Add(childVm); + } + + // Recurse — will read grandchildren if this child is expanded, or add placeholder if collapsed + SyncUiNodeLazy(childVm, uiElements); + } + } + private void UpdateMinimap(GameStateSnapshot snap) { // Skip rendering entirely during loading — terrain data is stale/invalid @@ -1390,6 +1447,90 @@ public partial class MemoryViewModel : ObservableObject ScanResult = _reader.Diagnostics!.ScanQuestStateObjects(); } + [RelayCommand] + private void ScanQuestOffsetsExecute() + { + if (_reader is null || !_reader.IsAttached) + { + ScanResult = "Error: not attached"; + return; + } + + ScanResult = _reader.Diagnostics!.ScanQuestStateOffsets(); + } + + [RelayCommand] + private void ScanActiveQuestsExecute() + { + if (_reader is null || !_reader.IsAttached) + { + ScanResult = "Error: not attached"; + return; + } + + ScanResult = _reader.Diagnostics!.ScanActiveQuests(); + } + + [RelayCommand] + private void ScanQuestContainersExecute() + { + if (_reader is null || !_reader.IsAttached) + { + ScanResult = "Error: not attached"; + return; + } + + ScanResult = _reader.Diagnostics!.ScanQuestStateContainers(); + } + + [RelayCommand] + private void ScanWorldDataVectorsExecute() + { + if (_reader is null || !_reader.IsAttached) + { + ScanResult = "Error: not attached"; + return; + } + + ScanResult = _reader.Diagnostics!.ScanWorldDataVectors(); + } + + [RelayCommand] + private void ProbeCompanionQuestObjectsExecute() + { + if (_reader is null || !_reader.IsAttached) + { + ScanResult = "Error: not attached"; + return; + } + + ScanResult = _reader.Diagnostics!.ProbeCompanionQuestObjects(); + } + + [RelayCommand] + private void ScanQuestLinkedListExecute() + { + if (_reader is null || !_reader.IsAttached) + { + ScanResult = "Error: not attached"; + return; + } + + ScanResult = _reader.Diagnostics!.ScanQuestLinkedList(); + } + + [RelayCommand] + private void ReadQuestLinkedListsExecute() + { + if (_reader is null || !_reader.IsAttached) + { + ScanResult = "Error: not attached"; + return; + } + + ScanResult = _reader.Diagnostics!.ReadQuestLinkedLists(); + } + [RelayCommand] private void ScanAreaTemplateExecute() { @@ -1401,4 +1542,18 @@ public partial class MemoryViewModel : ObservableObject ScanResult = _reader.Diagnostics!.ScanAreaTemplate(); } + + [RelayCommand] + private void ProbeQuestAddressesExecute() + { + if (_reader is null || !_reader.IsAttached) + { + ScanResult = "Error: not attached"; + return; + } + + var addresses = "3E129519BB0"; + + ScanResult = _reader.Diagnostics!.ProbeQuestAddresses(addresses, 4); + } } diff --git a/src/Automata.Ui/ViewModels/RobotoViewModel.cs b/src/Automata.Ui/ViewModels/RobotoViewModel.cs index 20e083d..61e7215 100644 --- a/src/Automata.Ui/ViewModels/RobotoViewModel.cs +++ b/src/Automata.Ui/ViewModels/RobotoViewModel.cs @@ -150,7 +150,19 @@ public partial class RobotoViewModel : ObservableObject, IDisposable var config = new BotConfig(); var reader = new GameMemoryReader(); var humanizer = new Humanizer(config); - var input = new InterceptionInputController(humanizer); + // Try Interception driver first, fall back to SendInput + var interception = new InterceptionInputController(humanizer); + IInputController input; + if (interception.Initialize()) + { + input = interception; + } + else + { + var sendInput = new SendInputController(humanizer); + sendInput.Initialize(); + input = sendInput; + } _engine = new BotEngine(config, reader, input, humanizer); diff --git a/src/Automata.Ui/Views/MainWindow.axaml b/src/Automata.Ui/Views/MainWindow.axaml index 8239e31..382e11b 100644 --- a/src/Automata.Ui/Views/MainWindow.axaml +++ b/src/Automata.Ui/Views/MainWindow.axaml @@ -784,8 +784,24 @@ Padding="10,4" FontWeight="Bold" Margin="0,0,6,4" />