QQ连连看 for Delphi 源码

kbhook.DLL

library kbhook;

{ Important note about DLL memory management
:  ShareMem must be the
first unit in your library
' s USES clause AND your project ' s ( select
Project
- View Source) USES clause  if  your DLL exports any procedures or
functions that pass strings as parameters or function results
.  This
applies to all strings passed to and from your DLL
-- even those that
are nested in records and classes
.  ShareMem is the interface unit to
the BORLNDMM
. DLL shared memory manager ,  which must be deployed along
with your DLL
.  To avoid using BORLNDMM . DLL ,  pass string information
using PChar or ShortString parameters
.  }

uses
  windows;
var
  hHk
:  HHOOK;
  BFirst
: Boolean = True;
  
// { $R   *. res}
procedure ModMemData();
var
  pData
:  pointer;
  dwOldProtect
:  DWORD;
  mbi_thunk
:  TMemoryBasicInformation;
begin
  pData 
:=  pointer($ 00403296 );
  
// 查询页信息。
  VirtualQuery(pData
,  mbi_thunk ,  sizeof(MEMORY_BASIC_INFORMATION));
  
// 改变页保护属性为读写。
  VirtualProtect(mbi_thunk
. BaseAddress ,  mbi_thunk . RegionSize ,
    PAGE_READWRITE
,  mbi_thunk . Protect);
  
// 清零。
  PByte(pData)
^   :=   0 ;
  
// 恢复页的原保护属性。
  VirtualProtect(mbi_thunk
. BaseAddress ,  mbi_thunk . RegionSize ,
    mbi_thunk
. Protect ,  dwOldProtect);
end;

function keyHookProc(nCode
:  Integer; WParam :  WPARAM; LParam :  LPARAM) :  LRESULT;
  stdcall;
const
  _KeyPressMask 
=  $ 80000000 ;
begin
  Result 
:=   0 ;
  
if  nCode  <   0  then
  begin
    Result 
:=  CallNextHookEx(hhk ,  nCode ,  wParam ,  lParam);

    
Exit ;
  end
  
else
  begin
    
if  BFirst then
    
//  侦测 Ctrl  +  B 组合键
    
// if  ((lParam and _KeyPressMask)  =   0 ) and (GetKeyState(vk_Control)  <   0 ) and
     
//  (wParam  =  VK_F2) then
      
// (GetKeyState(vk_Control)  <   0 ) and (wParam  =   Ord ( ' B ' )) then
    begin
      Result 
:=   1 ;
      ModMemData;
      BFirst
:= False;

      
// MessageBox( 0 ,   ' ok ' , '' , MB_OK);
     
//  MessageBox( 0 ,  pchar(GetModuleName(GetModuleHandle(nil))) ,
      
//  pchar(inttostr(GetCurrentThread)) ,   0 );
    end;
  end;

end;

function SetKbHook(threadid
:  DWORD) :  boolean; stdcall; export;  // 外部调用
begin
  
if  threadid  <>   0  then
  begin
    hHk 
:=  SetWindowsHookEx(WH_GETMESSAGE ,   @keyHookProc ,  HInstance ,  threadid);
    result 
:=  hhk  <>   0 ;
  end
  
else
  begin
    Result 
:=  UnHookWindowsHookEx(hHk);
  end;
  BFirst
:= True;
end;

exports
  SetKbHook;
end
.

 

LineGame.pas

{*******************************************************************************
  Copyright (C), 
2004, 风月工作室.
  作者: 追风逐月
  版本: 
1.0
  日期: 2005年12月28日
  描述: QQ连连看游戏控制类
  修改历史:
    徐明     
2005/12/28      1.0        创建该文件
    ...
********************************************************************************}


unit LineGame;

interface
uses
  Windows,
  Messages,
  ShellAPI,
  Classes;
const
  MAP_HLENGTH 
=   19 ;
  MAP_VLENGTH 
=   11 ;
  MAPCOUNT 
=   100 ;
  gLeft 
=   16 ;
  gTop 
=   184 ;
  hwidth 
=   31 ;
  vWidth 
=   35 ;
type
  TLineGame 
=   class
  
private
    Maps: array[
0 ..MAP_VLENGTH  -   1 0 ..MAP_HLENGTH  -   1 ] of integer;
    gh: THandle;
    RectA: TRect;
    LineMap: TStringList;
    ptLines: array[
1 ..MAPCOUNT] of Tlist;
    FGameThreadID:integer;
    procedure SetPtLines;
    function CanConnect(P1, P2: TPoint): boolean;
    function CanLine(P1, P2: TPoint): Boolean;
    function isEmptyPt(pt: TPoint): boolean;
    function GetMapIndex(Color: integer): integer;
    function LeftMapCount: integer;
    procedure GetColor(x, y: Integer; var col: Cardinal);
    function GetColorMx(i, j: integer): Cardinal;
    function isBackGround(Color: Integer): boolean;
    procedure SendMouse(x1, y1, x2, y2: Integer);
    function GetMapPos(i, j: integer): Tpoint;
    function Search(var P1, P2: TPoint): boolean;
    function isSameMap(Color1, Color2: integer): boolean;
    procedure GetBox;
    procedure SetMemData(hnd:THandle);
  
public
    constructor Create;
    destructor Destroy; 
override ;
    procedure AutoStart;
    procedure RunStep;
    procedure KillAll;

  end;
function SetKbHook(threadid:DWORD):
bool ;stdcall; external  ' kbhook.dll '  ;
implementation

function StrToInt(
const  S:  string ): Integer;
var
  E: Integer;
begin
  Val(S, Result, E);
  
// if E <> 0 then ConvertErrorFmt(@SInvalidInteger, [S]);
end;


{ TLineGame }
{*************************************************
  函数名: TLineGame.GetColor
  描  述: 获取指定位置(屏幕坐标)的颜色值
  参  数: x, y: Integer; var col: Cardinal
  返回值: None
 
*************************************************}

procedure TLineGame.GetColor(x, y: Integer; var col: Cardinal);
var
  WindowDC: THandle;
begin
  WindowDC :
=  GetWindowDC(gh);
  col :
=  GetPixel(WindowDC, x, y);
  ReleaseDC(gh, WindowDC);
