I used the "Legend of Zelda" NES game tiles and sprites as a template.
I don't want to continue this project anymore, so I thought I'd post the unfinished script here in case anyone else wants to finish it.
I just don't have the time anymore that it's going to require...
Let me know if you decide to work on it. I'd love to see someone else's ideas to make it work better.
Code: Select all
; Auto-Execute =================================================================
#SingleInstance, Force ; Allow only one running instance of script
#Persistent ; Keep the script permanently running until terminated
#NoEnv ; Avoid checking empty variables for environment variables
;#NoTrayIcon ; Disable the tray icon of the script
#KeyHistory, 0 ; Disable history of keystrokes & mouse clicks
SetWorkingDir, % A_ScriptDir ; Set the working directory of the script
SetBatchLines, -1 ; Run script at maximum speed
SetControlDelay, -1 ; Disable delay to occur after modifying a control
SendMode, Input ; The method for sending keystrokes and mouse clicks
ListLines, Off ; Disable history of executed script lines
CoordMode, Mouse, Client ; Mouse coordinates relative to active window client
OnExit("OnUnload") ; Run function when exiting the script
return ; End automatic execution
; ==============================================================================
; Functions ====================================================================
OnLoad() {
Global ; Assume-global mode
Static Init := OnLoad() ; Call function
GuiWidth := 512 ; Gui Width
GuiHeight := 352 ; Gui Height
DPI := (A_ScreenDPI / 96) ; Calculate DPI scaling
BackgroundColor := "FCD8A8"
BackgroundColors := []
BackgroundColors[1] := "FCD8A8" ; Map 1
BackgroundColors[2] := "000000" ; Map 2
BackgroundColors[3] := "FCD8A8" ; Map 3
Maps := [] ; Initialize array
; Start screen
Maps[1,1] := [1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1]
Maps[1,2] := [1,1,1,1,10,1,2,0,0,1,1,1,1,1,1,1]
Maps[1,3] := [1,1,1,2,0,0,0,0,0,1,1,1,1,1,1,1]
Maps[1,4] := [1,1,2,0,0,0,0,0,0,1,1,1,1,1,1,1]
Maps[1,5] := [1,2,0,0,0,0,0,0,0,5,1,1,1,1,1,1]
Maps[1,6] := [0,0,0,0,0,0,0,6,0,0,0,0,0,0,0,0]
Maps[1,7] := [3,4,0,0,0,0,0,0,0,0,0,0,0,0,3,3]
Maps[1,8] := [1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1]
Maps[1,9] := [1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1]
Maps[1,10] := [1,1,3,3,3,3,3,3,3,3,3,3,3,3,1,1]
Maps[1,11] := [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
; Start cave
Maps[2,1] := [11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11]
Maps[2,2] := [11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11]
Maps[2,3] := [11,11,0,0,0,0,0,0,0,0,0,0,0,0,11,11]
Maps[2,4] := [11,11,0,0,0,0,0,0,0,0,0,0,0,0,11,11]
Maps[2,5] := [11,11,0,0,0,0,0,0,0,0,0,0,0,0,11,11]
Maps[2,6] := [11,11,0,0,0,0,0,0,0,0,0,0,0,0,11,11]
Maps[2,7] := [11,11,0,0,0,0,0,0,0,0,0,0,0,0,11,11]
Maps[2,8] := [11,11,0,0,0,0,0,0,0,0,0,0,0,0,11,11]
Maps[2,9] := [11,11,0,0,0,0,0,0,0,0,0,0,0,0,11,11]
Maps[2,10] := [11,11,12,12,12,12,12,0,0,12,12,12,12,12,11,11]
Maps[2,11] := [11,11,11,11,11,11,11,6,0,11,11,11,11,11,11,11]
; North of start
Maps[3,1] := [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
Maps[3,2] := [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
Maps[3,3] := [1,2,0,0,0,0,0,0,0,5,2,0,0,0,5,1]
Maps[3,4] := [2,0,0,14,0,14,0,0,0,0,0,0,14,0,0,5]
Maps[3,5] := [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
Maps[3,6] := [0,0,0,14,0,14,0,0,0,0,0,0,14,0,0,0]
Maps[3,7] := [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
Maps[3,8] := [4,0,0,14,0,14,0,0,0,0,0,0,14,0,0,13]
Maps[3,9] := [1,4,0,0,0,0,0,0,0,13,4,0,0,0,13,1]
Maps[3,10] := [1,1,3,3,3,3,3,0,0,1,1,3,3,3,1,1]
Maps[3,11] := [1,1,1,1,1,1,1,6,0,1,1,1,1,1,1,1]
Actions := []
Actions[1,2,5] := ["MapSet", 2] ; Cave entrance
Actions[1,0,8] := ["MapSet", 3] ; Start North (left)
Actions[1,0,9] := ["MapSet", 3] ; Start North (right)
Actions[2,12,8] := ["MapSet", 1] ; Cave exit (left)
Actions[2,12,9] := ["MapSet", 1] ; Cave exit (right)
Actions[3,12,8] := ["MapSet", 1] ; North > South
Actions[3,12,9] := ["MapSet", 1] ; North > South
Zelda := "iVBORw0KGgoAAAANSUhEUgAAAeAAAAAgCAMAAAD9lVunAAADAFBMVEUAAAB7SwAAqACDAAKDywzYmCz82Kj///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAy3zVaAAABAHRSTlP///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8AU/cHJQAAA1lJREFUeAHt2tFyWykQhOGzu0L7/k+clNz50+Q0eEw5vvL0hX0GBqzlq6Ikba5/33Jdz+d1vX6qUv161oiiGbp4ntech531lH/hx+88pmgkk/Pl+mLtVeS/t/xZ3589lnlG8jwRWuX/Ig38PYDZMCk9ln1+zpfEs+samMx/IXnWRP/8yq6D0WqWfT4OrFCRXQdjO+A8T9cN3MBnwNqU5+xj/P4SXGVNcm4PPMYKaIzkhXgMurx+7mE9uKyYez4HDCP1TDxXeR4OwL60GWddAzdwXrcEYLbPPmYTfzd/AqwI6LEIQEk8hlcAzBwB2LuNkbwJnFkDCzfQo96diEn3Z/RKAzdwLmeE0RNg+pin/hyw3zyRFyO06gFpvEW4AGv88dCMKoCFPAZd6mDnA2A9fQJYdQ1shwZu4DxUZw/Mb4iYoZ9kfQ4MkJG5hgEGh590a97AzBlYYUfG2bEGhiuBc9QrnPwweT9PgNOngRt4D+wLI4GV9ZuCGlh7O9eV6+YaIn8E0hM8vqIJvcxrNjN3sKfDzufAVAn6PvDyGg6f+wfWJG3gBuZpB3wHIvSTFdx8mbND9nkEAMEqqtbA+68ioaeLHehQ/gYwTwEcdQLfzzOB6WtgpYGTIrZl4+BlltRvsbQT1Yz8HrBQHXEzJ7jsYJ6Ax3o6cqW6a+A1ZT7d6gDmv18/nXwb6r4GbuCvA849s46rPq4ff0yqgZmHEECoFPWojx2Yma/4rwdmDWf4cWBOqoEbmK8q+VPa3gyM18D0HwAnMSmB4VVgNDCgmmEEaHcBrNyBP/5VZXAHMFUAx4nk+dTADdzAPmJV3rQCPiHN+hlZAXPpKiABDDXEcOYVzS7mvAPDy47nwKpOPibNtHleSS8jpYEbODfdAa958+ONnpWoS2BCJ19DZBIYfDGqA2D/zn8wQD/8J8D5FUcN7Pg8zFYBe8bADdzALFwDV//IU6HDL2xV37t30RozmhYEAlMGdoA9U6wjB8CJmJzOBjgZI1rPh6gGbuBE0vKL1AxxuecLpob7FFhU/OY5k7ODjHl9se4IOAkTOyuN5Nm7yv954xny3YEbWCjaRhcyG1cAygxpYI+49qWvqtpdnSvEmjfnY7RYmcA1Mmiq8hLPy3sNnKeVQeebAzfwT9WcZEukdvhrAAAAAElFTkSuQmCC"
; GDI+ Startup
hGdip := DllCall("Kernel32.dll\LoadLibrary", "Str", "Gdiplus.dll") ; Load module
VarSetCapacity(GdiplusStartupInput, (A_PtrSize = 8 ? 24 : 16), 0) ; GdiplusStartupInput structure
NumPut(1, GdiplusStartupInput, 0, "UInt") ; GdiplusVersion
DllCall("Gdiplus.dll\GdiplusStartup", "PtrP", pToken, "Ptr", &GdiplusStartupInput, "Ptr", 0) ; Initialize GDI+
; Create a Bitmap object
DllCall("Gdiplus.dll\GdipCreateBitmapFromScan0", "Int", (GuiWidth * DPI), "Int", (GuiHeight * DPI), "Int", 0, "Int", 0x26200A, "Ptr", 0, "PtrP", pBackgroundImage)
; Create a Graphics object associated with pBackgroundImage
DllCall("Gdiplus.dll\GdipGetImageGraphicsContext", "Ptr", pBackgroundImage, "PtrP", pBackgroundGraphics)
; Set the rendering quality of the pBackgroundGraphics Graphics object
DllCall("Gdiplus.dll\GdipSetSmoothingMode", "Ptr", pBackgroundGraphics, "Int", 3) ; None
; Set the interpolation mode of the pBackgroundGraphics Graphics object
DllCall("Gdiplus.dll\GdipSetInterpolationMode", "Ptr", pBackgroundGraphics, "Int", 5) ; NearestNeighbor
; Create a SolidBrush object for the background color of the game
DllCall("Gdiplus.dll\GdipCreateSolidFill", "UInt", "0xFF" BackgroundColor, "PtrP", pBackgroundBrush)
}
OnUnload(ExitReason, ExitCode) {
Global ; Assume-global mode
; Release resources used by the Image objects
DllCall("Gdiplus.dll\GdipDisposeImage", "Ptr", pTheme)
DllCall("Gdiplus.dll\GdipDisposeImage", "Ptr", pBackgroundImage)
; Delete Graphics objects
DllCall("Gdiplus.dll\GdipDeleteGraphics", "Ptr", pBackgroundGraphics)
DllCall("Gdiplus.dll\GdipDeleteGraphics", "Ptr", pControlGraphics)
; Delete Brush objects
DllCall("Gdiplus.dll\GdipDeleteBrush", "Ptr", pBackgroundBrush)
; Clean up resources used by GDI+
DllCall("Gdiplus.dll\GdiplusShutdown", "Ptr", pToken)
; Free module from memory
DllCall("Kernel32.dll\FreeLibrary", "Ptr", hGdip)
}
GuiCreate() {
Global ; Assume-global mode
Static Init := GuiCreate() ; Call function
Gui, +HWNDhGame -Resize
Gui, Margin, 0, 0
Gui, Color, FFFFFF
Gui, Add, Edit, x0 y0 w0 h0 0x800 vCatch ; Catch keypresses to avoid dinging
Gui, Add, Picture, % "x" 0 " y" 0 " w" (GuiWidth * DPI) " h" (GuiHeight * DPI) " vGameImage HwndhBackground 0xE"
; Get pointer to control graphics image
DllCall("Gdiplus.dll\GdipCreateFromHWND", "Ptr", hBackground, "PtrP", pControlGraphics)
SubclassControl(hBackground, "BgSubclassProc") ; Subclass static image
pTheme := GdipCreateFromBase64(Zelda)
MapSet(1)
Gui, Show, w%GuiWidth% h%GuiHeight%, Example
}
MapSet(MapID) {
Global ; Assume-global mode
BackgroundLoad(MapID)
MapNo := MapID
}
GuiClose(GuiHwnd) {
Global ; Assume-global mode
ExitApp ; Terminate the script unconditionally
}
WM_KEYDOWN(wParam, lParam, Msg, Hwnd) {
Global ; Assume-global mode
Static Init := OnMessage(0x0100, "WM_KEYDOWN") ; Call function
KF_REPEAT := (lParam & 0x40000000)
VK := Format("{:x}", wParam) ; Get virtual-key code
GuiControl, Focus, Catch
If (VK = "26" || VK = "57") { ; UP ARROW/W
Move(0, -1)
} Else If (VK = "25" || VK = "41") { ; LEFT ARROW/A
Move(-1, 0)
} Else If (VK = "28" || VK = "53") { ; DOWN ARROW/S
Move(0, 1)
} Else If (VK = "27" || VK = "44") { ; RIGHT ARROW/D
Move(1, 0)
}
}
Move(X, Y) {
Global ; Assume-global mode
Update := []
PlayerGetPos(BDRow, BDCol, Char)
NewX := (X < 0 ? BDCol - 1 : X > 0 ? BDCol + 1 : BDCol)
NewY := (Y < 0 ? BDRow - 1 : Y > 0 ? BDRow + 1 : BDRow)
NewChar := (X < 0 ? 7 : X > 0 ? 9 : Y < 0 ? 6 : 8)
NextChar := Maps[MapNo, NewY, NewX]
DoAction(Actions[MapNo, NewY, NewX])
If (NewX > 16 || NewX < 1)
|| (NewY > 11 || NewY < 1)
|| (NextChar <> 0) {
return
}
Maps[MapNo, NewY, NewX] := NewChar ; Move player
Update.Push({Row: NewY, Col: NewX, Char: NewChar}) ; Update screen
Maps[MapNo, BDRow, BDCol] := 0 ; Clear previous position
Update.Push({Row: BDRow, Col: BDCol, Char: 0}) ; Update screen
BackgroundUpdate(Update)
}
DoAction(Action) {
Global ; Assume-global mode
PrevMapNo := MapNo
If (Action = "") {
return
}
ActionFunc := Func(Action[1])
ActionFunc.Call(Action[2])
PlayerGetPos(BDRow, BDCol, Char)
Update.Push({Row: BDRow, Col: BDCol, Char: NewChar})
BackgroundUpdate(Update)
}
PlayerGetPos(ByRef Row, ByRef Col, ByRef Val) {
Global ; Assume-global mode
For Row In Maps[MapNo] {
For Col, Val In Maps[MapNo, Row] {
If (RegExMatch(Val, "(6|7|8|9)")) {
return Row, Col, Val
}
}
}
}
BackgroundLoad(MapNo) {
Global ; Assume-global mode
; Clear graphic to specific color
DllCall("Gdiplus.dll\GdipGraphicsClear", "Ptr", pBackgroundGraphics, "UInt", "0xFF" BackgroundColors[MapNo])
; Generate image using tileset coordinates from map data
For Row In Maps[MapNo] {
For Col, Val In Maps[MapNo, Row] {
DrawTile(Val, Col, Row)
}
}
; Redraw background image
GuiControl, +Redraw, % hBackground
}
BackgroundUpdate(arrUpdate) {
Global ; Assume-global mode
For K, V In arrUpdate {
DllCall("Gdiplus.dll\GdipCreateSolidFill", "UInt", "0xFF" BackgroundColors[MapNo], "PtrP", pBackgroundBrush)
DllCall("Gdiplus.dll\GdipFillRectangle", "Ptr", pBackgroundGraphics, "Ptr", pBackgroundBrush, "Float", ((V.Col - 1) * 32) * DPI, "Float", ((V.Row - 1) * 32) * DPI, "Float", (32 * DPI), "Float", (32 * DPI))
DrawTile(V.Char, V.Col, V.Row)
}
; Redraw background image
GuiControl, +Redraw, % hBackground
}
DrawTile(TileChar, X, Y) {
Global ; Assume-global mode
DllCall("Gdiplus.dll\GdipDrawImageRectRect", "Ptr", pBackgroundGraphics, "Ptr", pTheme, "Float", ((32 * X) - 32) * DPI, "Float", ((32 * Y) - 32) * DPI, "Float", 32 * DPI, "Float", 32 * DPI, "Float", (TileChar - 1) * 32, "Float", 0, "Float", 32, "Float", 32, "Int", 2, "Ptr", 0, "Ptr", 0, "Ptr", 0)
}
GdipCreateFromBase64(B64) {
VarSetCapacity(B64Len, 0)
DllCall("Crypt32.dll\CryptStringToBinary", "Ptr", &B64, "UInt", StrLen(B64), "UInt", 0x01, "Ptr", 0, "UIntP", B64Len, "Ptr", 0, "Ptr", 0)
VarSetCapacity(B64Dec, B64Len, 0) ; pbBinary size
DllCall("Crypt32.dll\CryptStringToBinary", "Ptr", &B64, "UInt", StrLen(B64), "UInt", 0x01, "Ptr", &B64Dec, "UIntP", B64Len, "Ptr", 0, "Ptr", 0)
pStream := DllCall("Shlwapi.dll\SHCreateMemStream", "Ptr", &B64Dec, "UInt", B64Len, "UPtr")
VarSetCapacity(pBitmap, 0)
DllCall("Gdiplus.dll\GdipCreateBitmapFromStreamICM", "Ptr", pStream, "PtrP", pBitmap)
ObjRelease(pStream)
return pBitmap
}
SubclassControl(HCTL, FuncName, Data := 0) {
Static ControlCB := []
If (Controls.HasKey(HCTL)) {
DllCall("Comctl32.dll\RemoveWindowSubclass", "Ptr", HCTL, "Ptr", ControlCB[HCTL], "Ptr", HCTL)
DllCall("Kernel32.dll\GlobalFree", "Ptr", Controls[HCTL], "Ptr")
Controls.Delete(HCTL)
If (FuncName = "") {
return true
}
}
If (!DllCall("User32.dll\IsWindow", "Ptr", HCTL, "UInt"))
|| (!IsFunc(FuncName) || (Func(FuncName).MaxParams <> 6))
|| (!CB := RegisterCallback(FuncName,, 6)) {
return false
}
If (!DllCall("Comctl32.dll\SetWindowSubclass", "Ptr", HCTL, "Ptr", CB, "Ptr", HCTL, "Ptr", Data)) {
return (DllCall("Kernel32.dll\GlobalFree", "Ptr", CB, "Ptr") & 0)
}
return (ControlCB[HCTL] := CB)
}
BgSubclassProc(hWnd, uMsg, wParam, lParam, uIdSubclass, dwRefData) {
Global pControlGraphics, pBackgroundImage
If (uMsg = 0x0014) { ; WM_ERASEBKGND
return 1 ; Do nothing
}
If (uMsg = 0x000F) { ; WM_PAINT
; Draw the background image
VarSetCapacity(PAINTSTRUCT, 60 + A_PtrSize, 0)
DllCall("User32.dll\BeginPaint", "Ptr", hWnd, "Ptr", &PAINTSTRUCT, "UPtr")
DllCall("Gdiplus.dll\GdipDrawImage", "Ptr", pControlGraphics, "Ptr", pBackgroundImage, "Float", 0, "Float", 0)
DllCall("User32.dll\EndPaint", "Ptr", hWnd, "Ptr", &PAINTSTRUCT, "UPtr")
return 0
}
If (uMsg = 0x0002) { ; WM_DESTROY
SubclassControl(hWnd, "") ; Remove the Subclass
}
; Pass all other messages to DefSubclassProc
return DllCall("Comctl32.dll\DefSubclassProc", "Ptr", hWnd, "UInt", uMsg, "Ptr", wParam, "Ptr", lParam, "Ptr")
}
; ==============================================================================
https://www.airpair.com/javascript/posts/the-legend-of-canvas
http://anonymous-function.com/zelda-canvas/