[wotlk] 3.4.1.49822 menu

User Tag List

Page 2 of 2 FirstFirst 12
Results 16 to 26 of 26
  1. #16
    _chase's Avatar Established Member
    Reputation
    97
    Join Date
    Dec 2019
    Posts
    58
    Thanks G/R
    17/51
    Trade Feedback
    0 (0%)
    Mentioned
    5 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Rage76152 View Post
    This would be cool to see; but bad for the game. I would like to see the c++ base though.
    I see you're external now so this might not be helpful aside from when you're statically reversing but these spell book interactions are still valid from the original classic client.
    Scorch/CGSpellBook.cpp at master . ohchase/Scorch . GitHub
    Last edited by _chase; 06-09-2023 at 06:50 AM.

    [wotlk] 3.4.1.49822
  2. Thanks Razzue, Rage76152, klumpen, maikel233 (4 members gave Thanks to _chase for this useful post)
  3. #17
    Razzue's Avatar Elite User Avid Ailurophile

    CoreCoins Purchaser Authenticator enabled
    Reputation
    398
    Join Date
    Jun 2017
    Posts
    608
    Thanks G/R
    193/283
    Trade Feedback
    2 (100%)
    Mentioned
    14 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Rage76152 View Post
    I'm not sure why corpses are showing up as units instead of loot?
    Because corpses are units, just with a "lootable" flag on them (if applicable)
    Originally Posted by Rage76152 View Post
    My current understanding is that spellbook_base + 0x8 is a pointer to an array.
    Technically correct, but Game + SpellbookOffset = what some people call a "TSArray" (pseudo below)
    Originally Posted by Rage76152 View Post
    What I don't know is the name of the spell, how to determine if a player can cast the spell
    Here I read static .db2 files for most constant data, then use what data i can from in game for conditions :shrugs: (also referenced below)

    Code:
    [StructLayout(LayoutKind.Explicit)]public struct TArray
    {
        /// <summary>
        /// Current count of the array.
        /// ie XX/00
        /// </summary>
        [FieldOffset(0x0)] 
        public int Count;
    
    
        /// <summary>
        /// A pointer to an array of data.
        /// </summary>
        [FieldOffset(0x8)]
        public IntPtr Pointer;
    
    
        /// <summary>
        /// Current maximum size of the array.
        /// ie 00/XX
        /// </summary>
        [FieldOffset(0x10)]
        public int Size;
    }
    
    
    [StructLayout(LayoutKind.Explicit)]
    
    
    public struct TSpellbook
    {
        [FieldOffset(0x0)]
        public TArray PlayerSpells;
        public unsafe string SpellName(int id)
        {
            var result = string.Empty;
            try
            {
                fixed (TArray* data = &PlayerSpells)
                {
                    var pointers = Memory.Read<IntPtr>(data->Pointer, data->Size);
                    if (pointers is null or { Length: <= 0 })
                        throw new Exception("Could not get array data.");
    
    
                    for (int i = 0; i < pointers.Length; i++)
                    {
                        var spellId = Memory.Read<int>(pointers[i] + 0x4);
                        if (id != spellId) continue;
    
    
                        result = (string)DB2.GetValue(
                            Definitions.SpellName, // DB2 Template
                            spellId, // Collection key
                            "Name_lang"); // Property name
                        break;
                    }
                }
            }
            catch (Exception e) { Console.WriteLine(e); }
            return result;
        }
    
    
        [FieldOffset(0x40)] // 48?
        public TArray SpellTabs;
    
    
        //[FieldOffset(0x70)] 
        //public TArray Tracking;  <- ??
    
    
        [FieldOffset(0xD0)]
        public TArray MountSpells;
        public unsafe string MountName(int id)
        {
            var result = string.Empty;
            try
            {
                fixed (TArray* data = &MountSpells)
                {
                    var mounts = Memory.Read<int>(data->Pointer, data->Size);
                    if (mounts is null or { Length: <= 0 })
                        throw new Exception("Could not get array data.");
    
    
                    for (var i = 0; i < mounts.Length; i++)
                    {
                        if (id != mounts[i]) continue;
                        result = (string)DB2.GetValue(
                            Definitions.SpellName, // DB2 Template
                            mounts[i], // Collection key
                            "Name_lang"); // Property name
                        break;
                    }
                }
            }
            catch (Exception e) { Console.WriteLine(e); }
            return result;
        }
    
    
        [FieldOffset(0x110)]
        public TArray Stances;
    }
    For using keys, I like iterate the key bindings, then just parse the string to expected key codes. A simple PostMessage later and now we're sending spells! :P
    You can also walk the action bars, and get spell/item/macro id per slot (cant remember if it shows binding, it's been a hot minute).

    Struct i use for key binding data:
    Code:
    [StructLayout(LayoutKind.Explicit, Pack = 1)]public struct KeyDataEntry
    {
        [FieldOffset(0x18)]
        public IntPtr NextEntry;
    
    
        [FieldOffset(0x30)]
        public IntPtr KeyPointer;
    
    
        [FieldOffset(0x40)]
        public IntPtr ActPointer;
    
    
        public string Key()
        {
            var result = string.Empty;
            try
            {
                unsafe
                {
                    if (KeyPointer.ToInt64() == 0) return result;
                    fixed (byte* bytes = Memory.Read<byte>(KeyPointer, 100))
                    {
                        result = Encoding.UTF8.GetString(bytes, GetLength(bytes));
                    }
                }
    
    
            }
            catch (Exception e) { Console.WriteLine(e); }
            return result;
        }
        public string Action()
        {
            var result = string.Empty;
            try
            {
                unsafe
                {
                    if (ActPointer.ToInt64() == 0) return result;
                    fixed (byte* bytes = Memory.Read<byte>(ActPointer, 100))
                    {
                        result = Encoding.UTF8.GetString(bytes, GetLength(bytes));
                    }
                }
    
    
            }
            catch (Exception e) { Console.WriteLine(e); }
            return result;
        }
        private unsafe int GetLength(byte* bytes)
        {
            var result = -1;
            if ((IntPtr)bytes == IntPtr.Zero) return 0;
            try
            {
                do
                {
                    result++;
                } while (bytes[result] != 0);
            }
            catch (Exception e) { Console.WriteLine(e); }
            return result;
        }
    }
    "May all your bacon burn"

  4. Thanks Rage76152, klumpen (2 members gave Thanks to Razzue for this useful post)
  5. #18
    Rage76152's Avatar Member CoreCoins Purchaser
    Reputation
    1
    Join Date
    Aug 2021
    Posts
    11
    Thanks G/R
    5/0
    Trade Feedback
    0 (0%)
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Very nice, I now have a very basic bot that attacks and loots. Albeit, it currently only know how to cast a single spell lmao but for two days of work I'll take it.


    So here's a few more questions if you guys don't mind.

    I'm currently using tab to target which is "meh", I've tried writing directly to current_target_guid which as expected does not work. Someone said something about a targeting array to manually set targets but search is failing me. Reading an older post it seems I can write to mouseover GUID; though it seems whatever value I write does not stick there for very long?

    Is quest autocomplete a reasonable goal? (this may not be worth the work externally)

  6. #19
    Razzue's Avatar Elite User Avid Ailurophile

    CoreCoins Purchaser Authenticator enabled
    Reputation
    398
    Join Date
    Jun 2017
    Posts
    608
    Thanks G/R
    193/283
    Trade Feedback
    2 (100%)
    Mentioned
    14 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Rage76152 View Post
    ~~
    This is simple to do externally.
    - Set bindings in game for TargetFocus, TargetMouseover, InteractMouseover, InteractTarget, and any other neat bindings: BindingId's
    - Walk binding manager and store actions for such commands (note, each can have more than one active binding)
    - Write unit guid to mouseover guid
    - Send InteractWithMouseover or TargetMouseover binding
    - Profit.

    If you're mouseover guid is changing after a write... move your mouse off to a corner
    Of course the written value is going to change if you're actively mousing over interactable entities :P

    As for quest auto complete, I just use an addon to accept/choose rewards/turn in. Walking the active quests, as well as objective counts for those quests in the player struct is relatively simple, Though I will say it seems much easier to work with in retail :P
    Last edited by Razzue; 06-10-2023 at 02:25 AM.
    "May all your bacon burn"

  7. Thanks Rage76152 (1 members gave Thanks to Razzue for this useful post)
  8. #20
    klumpen's Avatar Active Member
    Reputation
    28
    Join Date
    Apr 2007
    Posts
    79
    Thanks G/R
    32/17
    Trade Feedback
    2 (100%)
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Been working on long pathing. Got the formulas down to go from zone X/Y to X/Y/Z Vectors. I haven't started to read ADT/WDL(?)'s to figure out the "correct" Z value of an arbitrary set of map coordinates. So, I decided to brute it from -100.0 in increments of +2.0 until we find a position. Kind of ugly, but it should satisfy the usecase of generating a long path. If anyone has a pointer to an up to date resource for reading out the heightfield(?) of a movement tile that'd be much appreciated. Found some things already but most seem to target the old data format(s).

    Code:
    struct WorldMapArea{
        pub id: u32,
        pub map_id: u32,
        pub area_id: u32,
        pub name: &'static str,
        pub left: f32,
        pub right: f32,
        pub top: f32,
        pub bottom: f32,
    }; 
    
    pub static WORLD_MAP_AREAS: Map<u32, WorldMapArea> = phf_map! {
        4u32 => WorldMapArea{
            id: 4,
            map_id: 1,
            area_id: 14,
            name: "Durotar",
            left: -1962.4999,
            right: -7249.9995,
            top: 1808.3333,
            bottom: -1716.6666,
        },
        // ... 
    };
    
    impl WorldMapArea {
      fn width(&self) -> f32 { self.right - self.left }
      fn height(&self) -> f32 { self.bottom - self.top }
      fn coords(&self, pos: Vec3) -> Vec2 {
            Vec2::new(
                ((pos.y - self.left) / self.width()) * 100.0,
                ((pos.x - self.top) / self.height()) * 100.0,
            )
      }
      fn position(&self, coords: Vec2) -> Vec3 {
            Vec3::new(
              self.top + (coords.y / 100.0) * self.height(),
              self.left + (coords.x / 100.0) * self.width(),
              0.0,
            )
      }
    }
    
    fn long_path_system(
      mut query: Query<(&Position, &mut Path), With<ActivePlayerMarker>>,
      map_id: Res<MapID>,
      maps: Res<WorldMapAreas>,
      mut navigators: Res<Navigators>,
    ) {
      if let Ok((src, mut path)) = query.get_single_mut() {
        let navmesh = navigators.get_or_create(map_id);
        let mut dst = maps[map_id].position(51.51, 41.64); // innkeeper grosk. {340.36206, -4686.2876, 0.0}
    
        // this the nasty bit
        dst.z = -100.0; 
        while let Err(_) = navmesh.find_path(src, dst) { 
          dst.z += 2.0;
        }
    
        // should we pathable now
        if let Ok(dt_path) = navmesh.find_path(src, dst) {
          *path = dt_path.iter().map(|dtv| dtv.into()).collect();
        }
      }
    }

  9. #21
    klumpen's Avatar Active Member
    Reputation
    28
    Join Date
    Apr 2007
    Posts
    79
    Thanks G/R
    32/17
    Trade Feedback
    2 (100%)
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Pretty happy with the latest progress. Got actionbars & keybindings mapped together. About to add player spellbook & cooldowns into the mix to ensure the spellID on the bar is learnt and available.

    Code:
    pub mod keybindings {
        pub const BASE: usize = 0x2CCB4E8;
    }
    
    pub mod actionbar {
        pub const CURRENT_PAGE: usize = 0x2DBED74;
        pub const FIRST_SLOT: usize = 0x2DBE570;
    }
    Code:
    let keybindings = HashMap::new();
    let mut ptr = proc.read_addr64(module_base + offsets::keybindings::BASE + 0x50);
    while !ptr.is_null() {
      let key = proc.read_char_array(proc.read_addr64(ptr + 0x30), 100);
      let action = proc.read_char_array(proc.read_addr64(ptr + 0x40), 100);
      keybindings.insert(action, key); // ACTIONBUTTON1=1, etc
      ptr = proc.read_addr64(ptr + 0x18);
    }
    Code:
    let actionbar_first_slot = module_base + 0x2DBE570;
    const NUM_SLOTS = 80;
    for i in 0..NUM_SLOTS {
      let name = ACTION_BAR_SLOT_NAMES[i];
      let slot: [u16;2] = proc.read(actionbar_first_slot + (i * 0x4) as usize);
      let id = slot[0];
      let typ = slot[1];
      if id == 0 { continue; }
      if let Some(binding) = keybindings.get(slot_name) {
        println!("INFO {}:{}:{} - {} ({})", i, slot_name, binding, name_from(id, typ), typ);
      }
    }
    Produces
    Code:
    > INFO 0:ACTIONBUTTON1:1 - Heroic Strike (0x0)
    > INFO 1:ACTIONBUTTON2:2 - Healing Potion (0x8000)
    > INFO 2:ACTIONBUTTON3:CTRL-3 - UNKNOWN_MACRO_NAME (0x4000) // TODO macros!
    > INFO 3:ACTIONBUTTON4:CTRL-4 - UNKNOWN_MACRO_NAME (0x4100)
    I think this is quite nice from a reusability point of view. No need to hardcode keycodes for a particular class/spec

  10. #22
    InnerSilence's Avatar Active Member
    Reputation
    38
    Join Date
    Oct 2019
    Posts
    70
    Thanks G/R
    14/23
    Trade Feedback
    0 (0%)
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by _chase View Post
    Up to date object type numerator to my knowledge:
    Code:
    #[repr(u8)]
    #[derive(PartialEq, Eq, Clone, Copy, Debug)]
    pub enum ObjectType {
        Object = 0,
        Item = 1,
        Container = 2,
        AzeriteEmpoweredItem = 3,
        AzeriteItem = 4,
        Unit = 5,
        Player = 6,
        ActivePlayer = 7,
        GameObject = 8,
        DynamicObject = 9,
        Corpse = 10,
        AreaTrigger = 11,
        SceneObject = 12,
        Conversation = 13,
        AiGroup = 14,
        Scenario = 15,
        Loot = 16,
        Invalid = 17,
    }



    For movement I am working on the above; generally known as "Steering behavior"; some resources below
    https://crates.io/crates/steering
    Understanding Steering Behaviors - Envato Tuts+ Game Development Tutorials

    As you mentioned above the turning behavior is the largest difficulty, I've found set facing is best for this with a capped delta by game time. So you need to keep track of delta time between your bots updates.
    As a compromise you can just set facing directly to the position of your next step. This produces the bot like instant turn behavior though.
    In my experience, facing toward the target by small delta over each tick is the smoothest. you need to find the best delta that works for you. you can then use set facing or other means to set the facing to the absolute facing value.

  11. #23
    InnerSilence's Avatar Active Member
    Reputation
    38
    Join Date
    Oct 2019
    Posts
    70
    Thanks G/R
    14/23
    Trade Feedback
    0 (0%)
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by klumpen View Post
    Been working on long pathing. Got the formulas down to go from zone X/Y to X/Y/Z Vectors. I haven't started to read ADT/WDL(?)'s to figure out the "correct" Z value of an arbitrary set of map coordinates. So, I decided to brute it from -100.0 in increments of +2.0 until we find a position. Kind of ugly, but it should satisfy the usecase of generating a long path. If anyone has a pointer to an up to date resource for reading out the heightfield(?) of a movement tile that'd be much appreciated. Found some things already but most seem to target the old data format(s).
    You want to do it without a navmesh I guess? In my opinion it is not reliable at all. You an get the terrain z value but what about the WMOs? Your best bet is to call the WorldFrameIntersect and find the z values using that if you are not going to use a navmesh.

  12. #24
    klumpen's Avatar Active Member
    Reputation
    28
    Join Date
    Apr 2007
    Posts
    79
    Thanks G/R
    32/17
    Trade Feedback
    2 (100%)
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by InnerSilence View Post
    You want to do it without a navmesh I guess?
    I am in fact using a mesh (R&D) based off of TrinityCore. I don't know full well if they've prebaked all/any WMOs.

    That said, the problem I am looking to solve in this particular method is to generate a path to a known set of coordinates without knowing the Z value up front. F.e. X/Y coords of a questgiver as datamined from Wowhead or similar. That's sure to hit a wall at some point, but that should be trivial to fix with alternative short paths being generated on the fly. I think. We'll just have to see about that!

    I'm external, so calling WorldFrameIntersect is a no-go as far as I'm aware.

  13. #25
    InnerSilence's Avatar Active Member
    Reputation
    38
    Join Date
    Oct 2019
    Posts
    70
    Thanks G/R
    14/23
    Trade Feedback
    0 (0%)
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by klumpen View Post
    I am in fact using a mesh (R&D) based off of TrinityCore. I don't know full well if they've prebaked all/any WMOs.

    That said, the problem I am looking to solve in this particular method is to generate a path to a known set of coordinates without knowing the Z value up front. F.e. X/Y coords of a questgiver as datamined from Wowhead or similar. That's sure to hit a wall at some point, but that should be trivial to fix with alternative short paths being generated on the fly. I think. We'll just have to see about that!

    I'm external, so calling WorldFrameIntersect is a no-go as far as I'm aware.
    With navmesh what you can do is to use navmeshQuery to queryPolygons for the positon u want. After you found the polygon ref you can query the navmesh using the getPolyHeight function to get the height of the polygon at the positon. Ofcourse you might get more than one polygon in the case of different elevations exists at that point which in that case you need to guess which height is right one. You can refer to the Namigator project by the namreeb to see how it can be done.

  14. Thanks klumpen (1 members gave Thanks to InnerSilence for this useful post)
  15. #26
    klumpen's Avatar Active Member
    Reputation
    28
    Join Date
    Apr 2007
    Posts
    79
    Thanks G/R
    32/17
    Trade Feedback
    2 (100%)
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by InnerSilence View Post
    use navmeshQuery -> queryPolygons -> getPolyHeight
    That sounds much smarter than brute forcing it as I have, thank you for that!
    I'll be sure to take a look at Namigator.

Page 2 of 2 FirstFirst 12

Similar Threads

  1. Information on WoTLK
    By Snitch in forum World of Warcraft General
    Replies: 4
    Last Post: 08-30-2007, 01:20 PM
All times are GMT -5. The time now is 03:41 AM. Powered by vBulletin® Version 4.2.3
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved. User Alert System provided by Advanced User Tagging (Pro) - vBulletin Mods & Addons Copyright © 2025 DragonByte Technologies Ltd.
Google Authenticator verification provided by Two-Factor Authentication (Free) - vBulletin Mods & Addons Copyright © 2025 DragonByte Technologies Ltd.
Digital Point modules: Sphinx-based search