├── ControllerMod ├── Bindings.xml ├── ControllerMod.lua ├── ControllerMod.toc └── ControllerMod.url ├── Extensions.dll ├── Patcher.exe ├── patch-controllermod.mpq └── readme.md /ControllerMod/Bindings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ControllerMod_Start(); 4 | 5 | 6 | ControllerMod_Back(); 7 | 8 | 9 | ControllerMod_Button_A(); 10 | 11 | 12 | ControllerMod_Button_B(); 13 | 14 | 15 | ControllerMod_Button_X(); 16 | 17 | 18 | ControllerMod_Button_Y(); 19 | 20 | 21 | ControllerMod_Left(); 22 | 23 | 24 | ControllerMod_Right(); 25 | 26 | 27 | ControllerMod_Up(); 28 | 29 | 30 | ControllerMod_Down(); 31 | 32 | 33 | -------------------------------------------------------------------------------- /ControllerMod/ControllerMod.lua: -------------------------------------------------------------------------------- 1 | ControllerMod = {} 2 | 3 | -- @robinsch: check if DLL lua API is injected 4 | function CheckDLL(self) 5 | return InteractNearest and SetCursorPosition and DeleteCursorItemConfirm; 6 | end 7 | 8 | S_DEBUG = true; 9 | 10 | S_BUTTON = nil; 11 | 12 | BINDING_HEADER_CONTROLLERMOD = "Controller Mod" 13 | BINDING_NAME_START = "Start" 14 | BINDING_NAME_INTERACT = "Interact" 15 | BINDING_NAME_BACK = "Back" 16 | BINDING_NAME_BUTTON_A = "Button A" 17 | BINDING_NAME_BUTTON_B = "Button B" 18 | BINDING_NAME_BUTTON_X = "Button X" 19 | BINDING_NAME_BUTTON_Y = "Button Y" 20 | BINDING_NAME_LEFT = "Left" 21 | BINDING_NAME_RIGHT = "Right" 22 | BINDING_NAME_UP = "Up" 23 | BINDING_NAME_DOWN = "Down" 24 | 25 | StaticPopupDialogs["POPUP_EXTENSIONS"] = { 26 | text = "Couldn\'t load |cffFF8800Extensions.dll|r.\n\nPlease visit |cffFF8800https://github.com/robinsch/ControllerMod335|r for more details.", 27 | button1 = "Exit Game", 28 | OnAccept = function() 29 | ForceQuit(); 30 | end, 31 | timeout = 0, 32 | whileDead = true, 33 | hideOnEscape = true, 34 | } 35 | 36 | function CloseMenus() 37 | CloseGossip(); 38 | CloseQuest(); 39 | end 40 | 41 | function Unbind() 42 | return true; 43 | end 44 | 45 | -- @robinsch: button helpers 46 | function ClickButtonA() 47 | if UnitExists("target") and not UnitIsFriend("player", "target") and not UnitIsDead("target") then 48 | ActionButton1:Click(); 49 | else 50 | InteractNearest(); 51 | end 52 | end 53 | 54 | function ClickButtonB() 55 | if UnitExists("target") then 56 | ClearTarget(); 57 | end 58 | end 59 | 60 | function ClickButtonX() 61 | if S_DEBUG then 62 | print("(ClickButtonX)"); 63 | end 64 | ActionButton3:Click(); 65 | end 66 | 67 | function ClickButtonY() 68 | if S_DEBUG then 69 | print("(ClickButtonY)"); 70 | end 71 | ActionButton4:Click(); 72 | end 73 | 74 | function ClickButtonLeft() 75 | if S_BUTTON == nil then 76 | if S_DEBUG then 77 | print("(ClickButtonLeft) nil"); 78 | end 79 | return false 80 | elseif S_BUTTON:GetName() == "CharacterMicroButton" then 81 | ToggleCharacter("PaperDollFrame"); 82 | return true 83 | end 84 | 85 | if string.find(S_BUTTON:GetName(), "CloseButton") then 86 | ClearButton(); 87 | end 88 | 89 | if S_DEBUG then 90 | print("(ClickButtonLeft) "..S_BUTTON:GetName()); 91 | end 92 | 93 | S_BUTTON:Click("LeftButton"); 94 | return true 95 | end 96 | 97 | function ClickButtonRight() 98 | if S_BUTTON == nil then 99 | return false 100 | end 101 | 102 | S_BUTTON:Click("RightButton"); 103 | return true 104 | end 105 | 106 | function MoveCursor(button) 107 | if button:IsVisible() then 108 | x, y = GetNormalizedPosition(button); 109 | SetCursorPosition(x, y); 110 | end 111 | end 112 | 113 | function ClearButton() 114 | if S_BUTTON == nil then return end 115 | 116 | if S_DEBUG then 117 | print("(ClearButton) "..S_BUTTON:GetName()); 118 | end 119 | 120 | S_BUTTON = nil; 121 | SetCursorPosition(0.5, 0.25); 122 | end 123 | 124 | function SetButton(button) 125 | if button == nil then return end 126 | 127 | if S_DEBUG then 128 | print("(SetButton) "..button:GetName()); 129 | end 130 | 131 | MoveCursor(button) 132 | S_BUTTON = button 133 | end 134 | 135 | function SetButtonIndex(index) 136 | if S_BUTTON == nil then return end 137 | local buttonName = S_BUTTON:GetName(); 138 | local buttonIndex 139 | for idx in string.gmatch (buttonName, "%d+") do 140 | buttonIndex = idx 141 | end 142 | 143 | if buttonIndex == nil then 144 | return false 145 | end 146 | 147 | local newButtonName = string.gsub(buttonName, buttonIndex .. "$", buttonIndex + index); 148 | if _G[newButtonName] and _G[newButtonName]:IsVisible() then 149 | SetButton(_G[newButtonName]); 150 | return true 151 | end 152 | 153 | return false 154 | end 155 | 156 | function SetTradeSkillIndex(index) 157 | if not SetButtonIndex(index) then 158 | if index == 1 then 159 | if _G["TradeSkillListScrollFrameScrollBarScrollDownButton"]:IsEnabled() == 1 then 160 | _G["TradeSkillListScrollFrameScrollBarScrollDownButton"]:Click(); 161 | SetButtonIndex(-3); 162 | end 163 | else 164 | if _G["TradeSkillListScrollFrameScrollBarScrollUpButton"]:IsEnabled() == 1 then 165 | _G["TradeSkillListScrollFrameScrollBarScrollUpButton"]:Click(); 166 | SetButtonIndex(3); 167 | end 168 | end 169 | end 170 | 171 | local _, type = GetTradeSkillInfo(S_BUTTON:GetID()); 172 | if type ~= "header" then 173 | ClickButtonLeft(); 174 | end 175 | end 176 | 177 | function SetTradeSkillButton() 178 | local _, type = GetTradeSkillInfo(S_BUTTON:GetID()); 179 | if type == "header" then 180 | ClickButtonLeft(); 181 | else 182 | _G["TradeSkillCreateButton"]:Click(); 183 | end 184 | end 185 | 186 | function SetMerchantIndex(index) 187 | if S_BUTTON == nil then return end 188 | local buttonName = S_BUTTON:GetName(); 189 | 190 | -- @robinsch: ContainerFrame priorty if cursor is in container 191 | if string.find(buttonName, "ContainerFrame") then 192 | return false 193 | end 194 | 195 | local merchantIndex = string.match(buttonName, "%d+"); 196 | 197 | local numSlots = GetMerchantNumItems(); 198 | if ( merchantIndex + index ) > numSlots then 199 | return 200 | else 201 | merchantIndex = merchantIndex + index; 202 | end 203 | 204 | local newButtonName = "MerchantItem" .. merchantIndex .. "ItemButton"; 205 | if _G[newButtonName] and _G[newButtonName]:IsVisible() then 206 | SetButton(_G[newButtonName]); 207 | return true 208 | end 209 | 210 | return false 211 | end 212 | 213 | function SetSpellIndex(index) 214 | if S_BUTTON == nil then return end 215 | local buttonName = S_BUTTON:GetName(); 216 | 217 | local buttonIndex 218 | for idx in string.gmatch (buttonName, "%d+") do 219 | buttonIndex = idx 220 | end 221 | 222 | if buttonIndex == nil then 223 | -- Cursor @ Prev / Next Page Button 224 | if string.find(buttonName, "SpellBookPrevPageButton") then 225 | -- Right => Next Page Button 226 | if index == 1 then 227 | SetButton(_G["SpellBookNextPageButton"]); 228 | return true 229 | -- Left => Unbind 230 | elseif index == -1 then return true 231 | -- Up => Spell Book 232 | elseif index == -2 then 233 | SetButton(_G["SpellButton11"]); 234 | return true 235 | -- Down => Action Bar 236 | elseif index == 2 then 237 | SetButton(_G["ActionButton1"]); 238 | return true 239 | end 240 | elseif string.find(buttonName, "SpellBookNextPageButton") then 241 | -- Right => Unbind 242 | if index == 1 then return true 243 | -- Left => Next Page Button 244 | elseif index == -1 then 245 | SetButton(_G["SpellBookPrevPageButton"]); 246 | return true 247 | -- Up => Spell Book 248 | elseif index == -2 then 249 | SetButton(_G["SpellButton12"]); 250 | return true 251 | -- Down => Action Bar 252 | elseif index == 2 then 253 | SetButton(_G["ActionButton1"]); 254 | return true 255 | end 256 | end 257 | 258 | return false 259 | end 260 | 261 | -- Cursor @ Spell Book (Skill Line Tab) 262 | if string.find(buttonName, "SpellBookSkillLineTab") then 263 | -- Right => Unbind 264 | if index == 1 then return true end 265 | 266 | -- Left => SpellButton 267 | if index == -1 then 268 | SetButton(_G["SpellButton1"]); 269 | return true 270 | end 271 | 272 | -- @robinsch: clamp 273 | if index == -2 then index = -1 end 274 | if index == 2 then index = 1 end 275 | -- Cursor @ Spell Book 276 | elseif string.find(buttonName, "SpellButton") then 277 | -- Cursor @ Right Side 278 | if buttonIndex % 2 == 0 then 279 | -- Right => Spell Book (Skill Line Tab) 280 | if index == 1 then 281 | SetButton(_G["SpellBookSkillLineTab1"]); 282 | return true 283 | end 284 | -- Cursor @ Left Side 285 | elseif buttonIndex % 1 == 0 then 286 | -- Left => Unbind 287 | if index == -1 then return true end 288 | end 289 | 290 | -- Down => Action Bar 291 | if (buttonIndex + index) == 13 then 292 | SetButton(_G["SpellBookPrevPageButton"]); 293 | return true 294 | elseif (buttonIndex + index) == 14 then 295 | SetButton(_G["SpellBookNextPageButton"]); 296 | return true 297 | end 298 | -- Cursor @ Action Bar 299 | elseif string.find(buttonName, "ActionButton") then 300 | -- Up to SpellButton 301 | if index == -2 then 302 | SetButton(_G["SpellButton1"]); 303 | end 304 | 305 | -- Down => Unbind 306 | if index == 2 then return true end 307 | end 308 | 309 | local newButtonName = string.gsub(buttonName, buttonIndex .. "$", buttonIndex + index); 310 | if _G[newButtonName] and _G[newButtonName]:IsVisible() then 311 | SetButton(_G[newButtonName]); 312 | return true 313 | else 314 | -- @robinsch: for some classes ActionButton is called BonusActionButton 315 | if string.find(buttonName, "ActionButton") then 316 | newButtonName = "Bonus"..newButtonName; 317 | if _G[newButtonName] and _G[newButtonName]:IsVisible() then 318 | SetButton(_G[newButtonName]); 319 | return true 320 | end 321 | end 322 | end 323 | 324 | return false 325 | end 326 | 327 | function SetBagIndex(index) 328 | if S_BUTTON == nil then return end 329 | local buttonName = S_BUTTON:GetName(); 330 | 331 | local bagIndex, itemIndex; 332 | local i = 1; 333 | for idx in string.gmatch(buttonName, "%d+") do 334 | if i == 1 then bagIndex = tonumber(idx) end 335 | if i == 2 then itemIndex = tonumber(idx) end 336 | i = i + 1; 337 | end 338 | 339 | if bagIndex == nil or itemIndex == nil then 340 | return false 341 | end 342 | 343 | local numSlots = GetContainerNumSlots(bagIndex - 1); 344 | if ( itemIndex + index ) > GetContainerNumSlots(bagIndex - 1) then 345 | if bagIndex < 5 then 346 | bagIndex = bagIndex + 1; 347 | itemIndex = ( itemIndex + index ) % numSlots; 348 | end 349 | elseif ( itemIndex + index ) < 1 then 350 | bagIndex = bagIndex - 1; 351 | itemIndex = GetContainerNumSlots(bagIndex - 1) + ( itemIndex + index ); 352 | else 353 | itemIndex = itemIndex + index; 354 | end 355 | 356 | local newButtonName = "ContainerFrame" .. bagIndex .. "Item" .. itemIndex; 357 | if _G[newButtonName] and _G[newButtonName]:IsVisible() then 358 | SetButton(_G[newButtonName]); 359 | return true 360 | end 361 | 362 | return false 363 | end 364 | 365 | function SetSpellButton() 366 | if S_BUTTON == nil then return false end 367 | 368 | if string.find(S_BUTTON:GetName(), "SpellBookSkillLineTab") or S_BUTTON:GetName() == "SpellBookPrevPageButton" or S_BUTTON:GetName() == "SpellBookNextPageButton" then 369 | ClickButtonLeft(); 370 | return true; 371 | end 372 | 373 | if string.find(S_BUTTON:GetName(), "ActionButton") then 374 | if GetCursorInfo() ~= nil then 375 | PlaceAction(S_BUTTON:GetID()); 376 | else 377 | PickupAction(S_BUTTON:GetID()); 378 | end 379 | 380 | return true; 381 | end 382 | 383 | if S_BUTTON == _G["SpellbookMicroButton"] then 384 | ClickButtonLeft(); 385 | return true; 386 | end 387 | 388 | local id = SpellBook_GetSpellID(S_BUTTON:GetID()); 389 | PickupSpell(id, BOOKTYPE_SPELL); 390 | return true; 391 | end 392 | 393 | function ClearSpellButton() 394 | if S_BUTTON == nil then 395 | ClearButton(); 396 | _G["SpellBookCloseButton"]:Click(); 397 | end 398 | 399 | if S_DEBUG then 400 | print(S_BUTTON:GetName()); 401 | end 402 | 403 | if not CursorHasSpell() then 404 | ClearButton(); 405 | _G["SpellBookCloseButton"]:Click(); 406 | end 407 | 408 | ClearCursor(); 409 | return true; 410 | end 411 | 412 | function DeleteItem() 413 | if GetCursorInfo() == nil then 414 | ClickButtonLeft(); 415 | end 416 | 417 | DeleteCursorItemConfirm(); 418 | 419 | return true; 420 | end 421 | 422 | FRAME_BUTTONS = 423 | { 424 | QuestLogFrame = 425 | { 426 | "QuestLogFrameAbandonButton", "QuestLogFramePushQuestButton", "QuestLogFrameTrackButton", "QuestLogFrameCancelButton" 427 | }, 428 | } 429 | 430 | function SetFrameLRIndex(frame, index) 431 | if S_BUTTON == nil then return end 432 | local buttonName = S_BUTTON:GetName(); 433 | 434 | local buttonIndex = 0; 435 | for i, v in ipairs(FRAME_BUTTONS[frame:GetName()]) do 436 | if v == buttonName then 437 | buttonIndex = i; 438 | break; 439 | end 440 | end 441 | 442 | if index > 0 then 443 | for i = ( buttonIndex + index ), #FRAME_BUTTONS[frame:GetName()] do 444 | local newButton = _G[FRAME_BUTTONS[frame:GetName()][i]]; 445 | if newButton and newButton:IsEnabled() == 1 then 446 | SetButton(_G[FRAME_BUTTONS[frame:GetName()][i]]); 447 | return 448 | end 449 | end 450 | else 451 | for i = ( buttonIndex + index ), 1, index do 452 | local newButton = _G[FRAME_BUTTONS[frame:GetName()][i]]; 453 | if newButton and newButton:IsEnabled() == 1 then 454 | SetButton(_G[FRAME_BUTTONS[frame:GetName()][i]]); 455 | return 456 | end 457 | end 458 | end 459 | end 460 | 461 | function QuestLogFrame_Right() 462 | if CursorHasItem() then 463 | ClearCursor(); 464 | else 465 | ClearButton(); 466 | CloseAllBags(); 467 | end 468 | end 469 | 470 | -- @robinsch: micro button helpers 471 | MICRO_BUTTONS = { "CharacterMicroButton", "SpellbookMicroButton", "TalentMicroButton", "AchievementMicroButton", "QuestLogMicroButton", "SocialsMicroButton", "PVPMicroButton", "LFDMicroButton", "MainMenuMicroButton", "HelpMicroButton" }; 472 | function SetMicroButton(button) 473 | if S_BUTTON == nil then 474 | SetButton(button); 475 | else 476 | ClearButton(); 477 | CloseMenus(); 478 | end 479 | end 480 | 481 | function SetMicroButtonIndex(index) 482 | if S_BUTTON == nil then return end 483 | local buttonName = S_BUTTON:GetName(); 484 | for i, v in ipairs(MICRO_BUTTONS) do 485 | if v == buttonName then 486 | if _G[MICRO_BUTTONS[i + index]] then 487 | SetButton(_G[MICRO_BUTTONS[i + index]]); 488 | end 489 | end 490 | end 491 | end 492 | 493 | function GetNormalizedPosition(frame) 494 | if GetCVar("gxWindow") == 1 and GetCVar("gxMaximize") ~= 1 then 495 | print("ControllerMod: Windowed Mode (Maximized = 0) is not supported yet!"); 496 | end 497 | 498 | local w, h = GetScreenWidth(), GetScreenHeight() 499 | local x, y = frame:GetCenter() 500 | return x/w, y/h 501 | end 502 | 503 | -- @robinsch: event handlers (https://wowpedia.fandom.com/wiki/Events) 504 | EVENT_HANDLERS = 505 | { 506 | GOSSIP_SHOW = { SetButton, "GossipTitleButton1" }, 507 | GOSSIP_CLOSED = { ClearButton }, 508 | 509 | QUEST_GREETING = { SetButton, "QuestTitleButton1" }, 510 | QUEST_DETAIL = { SetButton, "QuestFrameAcceptButton" }, 511 | QUEST_FINISHED = { ClearButton }, 512 | QUEST_COMPLETE = { SetButton, "QuestFrameCompleteQuestButton" }, 513 | QUEST_PROGRESS = { SetButton, "QuestFrameCompleteButton" }, 514 | 515 | SPELLS_CHANGED = { SetButton, "SpellButton1" }, 516 | 517 | TRADE_SKILL_SHOW = { SetButton, "TradeSkillSkill2" }, 518 | TRADE_SKILL_CLOSE = { ClearButton }, 519 | 520 | CHAT_MSG_SYSTEM = { ParseChat }, 521 | } 522 | 523 | -- @robinsch: binding handlers (Esc -> Key Bindings -> ControllerMod) 524 | -- sorted by priority 525 | BINDING_HANDLERS = 526 | { 527 | StaticPopup1 = 528 | { 529 | Button_A = { ClickButtonLeft, "StaticPopup1Button1" }, 530 | Button_B = { ClickButtonLeft, "StaticPopup1Button2" }, 531 | Left = { Unbind }, 532 | Right = { Unbind }, 533 | }, 534 | 535 | GossipFrame = 536 | { 537 | Button_A = { ClickButtonLeft }, 538 | Button_B = { CloseGossip }, 539 | Left = { SetButton, "GossipTitleButton1" }, 540 | Right = { SetButton, "GossipFrameGreetingGoodbyeButton" }, 541 | Up = { SetButtonIndex, -1 }, 542 | Down = { SetButtonIndex, 1 }, 543 | }, 544 | 545 | QuestFrameGreetingPanel = 546 | { 547 | Button_A = { ClickButtonLeft }, 548 | Button_B = { CloseQuest }, 549 | Left = { SetButton, "QuestTitleButton1" }, 550 | Right = { SetButton, "QuestFrameGreetingGoodbyeButton" }, 551 | Up = { SetButtonIndex, -1 }, 552 | Down = { SetButtonIndex, 1 }, 553 | }, 554 | 555 | QuestFrameDetailPanel = 556 | { 557 | Button_A = { ClickButtonLeft }, 558 | Button_B = { CloseQuest }, 559 | Left = { SetButton, "QuestFrameAcceptButton" }, 560 | Right = { SetButton, "QuestFrameDeclineButton" }, 561 | Up = { ClickButtonLeft, "QuestDetailScrollFrameScrollBarScrollUpButton" }, 562 | Down = { ClickButtonLeft, "QuestDetailScrollFrameScrollBarScrollDownButton" }, 563 | }, 564 | 565 | QuestFrameRewardPanel = 566 | { 567 | Button_A = { ClickButtonLeft }, 568 | Button_B = { CloseQuest }, 569 | Left = { SetButton, "QuestFrameCompleteQuestButton" }, 570 | Right = { SetButton, "QuestFrameCancelButton" }, 571 | Up = { ClickButtonLeft, "QuestRewardScrollFrameScrollBarScrollUpButton" }, 572 | Down = { ClickButtonLeft, "QuestRewardScrollFrameScrollBarScrollDownButton" }, 573 | }, 574 | 575 | QuestLogFrame = 576 | { 577 | Button_A = { ClickButtonLeft }, 578 | Button_B = { ClickButtonLeft, "QuestLogFrameCloseButton" }, 579 | Left = { SetFrameLRIndex, -1 }, 580 | Right = { SetFrameLRIndex, 1 }, 581 | Up = { SetButtonIndex, -1 }, 582 | Down = { SetButtonIndex, 1 }, 583 | }, 584 | 585 | TradeSkillFrame = 586 | { 587 | Button_A = { ClickTradeSkillButton }, 588 | Button_B = { ClickButtonLeft, "TradeSkillFrameCloseButton" }, 589 | Button_X = { ClickButtonLeft, "TradeSkillCreateAllButton" }, 590 | Up = { SetTradeSkillIndex, -1 }, 591 | Down = { SetTradeSkillIndex, 1 }, 592 | }, 593 | 594 | MerchantFrame = 595 | { 596 | Button_A = { ClickButtonLeft }, 597 | Button_B = { ClickButtonLeft, "MerchantFrameCloseButton" }, 598 | Button_X = { ClickButtonRight }, 599 | Left = { SetMerchantIndex, -1 }, 600 | Right = { SetMerchantIndex, 1 }, 601 | Up = { SetMerchantIndex, -2 }, 602 | Down = { SetMerchantIndex, 2 }, 603 | }, 604 | 605 | SpellBookFrame = 606 | { 607 | Button_A = { SetSpellButton }, 608 | Button_B = { ClearSpellButton }, 609 | Left = { SetSpellIndex, -1 }, 610 | Right = { SetSpellIndex, 1 }, 611 | Up = { SetSpellIndex, -2 }, 612 | Down = { SetSpellIndex, 2 }, 613 | }, 614 | 615 | ContainerFrame1 = 616 | { 617 | Button_A = { ClickButtonLeft }, 618 | Button_B = { ClickButtonLeft, "MainMenuBarBackpackButton" }, 619 | Button_X = { ClickButtonRight }, 620 | Button_Y = { DeleteItem }, 621 | Left = { SetBagIndex, 1 }, 622 | Right = { SetBagIndex, -1 }, 623 | Up = { SetBagIndex, 4 }, 624 | Down = { SetBagIndex, -4 }, 625 | }, 626 | 627 | WorldFrame = 628 | { 629 | Button_A = { ClickButtonLeft }, 630 | Button_B = { ClearButton }, 631 | Start = { ClickButtonLeft, "MainMenuBarBackpackButton"}, 632 | Back = { SetMicroButton, "CharacterMicroButton" }, 633 | Left = { SetMicroButtonIndex, -1 }, 634 | Right = { SetMicroButtonIndex, 1 }, 635 | }, 636 | } 637 | BINDING_HANDLERS_QUERY = { "StaticPopup1", "GossipFrame", "QuestFrameGreetingPanel", "QuestFrameDetailPanel", "QuestFrameRewardPanel", "QuestLogFrame", "TradeSkillFrame", "MerchantFrame", "SpellBookFrame", "ContainerFrame1", "WorldFrame" } 638 | 639 | QuestLogFrame:HookScript("OnShow", function(self) 640 | SetButton(_G["QuestLogScrollFrameButton1"]); 641 | end) 642 | 643 | QuestLogFrame:HookScript("OnHide", function(self) 644 | ClearButton(); 645 | end) 646 | 647 | MerchantFrame:HookScript("OnShow", function(self) 648 | SetButton(_G["MerchantItem1ItemButton"]); 649 | _G["MainMenuBarBackpackButton"]:Click(); 650 | end) 651 | 652 | ContainerFrame1:HookScript("OnShow", function(self) 653 | SetButton(_G["ContainerFrame1Item16"]); 654 | _G["CharacterBag0Slot"]:Click(); 655 | _G["CharacterBag1Slot"]:Click(); 656 | _G["CharacterBag2Slot"]:Click(); 657 | _G["CharacterBag3Slot"]:Click(); 658 | _G["SpellBookCloseButton"]:Click(); 659 | end) 660 | 661 | ContainerFrame1:HookScript("OnHide", function(self) 662 | ClearButton(); 663 | 664 | if _G["MerchantFrame"]:IsVisible() then 665 | SetButton(_G["MerchantItem1ItemButton"]); 666 | end 667 | end) 668 | 669 | StaticPopup1:HookScript("OnShow", function(self) 670 | SetButton(_G["StaticPopup1Button1"]); 671 | end) 672 | 673 | StaticPopup1:HookScript("OnHide", function(self) 674 | ClearButton(); 675 | 676 | if QuestLogFrame:IsVisible() then 677 | SetButton(_G["QuestLogScrollFrameButton1"]); 678 | end 679 | end) 680 | 681 | function SetDefaultBindings() 682 | SetBinding("SHIFT-PAGEUP"); 683 | SetBinding("SHIFT-PAGEDOWN"); 684 | SetBinding("1"); 685 | SetBinding("2"); 686 | SetBinding("3"); 687 | SetBinding("4"); 688 | 689 | if GetBindingKey("BUTTON_A") == nil then SetBinding("NUMPADPLUS", "BUTTON_A"); end 690 | if GetBindingKey("BUTTON_B") == nil then SetBinding("NUMPADMINUS", "BUTTON_B"); end 691 | if GetBindingKey("BUTTON_X") == nil then SetBinding("PAGEUP", "BUTTON_X"); end 692 | if GetBindingKey("BUTTON_Y") == nil then SetBinding("PAGEDOWN", "BUTTON_Y"); end 693 | 694 | if GetBindingKey("START") == nil then SetBinding("NUMPADMULTIPLY", "START"); end 695 | if GetBindingKey("BACK") == nil then SetBinding("NUMPADDIVIDE", "BACK"); end 696 | 697 | if GetBindingKey("LEFT") == nil then SetBinding("NUMPAD4", "LEFT"); end 698 | if GetBindingKey("RIGHT") == nil then SetBinding("NUMPAD6", "RIGHT"); end 699 | if GetBindingKey("UP") == nil then SetBinding("NUMPAD8", "UP"); end 700 | if GetBindingKey("DOWN") == nil then SetBinding("NUMPAD2", "DOWN"); end 701 | end 702 | 703 | local frame = CreateFrame("Frame") 704 | frame:RegisterEvent("ADDON_LOADED") 705 | 706 | -- @robinsch: register event listeners 707 | for event, _ in pairs(EVENT_HANDLERS) do 708 | frame:RegisterEvent(event); 709 | end 710 | 711 | frame:SetScript("OnEvent", function(self, event, ...) 712 | if not CheckDLL() then 713 | return StaticPopup_Show("POPUP_EXTENSIONS") 714 | end 715 | 716 | if event == "ADDON_LOADED" then 717 | SetCVar("autoLootDefault", 1); 718 | SetCVar("cameraTerrainTilt", 1); 719 | SetDefaultBindings(); 720 | ClearButton(); 721 | end 722 | 723 | handler = EVENT_HANDLERS[event]; 724 | if handler then 725 | ControllerMod_Handle(nil, handler); 726 | end 727 | end) 728 | 729 | -- @robinsch: Bindings.xml handlers 730 | function ControllerMod_Start() 731 | for _, frame in pairs(BINDING_HANDLERS_QUERY) do 732 | local handler = BINDING_HANDLERS[frame] 733 | if _G[frame] and _G[frame]:IsVisible() and handler["Start"] then 734 | ControllerMod_Handle(_G[frame], handler["Start"]) 735 | return 736 | end 737 | end 738 | end 739 | 740 | function ControllerMod_Back() 741 | for _, frame in pairs(BINDING_HANDLERS_QUERY) do 742 | local handler = BINDING_HANDLERS[frame] 743 | if _G[frame] and _G[frame]:IsVisible() and handler["Back"] then 744 | if ControllerMod_Handle(_G[frame], handler["Back"]) then 745 | return 746 | end 747 | end 748 | end 749 | end 750 | 751 | function ControllerMod_Button_A() 752 | for _, frame in pairs(BINDING_HANDLERS_QUERY) do 753 | local handler = BINDING_HANDLERS[frame] 754 | if _G[frame] and _G[frame]:IsVisible() and handler["Button_A"] then 755 | if ControllerMod_Handle(_G[frame], handler["Button_A"]) then 756 | return 757 | end 758 | end 759 | end 760 | 761 | ClickButtonA(); 762 | end 763 | 764 | function ControllerMod_Button_B() 765 | for _, frame in pairs(BINDING_HANDLERS_QUERY) do 766 | local handler = BINDING_HANDLERS[frame] 767 | if _G[frame] and _G[frame]:IsVisible() and handler["Button_B"] then 768 | if ControllerMod_Handle(_G[frame], handler["Button_B"]) then 769 | return 770 | end 771 | end 772 | end 773 | 774 | ClickButtonB(); 775 | end 776 | 777 | function ControllerMod_Button_X() 778 | for _, frame in pairs(BINDING_HANDLERS_QUERY) do 779 | local handler = BINDING_HANDLERS[frame] 780 | if _G[frame] and _G[frame]:IsVisible() and handler["Button_X"] then 781 | if ControllerMod_Handle(_G[frame], handler["Button_X"]) then 782 | return 783 | end 784 | end 785 | end 786 | 787 | ClickButtonX(); 788 | end 789 | 790 | function ControllerMod_Button_Y() 791 | for _, frame in pairs(BINDING_HANDLERS_QUERY) do 792 | local handler = BINDING_HANDLERS[frame] 793 | if _G[frame] and _G[frame]:IsVisible() and handler["Button_Y"] then 794 | if ControllerMod_Handle(_G[frame], handler["Button_Y"]) then 795 | return 796 | end 797 | end 798 | end 799 | 800 | ClickButtonY(); 801 | end 802 | 803 | function ControllerMod_Left() 804 | for _, frame in pairs(BINDING_HANDLERS_QUERY) do 805 | local handler = BINDING_HANDLERS[frame] 806 | if _G[frame] and _G[frame]:IsVisible() and handler["Left"] then 807 | if ControllerMod_Handle(_G[frame], handler["Left"]) then 808 | return 809 | end 810 | end 811 | end 812 | end 813 | 814 | function ControllerMod_Right() 815 | for _, frame in pairs(BINDING_HANDLERS_QUERY) do 816 | local handler = BINDING_HANDLERS[frame] 817 | if _G[frame] and _G[frame]:IsVisible() and handler["Right"] then 818 | if ControllerMod_Handle(_G[frame], handler["Right"]) then 819 | return 820 | end 821 | end 822 | end 823 | end 824 | 825 | function ControllerMod_Up() 826 | for _, frame in pairs(BINDING_HANDLERS_QUERY) do 827 | local handler = BINDING_HANDLERS[frame] 828 | if _G[frame] and _G[frame]:IsVisible() and handler["Up"] then 829 | if ControllerMod_Handle(_G[frame], handler["Up"]) then 830 | return 831 | end 832 | end 833 | end 834 | end 835 | 836 | function ControllerMod_Down() 837 | for _, frame in pairs(BINDING_HANDLERS_QUERY) do 838 | local handler = BINDING_HANDLERS[frame] 839 | if _G[frame] and _G[frame]:IsVisible() and handler["Down"] then 840 | if ControllerMod_Handle(_G[frame], handler["Down"]) then 841 | return 842 | end 843 | end 844 | end 845 | end 846 | 847 | function ControllerMod_Handle(frame, handle) 848 | if handle == nil then 849 | return false 850 | end 851 | 852 | local fn = handle[1]; 853 | if fn == nil then 854 | return false 855 | end 856 | 857 | -- @robinsch: handle fn parameter parsing 858 | if fn == SetButton or fn == SetMicroButton then 859 | return fn(_G[handle[2]]); 860 | elseif fn == ClickButtonLeft then 861 | if handle[2] then 862 | _G[handle[2]]:Click("LeftButton"); 863 | return true; 864 | else 865 | return ClickButtonLeft(S_BUTTON); 866 | end 867 | elseif fn == ClickButtonRight then 868 | if handle[2] then 869 | _G[handle[2]]:Click("RightButton"); 870 | return true; 871 | else 872 | return ClickButtonRight(S_BUTTON); 873 | end 874 | elseif fn == SetButtonIndex or fn == SetTradeSkillIndex or fn == SetMicroButtonIndex or fn == SetBagIndex or fn == SetMerchantIndex or fn == SetSpellIndex then 875 | return fn(handle[2]); 876 | elseif fn == SetFrameLRIndex then 877 | return fn(frame, handle[2]) 878 | else 879 | return fn(); 880 | end 881 | 882 | return false 883 | end 884 | -------------------------------------------------------------------------------- /ControllerMod/ControllerMod.toc: -------------------------------------------------------------------------------- 1 | ## Interface: 30300 2 | ## Title: ControllerMod 3 | ## Author: robinsch 4 | ## Notes: Controller support for 3.3.5 5 | 6 | ControllerMod.lua 7 | 8 | -------------------------------------------------------------------------------- /ControllerMod/ControllerMod.url: -------------------------------------------------------------------------------- 1 | =https://github.com/robinsch/ControllerMod335 -------------------------------------------------------------------------------- /Extensions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robinsch/ControllerMod335/452680fc9ecde00d7e808929139248531349d510/Extensions.dll -------------------------------------------------------------------------------- /Patcher.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robinsch/ControllerMod335/452680fc9ecde00d7e808929139248531349d510/Patcher.exe -------------------------------------------------------------------------------- /patch-controllermod.mpq: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robinsch/ControllerMod335/452680fc9ecde00d7e808929139248531349d510/patch-controllermod.mpq -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Notes 2 | In development, ControllerMod does not offer native controller support. You need to use [Steam Big Picture Mode](https://store.steampowered.com/bigpicture). 3 | 4 | ## Steam Setup 5 | 0. Download and Install Steam (https://cdn.cloudflare.steamstatic.com/client/installer/SteamSetup.exe) 6 | 1. Launch Steam 7 | 2. Click the **Games** menu, choose **Add a Non-Steam Game to My Library**. 8 | 3. Browse for Wow.exe on your computer. 9 | 4. Click on **Add Selected Programs**. 10 | 11 | ## Steam Controller 12 | 1. Launch Steam in Big Picture Mode. 13 | 2. Browse for Wow in your Steam Library. 14 | 3. Select the gamepad icon on the right. 15 | 4. Click the **Search** menu, search for **ControllerMod335** (or download it through your browser: steam://controllerconfig/3255281634/3069784775) 16 | 5. Download and activate the controller layout. 17 | 18 | ![](https://i.gyazo.com/e4492b0812555a3e4f5834346b413396.png) 19 | 20 | ## Steam Auto-Login 21 | 1. Launch Steam in Big Picture Mode. 22 | 2. Browse for Ascension in your Steam Library. 23 | 3. Select the setting icon on the right (next to the gamepad icon). 24 | 4. Copy and paste the text segement below and adjust it to your 25 | 26 | `-login "user e.g. Myaccount" -password "pass e.g. Mysecretpass" -realm "realm e.g. Sunfury" -character "char e.g. Mychar" -controller "1"` 27 | 28 | 29 | ## Installation (Not needed on Ascension.gg) 30 | 1. Move Patcher.exe to WoW folder and run it. 31 | 2. Move Extensions.dll to WoW folder. 32 | 3. Move patch-controllermod.mpq to WoW/Data folder. 33 | 4. Import steam controller profile. 34 | 35 | 36 | # Updating 37 | 1. Replace Extensions.dll. 38 | 2. Replace patch-controllermod in WoW/Data folder. 39 | --------------------------------------------------------------------------------