在此声明,本博客仅作学习交流使用,不可用于任何商业途径,对产生的任何影响,本人概不负责。如有侵权,请联系删除。
本文参考自https://blog.csdn.net/chijiandi/article/details/85276661#_1
玩了好几年dnf,虽然脱坑了,正好有个兴趣,就做了一个简陋的脚本。代码水平有限,正在努力学习中。
语言:python
工具:pycharm
win10使用键盘模拟时,开启测试模式。
bcdedit /set testsigning on
首先需要定位人物和怪物,本文修改了npk文件,怪物大小设置25*25,颜色为纯色。通过找色块找到位置。
author = 'Yanxiang'
version = '1.0'
date = '10/06/2019'
import cv2
import numpy as np
import time
def detective(img_path,flag):
kernel_4 = np.ones((4, 4), np.uint8) # 4x4的卷积核
def separate_color_yellow(hsv):
# 怪物颜色提取
lower_hsv = np.array([0, 0, 255]) # 提取颜色的低值..
high_hsv = np.array([0, 0, 255]) # 提取颜色的高值
mask = cv2.inRange(hsv, lowerb=lower_hsv, upperb=high_hsv)
print("怪物坐标提取完成")
return mask
def separate_color_door(hsv):
# 门颜色提取
lower_hsv = np.array([120, 255, 255]) # 提取颜色的低值
high_hsv = np.array([120, 255, 255]) # 提取颜色的高值
mask = cv2.inRange(hsv, lowerb=lower_hsv, upperb=high_hsv)
print("门的坐标提取完成")
return mask
def erocode_dilate(mask):
erosion = cv2.erode(mask, kernel_4, iterations=1)
erosion = cv2.erode(erosion, kernel_4, iterations=1)
dilation = cv2.dilate(erosion, kernel_4, iterations=1)
dilation = cv2.dilate(dilation, kernel_4, iterations=1)
return dilation
# flag = 0 表示找到怪物的平均坐标
# flag = 1 表示找到人物坐标
def contours(dilation, flag):
# target是把原图中的非目标颜色区域去掉剩下的图像
target = cv2.bitwise_and(Img, Img, mask=dilation)
# 将滤波后的图像变成二值图像放在binary中
ret, binary = cv2.threshold(dilation, 127, 255, cv2.THRESH_BINARY)
# 在binary中发现轮廓,轮廓按照面积从小到大排列
img_contours, contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
p = 0
inumber = 1
# 新建数组存放所有怪物 X,Y坐标的中心值
monster_x = []
monster_Y = []
# pos_x =0
# pos_y =0
if(len(contours)!=0):
for i in contours: # 遍历所有的轮廓
x, y, w, h = cv2.boundingRect(i) # 将轮廓分解为识别对象的左上角坐标和宽、高
if w * h > 100:
if flag == 0:
p += 1
print('第 %d 个怪物的底中的位置为' % (p), x + w / 2, y + h )
monster_x.append(x + w / 2)
monster_Y.append(y + h )
return monster_x,monster_Y
if flag == 2:
p += 1
print('第 %d 个的中心像素位置为' % (p), x + w / 2, y + h / 2)
pos_xx = x
pos_yy = y
return pos_xx,pos_yy
else:
if flag == 0:
return monster_x,monster_Y
if flag == 2:
return 0,0
Img = cv2.imread(img_path)#读入一幅图像
if Img is not None:#判断图片是否读入
HSV = cv2.cvtColor(Img, cv2.COLOR_BGR2HSV)#把BGR图像转换为HSV格式
# 提取颜色
if flag==0:
mask_yellow = separate_color_yellow(HSV)
dilation_yellow = erocode_dilate(mask_yellow)
pos_x,pos_y = contours(dilation_yellow,flag)
return pos_x,pos_y
elif flag==2:
mask_door = separate_color_door(HSV)
dilation_red = erocode_dilate(mask_door)
pos_x,pos_y = contours(dilation_red,flag)
return pos_x,pos_y
和上面差不多,不贴了。还有另外一种方法定位的人物。
首先将屏幕置于最前窗口。
class setf():
def __init__(self):
self.gamename = '地下城与勇士'
self.shell = win32com.client.Dispatch("WScript.Shell")
self.dll = CDLL("user32.dll")
def setfocus(self):
# pid = self.get_pid_for_pname(self.gamename)
# if pid:
# print(222)
# for hwnd in self.get_hwnds_for_pid(pid):
# 获得窗口句柄
hwnd = win32gui.FindWindow(None, "地下城与勇士")
# 将dnf窗口保持焦点
self.shell.SendKeys('%')
self.dll.LockSetForegroundWindow(2)
if self.dll.IsIconic(hwnd):
win32gui.SendMessage(hwnd, win32con.WM_SYSCOMMAND, win32con.SC_RESTORE, 0)
self.dll.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 0, 0,
win32con.SWP_NOSIZE | win32con.SWP_NOMOVE)
self.dll.SetForegroundWindow(hwnd)
self.dll.SetActiveWindow(hwnd)
移动 参考模拟按键
完整的按键模拟方法包引用自网络。
import rabird.winio
import time
import atexit
# KeyBoard Commands
# Command port
KBC_KEY_CMD = 0x64
# Data port
KBC_KEY_DATA = 0x60
__winio = None
def __get_winio():
global __winio
if __winio is None:
__winio = rabird.winio.WinIO()
def __clear_winio():
global __winio
__winio = None
atexit.register(__clear_winio)
return __winio
def wait_for_buffer_empty():
'''
Wait keyboard buffer empty
'''
winio = __get_winio()
dwRegVal = 0x02
while (dwRegVal & 0x02):
dwRegVal = winio.get_port_byte(KBC_KEY_CMD)
def key_down(scancode):
winio = __get_winio()
wait_for_buffer_empty();
winio.set_port_byte(KBC_KEY_CMD, 0xd2);
wait_for_buffer_empty();
winio.set_port_byte(KBC_KEY_DATA, scancode)
def SPkey_down(scancode):
winio = __get_winio()
wait_for_buffer_empty();
winio.set_port_byte(KBC_KEY_CMD, 0xd2);
wait_for_buffer_empty();
winio.set_port_byte(KBC_KEY_DATA, 0xe0)
wait_for_buffer_empty();
winio.set_port_byte(KBC_KEY_CMD, 0xd2);
wait_for_buffer_empty();
winio.set_port_byte(KBC_KEY_DATA, scancode)
def key_up(scancode):
winio = __get_winio()
wait_for_buffer_empty();
winio.set_port_byte( KBC_KEY_CMD, 0xd2);
wait_for_buffer_empty();
winio.set_port_byte( KBC_KEY_DATA, scancode | 0x80);
def SPkey_up(scancode):
winio = __get_winio()
wait_for_buffer_empty();
winio.set_port_byte(KBC_KEY_CMD, 0xd2);
wait_for_buffer_empty();
winio.set_port_byte(KBC_KEY_DATA, 0xe0)
wait_for_buffer_empty();
winio.set_port_byte(KBC_KEY_CMD, 0xd2);
wait_for_buffer_empty();
winio.set_port_byte(KBC_KEY_DATA, scancode | 0x80)
def mouse_down():
winio = __get_winio()
wait_for_buffer_empty();
winio.set_port_byte(KBC_KEY_CMD, 0xd3);
wait_for_buffer_empty();
winio.set_port_dword(KBC_KEY_DATA, 0x09)
def mouse_up():
winio = __get_winio()
wait_for_buffer_empty();
winio.set_port_byte(KBC_KEY_CMD, 0xd3);
wait_for_buffer_empty();
winio.set_port_dword(KBC_KEY_DATA, 0x08)
def key_press(scancode, press_time = 0.05):
key_down( scancode )
time.sleep( press_time )
key_up( scancode )
time.sleep(0.05)
def SPkey_press(scancode, press_time = 0.05):
SPkey_down( scancode )
time.sleep( press_time )
SPkey_up( scancode )
time.sleep(press_time)
def mouse_clicked(clicked_time = 0.05):
mouse_down()
time.sleep( clicked_time )
mouse_up()
time.sleep( clicked_time )
https://blog.csdn.net/qq_37232329/article/details/79926440
上面的链接为扫描码
从上面得知人物、怪物、门的坐标,可以移动。在移动之前,先计算移动速度。
主要代码如下:
def moveTo(monsters_x,monsters_y,person_x,person_y,flag = 0):
''' flag = 1 时,为 人物 向 门口 移动
flag = 0 时,为 人物 向 怪物 移动 , 距离怪物 80 个像素时 , 释放技能
'''
if(flag==0):
# 释放技能的位置,在怪物前方60像素
skill_pos = 60
else:
skill_pos = 0
# 1 怪物 在 人物 的 右上
if((person_x - monsters_x)<0 and (person_y - monsters_y) >0):
if(monsters_x - person_x <skill_pos):
print('距离右边怪物挺近,直接往上移动')
key_press(0x34)
key_press(0x25, abs(person_y - monsters_y) / y_speed)
else:
move_time = abs(monsters_x - person_x - skill_pos) / x_speed - abs(person_y - monsters_y) / y_speed
if (move_time > 0):
print('跑啊跑,往右上移动')
key_press(0x34)
key_down(0x34)
time.sleep(0.1)
key_press(0x25, abs(person_y - monsters_y) / y_speed)
time.sleep(move_time)
key_up(0xb4)
else:
print('不值得跑,先往右,后往上')
key_press(0x34)
key_press(0x34, abs(monsters_x -skill_pos- person_x) / x_speed)
key_press(0x25, abs(person_y - monsters_y) / y_speed)
剑魂的技冷却时间,释放技能时间。此方法计算的是可用的技能。可以继续优化,如:加入技能释放范围;或者按照一定顺序计算冷却时间最短的技能。
list_to_time = [0,0,0,0,0,0,0,0,0,0,0]
list_skill = ['q', 'e', 'w', 'd','t','y','a','s','f','g','h']
list_skill_cooling = [ 6.8, 9.6, 32, 9, 37, 103, 22, 0, 17.5,36, 120]
skill_release_time = [ 0.5, 0.5, 3, 0.5, 0.5, 7, 5, 0.3, 1, 1.5, 2]
list_key = [0x10,0x12,0x11,0x20,0x14,0x15,0x1e,0x1f,0x21,0x22,0x23]
def rel_skill(skill,ini_time):
for i in list_skill:
while(skill==i):
list_to_time[list_skill.index(i)] = ini_time + list_skill_cooling[list_skill.index(i)] + skill_release_time[list_skill.index(i)]
f = open('data.txt','w')
for j in range(len(list_skill)):
f.write(str(list_to_time[j])+'\n')
f.close()
key_press(list_key[list_skill.index(i)])
time.sleep(skill_release_time[list_skill.index(i)]+0.8)
break
return
截图
import win32gui,win32ui,win32con,win32api
from key import *
def window_capture(filename,flag = 0):
hwnd = win32gui.FindWindow(None, "地下城与勇士")
# 根据窗口句柄获取窗口的设备上下文DC(Divice Context)
hwndDC = win32gui.GetWindowDC(hwnd)
win32gui.SetForegroundWindow(hwnd)
# 根据窗口的DC获取mfcDC
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
# mfcDC创建可兼容的DCW
saveDC = mfcDC.CreateCompatibleDC()
# 创建bigmap准备保存图片
saveBitMap = win32ui.CreateBitmap()
# 获取监控器信息
left, top, right, bottom = win32gui.GetWindowRect(hwnd)
width = right - left
height = bottom - top
saveBitMap.CreateCompatibleBitmap(mfcDC, width, height)
# 高度saveDC,将截图保存到saveBitmap中
saveDC.SelectObject(saveBitMap)
# 截取从左上角(0,0)长宽为(w,h)的图片
if flag==0:
saveDC.BitBlt((0, 0), (width, height), mfcDC, (0, 0), win32con.SRCCOPY)
saveBitMap.SaveBitmapFile(saveDC, filename)
else:
saveDC.BitBlt((720, 795), (75, 60), mfcDC, (0, 0), win32con.SRCCOPY)
saveBitMap.SaveBitmapFile(saveDC, './pos.png')
return width, height
水平有限,花了5天时间修改npk文件,10几天写代码。初学python,我所会的就只是一些基础以及一丁点制作思路,没有后续的成品,谢谢。