end;

{*************************************************
  函数名: TLineGame.GetColorMx
  描  述: 获取指定位置(对子矩阵坐标)的评估值
  参  数: i, j: integer
  返回值: Cardinal  
- 评估值
 
*************************************************}

function TLineGame.GetColorMx(i, j: integer): Cardinal;
var
  x, y: integer;
  col1, col2: Cardinal;
begin
  x :
=  gLeft  +   14   +  hwidth  *  i;
  y :
=  gTop  +   18   +  vwidth  *  j;
  GetColor(x, y, col1);
  x :
=  x  -   6 ;
  GetColor(x, y, col2);
  result :
=  col1  +  col2;
end;

{*************************************************
  函数名: TLineGame.Search
  描  述: 搜索可以消除的对子的位置
  参  数: var P1, P2: TPoint  可以消除的对子坐标
  返回值: boolean
 
*************************************************}

function TLineGame.Search(var P1, P2: TPoint): boolean;
var
  i, j, k: integer;
  LineList: TList;
begin
  result :
=   false ;
  
for  i : =  Low(ptlines) to High(ptlines)  do
  begin
    LineList :
=  ptLines[i];
    
for  j : =   0  to LineList.Count  -   1   do
      
for  k : =  j  +   1  to LineList.Count  -   1   do
      begin
        p1 :
=  pPoint(LineList.Items[j]) ^ ;
        p2 :
=  pPoint(LineList.Items[k]) ^ ;
        
if  CanConnect(p1, p2) then
        begin
          result :
=   true ;
          Dispose(LineList.Items[k]);
          LineList.Delete(k);
          Maps[p1.X, p1.Y] :
=   - 2 ;
          Dispose(LineList.Items[j]);
          LineList.Delete(j);
          Maps[p2.X, p2.Y] :
=   - 2 ;

          exit;
        end;
      end;
  end;

end;
{*************************************************
  函数名: TLineGame.CanConnect
  描  述: 判断两点是否连通
  参  数: P1, P2: TPoint
  返回值: boolean
 
*************************************************}

function TLineGame.CanConnect(P1, P2: TPoint): boolean;
var
  mpt1, mpt2: TPoint;
begin
  result :
=   false ;
  
if  (p1.x  =  p2.X) and (p1.y  =  p2.Y) then
    exit;

  
// 可以直线相连
  Result : =  Canline(P1, p2);
  
if  result then
    exit;

  
// 一个拐点
  mpt1.X : =  p1.X;
  mpt1.Y :
=  p2.Y;
  Result :
=  (isEmptyPt(mpt1)) and Canline(P1, mpt1) and Canline(mpt1, P2);
  
if  result then
    exit;

  mpt1.X :
=  p2.X;
  mpt1.Y :
=  p1.Y;
  Result :
=  (isEmptyPt(mpt1)) and Canline(P1, mpt1) and Canline(mpt1, P2);
  
if  result then
    exit;

  
// 两个拐点
  
// 以p1为基准
  
// 获取y坐标方向的空点
  mpt1.y : =  p1.Y;
  mpt2.Y :
=  p2.Y;

  mpt1.X :
=  p1.X  -   1 ;
  
while  (mpt1.x  >   - 1 ) and (isEmptyPt(mpt1))  do
  begin
    mpt2.X :
=  mpt1.X;
    
if  isEmptyPt(mpt2) then
      result :
=  CanLine(mpt1, mpt2) and CanLine(mpt2, p2);
    
if  result then
      exit;
    dec(mpt1.X);
  end;

  mpt1.X :
=  p1.X  +   1 ;
  
while  (mpt1.x  <  MAP_VLENGTH) and (isEmptyPt(mpt1))  do
  begin
    mpt2.X :
=  mpt1.X;
    
if  isEmptyPt(mpt2) then
      result :
=  CanLine(mpt1, mpt2) and CanLine(mpt2, p2);
    
if  result then
      exit;
    inc(mpt1.X);
  end;

  
// 获取x坐标方向的空点
  mpt1.x : =  p1.x;
  mpt2.x :
=  p2.x;

  mpt1.y :
=  p1.y  -   1 ;
  
while  (mpt1.y  >   - 1 ) and (isEmptyPt(mpt1))  do
  begin
    mpt2.y :
=  mpt1.y;
    
if  isEmptyPt(mpt2) then
      result :
=  CanLine(mpt1, mpt2) and CanLine(mpt2, p2);
    
if  result then
      exit;
    dec(mpt1.y);
  end;

  mpt1.y :
=  p1.y  +   1 ;
  
while  (mpt1.y  <  MAP_HLENGTH) and (isEmptyPt(mpt1))  do
  begin
    mpt2.y :
=  mpt1.y;
    
if  isEmptyPt(mpt2) then
      result :
=  CanLine(mpt1, mpt2) and CanLine(mpt2, p2);
    
if  result then
      exit;
    inc(mpt1.y);
  end;

end;
{*************************************************
  函数名: TLineGame.CanLine
  描  述: 判断两点是否可以直线相连
  参  数: P1, P2: TPoint
  返回值: Boolean
 
*************************************************}

function TLineGame.CanLine(P1, P2: TPoint): Boolean;
var
  i: integer;
begin
  result :
=   false ;

  
//  横1....1
   if  (p1.y  =  p2.Y) then
  begin
    
if  p1.x  >  p2.X then
    begin
      result :
=  CanLine(P2, P1);
    end
    
else
    begin
      result :
=   true ;
      
for  i : =  p1.X  +   1  to p2.X  -   1   do
      begin
        result :
=  Maps[i, p1.Y]  =   - 2 ;
        
if  not result then
          exit;
      end;
    end;
  end
  
else   if  (p1.x  =  p2.x) then  //  竖
  begin
    
if  p1.y  >  p2.y then
    begin
      result :
=  CanLine(P2, P1);
    end
    
else
    begin
      result :
=   true ;
      
for  i : =  p1.y  +   1  to p2.y  -   1   do
      begin
        result :
=  Maps[p1.x, i]  =   - 2 ;
        
if  not result then
          exit;
      end;
    end;
  end;

end;

