Torchlight II

Torchlight II

İstatistiklere Bak:
WASD Move for Torchlight - Prototype script (AutoHotkey required)
So I picked up Torchlight 2 in the summer sale, but I realized that I am too old for point and click games of this kind any more - I always avoided point and click MOBAs, and had played Torchlight 1 back when it came out and got a fair amount of enjoyment out of it, but now, playing Torchlight 2, I just couldn't stand the clickfest.

So I had a stab at writing a script to let me play using WASD.

I have been playing through as an Outlander, so this script may only be suitable for ranged classes. I will probably try it out with a melee class at some point, but please feel free to feed back how it works.

Features:
  1. Currently only supports keyboard inputs / outputs, but theoretically compatible with a Steam Controller if you remap the controller to keyboard keys.
  2. Configurable keys for inputs / outputs and various settings (timings, click offsets etc)
    Simply edit the provided configuration variables in the script
  3. Default setup provided intended for use with Torchlight 2.
  4. Allows WASD style movement by clicking around your character
  5. Can Remap an "Attack" button to spam (eg Left Mouse), with optional modifier key (eg "Attack only valid target" modifier of CTRL in Torchlight)
  6. User can also specify "Skill keys" (eg 1-0 number keys) so the script is aware of when the user is trying to use a skill.
  7. If the user is moving, and they press an attack or skill button, movement will stop until all attack or skill buttons are released, then continue moving.
  8. If the user is moving (ie the mouse has been moved to click and move the character), and presses an attack or skill button, that button will be blocked until the mouse returns to it's normal position.
  9. While the mouse is being moved to click and move the character, all mouse movement and clicks are blocked, so you do not accidentally click somewhere you don't want to.

Instructions for torchlight 2:
Default movement keys are WSAD (You need to clear some mappings in Torchlight, eg S for skills)

It is configured to send PAUSE as the movement key. In Torchlight 2, configure Pause to be Move or Move / Attack.

Attack is configured to send Ctrl + Click (Only attack valid target)

Default Attack button is Middle Mouse - press this to attack stuff or pick up loot. Because the button is spammed and it spams Ctrl+Click, you can hold the button and walk it onto the target and you wont fire off lots of pointless shots or move somewhere where there is nothing. You will stop moving when you hold this button.

