Quick Buy AHK/Python script(s)

General Discussion is for anything related to Blackstone not covered in the other forums.
Post Reply
Drow Arrow
Baron
Baron
Posts: 57
Joined: Sat Jun 12, 2021 9:19 am

Quick Buy AHK/Python script(s)

Post by Drow Arrow »

Hey Folks,

This is my collection of scripts to simplify buying multiples of items such a scrolls or ammo from vendors easier. the python scripts are currently my main focus as the AHK scripts have been finished to a good standard as far as I'm concerned. I will be posting them here but I am also maintaining Github repositories for them:

https://github.com/DrowArrow/NWNHotkeys (AHK)
https://github.com/DrowArrow/NWNHotkeys-V2 (Python)

feedback is welcome should you have any.

Use of the AHK scripts will require you to have AutoHotkey V2 installed on your system. and if you're a windows user I'd probably recommend using the AHK version for the sake of simplicity.

AHK Scripts

Script 1:
This is the first version that i successfully got working.

Code: Select all

#Requires AutoHotkey v2.0-rc.2
#Hotif WinActive("AHK_exe nwmain.exe")

Global DragDistance := 300

; Use Ctrl + F to quickly buy 10 of the item you are hovering the cursor over.
^f:: {
    MouseGetPos &StartX, &StartY
    Loop(10) { ; change the loop count to however many times you want it to make the purchase before stopping
        Click "Down Right"
        Sleep 220
        MouseMove StartX + DragDistance, StartY
        Click "Up Right"
        Sleep 220
        MouseMove StartX, StartY
        }
}
Script 2:
Version 2 of my quick buy script, functionally works just the same but I approached it in a more compact manner.

Code: Select all

#Requires AutoHotkey v2.0-rc.2
#Hotif WinActive("AHK_exe nwmain.exe")

Global DragDistance := 300

; Use Ctrl + F to quickly buy 10 of the item you are hovering the cursor over.
^f:: {
    MouseGetPos &StartX, &StartY
    Loop(10) { ; change the loop count to however many times you want it to make the purchase before stopping
        MouseClickDrag "right", StartX, StartY, StartX + DragDistance, StartY, 100
        Sleep 230
        MouseMove StartX, StartY
        }
}
Script 3: Quick Buy script with GUI
decided to add a GUI so the end user can easily change the hotkey used as well as the amount of times it will buy the item.

Code: Select all

#Requires Autohotkey v2
#SingleInstance Force
#NoTrayIcon
HotIfWinActive("ahk_exe nwmain.exe")

; INI file configuration
if FileExist("NWNHotkeys.ini"){
	QBHotkey := IniRead("NWNHotkeys.ini", "Hotkeys", "QBHotkey", "^f")
	QBLoop := IniRead("NWNHotkeys.ini", "Loops", "QBLoop")
	OpLevel := IniRead("NWNHotkeys.ini", "Options", "Opacity")
	AOTCheck := IniRead("NWNHotkeys.ini", "Options", "AlwaysOnTop", 0)
}
else {
	IniWrite "^f", "NWNHotkeys.ini", "Hotkeys", "QBHotkey"
	IniWrite 10, "NWNHotkeys.ini", "Loops", "QBLoop"
	IniWrite 255, "NWNHotkeys.ini", "Options", "Opacity"
	IniWrite 0, "NWNHotkeys.ini", "Options", "AlwaysOnTop"
	QBHotkey := IniRead("NWNHotkeys.ini", "Hotkeys", "QBHotkey", "^f")
	QBLoop := IniRead("NWNHotkeys.ini", "Loops", "QBLoop", 10)
	OpLevel := IniRead("NWNHotkeys.ini", "Options", "Opacity", 255)
	AOTCheck := IniRead("NWNHotkeys.ini", "Options", "AlwaysOnTop", 0)
}

{
	myGui := Constructor()
	myGui.Show("w300 h200")
}