{*************************************************
  函数名: TLineGame.isEmptyPt
  描  述: 是否空白点
  参  数: pt: TPoint
  返回值: boolean
 
*************************************************}

function TLineGame.isEmptyPt(pt: TPoint): boolean;
begin
  result :
=  Maps[pt.X, pt.Y]  =   - 2 ;
end;



{*************************************************
  函数名: TLineGame.Create
  描  述: 创建TlineGame类
  参  数: None
  返回值: None
 
*************************************************}

constructor TLineGame.Create;
var
  i: integer;
  Res: TResourceStream;
begin
  LineMap :
=  TStringList.Create;
  Res :
=  TResourceStream.Create(HInstance, ' SRC1 ' , PChar( ' FILE1 ' ));
  LineMap.LoadFromStream(res);
  Res.Free;
  
for  i : =   1  to MAPCOUNT  do
  begin
    ptLines[i] :
=  TList.Create;
  end;
end;

{*************************************************
  函数名: TLineGame.Destroy
  描  述: 消耗TLineGame类
  参  数: None
  返回值: None
 
*************************************************}

destructor TLineGame.Destroy;
var
  i: integer;
begin
  LineMap.Free;
  
for  i : =  MAPCOUNT downto  1   do
  begin
    ptLines[i].Free;
  end;
  SetKbHook(
0 );
end;

{*************************************************
  函数名: TLineGame.SetPtLines
  描  述:  根据矩阵设置对子队列
  参  数: None
  返回值: None
 
*************************************************}

procedure TLineGame.SetPtLines;
var
  i, j: integer;
  pt: pPoint;
  mapValue: integer;
begin
  
try
    
for  i : =   1  to MAPCOUNT  do
      
for  j : =  ptLines[i].Count  -   1  downto  0   do
      begin
        Dispose(ptLines[i].Items[j]);
        ptLines[i].Delete(j);

      end;

    
for  i : =   0  to MAP_VLENGTH  -   1   do
      
for  j : =   0  to MAP_HLENGTH  -   1   do
      begin
        
new (pt);
        pt.X :
=  i;
        pt.Y :
=  j;
        mapValue :
=  Maps[i, j];
        
if  mapValue  <>   - 2  then
        begin
          ptLines[mapValue].Add(pt);
        end;
      end;
  except

  end;
end;
{*************************************************
  函数名: TLineGame.isSameMap
  描  述: 判断两点是否相似,如相似则认为是同一类型的点
  参  数: Color1, Color2: integer
  返回值: boolean
 
*************************************************}

function TLineGame.isSameMap(Color1, Color2: integer): boolean;
var
  r1, g1, b1: Integer;
  r2, g2, b2: Integer;
begin
  r1 :
=  GetRValue(Color1);
  g1 :
=  GetGValue(Color1);
  b1 :
=  GetBValue(Color1);

  r2 :
=  GetRValue(Color2);
  g2 :
=  GetGValue(Color2);
  b2 :
=  GetBValue(Color2);

  Result :
=  (abs(r1  -  r2)  <   5 ) and (abs(g1  -  g2)  <   5 ) and (abs(b1  -  b2)  <   5 )
end;

{*************************************************
  函数名: TLineGame.GetMapIndex
  描  述:  根据颜色值,判断其所属的类型队列的位置
  参  数: Color: integer
  返回值: integer
 
*************************************************}

function TLineGame.GetMapIndex(Color: integer): integer;
var
  i: integer;
  Color1: integer;
begin
  result :
=   - 2 ;
  
for  i : =   0  to LineMap.Count  -   1   do
  begin
    Color1 :
=  StrToInt(LineMap.Names[i]);
    
if  isSameMap(Color, Color1) then
    begin
      result :
=  strtoint(LineMap.ValueFromIndex[i]);
      exit;
    end;
  end;
end;
{*************************************************
  函数名: TLineGame.LeftMapCount
  描  述:  计算ptLine中剩余的点数
  参  数: None
  返回值: integer
 
*************************************************}

function TLineGame.LeftMapCount: integer;
var
  i: integer;
begin
  Result :
=   0 ;
  
for  i : =   1  to MAPCOUNT  do
  begin
    inc(Result, ptLines[i].Count);
  end;
end;

{*************************************************
  函数名: TLineGame.GetBox
  描  述:  获取游戏界面布局数据
  参  数: None
  返回值: None
 
*************************************************}

procedure TLineGame.GetBox;
var
  i, j: Integer;
  color1: Cardinal;
begin
  gh :
=  FindWindow(nil, PChar( ' QQ连连看 ' ));
  
// 生成数组
  GetWindowRect(gh, Recta);
  
for  i : =   0  to MAP_VLENGTH  -   1   do
    
for  j : =   0  to MAP_HLENGTH  -   1   do
    begin
      color1 :
=  GetColorMx(j, i);

      
if  isBackGround(color1) then
        maps[i, j] :
=   - 2
      
else
        maps[i, j] :
=  GetMapIndex(color1);
    end;
end;
{*************************************************
  函数名: TLineGame.isBackGround
  描  述:  判断是否游戏中的背景
  参  数: Color: Integer
  返回值: boolean
 
*************************************************}

function TLineGame.isBackGround(Color: Integer): boolean;
var
  r, g, b: Integer;
begin
  r :
=  GetRValue(Color);
  g :
=  GetGValue(Color);
  b :
=  GetBValue(Color);
  Result :
=  (Abs( 110   -  r)  <   20 ) and (abs( 154   -  g)  <   20 ) and (abs( 236   -  b)  <   20 );

end;
{*************************************************
  函数名: TLineGame.GetMapPos
  描  述: 获取对子矩阵中点在游戏中的位置
  参  数: i, j: integer
  返回值: Tpoint
 
*************************************************}

function TLineGame.GetMapPos(i, j: integer): Tpoint;
begin
  result.x :
=  Recta.Left  +  gLeft  +   16   +  hwidth  *  j;
  result.y :
=  recta.Top  +  gTop  +   18   +  vwidth  *  i;
end;

{*************************************************
  函数名: TLineGame.SendMouse
  描  述: 模拟发送消除对子的消息
  参  数: x1, y1, x2, y2: Integer
  返回值: None
 
*************************************************}