/* Point and Click to WASD script v 0.0.3 by evilc@evilc.com */ #SingleInstance force OutputDebug DBGVIEWCLEAR #MaxHotkeysPerInterval 999 SetBatchLines, -1 km := new KeyMove() return #IfWinActive ahk_exe Torchlight2.exe wheelup:: km.SkillEvent({key: "1", event: 1, wait: 200}) km.SkillEvent({key: "1", event: 0, wait: 200}) return wheeldown:: km.SkillEvent({key: "2", event: 1, wait: 200}) km.SkillEvent({key: "2", event: 0, wait: 200}) return #IfWinActive ahk_exe Class KeyMove { ; USER CONFIGURATION SECTION. Alter values in here according to your requirements ; Set this to the title, class or process name of the game, as it appears in the AHK Window Spy AppName := "ahk_exe Torchlight2.exe" ; Input Buttons - Things you press to tell the script to do something. ; These are all AHK key names. ; Joystick button names are valid, but not supported, as AHK does not properly support detection of up events for joystick buttons. ; Buttons you use to move IB_Up := "w" IB_Down := "s" IB_Left := "a" IB_Right := "d" ; If you wish to remap an attack button (eg to force a shift-click) then you can set this to the name of the button you wish to remap ; When this button is pressed, all movement will stop and the button(s) specified by OB_Attack will be spammed. ; Also, if the cursor is currently clicking for a move, the attack will be "buffered" until the cursor has been returned to its normal position ; If you do not wish to use this feature, set it to "" ;IB_Attack := "" IB_Attack := "MButton" ; Set these to button names that you also wish to "buffer". No remapping will occur for these buttons ; If you do not wish to use this feature, set it to [] ;IB_Skills := [] IB_Skills := [{key: "1", wait: 200} , {key: "2", wait: 0} , {key: "3", wait: 0} , {key: "4", wait: 0} , {key: "5", wait: 0} , {key: "6", wait: 0} , {key: "7", wait: 0} , {key: "8", wait: 0} , {key: "9", wait: 0} , {key: "0", wait: 0} , {key: "RButton", wait: 0}] ; Output Buttons - Things the script presses to do what you ask ; The key combination that the macro presses to initiate a move. For Torchlight, suggest use Pause and bind it in-game to Move or Move / Attack OB_Move := "{Pause}" ; The ley combination that will be spammed when IB_Attack is held OB_Attack := "^{LButton}" ; Configuration values AttackDelay := 100 ; The amount of time to wait after pressing the attack button MoveDistance := 100 ; How many pixels to move from the center when performing a click DelayAfterClick := 50 ; How long to wait after a click before restoring mouse position (in Milliseconds). If movement is erratic, raise this ClickRate := 100 ; How often to click if you hold WASD (in Milliseconds) XClickOffset := 0 ; Allows you to adjust click position on x axis YClickOffset := 20 ; Allows you to adjust click position on y axis ; Internal, do not edit below here ==================================== ;~ MoveEvents := [] MoveRequested := 0 MoveStates := {x: {-1: 0, 1: 0} ; State that the move keys are in , y: {-1: 0, 1: 0}} MouseIsLocked := 0 ; True if the script is currently moving the mouse MoveTimerRunning := 0 ; True if the move timer is running CurrentMoveDir := {x: 0, y: 0} ; The current direction that we want to move in, for each axis SkillEvents := [] ; Waiting skill up or down events SkillStates := {} ; State of the user's current input _SkillStates := {} ; State that the script currently has each skill key in SkillHeld := 0 ; Does the script currently have any skills held? AttackEvents := [] AttackHeld := 0 AttackState := 0 ; State of the attack button __New(){ SetKeyDelay, 0, 0 SetMouseDelay, 0 CoordMode, Mouse, Screen ; Work out coordinates to be used for screen center this.ScreenCenter := {x: (A_ScreenWidth / 2) + this.XClickOffset, y: (A_ScreenHeight / 2) + this.YClickOffset} Hotkey, IfWinActive, % this.AppName if (this.IB_Attack){ fn := this.AttackEvent.Bind(this, 1) hotkey, % "*$" this.IB_Attack, % fn fn := this.AttackEvent.Bind(this, 0) hotkey, % "*$" this.IB_Attack " up", % fn } fn := this.MoveEvent.Bind(this, "y",-1, 1) hotkey, % "~*" this.IB_Up, % fn fn := this.MoveEvent.Bind(this, "y",1, 1) hotkey, % "~*" this.IB_Down, % fn fn := this.MoveEvent.Bind(this, "y",-1, 0) hotkey, % "~*" this.IB_Up " up", % fn fn := this.MoveEvent.Bind(this, "y",1, 0) hotkey, % "~*" this.IB_Down " up", % fn fn := this.MoveEvent.Bind(this, "x",-1, 1) hotkey, % "~*" this.IB_Left, % fn fn := this.MoveEvent.Bind(this, "x",1, 1) hotkey, % "~*" this.IB_Right, % fn fn := this.MoveEvent.Bind(this, "x", -1, 0) hotkey, % "~*" this.IB_Left " up", % fn fn := this.MoveEvent.Bind(this, "x", 1, 0) hotkey, % "~*" this.IB_Right " up", % fn Loop % this.IB_Skills.length(){ key := this.IB_Skills[A_Index].key wait := this.IB_Skills[A_Index].wait fn := this.SkillEvent.Bind(this, {key: key, event: 1, wait: wait}) hotkey, % "*$" key, % fn fn := this.SkillEvent.Bind(this, {key: key, event: 0, wait: wait}) hotkey, % "*$" key " up", % fn this.SkillStates[key] := 0 this._SkillStates[key] := 0 } Hotkey, IfWinActive fn := this.Worker.Bind(this) SetTimer, % fn, -0 this.Movefn := this.MoveCharacter.Bind(this) } AttackEvent(e){ if (this.AttackState == e) return ; filter repeats OutputDebug % "AHK| AttackEvent - " e this.AttackState := e this.AttackEvents.push(e) } SkillEvent(e){ if (this.SkillStates[e.key] == e.event) return ; filter repeats OutputDebug % "AHK| SkillEvent - key: " e.key ", event: " e.event ", wait: " e.wait this.SkillStates[e.key] := e.event this.SkillEvents.push(e) } MoveEvent(axis, dir, e){ static axes := {x: 1, y: 1} if (this.MoveStates[axis, dir] == e) return ; filter repeats this.MoveStates[axis, dir] := e ; Decide the new state of the axis. ; Cater for the scenario where if left was held, then right held ; In this instance, when we release right, we should go back to left, not to center otherdir := dir * -1 this.CurrentMoveDir[axis] := e ? dir : (this.MoveStates[axis, otherdir] ? otherdir : 0) found := 0 for axis, unused in axes { for dir, state in this.MoveStates[axis] { if (state){ found := 1 break } } } ;OutputDebug % "AHK| MoveRequested: " found this.MoveRequested := found } Worker(){ static state_names := {0: "Nothing", 1: "Move", 2: "Skill", 3: "Attack"} static state_ids := {Nothing: 0, Move: 1, Skill: 2, Attack: 3} state := state_ids.Nothing Loop { old_state := state se := this.SkillEvents.length() ae := this.AttackEvents.length() state := (se || this.SkillHeld) ? state_ids.Skill : (ae || this.AttackHeld) ? state_ids.Attack : ( this.MoveRequested ? state_ids.Move : state_ids.Nothing ) if (state != old_state){ OutputDebug % "AHK| Worker Changing State to: " state_names[state] } if (state != state_ids.Move) this.SetMoveState(0) if (state == state_ids.Attack){ if (ae) this.ProcessAttackEvents() Send % this.OB_Attack Sleep % this.AttackDelay } else if (state == state_ids.Skill){ if (se) this.ProcessSkillEvents() } else if (state == state_ids.Move){ this.SetMoveState(1) } sleep 10 } } ProcessAttackEvents(){ Loop % this.AttackEvents.Length(){ state := this.AttackEvents[A_Index] this.AttackHeld := state } this.AttackEvents := [] } ProcessSkillEvents(){ static strings := {0: " up", 1: " down"} Loop % this.SkillEvents.length() { key := this.SkillEvents[A_Index].key e := this.SkillEvents[A_Index].event wait := this.SkillEvents[A_Index].wait str := "{" key strings[e] "}" OutputDebug % "AHK| ProcessSkillEvents Sending " str Send % str this._SkillStates[key] := e if (e && wait) Sleep % wait } found := 0 for key, state in this._SkillStates { if (state){ found := 1 break } } this.SkillHeld := found ;OutputDebug % "AHK| ProcessSkillEvents - SkillHeld: " this.SkillHeld this.SkillEvents := [] } SetMoveState(state){ fn := this.Movefn if (state){ if (!this.MoveTimerRunning){ SetTimer, % fn, % this.ClickRate fn.Call() } } else { if (this.MoveTimerRunning) SetTimer, % fn, Off } this.MoveTimerRunning := state } ; Moves the mouse and issues a click / keypress to move the character MoveCharacter(){ ;~ if (!(this.SkillHeld || this.AttackRequested)){ ; Let other functions know that the mouse cursor is not under the user's control this.MouseIsLocked := 1 BlockInput, MouseMove ; Store the location of the cursor pos := this.GetMousePos() ; Move to the appropriate coordinate that will move us in the desired direction MouseMove, % this.ScreenCenter.x + (this.CurrentMoveDir.x * this.MoveDistance), % this.ScreenCenter.y + (this.CurrentMoveDir.y * this.MoveDistance), 0 ; Issue the keypress Send % "{Blind}" this.OB_Move Sleep % this.DelayAfterClick ; Restore the mouse position this.SetMousePos(pos) ; Allow the user to move or click again BlockInput, MouseMoveOff ;~ } ; Let other functions know that the mouse cursor is back under the user's control this.MouseIsLocked := 0 } ; Stores the position that the user's curosr is at GetMousePos(){ MouseGetPos, x, y return {x: x, y: y} } ; Restores the position of the mouse cursor SetMousePos(coords){ MouseMove, % coords.x, % coords.y, 0 } }

