#NoTrayIcon #include #include #include ; ... (DLL setup unchanged) ; --- Load icon texture --- _GDIPlus_Startup() Local $hImage = _GDIPlus_ImageLoadFromFile("C:\Program Files (x86)\AutoIt3\Icons\MyAutoIt3_Blue.ico") Local $iIconW = 128, $iIconH = 128 Local $tIconBytes = DllStructCreate("dword[" & ($iIconW * $iIconH) & "]") Local $pIconBytes = DllStructGetPtr($tIconBytes) If $hImage Then ; Create 128x128 bitmap Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iIconW, $iIconH, $GDIP_PXF32ARGB) Local $hCtx = _GDIPlus_ImageGetGraphicsContext($hBitmap) ; Clear with transparent background _GDIPlus_GraphicsClear($hCtx, 0x00000000) ; Draw icon, stretching to the needed size _GDIPlus_GraphicsDrawImageRect($hCtx, $hImage, 0, 0, $iIconW, $iIconH) ; Lock bits and copy to our raw buffer Local $tBitmapData = _GDIPlus_BitmapLockBits($hBitmap, 0, 0, $iIconW, $iIconH, $GDIP_ILMREAD, $GDIP_PXF32ARGB) Local $pScan0 = DllStructGetData($tBitmapData, "Scan0") Local $iStride = DllStructGetData($tBitmapData, "Stride") For $iy = 0 To $iIconH - 1 Local $tRow = DllStructCreate("dword[" & $iIconW & "]", $pScan0 + $iy * $iStride) For $ix = 0 To $iIconW - 1 DllStructSetData($tIconBytes, 1, DllStructGetData($tRow, 1, $ix + 1), $iy * $iIconW + $ix + 1) Next Next _GDIPlus_BitmapUnlockBits($hBitmap, $tBitmapData) _GDIPlus_GraphicsDispose($hCtx) _GDIPlus_BitmapDispose($hBitmap) _GDIPlus_ImageDispose($hImage) EndIf ; =========================================================== ; Continuation of main loop — overriding collision logic here ; =========================================================== ; ... (rotation and speed limits unchanged) ; ============================================================ ; AutoIt3 — 3D Maze Ball ; Controls: Up/Down = push ball, Left/Right = turn camera ; Q / Esc = Exit ; ============================================================ ; --- Load DLL --- Local $sDllFile = @ScriptDir & "\mazeball_rc_32.dll" If @AutoItX64 Then $sDllFile = @ScriptDir & "\mazeball_rc.dll" If Not FileExists($sDllFile) Then MsgBox(16, "Error", "DLL not found: " & $sDllFile & @CRLF & "Run build_maze.bat first!") Exit EndIf Local $hDll = DllOpen($sDllFile) If $hDll = -1 Then MsgBox(16, "Error", "Failed to load DLL: " & $sDllFile) Exit EndIf ; --- Screen parameters --- Local Const $SW = 640 Local Const $SH = 480 Local Const $MAP_W = 16 Local Const $MAP_H = 16 ; --- Maze map --- Local $aMap = _BuildMazeMap() ; ----------------------------------------------------------- ; Player / ball state ; ----------------------------------------------------------- Local $posX = 1.5, $posY = 1.5 ; position in world (in tiles) Local $velX = 0.0, $velY = 0.0 ; velocity (world units per frame) ; Camera direction (unit vector, updated with position) Local $dirX = 1.0, $dirY = 0.0 Local $planeX = 0.0, $planeY = 0.66 ; camera plane (FOV ~66 degrees) Local $rollX = 0.0 ; forward/backward rotation Local $rollY = 0.0 ; left/right rotation Local $fDrag = 0.985 ; inertia — closer to 1 rolls longer Local $fAccel = 0.005 ; acceleration per frame Local $fBounce = 0.65 ; bounce elasticity coefficient Local $fRotSpeed = 0.035 ; manual camera turn speed Local $fAutoRot = 0.03 ; auto-turn speed into corridor Local $margin = 0.45 ; margin from wall (matches visual shadow) ; --- Window --- Local $hWnd = GUICreate("AutoIt3 3D Maze", $SW, $SH, -1, -1) GUISetState(@SW_SHOW, $hWnd) ; --- Pixel buffer --- Local $tBuf = DllStructCreate("dword[" & ($SW * $SH) & "]") Local $pBuf = DllStructGetPtr($tBuf) ; --- BITMAPINFOHEADER for StretchDIBits --- Local $tBMI = DllStructCreate("dword biSize;long biWidth;long biHeight;word biPlanes;word biBitCount;dword biCompression;dword biSizeImage;long biXPelsPerMeter;long biYPelsPerMeter;dword biClrUsed;dword biClrImportant") DllStructSetData($tBMI, "biSize", 40) DllStructSetData($tBMI, "biWidth", $SW) DllStructSetData($tBMI, "biHeight", -$SH) DllStructSetData($tBMI, "biPlanes", 1) DllStructSetData($tBMI, "biBitCount", 32) DllStructSetData($tBMI, "biCompression", 0) ; Initialize Z-buffer in C DLL DllCall($hDll, "none:cdecl", "Maze_Init", "int", $SW) ; Create map structure for C DLL Local $tMap = DllStructCreate("byte[" & ($MAP_W * $MAP_H) & "]") For $y = 0 To $MAP_H - 1 For $x = 0 To $MAP_W - 1 DllStructSetData($tMap, 1, $aMap[$y][$x], $y * $MAP_W + $x + 1) Next Next Local $pMap = DllStructGetPtr($tMap) Local $tLast = TimerInit() Local $bQuit = False Local $iFrames = 0 ; =========================================================== ; MAIN LOOP ; =========================================================== While Not $bQuit If GUIGetMsg() = $GUI_EVENT_CLOSE Then $bQuit = True If _Key(0x51) Or _Key(0x1B) Then $bQuit = True ; Q or Esc Local $bManualRot = False If _Key(0x25) Then ; Left arrow $bManualRot = True Local $hitL = False Local $tx = $posX - $planeX * 0.04 Local $ty = $posY - $planeY * 0.04 If _CheckCircleWall($tx, $ty, $margin, $aMap)[0] Then $hitL = True If Not $hitL Then $velX -= $planeX * $fAccel $velY -= $planeY * $fAccel Else ; Turn right (camera inversion) Local $ang = -$fRotSpeed Local $oX = $dirX $dirX = $dirX * Cos($ang) - $dirY * Sin($ang) $dirY = $oX * Sin($ang) + $dirY * Cos($ang) Local $oPX = $planeX $planeX = $planeX * Cos($ang) - $planeY * Sin($ang) $planeY = $oPX * Sin($ang) + $planeY * Cos($ang) ; Rotate velocity vector to preserve momentum relative to screen Local $ovX = $velX $velX = $velX * Cos($ang) - $velY * Sin($ang) $velY = $ovX * Sin($ang) + $velY * Cos($ang) EndIf EndIf If _Key(0x27) Then ; Right arrow $bManualRot = True Local $hitR = False Local $tx2 = $posX + $planeX * 0.04 Local $ty2 = $posY + $planeY * 0.04 If _CheckCircleWall($tx2, $ty2, $margin, $aMap)[0] Then $hitR = True If Not $hitR Then $velX += $planeX * $fAccel $velY += $planeY * $fAccel Else ; Turn left (camera inversion) Local $ang2 = $fRotSpeed Local $oX2 = $dirX $dirX = $dirX * Cos($ang2) - $dirY * Sin($ang2) $dirY = $oX2 * Sin($ang2) + $dirY * Cos($ang2) Local $oPX2 = $planeX $planeX = $planeX * Cos($ang2) - $planeY * Sin($ang2) $planeY = $oPX2 * Sin($ang2) + $planeY * Cos($ang2) ; Rotate velocity vector to preserve momentum relative to screen Local $ovX2 = $velX $velX = $velX * Cos($ang2) - $velY * Sin($ang2) $velY = $ovX2 * Sin($ang2) + $velY * Cos($ang2) EndIf EndIf ; --- Acceleration along camera direction --- ; Player pushes ball where camera looks If _Key(0x26) Then ; Up — push forward $velX += $dirX * $fAccel $velY += $dirY * $fAccel EndIf If _Key(0x28) Then ; Down — push backward $velX -= $dirX * $fAccel $velY -= $dirY * $fAccel EndIf ; --- Automatic turn (steer into corridor after turning) --- Local $vSpd = Sqrt($velX * $velX + $velY * $velY) If Not $bManualRot And $vSpd > 0.015 Then ; Determine target direction by velocity vector Local $tDirX = 0, $tDirY = 0 If Abs($velX) > Abs($velY) Then $tDirX = ($velX > 0) ? 1 : -1 Else $tDirY = ($velY > 0) ? 1 : -1 EndIf ; Check if corridor is open in target direction If Not _IsWall($aMap, $posX + $tDirX * 0.6, $posY + $tDirY * 0.6) Then ; Dot product — how aligned we are Local $dot = $dirX * $tDirX + $dirY * $tDirY If $dot < 0.99 Then ; Not aligned — need to turn ; Cross product — determine turn direction Local $cross = $dirX * $tDirY - $dirY * $tDirX Local $turn = ($cross > 0) ? $fAutoRot : -$fAutoRot Local $oX_a = $dirX $dirX = $dirX * Cos($turn) - $dirY * Sin($turn) $dirY = $oX_a * Sin($turn) + $dirY * Cos($turn) Local $oPX_a = $planeX $planeX = $planeX * Cos($turn) - $planeY * Sin($turn) $planeY = $oPX_a * Sin($turn) + $planeY * Cos($turn) EndIf EndIf EndIf ; --- Max speed limit --- Local $spd = Sqrt($velX * $velX + $velY * $velY) Local $maxSpd = 0.10 If $spd > $maxSpd Then $velX *= $maxSpd / $spd $velY *= $maxSpd / $spd EndIf ; --- Apply friction (inertia) --- $velX *= $fDrag $velY *= $fDrag ; ============================================================ ; COLLISIONS: Circle physics (bounce + slide) ; ============================================================ $posX += $velX $posY += $velY ; Multiple passes for smooth sliding and bounce For $pass = 1 To 3 Local $aHit = _CheckCircleWall($posX, $posY, $margin, $aMap, True) If $aHit[0] Then ; Reflection: V' = V - 2(V·N)N Local $nx = $aHit[1], $ny = $aHit[2] Local $vdotn = $velX * $nx + $velY * $ny If $vdotn < 0 Then ; Reflect only if moving TOWARDS the wall $velX = ($velX - 2 * $vdotn * $nx) * $fBounce $velY = ($velY - 2 * $vdotn * $ny) * $fBounce EndIf Else ExitLoop EndIf Next ; --- Update ball rotation by relative speed --- ; Speed relative to camera Local $vpRight = ($velX * $planeX + $velY * $planeY) * 2.5 Local $vpFwd = ($velX * $dirX + $velY * $dirY) * 2.5 ; Ball diameter in world units ~0.6 (radius 0.3) ; Circumference = PI * 0.6 ≈ 1.88 units per full rotation ; Scale speed roughly to this size to avoid slipping effect $rollX -= $vpFwd * 3.3 $rollY += $vpRight * 3.3 ; --- Third-person camera calculation --- Local $camRetF = _GetCamPos($posX, $posY, $dirX, $dirY, $aMap) Local $camX = $camRetF[0] Local $camY = $camRetF[1] Local $camDist = Sqrt(($posX - $camX) * ($posX - $camX) + ($posY - $camY) * ($posY - $camY)) ; --- Rendering --- DllCall($hDll, "none:cdecl", "Maze_RenderFrame", _ "ptr", $pBuf, _ "int", $SW, "int", $SH, _ "double", $camX, "double", $camY, _ "double", $dirX, "double", $dirY, _ "double", $planeX, "double", $planeY, _ "ptr", $pMap, _ "double", $rollX, "double", $rollY, "ptr", $pIconBytes, "double", $camDist) ; --- Blitting via StretchDIBits --- Local $hDC = DllCall("user32.dll", "handle", "GetDC", "hwnd", $hWnd)[0] DllCall("gdi32.dll", "int", "StretchDIBits", _ "handle", $hDC, _ "int", 0, "int", 0, "int", $SW, "int", $SH, _ "int", 0, "int", 0, "int", $SW, "int", $SH, _ "ptr", $pBuf, _ "ptr", DllStructGetPtr($tBMI), _ "uint", 0, _ "dword", 0x00CC0020) DllCall("user32.dll", "int", "ReleaseDC", "hwnd", $hWnd, "handle", $hDC) ; --- FPS Counter --- $iFrames += 1 If TimerDiff($tLast) >= 1000 Then WinSetTitle($hWnd, "", "Maze FPS:" & $iFrames & " pos=" & Round($posX,2) & "," & Round($posY,2) & " vel=" & Round($velX,3) & "," & Round($velY,3)) $iFrames = 0 $tLast = TimerInit() EndIf Sleep(1) Wend DllCall($hDll, "none:cdecl", "Maze_Cleanup") DllClose($hDll) Func _Key($vk) Return BitAND(DllCall("user32.dll", "short", "GetAsyncKeyState", "int", $vk)[0], 0x8000) <> 0 EndFunc Func _BuildMazeMap() Local $m[16][16] = [ _ [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], _ [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], _ [1,0,1,1,0,1,1,1,0,1,1,0,1,1,0,1], _ [1,0,1,0,0,0,0,1,0,1,0,0,0,1,0,1], _ [1,0,0,0,1,1,0,0,0,0,0,1,0,0,0,1], _ [1,0,1,0,1,0,0,1,1,1,0,1,0,1,0,1], _ [1,0,1,0,0,0,1,0,0,0,0,1,0,1,0,1], _ [1,0,1,1,1,0,0,0,1,0,0,0,0,0,0,1], _ [1,0,0,0,0,0,1,0,1,0,1,1,1,1,0,1], _ [1,0,1,1,0,1,1,0,0,0,0,0,0,1,0,1], _ [1,0,0,1,0,0,0,0,1,1,0,1,0,0,0,1], _ [1,0,1,1,0,1,1,1,1,0,0,1,0,1,0,1], _ [1,0,0,0,0,0,0,0,0,0,1,0,0,1,0,1], _ [1,0,1,1,1,1,0,1,0,1,1,0,1,1,0,1], _ [1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1], _ [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] _ ] Return $m EndFunc Func _IsWall(ByRef $map, $wx, $wy) Local $tx = Int($wx) Local $ty = Int($wy) If $tx < 0 Or $tx >= 16 Or $ty < 0 Or $ty >= 16 Then Return True Return $map[$ty][$tx] <> 0 EndFunc Func _GetCamPos($bx, $by, $dx, $dy, ByRef $map) Local $cx = $bx Local $cy = $by Local $bHit = False Local $targetD = 1.5 ; Base camera distance from ball For $d = 0.05 To $targetD Step 0.05 Local $chkX = $bx - $dx * $d Local $chkY = $by - $dy * $d If _IsWall($map, $chkX, $chkY) Then $bHit = True ExitLoop EndIf $cx = $chkX $cy = $chkY Next If Not $bHit Then $cx = $bx - $dx * $targetD $cy = $by - $dy * $targetD EndIf Local $aRet[2] = [$cx, $cy] Return $aRet EndFunc ; Universal circle-wall collision check ; Returns: [Collision?, NormalX, NormalY] Func _CheckCircleWall(ByRef $x, ByRef $y, $rad, ByRef $map, $bPush = False) Local $cx = Int($x), $cy = Int($y) Local $hit = False, $nx = 0, $ny = 0 For $ty = $cy - 1 To $cy + 1 For $tx = $cx - 1 To $cx + 1 If _IsWall($map, $tx + 0.5, $ty + 0.5) Then Local $clX = ($x < $tx) ? $tx : (($x > $tx + 1) ? $tx + 1 : $x) Local $clY = ($y < $ty) ? $ty : (($y > $ty + 1) ? $ty + 1 : $y) Local $dx = $x - $clX, $dy = $y - $clY Local $d2 = $dx * $dx + $dy * $dy If $d2 > 0 And $d2 < ($rad * $rad) Then Local $dist = Sqrt($d2) $hit = True $nx += $dx / $dist $ny += $dy / $dist If $bPush Then Local $pen = $rad - $dist $x += ($dx / $dist) * $pen $y += ($dy / $dist) * $pen EndIf EndIf EndIf Next Next ; Normalize final normal Local $nl = Sqrt($nx*$nx + $ny*$ny) If $nl > 0 Then $nx /= $nl $ny /= $nl EndIf Local $aRet[3] = [$hit, $nx, $ny] Return $aRet EndFunc