Constructor()
{
	Global QBHotkey
	myGui := Gui()
	Tab := myGui.Add("Tab3", "x0 y0 w303 h210", ["Neverwinter Nights", "Options"])
	Tab.UseTab(1)
	myGui.AddHotkey("vQuickBuy Limit1 x8 y32 w120 h21", QBHotkey) ; "vQuickBuy": used to assign a variable name for the hotkey which can be called later on with gui.submit | "QBHotkey": A variable that holds the key combination for the hotkey which is defined as a Global variable at the top of the script
	myGui.Add("Text", "x136 y32 w120 h23 +0x200", "Quick Buy")
	UpdBtn1 := myGui.Add("Button", "x200 y32 w80 h21", "Update")
	myGui.AddEdit("vQBEdit Number x8 y64 w120 h21", QBLoop)
	myGui.Add("Text", "x136 y64 w120 h23 +0x200", "Buy Count")
	UpdBtn2 := myGui.Add("Button", "x200 y64 w80 h21", "Update")
	Tab.UseTab(2)
	AOT := myGui.AddCheckbox("vcb_aot x200 y180 Checked" AOTCheck, "Always on top?")
	Opacity := myGui.AddSlider("AltSubmit Center NoTicks ToolTipTop range100-255 x8 y55 w120 h21", OpLevel)
	myGui.Add("Text", "x30 y32 w120 h23 +0x200", "Transparency")
	Tab.UseTab()
	UpdBtn1.OnEvent("Click", UpdateHotkey) ; Calls the UpdateHotkey function when the update button is clicked.
	UpdBtn2.OnEvent("Click", UpdateLoop)
	AOT.OnEvent("Click", AlwaysOnTop)
	Opacity.OnEvent("Change", Op_Adjust)
	myGui.OnEvent('Close', ExitProcedure)
	myGui.Title := "NWNHotkeys"
	
	return myGui
}

;Startup Variables Read
WinWait("NWNHotkeys")
	WinSetTransparent(OpLevel)

	AOT_Startup := myGui.Submit(0).cb_aot
	if (AOT_Startup = 1)
		WinSetAlwaysOnTop 1
	else
		WinSetAlwaysOnTop 0

;Hotkey Update Function

UpdateHotkey(*) {
	Global QBHotkey
	QBHK := myGui.Submit(0).Quickbuy ; Grabs the updated hotkey from the hotkey gui box
	; assigns the updated Hotkey value from the gui to the QBHK variable

	ChangeHotkey(QBHK) {
		Hotkey(QBHotkey, QB, 'Off') ; Disables Hotkey
		QBHotkey := QBHK ; Updates the QBHotkey variable with the Hotkey value from the QBHK variable
		Hotkey(QBHotkey, QB) ; Enables hotkey with the newly assigned Hotkey combination
		IniWrite QBHotkey, "NWNHotkeys.ini", "Hotkeys", "QBHotkey"
	}

	if (QBHK != "") {
		ChangeHotkey(QBHK)
		ToolTip("Hotkey changed!")
		Sleep 1000
		ToolTip() ; Clear the tooltip
	}
}

;Buy Count Update Function

UpdateLoop(*) {
	Global QBLoop
	LoopCount := myGui.Submit(0).QBEdit ;grabs the value from the Buy Count GUI box and assigned it to the LoopCount variable 
	 ;assigns the updated buy count from the gui to the new loop variable

	ChangeLoop(LoopCount){
		QBLoop := LoopCount ; assigns the updated buy count to the QBLoop variable
		IniWrite QBLoop, "NWNHotkeys.ini", "Loops", "QBLoop" ;writes the new value of the QBLoop variable to the configuration file
	}

	if (LoopCount !=""){
		ChangeLoop(LoopCount)
		ToolTip("Buy Count Changed!")
		sleep 1000
		ToolTip()
	}
}

;Function for "ALways on Top?"" Checkbox

AlwaysOnTop(AOT, info){
	if (aot.Value = 1)
		WinSetAlwaysOnTop 1, "A"
	else
		WinSetAlwaysOnTop 0, "A"
}

Op_Adjust(Opacity, *){
	WinSetTransparent(Opacity.value)
	IniWrite(Opacity.value, "NWNHotkeys.ini", "Options", "Opacity")
}

Global DragDistance := 300

;Hotkeys

Hotkey(QBHotkey, QB)

; Quickbuy Function

QB(*) {
	MouseGetPos &StartX, &StartY
	Loop(QBLoop) { ; change the loop count to however many times you want it to make the purchase before stopping
		MouseClickDrag "right", StartX, StartY, StartX + DragDistance, StartY
		sleep 285 ; Edit the sleep time (milliseconds) to change how quick it makes each purchase
		}
	MouseMove StartX, StartY
}

ExitProcedure(AOTCheck){
	AOTCheck := mygui.Submit(0).cb_aot
	IniWrite(AOTCheck, "NWNHotkeys.ini", "Options", "AlwaysOnTop")
	ExitApp()
}

HotIf
ImageImage

Python Scripts

Script 1:

this is the first iteration of the python script I'm working on that i feel is worth sharing. it will buy 10 of what ever item you're hovering your cursor over by pressing ctrl+f on your keyboard. this can be changed in the script pretty easily should you wish to do so. it also only works while the game is in focus if you have another window as your main focus the hotkey will do nothing unless the window itself has a use for that key combination.