Pastebin link : http://pastebin.com/p1LSmfqR
En son evilC tarafından düzenlendi; 3 Tem 2016 @ 3:34
< >
8 yorumdan 1 ile 8 arası gösteriliyor
Updated to 0.0.3 - Rewrite of large parts of the script. Now a lot more reliable.
En son evilC tarafından düzenlendi; 3 Tem 2016 @ 3:46
The proper way to do this would be to allow the cursor and WASD movement to work simultaneously, with the cursor taking priority.

This would require a certain degree of reverse engineering and low-level code.
That's basically what it does.
If you are aiming at something, then move to the left, the cursor is locked (You cannot move the mouse), then very quickly moved to the left of your character, a click sent, then the cursor is moved back to where it was, and the cursor is unlocked.
If you pressed any skills etc while it was moving the mouse, these will be "buffered" until after it has finished moving the mouse.
you did not even wrote where to put those codes.
Honestly i dont think this will work.Thanks
Well if it work congrate...
does it only works on full screen mode?
https://www.youtube.com/watch?v=aB9VniRFytQ&ab_channel=chairsgotoschool
alternative with controller

Lock Cursor Tools , works
easy lock mouse lock did work for me on win 10
En son 𓅃 スノ 𓅃 tarafından düzenlendi; 8 Tem 2023 @ 15:37
how do you toggle on and off the scritpt?
< >
8 yorumdan 1 ile 8 arası gösteriliyor
Sayfa başına: 1530 50

Gönderilme Tarihi: 2 Tem 2016 @ 14:11
İleti: 8