Been at it again. This time around it's more about the system I've developed than it is the game { The game is only to show how to use the system }
Again, this one
requires Antonio's getkey.exe for input, which is included in the below games archive link:
Games Archive:
https://drive.google.com/file/d/1cAQLd- ... sp=sharing
Spites.bat uses macro's to convert ASCII art within the batch script to a list containing Y;X cell coordinates and character information for each cell. The approach used to convert the ASCII art limits each sprite to a single color.
Additional macro's are used to display and move sprites; Iterating over the Variables associated with each sprite and performing collision testing against a list of characters supplied as the Move.Sprite macro is expanded.
A sprite that will be moved CANNOT contain any characters that will be used in it's Collision Detection testing ( It will detect it's own cells colliding with each other, as testing is performed against current cell definitions ).
Sprites.bat
Code: Select all
@Echo off & CD "%~dp0"
CHCP 65001 > nul
cls
:::::::::: Version 1.5
::: Games_by_T3RRY: https://drive.google.com/file/d/1cAQLd-f9HN6SuWowBUETccKCZd2dVbWj/view?usp=sharing
::: Header: Game building tool + tutorial for Batch Arcade Games
::: Dev Notes:
::: Purpose: Create a system to convert ASCII Art into sprites that can be moved and interacted with.
::: Move.Sprite macro Performance Decreases as Sprite size increases.
::: Creation Date 16/12/2020
::: [ * Author: T3RRY * ]
::: Additional Accreditation
::: Getkey.Exe by Antonio {https://stackoverflow.com/users/778560/aacini} for Extended key controls.
:::::::::::::::
::: Version Changes:
::: Removed Response handling of collision from Move.Sprite macro
::: Upgraded move.sprite macro to accept arguments for Collision or Capture Characters
::: - return Vars [Collision is True: Valid=0] [Capture is True: Captured=1]
:::
:::::::::::::::
rem /* Establish and verify required resources. Clearly separate script elements */
rem /* Test Environment is suitable for definition of Macro's */
If "!![" == "[" (Echo/Delayed expansion Must not be enabled prior to starting %~n0.bat & Pause & Exit /B 0)
=============================================================================
::: CRITICAL RESOURCE DEFINITION AND VALIDATION
=============================================================================
Set "MSG=Title ?"
%MSG:?= Verifying Resources ...%
rem /* use wmic output with find to test if system is windows 10. Exit with notice if not */
wmic OS get OSArchitecture,caption | FIND "10" > nul || (ECHO/Windows 10 required for ascii escape codes & Exit /B)
rem /* Define Escape character used for console virtual terminal sequences */
rem - https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences
for /F "delims=#" %%a in ('"prompt #$E# & for %%a in (1) do rem"') do set "\E=%%a"
rem /* Define Getkey path wit /n no wait parameter */
For /F "Delims=" %%C in ('dir "%~dp0*GetKey.exe" /B /S') Do If not defined GetKey Set "GetKey="%%C" /n"
rem /* Test getkey var is defined with valid exe path. Exit with notice if not. */
Echo/%GetKey%|findstr.exe /LIC:"GetKey.exe" > nul || (Echo/GetKey.Exe not found in Directory. Game cannot be played without GetKey.exe &Pause &Exit /B 0)
(For /F "Tokens=1,2 Delims==" %%G in ('Set "}" 2^> Nul ')Do Set "%%~G=") 2> nul
If not exist "%TEMP%" (Set "Save.Dir=%~dp0")Else Set "Save.Dir=%TEMP%\"
rem /* Test write permission in the assigned directory. Exit with notice if no write permission */
break >"%Save.Dir%%~n0_testpermission.com" || (
Echo/Write permission required in %Save.Dir% to continue
Pause
Exit /B
)
Set Background="%Save.Dir%%~n0_background.~tmp"
Set Foreground="%Save.Dir%%~n0_foreground.~tmp"
DEL %Foreground% 2> Nul
rem /* Ensure array used to display screen elements is undefined. */
(For /F "Tokens=1,2 Delims==" %%G in ('Set "}"')Do Set "%%~G=") 2> nul
%= --------------------------------------------------------- =%
::: MACRO DEFINITIONS
(Set \n=^^^
%= Macro newline var =%)
(Set COMMENT=rem {i} ^^^
%= Macro Comment var =%)
rem /* On win loss action */
Set "G.O=Call :End & Endlocal & Goto start"
%MSG:?= Crafting Animation tools ...%
Set ".Left=Right"&Set ".Right=Left"&Set ".Up=Down"&Set ".Down=Up"
rem /* Background.Border macro USAGE: %Background.Border%{VTcolorcode}{.Xmin}{.Xmax}{.Ymin}{.Ymax}{BorderCharacter} */
Set Background.Border=Set "Border=" ^& For %%n in (1 2)Do If %%n==2 ( %\n%
%COMMENT:{i}= Ensure Args 1-6 captured \n%
For /F "Tokens=1,2,3,4,5,6 Delims={}" %%G in ("!Border!")Do If not "%%L" == "" ( %\n%
%COMMENT:{i}= Assign Y;X min max values as border constraints for use within other macros {Args 2 -5} \n%
Set /A ".Xmin=%%H,.Xmax=%%I,.Ymin=%%J,.Ymax=%%K"%\n%
%COMMENT:{i}= Assign Border Char {Arg 6} \n%
Set ".Char=%%L"%\n%
%COMMENT:{i}= {SIDES} For each in xmin AND xmax do for each in Ymin TO Ymax Do \n%
For %%w in (%%H %%I)Do for /L %%h in (%%J,1,%%K)Do ( %\n%
%COMMENT:{i}= Define Cell of Border for Collision testing and display \n%
Set "}%%h;%%w=%\E%[%%h;%%wH%\E%[%%~Gm%%~L" %\n%
%COMMENT:{i}= {TOP + BASE} For each in Ymin AND Ymax do for each in Xmin TO Xmax Do \n%
For %%i in (%%J %%K)Do If %%h Equ %%i ( %\n%
For /L %%W in (%%H 1 %%I)Do ( %\n%
%COMMENT:{i}= Define Cell of Border for Collision testing and display {with VT Color Arg 1} \n%
Set "}%%h;%%W=%\E%[%%h;%%WH%\E%[%%~Gm%%~L" %\n%
))))Else ( Echo/Missing Args for %%Background.Border%% ^& Pause ^> nul ) %\n%
%COMMENT:{i}= Define Console Size Lines Cols and Control Panel X Position based on Y X max values \n%
Set /A "CNTRL=!.Xmax!+5,cols=!.Xmax!+25,lines=!.Ymax!+2" %\n%
)Else Set Border=
Set "UPDATE.Background=Set /A ".End=!.Ymax! + 1" & ((For /F "Tokens=2* Delims==" %%G in ('Set "}"')Do (< nul Set /P "=%%~G") & < nul Set /P "=%\E%[!.END!;1H%\E%[0m%\E%[?25l")) >%Background% & Type %Background%"
Set "Def.Key=Set "input="&For %%n in (1 2)Do if %%n==2 (For %%I in (!input!)Do Set "k%%~I")Else Set input="
Set "?Key#=For %%E in (!Errorlevel!) Do If not "!k%%E!" == "" (Set "Key=!k%%E!" & (If /I "!Key!" == "space" ( Pause > nul )) & (For %%d in (left right up down)Do If /I "!Key!" == "%%d" (Set "Dir=%%d")))Else If not "%%E" == "0" (If "mode" == "dev" TITLE Key Errorcode - %%E)"
========================================================================================================
::: DEF.SPRITE : Converts Ascii art to objects [ as a list ] with Defined Cordinates }Y;H=}Y;XHcharacter
========================================================================================================
rem /* Def.Sprite Macro. USAGE: %Def.Sprite:#=lineYpos%{SPRITEVARNAME}{VTCOLORCODE}{string defining characters for x positions of substituted Y line} */
Set Def.Sprite=For %%n in (1 2)Do if %%n==2 ( %\n%
%COMMENT:{i}= get Y coordinate value of current line from value supplied during macro expansion \n%
Set /A "{y}=#" %\n%
Set /A "{y}+=1" %\n%
For /F "Tokens=1,2,3 Delims={}" %%G in ("!Cells!")Do If not "%%~H" == "" ( %\n%
%COMMENT:{i}= Split Arg 2 cell data from Arg 1 Sprite name \n%
Set "cell=%%I" %\n%
Set "%%G.Color=%%H" %\n%
%COMMENT:{i}= Constrain sprite definition to Playfield X dimensions \n%
For /L %%i in (2 1 !.Xmin!)Do Set "cell= !cell!" %\n%
%COMMENT:{i}= Get x coordinate of non whitespace elements in Cell string \n%
For /L %%x in (0 1 !.Xmax!)Do If not "!cell:~%%x,1!" == "" ( %\n%
If not "!cell:~%%x,1!" == " " ( %\n%
%COMMENT:{i}= Shift {x} value from 0 index used by substring modification to 1 index of Virtual Terminal Sequences \n%
FOR /F "Delims=" %%X in ('Set /A "{x}=%%x+!.Xmin!+1"')Do ( %\n%
%COMMENT:{i}= Define Cell cordinate for collision testing \n%
For %%C in ("H" ";" "[")Do If /I "!cell:~%%x,1!" == "%%~C" ( %\n%
Echo/ Delim character %%C cannot be defined to a sprite. ^& Endlocal %\n%
Pause %\n%
Exit /B 0 %\n%
) %\n%
Set "}!{y}!;%%X=!{y}!;%%XH!cell:~%%x,1!" %\n%
%COMMENT:{i}= Define Sprite variable Arg 1 with converted Coordinates for non whitespace cells from Arg 2 \n%
Set "%%G=!%%G!"!{y}!;%%XH!cell:~%%x,1!"," %\n%
)))))Else Set Cells=
========================================================================================================
::: SHOW : Iterates over the list value of a substituted sprite variable and outputs value.
========================================================================================================
rem /* USAGE: %Show:obj=SpriteVARNAME% */ [ Displays spritenames cells ]
Set "Show=For %%n in (1 2)Do If %%n==2 (For %%g in (!obj!)Do ((Set "oStr=%%~g") & < nul Set /P "=%\E%[!Obj.Color!m%\E%[!oStr!%\E%[0m"))"
rem /* Variables used to convert Direction to Vector in Move.Sprite Macro*/
========================================================================================================
Set "Left={x}{-}"& Set "Right={x}{+}"& Set "Up={y}{-}"& Set "Down={y}{+}"
========================================================================================================
::: rem /* Move.Sprite macro. Usage:
::: %Move.Sprite:obj=SPRITEVARNAME%{x|y}{+|-}{collision characters}{capture characters} */
::: Args 3 and 4 are optional. To ensure correct results; Characters for testing should be unique to a
::: single test; Characters MUST NOT match the current object or characters in the other test.
========================================================================================================
Set Move.Sprite=For %%n in (1 2) Do if %%n==2 ( %\n%
%COMMENT:{i}= reset sprite clone SUB and VALID move flag variable \n%
Set "Sub=" %\n%
Set /A "valid=1,captured=0" %\n%
%COMMENT:{i}= Catch Move Vector and Collision objects \n%
For /F "Tokens=1,2,3,4 Delims={}" %%G in ("!Move.Dir!")Do ( %\n%
Set "axis=%%G" %\n%
Set "vector=%%H" %\n%
If not "%%~I" == "" (Set "Collide=%%I")Else (Set "Collide=") %\n%
If not "%%~J" == "" (Set "Flag=%%J")Else ( %\n%
Set "Flag=" %\n%
)) %\n%
%COMMENT:{i}= For X or Y axis \n%
If /I "!axis!" == "X" ( %\n%
%COMMENT:{i}= iterate over sprite cells \n%
For %%g in (!obj!) Do ( %\n%
%COMMENT:{i}= Seperate Y;X coords and Cell character from cell string \n%
For /F "Tokens=1,2,3 Delims=;H" %%t in ("%%~g")Do ( %\n%
%COMMENT:{i}= Adjust cells vector \n%
Set /A "cx=%%u!vector!1" %\n%
Set "cTkn=%%v" %\n%
%COMMENT:{i}= get value of new y/x coord for substring modification \n%
For /F "Delims=" %%c in ("!cx!")Do ( %\n%
%COMMENT:{i}= Test cell against each collision character Flag move invalid on collision \n%
For %%K in (!Collide!)Do If Not "!}%%t;%%c!" == "" ( %\n%
Set "KO=!}%%t;%%c:%%~K=!" %\n%
If Not "!}%%t;%%c!" == "!KO!" ( Set "valid=0" ) %\n%
) %\n%
For %%Q in (!Flag!)Do If Not "!}%%t;%%c!" == "" ( %\n%
Set "flag.C=!}%%t;%%c:%%~Q=!" %\n%
Set "flag.Cell=!}%%t;%%c!" %\n%
If not "!flag.Cell!" == "!flag.C!" ( Set "Captured=1" ) %\n%
) %\n%
%COMMENT:{i}= On valid move for cell build sprite clone string \n%
If "!Valid!" == "1" (Set "Sub=!Sub!"%%t;%%cH!cTKN!",") %\n%
)))) ELSE ( %\n%
For %%g in (!obj!) Do ( %\n%
For /F "Tokens=1,2,3 Delims=;H" %%i in ("%%~g") Do ( %\n%
Set /A "cy=%%i!vector!1" %\n%
Set "cTkn=%%k" %\n%
For /F "Delims=" %%o in ("!cy!") Do ( %\n%
For %%K in (!Collide!)Do If not "!}%%o;%%j!" == "" ( %\n%
Set "KO=!}%%o;%%j:%%~K=!" %\n%
If Not "!}%%o;%%j!" == "!KO!" ( Set "valid=0" ) %\n%
) %\n%
For %%P in (!Flag!)Do If Not "!}%%o;%%j!" == "" ( %\n%
Set "flag.C=!}%%o;%%j:%%~P=!" %\n%
Set "flag.Cell=!}%%o;%%j!" %\n%
If Not "!flag.Cell!" == "!flag.C!" ( Set "Captured=1" ) %\n%
) %\n%
If "!valid!" == "1" (Set "Sub=!Sub!"%%o;%%jH!cTKN!",") %\n%
))))%\n%
%Comment:{i}= If "valid" EQU "0" (Goto End) \n%
%COMMENT:{i}= On all moves being valid \n%
If "!valid!" == "1" (%\n%
%COMMENT:{i}= iterate over sprites previous y;x cells \n%
(For %%Q in (!obj!)Do ( %\n%
%COMMENT:{i}= Seperate Y/X values \n%
For /F "tokens=1,2 Delims=;H" %%1 in ("%%~Q") Do (%\n%
%COMMENT:{i}= undefine Cell from collision detection array and clear from display \n%
Set "}%%~1;%%~2=" %\n%
Echo/%\E%[%%1;%%2H %\n%
%COMMENT:{i}= redirect to foreground output file for display when new cell values are appended with SHOW macro \n%
)))^>%Foreground% %\n%
%COMMENT:{i}= Define new Cell Location with new value \n%
(For %%S in (!Sub!)Do ( %\n%
For /F "tokens=1,2,3 Delims=;H" %%3 in ("%%~S") Do (%\n%
Set "cTKN=%%5" %\n%
Set "}%%~3;%%~4=%%~3%;%~4H!cTKN!" %\n%
Echo/%\E%[%%~3;%%~4H%\E%[!Obj.Color!m!cTKN!%\n%
))) ^>^>%Foreground% %\n%
TYPE %Foreground% %\n%
%COMMENT:{i}= replace sprite with moved clone \n%
Set "obj=!Sub!" %\n%
%COMMENT:{i}= End the game on collision \n%
) %\n%
)Else Set Move.Dir=
========================================================================================================
::: Commence Game. Define BORDER and SPRITE properties
========================================================================================================
:start
Setlocal EnableDelayedExpansion
%MSG:?= Generating Border ...%
%Background.Border%{33}{1}{50}{1}{25}{#}
Set "}2;!CNTRL!=%\E%[2;!CNTRL!H%\E%[33mControls:"
Set "}3;!CNTRL!=%\E%[3;!CNTRL!H ▲ - Up"
Set "}4;!CNTRL!=%\E%[4;!CNTRL!H ◄ - Left"
Set "}5;!CNTRL!=%\E%[5;!CNTRL!H ▼ - Down"
Set "}6;!CNTRL!=%\E%[6;!CNTRL!H ► - Right"
Set "}7;!CNTRL!=%\E%[7;!CNTRL!HSpace - Pause"
Set "}8;!CNTRL!=%\E%[8;!CNTRL!HEscape - Quit"
mode %Cols%,%lines%
%Update.BackGround%
rem /* Y coordinate for each line of a sprite to be supplied during Def.Sprite Expansion */
========================================================================================================
%MSG:?= Generating Sprites ...%
%Def.Sprite:#=1%{player}{32}{ O }
%Def.Sprite:#=!{y}!%{player}{32}{MWM
%Def.Sprite:#=!{y}!%{player}{32}{ ^^ }
========================================================================================================
%Def.Sprite:#=3%{rocks}{90}{ @ }
%Def.Sprite:#=!{y}!%{rocks}{90}{ @ }
%Def.Sprite:#=!{y}!%{rocks}{90}{ # #@ }
%Def.Sprite:#=!{y}!%{rocks}{90}{ @@ }
%Def.Sprite:#=17%{rocks}{90}{ @ @ }
%Def.Sprite:#=!{y}!%{rocks}{90}{ @ @ }
%Def.Sprite:#=!{y}!%{rocks}{90}{ # }
%Def.Sprite:#=!{y}!%{rocks}{90}{ @ }
========================================================================================================
%Def.Sprite:#=1%{river}{36}{ .ll }
%Def.Sprite:#=2%{river}{36}{ ^l. }
%Def.Sprite:#=!{y}!%{river}{36}{ .l }
%Def.Sprite:#=!{y}!%{river}{36}{ .l_ }
%Def.Sprite:#=!{y}!%{river}{36}{ r^)._ }
%Def.Sprite:#=!{y}!%{river}{36}{ .l` ^). }
%Def.Sprite:#=!{y}!%{river}{36}{ l` . }
%Def.Sprite:#=!{y}!%{river}{36}{ _l. }
%Def.Sprite:#=!{y}!%{river}{36}{ . }
========================================================================================================
%Def.Sprite:#=9%{mountains}{37}{ x X xXx }
%Def.Sprite:#=!{y}!%{mountains}{37}{ XxXx Xx X }
%Def.Sprite:#=!{y}!%{mountains}{37}{ Xx xXxxX x}
========================================================================================================
%Def.Sprite:#=8%{money}{33}{ $}
%Def.Sprite:#=12%{money}{33}{ $}
========================================================================================================
rem /* varname[index#] naming convention can be used to define multiple sprites of a given type - allowing for random selection from an array of sprites */
%Def.Sprite:#=21%{enemy[1]}{31}{ /+\}
%Def.Sprite:#=!{y}!%{enemy[1]}{31}{ + }
%Def.Sprite:#=!{y}!%{enemy[1]}{31}{ \+/}
rem /* Show initial sprite positions */
rem /* Display order: Collectable;Background - Static or Collidable;Player;Foreground Collidable [enemys]*/
(
%Show:obj=money%
%Show:obj=mountains%
%Show:obj=river%
%Show:obj=rocks%
%Show:obj=player%
%Show:obj=enemy[1]%
)>%Foreground%
TYPE %Foreground%
rem /* Define valid keys */
%Def.Key%"-72=up" "-80=down" "-75=left" "-77=right" "27=escape" "32=space" "13=enter"
%Def.Key%"48=0" "49=1" "50=2" "51=3" "52=4" "53=5" "54=6" "55=7" "56=8" "57=9"
%MSG:?= Sprite Demo by T3RRY. Collect Both $ Coins%
For %%G in (start)Do < Nul Set /P "=%\E%[10;!CNTRL!H%\E%[32mPlayer: %%G%\E%[0m %\E%[11;!CNTRL!H %\E%[31mAI: waiting%\E%[0m "
========================================================================================================
:level[1] /* DEMO of Sprite movement and Display only. Not intended as an Actual Game. */
========================================================================================================
rem /* input is taken and assessed within the loop ; all game actions repeat with each loop until a specified end condition is met */
rem /* clear previous key; catch new input */
Set "Key="& %GetKey%
rem /* ::: ?Key# insert/remove substring modification ':mode=dev' for/after debugging. Dev mode allows testing of key inputs to identify errorcode values returned */
rem /* Expand ?Key# macro; test if captured errorlevel corresponds to a valid key */
%?Key#%
rem /* Static Sprite Objects NOT collision tested should be refreshed each cycle PRIOR to movement */
%Show:obj=river%
rem /* Test move in the current Direction for declared SPRITE OBJect valid against list of collision characters; Call :End on Collision */
rem /* Previous sprite position overwritten with whitespace during Move.Sprite macro execution */
For %%G in (!Dir!)Do < Nul Set /P "=%\E%[10;!CNTRL!H%\E%[32mPlayer: %%G%\E%[0m %\E%[11;!CNTRL!H %\E%[31mAI: !.%%G!%\E%[0m "
For %%M in (!Dir!)Do %Move.Sprite:obj=player%!%%M!{"#" "@" "x" "+" "/" "\"}{"$"}
::: Objective - Player must capture both $ coin to win.
::: Loss - player or AI experiences any collision
::: AI Captures any $ coin
If "!captured!" == "1" Set /A "Playercoin+=1 + 0"
If "!valid!" == "0" ( Set "Win=Lost" & set ".Win=Won" & %G.O%)
If "!Playercoin!" == "2" (Set "Win=Won" & set ".Win=Lost" & %G.O%)
rem /* Simple AI sprite motion. AI moves in opposite direction to player */
For %%. in (!Dir!)Do For %%M in (!.%%.!)Do %Move.Sprite:obj=enemy[1]%!%%M!{"#" "@" "x" "O" "M" "^^"}{"$"}
If "!captured!" == "1" (Set "Win=Lost" & set ".Win=Won" & %G.O%)
If "!valid!" == "0" (Set "Win=Lost" & set ".Win=Lost" & %G.O%)
If /I "!Key!" == "escape" (
<nul Set /P "=%\E%[!.End!;1H%\E%[?25h%\E%[0m"
DEL "%Save.Dir%%~n0*.~tmp" 2> Nul
Endlocal
Exit /B 0
)
::: OPTIONAL
::: Implement levels by using conditional tests that flag winning the current level jump to
::: a new label to define new sprites.
::: Before implementing a new level loop:
::: - endlocal environment to remove previous sprites and renew Setlocal EnableDelayedExpansion
::: prior to defining the next batch of sprites - MUST be done to remove }Y;X definitions used
::: for collision testing.
Goto :level[1]
=======================================================================================================
::: SCRIPT BREAK
========================================================================================================
:End
rem /* Display order: Background;Player;Collidable */
(
%Show:obj=mountains%
%Show:obj=river%
%Show:obj=rocks%
%Show:obj=player%
%Show:obj=enemy[1]%
)>%Foreground%
TYPE %Foreground%
%MSG:?= Game over%
For %%G in ("Win")Do < Nul Set /P "=%\E%[10;!CNTRL!H%\E%[32mPlayer: !%%~G!%\E%[0m %\E%[11;!CNTRL!H %\E%[31mAI: !.%%~G!%\E%[0m "
<nul Set /P "=%\E%[!.End!;1H%\E%[?25h%\E%[31m"
DEL "%Save.Dir%%~n0*.~tmp" 2> Nul
Pause
Timeout /T 2 /Nobreak > Nul
<nul Set /P "=%\E%[0m"
Exit /B 0