procedure TLineGame.SendMouse(x1, y1, x2, y2: Integer);
var
    pos1, pos2: TPoint;
  Recta: TRect;
begin
  GetWindowRect(gh, Recta);
  pos1 :
=  GetMapPos(x1, y1);
  PostMessage(gh, WM_LBUTTONDOWN, 
0 , MakeLong(pos1.X  -  Recta.Left, pos1.y  -
    Recta.Top));

  Pos2 :
=  GetMapPos(x2, y2);
  PostMessage(gh, WM_LBUTTONDOWN, 
0 , MakeLong(pos2.X  -  Recta.Left, pos2.y  -
    Recta.Top));

end;

{*************************************************
  函数名: TLineGame.RunStep
  描  述: 消除一组对子
  参  数:
  返回值: None
 
*************************************************}

procedure TLineGame.RunStep();
var
  p1, p2: TPoint;
begin
  gh :
=  FindWindow(nil, PChar( ' QQ连连看 ' ));
  SetMemData(gh);
  GetBox;
  SetPtLines;
  
if  Search(p1, p2) then
  begin
     SendMouse(p1.X, p1.Y, p2.X, p2.Y);
  end;
end;

{*************************************************
  函数名: TLineGame.KillAll
  描  述:  消除所有对子
  参  数:
  返回值: None
 
*************************************************}

procedure TLineGame.KillAll();
var
  p1, p2: TPoint;
  SearchFail: Boolean;
begin
  gh :
=  FindWindow(nil, PChar( ' QQ连连看 ' ));
  SetMemData(gh);
  GetBox;
  SetPtLines;
  repeat
    SearchFail :
=   true ;
    
while  Search(p1, p2)  do
    begin
      SearchFail :
=  False;
      SendMouse(p1.X, p1.Y, p2.X, p2.Y);
    end;
  until (LeftMapCount 
=   0 ) or SearchFail;

end;

{*************************************************
  函数名: TLineGame.AutoStart
  描  述: 自动开始游戏
  参  数: None
  返回值: None
 
*************************************************}

procedure TLineGame.AutoStart;
begin
  gh :
=  FindWindow(nil, PChar( ' QQ连连看 ' ));
  PostMessage(gh, WM_LBUTTONDOWN, 
0 , MakeLong( 684 532 ));
  PostMessage(gh, WM_LBUTTONUP, 
0 , MakeLong( 684 532 ));
end;

procedure TLineGame.SetMemData(hnd: THandle);
var ThreadProcessID:integer;
begin
  ThreadProcessID:
= GetWindowThreadProcessId(hnd,nil);
  
if  ThreadProcessID = FGameThreadID then exit;

  FGameThreadID:
= ThreadProcessID ;

  SetKbHook(FGameThreadID);
end;

end.

 

QQLLK.dpr

{*************************************************
  Copyright (C), 
2004, 风月工作室.
  作者: 追风逐月
  版本: 
1.0
  日期: 2005年02月01日
  描述:
  修改历史:
    徐明     
2005/02/01      1.0        创建该文件
    ...
*************************************************}

{$J+}
program QQLLK;
uses
  Windows,
  Messages,
  SysUtils,
  ShellAPI,
  LineGame in 
' LineGame.pas ' ;

{$R qqllk.res}
const
  

  
// 资源常量定义 //     ;不要修改!
  
  MAINICON  =   ' MAINICON ' ;
  IDD_MAINDLG 
=   1000 ;
  MAIN_SINGLE 
=   1002 ;
  MAIN_ALL 
=   1003 ;
  MAIN_OPTION 
=   1006 ;
  MAIN_ABOUT 
=   1001 ;
  MAIN_EXIT 
=   1004 ;

  IDD_ABOUTDLG 
=   3000 ;
  ABOUT_OK 
=   3001 ;
  ABOUT_CLOSE 
=   3002 ;
  ABOUT_FILE 
=   3003 ;
  ABOUT_AUTHOR 
=   3004 ;
  ABOUT_MEMO 
=   3005 ;

  IDD_OPTIONDLG 
=   2000 ;
  OPTION_OK 
=   2001 ;
  OPTION_CANCEL 
=   2002 ;
  OPTION_ABOUT 
=   2003 ;
  OPTION_CLOSE 
=   2004 ;
  OPTION_AUTOSTART 
=   1000 ;
  OPTION_AUTOTOOLS 
=   1001 ;
  OPTION_RANDOM 
=   1006 ;
  OPTION_COMPUTER 
=   1007 ;
  OPTION_TIMER 
=   1008 ;

const
  

  
// 常量数据声明 //
  
  ( * 颜色设定 * )
  
// clBackground = $8B190B;  // 背景颜色
  clBackground  =  $87D34;  // 背景颜色
  clText  =  $E4E4E4;  // 文字颜色
  
// clFrom = $871200;  // 标题栏渐变起始颜色
  
// clTo = $808080;  // 标题栏渐变结束颜色
  clFrom  =  $87D34;  // 标题栏渐变起始颜色
  clTo  =  $ 808080 // 标题栏渐变结束颜色
  ID_HOTKEYF2  =   200 ;    // 热键F2
  ID_HOTKEYF3  =   300 ;    // 热键F3
  ID_HOTKEYCTRLF4  =   400 ;   // 热键CTRL+F4
  szMainCaption  =   ' QQ连连看外挂 ' ;
  
{*选项对话框*}
  szOptionCaption 
=   ' 选项 ' // 关于对话框标题

  (
* 关于对话框 * )
  szAboutCaption 
=   ' 关于 QQ连连看外挂 ' // 关于对话框标题
  szFile  =   ' 版本 1.1.0.0 ' // 注册机说明
  szAuthor  =   ' 『由[追风逐月]编写』 ' // 注册机作者
  szGreet  =   // 字幕内容每行不要超过32个字符(16个汉字)
   ' 本软件由风月工作室出品 ' # 10 # 10   +   ' 〖联系方式〗 ' # 10 # 10 ' coolchyni@gmail.com ' # 10 # 10 +
     
' 〖快捷键〗 ' # 10 # 10 + ' F2:消除一组对子 ' # 10 ' F3:消除所有对子 ' # 10 ' CTRL+F4:显示/隐藏窗口 ' # 10 # 10 +
     