Code: Select all

import keyboard
import mouse
import time
import pygetwindow as gw

# version 0.0.1
QBHK = 'ctrl + f'
TARGET_WINDOW = 'Neverwinter Nights'

def QB():
	active_window = gw.getActiveWindow()
	MousePos = mouse.get_position()
	if TARGET_WINDOW in active_window.title:
			for _ in range(10):
				mouse.hold("right")
				mouse.move(300, 0, False, 0.15)
				mouse.release("right")
				mouse.move(*MousePos, True, 0.15)

keyboard.add_hotkey(QBHK, QB)

while True:
	try:
		time.sleep(1)
	except KeyboardInterrupt:
		break
Script 2:

This version of the script creates a configuration file in the scripts directory which stores the amount of times it will buy the item as well as the hotkey combination

Code: Select all

import keyboard
import mouse
import time
import pygetwindow as gw
import configparser as cp
import os

# version 0.1.0
TARGET_WINDOW = 'Neverwinter Nights'

def create_config():
	config = cp.ConfigParser()

	config["General"] = {"QBHK": "ctrl + f", "LOOPS": "10"}
#	config["Options"] = {"Opacity": "100", "AlwaysOnTop": "1"}

	with open('config.ini', 'w') as configfile:
		config.write(configfile)


if __name__ == "__main__": # if the script was launched directly and therefore the __name__ of the script is the __main__ module or parent as it was launched directly then check if config file exists and if it does not, create it.
	config_file_path = "config.ini"

	if not os.path.exists(config_file_path):
		create_config()
		print("No configuration found\nConfiguration file has been created")

def read_config():
	config = cp.ConfigParser()
	config.read("config.ini")
	
	LOOP = config.getint("General", "LOOPS")
	QBHK = config.get("General", "QBHK")
	
	config_values = {
		"LOOP": LOOP,
		"QBHK": QBHK
	}
	return config_values # returns the config values as a dictionary(dict) to be referenced by the rest of the code when looking for values assigned to variables in the config 

if __name__ == "__main__":
	config_data = read_config() # reads the values in the dict created and returns the requested settings value such as config_data["LOOP"] which would return the value assigned to the loop key in the config file which in this case is 10 by default.


def QB():
	config_data = read_config()
	active_window = gw.getActiveWindow()
	MousePos = mouse.get_position()
	loop_count = config_data["LOOP"]
	if TARGET_WINDOW in active_window.title:
		for _ in range(loop_count):
			mouse.hold("right")
			mouse.move(300, 0, False, 0.15)
			mouse.release("right")
			mouse.move(*MousePos, True, 0.15)

keyboard.add_hotkey(config_data["QBHK"], QB)

while True:
	try:
		time.sleep(1)
	except KeyboardInterrupt:
		break
Basic use is as follows:
  1. Put your cursor on the item you want to buy multiple of.
  2. Hold Ctrl and press the F key (Ctrl + F)
Known Issues:
  • None
To-Do List:
  • Figure out how to allow the GUI version to remember hotkey and loop changes between instances
    (This may take a while, especially if it means rewriting large parts of the script)
    was easier than I thought.
Last edited by Drow Arrow on Tue Dec 17, 2024 7:07 pm, edited 18 times in total.
Active Characters:
Ragos Meadsmith
Efiel Meadsmith
Ezekial Meadsmith
Parnos Meadsmith
Drow Arrow
Baron
Baron
Posts: 57
Joined: Sat Jun 12, 2021 9:19 am

Re: Quick Buy AHK script

Post by Drow Arrow »

Changelog:

04/04/2024:
  • Initial Release of Quick buy script 1
  • Quick Buy Script 2 added (later on the same day)
23/08/2024:
  • Added version of script with GUI allowing user to edit hotkey and buy count without needing to edit the script directly
  • Added checkbox that allows you to make the window Always on Top
  • the Program now generates an configuration file to remember it's settings between instances.
24/08/2024:
  • Created an Options tab and moved all options into it.
  • Added Transparency Slider
  • Always on top now remembers it state between instances. (forgot this when initially setting up the settings file)
31/08/2024:
  • Fixed mistake which caused the buy count to be forcibly set to 10 when the program was started instead of using the value in the configuration file.
  • Fixed Hotkeys in the GUI version so they are now only active when game window is in focus.
Active Characters:
Ragos Meadsmith
Efiel Meadsmith
Ezekial Meadsmith
Parnos Meadsmith
Post Reply