' 〖特别感谢〗 ' # 10 # 10 +
    
' 各位QQ游戏爱好者 ' # 10 ' 我的哥们 ' # 10 ' 以及所有曾帮助过我的人 ' # 10 # 10   +
    
' 〖免责声明〗 ' # 10 # 10 ' 本软件属于免费软件 ' # 10 ' 可以自由使用 ' # 10 ' 由此造成的一切后果(如QQ号被封) ' # 10 ' 均与作者无关 ' # 10 # 10   +
    
' 〖版本信息〗 ' # 10 # 10 ' [1.0.0.0] ' # 10 ' 实现外挂程序基本功能 ' # 10 ' [1.1.0.0] ' # 10 ' 使用内存补丁的方法, ' # 10 ' 去掉了原程序包中的连连看替换文件. ' # 10 '   '  ;
var
  BKC: HBRUSH; 
// 背景画刷
  
// h_Cur: HCURSOR;  // 鼠标指针句柄
  h_Inst: HINST;  // 程序图标句柄
  h_Icon: HICON;  // 实例句柄
  h_mainDlg: HWND;

  g_AutoStart: 
boolean   =   false // 自动开始
  g_AutoTools:  boolean   =   false // 自动使用工具
  g_Random:  boolean   =   false // 隐藏窗口
  g_Computer:  boolean   =   false // 电脑托管

  g_timer: array[
0 .. 254 ] of  char   =   ' 1000 ' // 消除频率
  g_internal:integer = 1000 ;                  // 定时间隔
  LineGames: TLineGame;                     // 游戏类
function LinesInStr(srcStr: string): smallint;
var
  i: integer;
begin
  Result :
=   1 ;
  
for  i : =   0  to Length(srcStr)  -   1   do
    
if  srcStr[i]  =  # 10  then
      Result :
=  Result  +   1 ;
  
if  Result  >   1  then
    Result :
=  Result  -   1 ;
end;
//
// 动态显示窗体函数
procedure AnimateShow(hDlg: HWND);
var
  Rt: TRECT;
  x, y, i: smallint;
  h_Rgn: HRGN;
begin
  ShowWindow(hDlg, SW_HIDE);
  GetWindowRect(hDlg, Rt);
  x :
=  (Rt.right  -  Rt.left) div  2 ;
  y :
=  (Rt.bottom  -  Rt.top) div  2 ;
  
for  i : =   0  to (Rt.Right div  2 do
  begin
    h_Rgn :
=  CreateRectRgn(x  -  i, y  -  i, x  +  i, y  +  i);
    SetWindowRgn(hDlg, h_Rgn, True);
    ShowWindow(hDlg, SW_SHOW);

    DeleteObject(h_Rgn);
  end;
  SetWindowPos(hDlg, HWND_TOPMOST, rt.Left, rt.Top, rt.Right 
-  rt.Left, rt.Bottom
    
-  rt.Top,  0 );

end;

//
// 绘制标题栏函数
// hDC:            绘制窗体的设备环境句柄
// hIco:            标题栏图标句柄
// szCaption:    标题栏标题
// rect:            标题栏矩形区域
// clBegin:        标题栏渐变起始颜色
// clEnd:        标题栏渐变结束颜色
procedure PaintCaption(h_DC: HDC; h_Ico: HICON;  const  szCaption: string; rect:
  TRECT;
  clBegin: COLORREF; clEnd: COLORREF);
var
  brush: HBRUSH;
  _logbrush: LOGBRUSH; 
// 上色画刷
  colorrect: TRECT;  // 上色矩形区域
  h_font: HFONT;  // 标题栏字体
  Haf, i: smallint;
  R, G, B, fr, fg, fb, dr, dg, db: smallint;
begin
  fr :
=  GetRValue(clFrom);  // 分解颜色
  fg : =  GetGValue(clFrom);
  fb :
=  GetBValue(clFrom);
  dr :
=  GetRValue(clTo);
  dg :
=  GetGValue(clTo);
  db :
=  GetBValue(clTo);

  Haf :
=  (rect.right  -  rect.left) div  2 // 计算标题栏矩形区域中心
  
// 设定上色矩形区域高度
  colorrect.top : =   0 ;
  colorrect.bottom :
=  rect.bottom  -  rect.top;

  
// 建立渐变上色画刷
  _logbrush.lbStyle : =  BS_SOLID;
  _logbrush.lbHatch :
=   0 ;
  
for  i : =   0  to Haf  do
  begin
    
// 设定左半上色矩形区域一次填充位置
    colorrect.left : =  MulDiv(i, Haf, Haf);
    colorrect.right :
=  MulDiv(i  +   1 , Haf, Haf);
    
// 颜色渐变
    R : =  fr  +  MulDiv(i, dr, Haf);
    G :
=  fg  +  MulDiv(i, dg, Haf);
    B :
=  fb  +  MulDiv(i, db, Haf);
    
if  (R  >   255 ) then
      R :
=   255 ;
    
if  (G  >   255 ) then
      G :
=   255 ;
    
if  (B  >   255 ) then
      B :
=   255 ;
    _logbrush.lbColor :
=  RGB(R, G, B);
    brush :
=  CreateBrushIndirect(_logbrush);
    FillRect(h_DC, colorrect, brush); 
// 填充左半区域
    
// 设定右半上色矩形区域一次填充位置
    colorrect.left : =  (rect.right  -  rect.left)  -  (MulDiv(i, Haf, Haf));
    colorrect.right :
=  (rect.right  -  rect.left)  -  (MulDiv(i  +   1 , Haf, Haf));
    FillRect(h_DC, colorrect, brush); 
// 填充右半区域
    DeleteObject(brush);
  end;

  _logbrush.lbColor :
=  $9E6A54;
  brush :
=  CreateBrushIndirect(_logbrush);
  FrameRect(h_DC, rect, brush); 
// 绘制标题栏边框
  DeleteObject(brush);

  SetTextColor(h_DC, $FFFFFF);
  SetBkMode(h_DC, TRANSPARENT); 
// 设定标题栏字体属性
  rect.left : =   2 ;
  rect.top :
=   2 ;
  rect.bottom :
=  rect.Bottom  -   2 ;
  h_font :
=  CreateFont( - 12 0 0 0 700 0 0 0 , DEFAULT_CHARSET,
    OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
    DEFAULT_PITCH or FF_DONTCARE, 
' 宋体 ' );
  
// (宋体9号粗体字)
  SelectObject(h_DC, h_font);
  
if  h_Ico  <>   0  then  // 若有图标则会制图标
  begin
    DrawIconEx(h_DC, 
2 2 , h_Ico,  16 16 0 0 , DI_NORMAL);
    rect.left :
=   20 ;
  end;
  
// 绘制标题栏标题
  DrawText(h_DC, PChar(szCaption),  - 1 , rect, DT_SINGLELINE or DT_VCENTER);
  DeleteObject(h_font);
end;

//
// 绘制按钮函数
// pdis:            绘制内容结构指针
procedure DrawButton(pdis: PDRAWITEMSTRUCT);
var
  szText: array[
0 .. 9 ] of  char // 按钮文字
begin
  FillRect(pdis.hDC, pdis.rcItem, BKC); 
// 以背景色填充按钮

  SetTextColor(pdis.hDC, clText);
  SetBkMode(pdis.hDC, TRANSPARENT);

  
// 尚未点击,绘制按钮边框-突起状态
  DrawEdge(pdis.hDC, pdis.rcItem, BDR_RAISEDOUTER, BF_RECT);
  GetWindowText(pdis.hwndItem, szText, sizeof(szText));
  DrawText(pdis.hDC, szText, 
- 1 , pdis.rcItem, DT_SINGLELINE or DT_CENTER or
    DT_VCENTER);

  
// 已被按下,绘制按钮边框-凹陷状态
  
// if (pdis.itemState and ODS_SELECTED)=ODS_SELECTED then

  
if  (pdis.itemState and ODS_SELECTED)  <>   0  then
  begin
    SetTextColor(pdis.hDC, $00DDFF);
    DrawText(pdis.hDC, szText, 
- 1 , pdis.rcItem, DT_SINGLELINE or DT_CENTER or
      DT_VCENTER);
    DrawEdge(pdis.hDC, pdis.rcItem, BDR_SUNKENOUTER, BF_RECT);
  end;
end;

function ScrollProc(h_Wnd: HWND; Msg, wParam, lParam: DWORD): LRESULT; stdcall;
var
  h_DC: HDC;
  ps: TPAINTSTRUCT;
  rc: TRECT;
  h_font: HFONT;

begin
  
case  Msg of
    WM_PAINT:
      begin
        
// 绘制字幕内容
        h_DC : =  BeginPaint(h_Wnd, ps);
        GetClientRect(h_Wnd, rc);
        SetTextColor(h_DC, clText);
        SetBkMode(h_DC, TRANSPARENT);
        h_font :
=  CreateFont( - 12 0 0 0 0 0 0 0 , DEFAULT_CHARSET,
          OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH
          or FF_DONTCARE, 
' 宋体 ' );
        SelectObject(h_DC, h_font);
        DrawText(h_DC, szGreet, 
- 1 , rc, DT_CENTER);
        EndPaint(h_Wnd, ps);
        DeleteObject(h_font);
      end;
  
else
    begin
      
// l:=GetWindowLong(h_Wnd,GWL_USERDATA);
      
// CallWindowProc(@l,h_Wnd,Msg,wParam,lParam);
    end;
  end;
  result :
=   1 ;
end;
function AboutProc(hDlg: HWND; Msg, wParam, lParam: DWORD): LRESULT; stdcall;
const
  rcCaption: TRECT 
=  ();
  i: smallint 
=   0 ;
  w: smallint 
=   0 ;
  h: smallint 
=   0 ;
  h_Memo: HWND 
=   0 ;
  memo: HWND 
=   0 ;
  lines: smallint 
=   1 // 字幕行数
var
  h_dc: HDC;
  ps: TPAINTSTRUCT;
  pdis: PDRAWITEMSTRUCT;
  pt: TPOINT;
  rcMemo: TRECT;
  lUser: integer;
  h_Font: HFONT;
  h_File: HWND;
begin
  
case  Msg of
    WM_INITDIALOG:
      begin
        GetClientRect(hDlg, rcCaption);
        rcCaption.bottom :
=  rcCaption.top  +   20 ;

        h_Memo :
=  GetDlgItem(hDlg, ABOUT_MEMO);
        h_File :
=  GetDlgItem(hDlg, ABOUT_FILE);
        h_Font :
=  CreateFont( - 12 0 0 0 700 0 0 0 , DEFAULT_CHARSET,
          OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH
          or FF_DONTCARE, 
' 宋体 ' );
        SendMessage(h_File, WM_SETFONT, h_Font, 
0 );

        SetDlgItemText(hDlg, ABOUT_FILE, szFile);
        SetDlgItemText(hDlg, ABOUT_AUTHOR, szAuthor);
        SetWindowText(hDlg, szAboutCaption);

        GetClientRect(h_Memo, rcMemo); 
// 得到字幕显示区域大小
        w : =  rcMemo.right  -  rcMemo.left;
        h :
=  rcMemo.bottom  -  rcMemo.top;
        i :
=  h;
        lines :
=  LinesInStr(szGreet);  // 计算字幕行数

        
// 建立显示字幕子窗体
        memo : =  CreateWindow( ' Static ' '' , WS_VISIBLE or WS_CHILD or SS_CENTER,
          
0 , h, w,  12   *  lines, h_Memo,  0 , h_Inst, nil);
        
// 设定子窗体消息处理函数
        lUser : =  SetWindowLong(memo, GWL_WNDPROC, integer(@ScrollProc));
        SetWindowLong(memo, GWL_USERDATA, lUser);

        AnimateShow(hDlg);
        SetTimer(hDlg, 
168 80 , nil);  // 设定定时器每80毫秒触发一次
        result : =   1 ;
      end;
    WM_TIMER:
      begin
        
// 定时器触发时移动子窗体,形成字幕
        Sleep( 20 );
        i :
=  i  -   1 ;
        SetWindowPos(memo, 
0 0 , i, w,  12   *  lines,  0 );
        
if  ( - (i  +  ( 12   *  lines))  >   0 ) then
          i :
=  h;  // 字幕到达尾部时,重新开始循环
      end;

    WM_LBUTTONDOWN:
      begin
        pt.x :
=  LOWORD(lParam);
        pt.y :
=  HIWORD(lParam);
        
if  (PtInRect(rcCaption, pt)) then
          PostMessage(hDlg, WM_NCLBUTTONDOWN, HTCAPTION, 
0 );
      end;

    WM_PAINT:
      begin
        h_dc :
=  BeginPaint(hDlg, ps);
        PaintCaption(h_dc, h_Icon, szAboutCaption, rcCaption, clFrom, clTo);
        EndPaint(hDlg, ps);
      end;

    WM_COMMAND:
      begin
        
case  wParam of
          ABOUT_OK:
            begin
              KillTimer(hDlg, 
168 );  // 销毁定时器
              EndDialog(hDlg,  0 );
            end;
          ABOUT_CLOSE:
            begin
              KillTimer(hDlg, 
168 );  // 销毁定时器
              EndDialog(hDlg,  0 );
            end;
        end;
        result :
=   0 ;
      end;

    WM_DRAWITEM:
      begin
        pdis :
=  PDRAWITEMSTRUCT(lParam);
        DrawButton(pdis);
        Result :
=   0 ;
      end;
    
// /
    
// 响应绘制窗体内容消息
    WM_CTLCOLORDLG:
      begin
        SetTextColor(wParam, clText);
        SetBkMode(wParam, TRANSPARENT);
        Result :
=  BKC;
      end;
    WM_CTLCOLORSTATIC:
      begin
        SetTextColor(wParam, clText);
        SetBkMode(wParam, TRANSPARENT);
        Result :
=  BKC;
      end;
  
else
    Result :
=   0 ;
  end;
end;
function OptionProc(hDlg: HWND; Msg, wParam, lParam: DWORD): LRESULT; stdcall;
const
  rcCaption: TRECT 
=  ();
  i: smallint 
=   0 ;
  w: smallint 
=   0 ;
  h: smallint 
=   0 ;
  h_Memo: HWND 
=   0 ;
  memo: HWND 
=   0 ;
  lines: smallint 
=   1 // 字幕行数
var
  h_dc: HDC;
  ps: TPAINTSTRUCT;
  pdis: PDRAWITEMSTRUCT;
  pt: TPOINT;
  h_Font: HFONT;
  h_File: HWND;
  e: integer;
begin
  
case  Msg of
    WM_INITDIALOG:
      begin
        GetClientRect(hDlg, rcCaption);
        rcCaption.bottom :
=  rcCaption.top  +   20 ;

        h_Memo :
=  GetDlgItem(hDlg, ABOUT_MEMO);
        h_File :
=  GetDlgItem(hDlg, ABOUT_FILE);
        h_Font :
=  CreateFont( - 12 0 0 0 700 0 0 0 , DEFAULT_CHARSET,
          OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH
          or FF_DONTCARE, 
' 宋体 ' );
        SendMessage(h_File, WM_SETFONT, h_Font, 
0 );

        CheckDlgButton(hdlg, OPTION_AUTOSTART, ord(g_AutoStart));
        CheckDlgButton(hdlg, OPTION_AUTOTOOLS, ord(g_AutoTools));
        CheckDlgButton(hdlg, OPTION_RANDOM, ord(g_Random));
        CheckDlgButton(hdlg, OPTION_COMPUTER, ord(g_Computer));
        SetDlgItemText(hDlg, OPTION_TIMER, g_timer);

        result :
=   1 ;
      end;

    WM_LBUTTONDOWN:
      begin
        pt.x :
=  LOWORD(lParam);
        pt.y :
=  HIWORD(lParam);
        
if  (PtInRect(rcCaption, pt)) then
          PostMessage(hDlg, WM_NCLBUTTONDOWN, HTCAPTION, 
0 );
      end;

    WM_PAINT:
      begin
        h_dc :
=  BeginPaint(hDlg, ps);
        PaintCaption(h_dc, h_Icon, szOptionCaption, rcCaption, clFrom, clTo);
        EndPaint(hDlg, ps);
      end;

    WM_COMMAND:
      begin
        
case  wParam of
          OPTION_OK:
            begin

              g_AutoStart :
=  IsDlgButtonChecked(hDlg, OPTION_AUTOSTART)  =
                BST_CHECKED;
              g_AutoTools :
=  IsDlgButtonChecked(hDlg, OPTION_AUTOTOOLS)  =
                BST_CHECKED;
              g_Random :
=  IsDlgButtonChecked(hDlg, OPTION_RANDOM)  =
                BST_CHECKED;
              g_Computer :
=  IsDlgButtonChecked(hDlg, OPTION_COMPUTER)  =
                BST_CHECKED;
              GetDlgItemText(hDlg, OPTION_TIMER, g_timer, 
255 );
              
// LineGames.AutoStart;
              Val(g_timer, g_internal, E);
              
if  (E  <>   0 ) or (g_internal  <   500 ) or (g_internal  >   10000 ) then
              begin
                g_internal :
=   1000 ;
                MessageBox(hDlg, pchar(
' 请输入一个有效的整数(500~10000)! ' ),
                  pchar(
' 输入错误 ' ),
                  MB_ICONERROR);
                exit;
              end;
              
if  g_autostart or g_Computer then
                SetTimer(h_mainDlg, 
169 , g_internal, nil)
              
else
                KillTimer(h_mainDlg, 
169 );
              
// 设定定时器每1000毫秒触发一次
              EndDialog(hDlg,  0 );
            end;
          OPTION_ABOUT: DialogBox(h_Inst, LPCTSTR(IDD_ABOUTDLG), hDlg,
              @AboutProc);

          OPTION_CANCEL, OPTION_CLOSE:
            begin
              EndDialog(hDlg, 
0 );
            end;
        end;
        result :
=   0 ;
      end;

    WM_DRAWITEM:
      begin
        pdis :
=  PDRAWITEMSTRUCT(lParam);
        DrawButton(pdis);
        Result :
=   0 ;
      end;
    
// /
    
// 响应绘制窗体内容消息
    WM_CTLCOLORDLG:
      begin
        SetTextColor(wParam, clText);
        SetBkMode(wParam, TRANSPARENT);
        Result :
=  BKC;
      end;
    WM_CTLCOLORSTATIC:
      begin
        SetTextColor(wParam, clText);
        SetBkMode(wParam, TRANSPARENT);
        Result :
=  BKC;
      end;
  
else
    Result :
=   0 ;
  end;
end;

function MainProc(hDlg: HWND; Msg, wParam, lParam: DWORD): LRESULT; stdcall;
const
  rcCaption: TRECT 
=  ();
var
  h_dc: HDC;
  ps: TPAINTSTRUCT;
  pdis: PDRAWITEMSTRUCT;
  pt: TPOINT;
begin
  
case  Msg of
    WM_INITDIALOG:
      begin
        h_mainDlg :
=  hDlg;
        GetClientRect(hDlg, rcCaption);
        rcCaption.bottom :
=  rcCaption.top  +   20 ;
        SetWindowText(hDlg, szMainCaption);
        AnimateShow(hDlg);

        
if  (RegisterHotKey(hDlg, ID_HOTKEYF2,  0 , VK_F2)  =   false ) then
        begin
          
// hotkey注册
          
// 失败了的话...
          MessageBox(hDlg, pchar( ' 注册热键F2失败! ' ), pchar( ' Error ' ),
            MB_ICONERROR);
          PostQuitMessage(
0 );
        end;
        
if  (RegisterHotKey(hDlg, ID_HOTKEYF3,  0 , VK_F3)  =   false ) then
        begin
          
// hotkey注册
          
// 失败了的话...
          MessageBox(hDlg, pchar( ' 注册热键F3失败! ' ), pchar( ' Error ' ),
            MB_ICONERROR);
          PostQuitMessage(
0 );
        end;
        
if  (RegisterHotKey(hDlg, ID_HOTKEYCTRLF4, MOD_CONTROL, VK_F4)  =   false )
          then
        begin
          
// hotkey注册
          
// 失败了的话...
          MessageBox(hDlg, pchar( ' 注册热键CTRL+F4失败! ' ), pchar( ' Error ' ),
            MB_ICONERROR);
          PostQuitMessage(
0 );
        end;
        result :
=   1 ;
      end;
    WM_HOTKEY: 
// 处理WM_HOTKEY消息
      begin
        
case  HIWORD(lParam) of
          VK_F3: LineGames.KillAll;
          vk_F2: LineGames.RunStep;
          VK_F4:
            begin
              
if  IsWindowVisible(hDlg) then
                showWindow(hDlg, SW_HIDE)
              
else
                showWindow(hDlg, SW_SHOW);

            end;
        end;
        result :
=   0 ;
      end;

    WM_LBUTTONDOWN:
      begin
        
// 响应鼠标左键按下消息,若在标题栏内则使窗体移动
        pt.x : =  LOWORD(lParam);
        pt.y :
=  HIWORD(lParam);
        
if  PtInRect(rcCaption, pt) then
          PostMessage(hDlg, WM_NCLBUTTONDOWN, HTCAPTION, 
0 );
      end;
    WM_PAINT:
      begin
        
// 响应绘制消息,绘制标题栏
        h_DC : =  BeginPaint(hDlg, ps);
        PaintCaption(h_DC, h_Icon, szMainCaption, rcCaption, clFrom, clTo);
        EndPaint(hDlg, ps);
      end;

    WM_COMMAND:
      begin
        
case  wParam of
          MAIN_SINGLE:
            begin
              LineGames.RunStep;
            end;
          MAIN_ALL: LineGames.KillAll();
          MAIN_OPTION: DialogBox(h_Inst, LPCTSTR(IDD_OPTIONDLG), hDlg,
              @OptionProc);
          MAIN_ABOUT:
            DialogBox(h_Inst, LPCTSTR(IDD_ABOUTDLG), hDlg, @AboutProc);

          MAIN_EXIT: EndDialog(hDlg, 
0 );
        end;
        result :
=   0 ;
      end;
    WM_DRAWITEM:
      begin
        pdis :
=  PDRAWITEMSTRUCT(lParam);
        DrawButton(pdis);
        Result :
=   0 ;
      end;
    WM_TIMER:
      begin
        
// 定时器触发时移动子窗体,形成字幕
         if  g_AutoStart then
          LineGames.AutoStart;
        
if  g_Computer then
          LineGames.RunStep;

        
if  g_Random then
        SetTimer(hDlg,
169 , 500 + Random(g_internal - 500 ),nil);
      end;
    
// /
    
// 响应绘制窗体内容消息
    WM_CTLCOLORDLG:
      begin
        SetTextColor(wParam, clText);
        SetBkMode(wParam, TRANSPARENT);
        Result :
=  BKC;
      end;
    WM_CTLCOLORSTATIC:
      begin
        SetTextColor(wParam, clText);
        SetBkMode(wParam, TRANSPARENT);
        Result :
=  BKC;
      end;
    WM_DESTROY:
      begin
        UnregisterHotKey(hDlg, ID_HOTKEYF2); 
// 用完记得要收回
        UnregisterHotKey(hDlg, ID_HOTKEYF3);  // 用完记得要收回
        UnregisterHotKey(hDlg, ID_HOTKEYCTRLF4);  // 用完记得要收回
        KillTimer(hDlg,  169 );
        PostQuitMessage(
0 );
      end;
  
else
    Result :
=   0 ;
  end;
end;
//
// 程序入口函数
//
begin
  h_Inst :
=  GetModuleHandle(nil);  // 保存实例句柄
  BKC : =  CreateSolidBrush(clBackground);  // 建立背景画刷
  
// h_Cur := LoadCursor(h_Inst, LPCTSTR(IDC_HAND));  // 载入鼠标指针
  h_Icon : =  LoadIcon(h_Inst, LPCTSTR(MAINICON));  // 载入程序图标


  
// 显示协议对话框
  LineGames : =  TLineGame.Create;

  DialogBox(h_Inst, LPCTSTR(IDD_MAINDLG), 
0 , @MainProc);
  LineGames.Free;
  DeleteObject(BKC); 
// 释放背景画刷
  
// 退出程序
  ExitProcess( 0 );
end.

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值