Ampersand Hunters, a Batch arcade game

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
T3RRY
Posts: 60
Joined: 06 May 2020 10:14

Ampersand Hunters, a Batch arcade game

#1 Post by T3RRY » 07 May 2020 09:47

Introducing Ampersand Hunters, A Batch Arcade Game.
It's no Snake.bat, but one day...

With thanks to Antonio /Aacini for his Getkey.exe program, and to Dave Benham, Ed Dyreen , Jeb and again, Antonio for what I've learned from their posts throughout this site and Stack overflow ( I began learning coding in November as something to fill spare time with )

The premise of the game is fairly simple - Make it to the Escape Cell without being Eaten by Ampersands (I was previously calling this game Escape the Ampersands)
The game becomes increasingly difficult to do so as the number of Enemies scales with the 'Level', and speed increases.

The complete game with sounds and the GetKey.exe can be downloaded in this zip file: https://drive.google.com/uc?export=down ... Lqul4G38GJ (Version 6.0)

There is of course more I could do with the game - Settings Menu's, Walls, Collectibles, 'Patterned' Playfields, Firing at enemies / Obstacles Etc. Perhaps in time I will.

A slightly earlier version of the game can be seen Here: https://www.youtube.com/watch?v=hMqt4JYzQyY

Would love to hear opinions / Feedback - Especially if you encounter any issues using the game.
A few points of note:
It's built for use on Systems running Windows 10 Version 1511 or newer, as the screen is built with the aid of ASCII Escape Codes
Additional Soundfiles and Antonio's getkey.exe are required to play the game in it's intended form - The game is scripted to attempt to download the required files if they aren't located in the necessary folder - After obtaining Permission. The game will not progress if the files are downloaded into the wrong folder, it will instead ask you to download the files again.
Speed of gameplay will vary depending on the capabilities of your computer. The difference between my old Notebook and my new PC is quite significant. Accordingly, The way in which a delay is effected in this game may need adjustment.

I did try build my own xcopy based controller to avoid having to use an external exe, but failed to make a stable version.

Code: Select all

@ECHO OFF & Title validating system compatility...
If Not "%~1" == "priority" (start "" /b /high "%~F0" priority && EXIT)
Setlocal EnableExtensions || (Echo Your version of windows does not support this program. Exiting & Timeout 4 & Exit /B)
If not exist "%TEMP%\AmpHunt_1st_run.log" (For /F "Delims=" %%a in ('systeminfo') Do Echo "%%~a" | Find "Windows 10" && > "%TEMP%\AmpHunt_1st_run.log" Echo True & Set _=True) Else < "%TEMP%\AmpHunt_1st_run.log" Set /P _=
IF not defined _ (Echo Your version of windows does not support this program. Exiting & Timeout 4 & Exit /B)
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
	TITLE Ampersand Hunters
REM ::: Arcade style Batch Game By T3RRY https://www.youtube.com/channel/UCo3tS26Eg-ITbdNgWhB0MRQ
::
:: { Game requirements:
:: Windows 10 Version 1511 or newer.
:: Additional downloads required for controller and sounds unless complete version is downloaded
:: }
:: Youtube: https://youtu.be/hMqt4JYzQyY
::: [Version 7] For Windows 10
:: - 10 June 2020
:: - Enhanced game speed by Building and executing screen display array in a local environment and utilising high priority on game start
:: - Added BG.exe to the package to hide the cursor
::    - Modified Call to sound effect and the Start vbs component of the music player to redirect errors to stderr (2>Nul)
::      as the game iterates over the loop more times per second than the vbs can be started.
:: - 08 May 2020
::   Modified Way the ampersand is displayed to Not require "^" escaping
::   Adjusted Trail coloring to use 24 bit Color with ASCII codes and R G B values %Æ%[38;2;red;green;bluem
::    - Color gradient scales according to X /Y positions, Escape position also scaled to X/Y Coordinates,
::      matching the player trail.
::   Added a Loading bar in the Title
::: [Version 6.0] For Windows 10
:: - 06 May 2020
::   Added Theme.Song
::    - Changed positioning of Call to the Music Stopper and moved movement sound effect to accomadate looping the theme song
::   Added Sound effects For Escaping, Enemy teleport and Death.
::   Modified Delay timing to compensate For the time cost of playing the sound effect during each move instead of looping it
::   Added a loop into the start up component test to ensure all files are downloaded before continuing
::   Simplified the approach used for displaying player / enemy positions
::   Corrected Setlocal recursion issue that was resulting in missing operator after ~ 30 wins / losses
::: [Version 5.0] For Windows 10
:: - 02 May 2020
::   Support For Direct download of all files added
::   Support For movement keys extended to cover both cases
::   Simplified Variable setting For display characters and their Color, Can now be changed from one Location
::   Built in a speed delay that gets reduced with each level
::   Converted enemy Generation into array Form Supporting growth in enemy Numbers
::   Adjusted colors of enemy and player trails to make characters easier to see
::   Modified the display of the screen For smoother initial Display
::   Corrected a typo that allowed enemies to spawn at the Players Location
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::: { Define pathways and create subfolders used For self generated component files
::: - calls subroutines to create component files.
::: [ Test if All component files were Downloaded
	If Not Exist "%~dp0AmpHunt_Components" (Set "AmpHunt_Components=%TEMP%\AmpHunt_Components") Else (Set "AmpHunt_Components=%~dp0AmpHunt_Components")
::: ]
::: [ Make folder in Temp drive to download component files if full version not downloaded
	If not exist "%AmpHunt_Components%" MD "%AmpHunt_Components%"
::: ]
	If not exist "%AmpHunt_Components%" MD "%AmpHunt_Components%"
	Set "Player=%AmpHunt_Components%\BatchMusicPlayer.bat"
	Set "VBSplayer=%AmpHunt_Components%\playMusic.vbs"
	Set "MusicStopper=%AmpHunt_Components%\StopMusic.bat"
	Set "Monitor=%AmpHunt_Components%\BatchMonitor.vbs"
::: { GetKeys.exe by Antonio Perez Ayala
::: - https://www.dostips.com/forum/viewtopic.php?t=3428
	Set GetKeys="%AmpHunt_Components%\GetKey.exe"
	Set "GetKeyTest=%AmpHunt_Components%\GetKey.exe"
::: }
::: { BG.exe Ver 3.9 SE by Carlos Montiers Aguilera
::: - https://github.com/carlos-montiers/consolesoft-mirror/releases
	Set "BG.Util=%AmpHunt_Components%\BG.exe"
:::: }
::: [ sound by elmasmalo1. Used and shared under Creative Commons License Attribution 3.0 Unported (CC BY 3.0)
::: - Source: https://freesound.org/people/elmasmalo1/sounds/376968/
::: - License details: https://creativecommons.org/licenses/by/3.0/legalcode
	Set "Move.Sound=%AmpHunt_Components%\bubble-pop.wav"
::: ]
::: [ Theme Song by Kai Engel. "Chant of the Nightblades"
::: - Used and shared under creative commons Attribution License 4.0
::: - Source : https://freemusicarchive.org/music/Kai_Engel/Death.Soundless_The_Renaissance/04_-_Chant_Of_Night_Blades
::: - https://creativecommons.org/licenses/by-nc-sa/4.0/
::: - License details: https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode
	Set "Theme.Song=%AmpHunt_Components%\ChantOfTheNightBlades.mp3"
::: ]
::: [ Escape Sound Effect by EVRetro
::: - Used and shared under creative commons 0 License - Universal Public domain
::: - Source : https://freesound.org/people/EVRetro/sounds/495005/
::: - License Details : https://creativecommons.org/publicdomain/zero/1.0/legalcode
	Set "Escape.Sound=%AmpHunt_Components%\Escape.wav"
::: ]
::: [ Death.Sound Sound Effect by Fupicat
::: - Used and shared under creative commons 0 License - Universal Public domain
::: - Source : https://freesound.org/people/Fupicat/sounds/475347/
::: - License Details : https://creativecommons.org/publicdomain/zero/1.0/legalcode
	Set "Death.Sound=%AmpHunt_Components%\Death.wav"
::: ]
::: [ Teleport Sound by Chinomaker
::: - Used and shared under creative commons 0 License - Universal Public domain
::: - Source : https://freesound.org/people/chinomaker/sounds/324644/
::: - License Details : https://creativecommons.org/publicdomain/zero/1.0/legalcode
	Set "Teleport.Sound=%AmpHunt_Components%\Teleport.wav"
::: ]
::: - Renew component .bat and .vbs files with each launch in case folder location changes
	Call :make_components
::: }
	CHCP 65001 & CLS & REM Code page allows For use of non standard characters
::: { Commence vbs monitor to close the vbs scripts when cmd.exe is closed
	start "" "%Monitor%"
::: }
:ReLoad
TITLE ? Verifiying Components
	IF exist "%BG.Util%" %BG.Util% Cursor 0
::: [ Begin playing Theme Song If it's been downloaded
	If Exist "%Theme.Song%" CALL "%Player%" "%Theme.Song%" 40 True
::: ]
::: { Starting speed enacted using a For /L loop Delay. Decremented to Increase speed in multiples of 50.
	Set "Speed=10000"
::: }
:NewGame
::: { Creates variable Æ = Ascii-27 Escape code.
::: - http://www.dostips.com/Forum/viewtopic.php?t=1733
::: - https://stackoverflow.com/a/34923514/12343998
:::
::: - Æ (Alt 146) can be used  with and without DelayedExpansion.
    Setlocal
    For /F "tokens=2 delims=#" %%a in ('"prompt #$H#$E# & echo on & For %%b in (1) do rem"') do (
        Endlocal
        Set "Æ=%%a"
    )
::: }
::: { REM Define 'Play Field' Parameters. Allows Play Field Characteristics to be easily Changed. Only Hieght and Width need changing
::: - Outer Border
REM Hieght Max = 35 Min = 20
REM Width Max = 160 Min =35
	If NOT "%~2" == "" (
		Set "Hieght=%~1"
		Set "Width=%~2"
	) Else (
		Set "Hieght=30"
		Set "Width=160"
	)

	Set /A EndScore=(Hieght * Width)
	Set /A Lines=Hieght+5
	Set /A Columns=Width+0
::: - Establish Screen size. Larger screen sizes will result in slower Load times and Gameplay.
	Cls & Mode Con cols=%Columns% Lines=%Lines% >nul 2>nul || (Echo.Invalid Params For width and hieght. Value must be an Integer. & Exit /B)
::: - Inner Border
	Set "Axis_mins=2"
	Set /A X_Axis_Boundary=Width - 1,Y_Axis_Boundary=Hieght - 1
:: Move Limits within Borders
	Set /A Limit_Axis_Min=3,Limit_X_Max=X_Axis_Boundary - 1,Limit_Y_Max=Y_Axis_Boundary - 1
::: }
::: { Game can be modified to function without GetKey.exe, however, PerFormance is Exceptionally enhanced though it's use.
::: - Test For the existance of Ancillary .Exe required to run this program 'out of the box'
::: - Provide Option to easily download / Abort Game.
	Set "Components=True"
	For %%A In ("%GetKeyTest%" "%Move.Sound%" "%Theme.Song%" "%Escape.Sound%" "%Death.Sound%" "%Teleport.Sound%" "%BG.Util%") Do  If Not Exist %%A Set "Components=False"
	If /I "%Components%" == "False" (
		PUSHD "%AmpHunt_Components%"
		TITLE Components missing For Ampersand Hunters. Downloads required to continue.
		Echo.%Æ%[33m This Program requires the following components to be downloaded in the folder:
		Echo.%Æ%[32m %AmpHunt_Components%
		Echo.
		If Not Exist "%GetKeyTest%" Echo.%Æ%[32m Getkey.exe must be saved to %Æ%[34m %GetKeys%
		If Not Exist "%Move.Sound%" Echo.%Æ%[32m bubble-pop.wav must be saved to %Æ%[34m %Move.Sound%
		If Not Exist "%GetKeyTest%" Echo.%Æ%[32m Getkey.exe must be saved to %Æ%[34m %GetKeys% "%BG.Util%"
		If Not Exist "%BG.Util%" Echo.%Æ%[32m BG.exe must be saved to %Æ%[34m "%BG.Util%"
		If Not Exist "%Death.Sound%" Echo.%Æ%[32m Death.Sound.wav must be saved to %Æ%[34m %Death.Sound%		
		If Not Exist "%Theme.Song%" Echo.%Æ%[32m Chant of the NightBlades must be saved to %Æ%[34m %Theme.Song%
		If Not Exist "%Teleport.Sound%" Echo.%Æ%[32m Teleport.wav must be saved to %Æ%[34m %Teleport.Sound%
		Echo.
		Echo.Would You Like to download them Now? %Æ%[33mY/N
		For /F "Usebackq Tokens=* Delims=" %%C in (`"Choice /N /C:YN"`) Do If /I "%%C" == "Y" (
			Echo.%Æ%[91mAmpersand Hunters will Continue once the Download browser is Closed.
			If Not Exist "%GetKeyTest%" start /Wait "" "https://drive.google.com/uc?export=download^&id=1q02Ur2a9Wvjlh-uc_QKgx2c6yaX82lNT"
			If Not Exist "%BG.Util%" start /Wait "" "https://drive.google.com/uc?export=download&id=1LMM3pxQlQYaV-w8WfFqnOl_3TMh4gT_f"
			If Not Exist "%Move.Sound%" start /Wait "" "https://drive.google.com/uc?export=download^&id=146w6quLBrCxhon5Sk-aaZo2zs_RnGZKt"
			If Not Exist "%Escape.Sound%" start /Wait "" "https://drive.google.com/uc?export=download&id=1okOTyKQa-t1zSL0Zf-LCs33y-zVad4mQ"
			If Not Exist "%Death.Sound%" start /Wait "" "https://drive.google.com/uc?export=download&id=1eZeesi-cRkk8BIySm2JgpZB0-AwHWgHH"
			If Not Exist "%Theme.Song%" start /Wait "" "https://drive.google.com/uc?export=download&id=1yeAYgoFYcxYIcss3IZlF5MW4OT3neKvL"
			If Not Exist "%Teleport.Sound%" start /Wait "" "https://drive.google.com/uc?export=download&id=1HNQrQw0USOecPMGl1tdYpUM9qcDbzqqe"
			POPD
			Pause & CLS & Goto :ReLoad & REM ensure all components are downloaded into the correct folder with the correct name.
		) Else (
			EXIT
		)
	)
::: }
::: { Define Base variables used For displaying characters on screen
::: - Characters and their Color are defined here to allow them to be easily changed from the one Location.
	Set "Player.Trail=•%Æ%[0m" & REM • Alt 7 ○ Alt 9
	Set "Enemy.Trail=•%Æ%[0m" & REM • Alt 7 ○ Alt 9
::: [ FG Set index variable count must match the number of Characters in the set.
	Set "FGSet=╬○ʘ¤" & REM Alt 206 ╬ 9 ○ 664 ʘ 207 ¤
	Set "FGSet[i]=4"
::: ]
	Set "ESCSYMBOL=☼%Æ%[0m" & REM Alt 15
::: [ Display character For enemy when escaped.
	Set "space=%Æ%[37m%Æ%[7m %Æ%[0m"
::: ]
::: [ Outer and Inner Border Symbols
	Set "BORDER.IN.SYMBOL=%Æ%[7m%Æ%[31m░%Æ%[0m" & rem alt 176
	Set "BORDER.OUT.SYMBOL=%Æ%[7m%Æ%[37m▓%Æ%[0m" & rem alt 178
::: ]
::: [ Player character.
	Set "P_C=%Æ%[33m☻%Æ%[0m" & REM Alt 21 § 1 ☺ 2 ☻ 3 ♥ 4 ♦ 5 ♣ 6 ♠ 8 ◘ 10 ◙ 11 ♂ 12 ♀
::: ]
::: [ Enemy Character.
	Set "E_C=&"
	Set "ENEMY=%Æ%[31m%E_C%%Æ%[0m"
::: ]
	Set "Esc=%Æ%[37m^%Æ%[0m"
::: [ Define Number of Enemies Incements by 1 with each replay. Number is Decreased by one when Eaten
	Set /A Enemies+=1
::: ]
:: Instruction Line Pos - Line number that inFormation is displayed to the player on
	Set /A Instructions=hieght + 2
::: }
::: { Define Movement Control Keys
	Set "Up=W"
	Set "Down=S"
	Set "Left=A"
	Set "Right=D"
	Set "Pause=P"
	Set "Keys=%Up% %Left% %Down% %Right%"
	Set "MoveKeys=%Keys%"
::: }
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Macro Definitions {
set LF=^


::Above 2 blank lines are required - do not remove
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"

::: { Loading bar Macro
	Set "Title.Text=Loading "
	Set @Load.Title=Set Title.Text=!Title.Text!◘^& TITLE !Title.Text!
::: }
::: { Macro For changing enemy color During each loop
	Set @Color.NPC=For %%n in (1 2) do if %%n==2 (%\n%
		For /F "tokens=1 delims=," %%G in ("!Param!") do (%\n%
			Set /A "Rand_Col=!random! * 6 / 32768 + 1" %\n%
			If "!Rand_Col!" == "6" (Set "%%G=%Æ%[31m!E_C!%Æ%[0m") %\n%
			If "!Rand_Col!" == "5" (Set "%%G=%Æ%[93m!E_C!%Æ%[0m") %\n%
			If "!Rand_Col!" == "4" (Set "%%G=%Æ%[35m!E_C!%Æ%[0m") %\n%
			If "!Rand_Col!" == "3" (Set "%%G=%Æ%[37m!E_C!%Æ%[0m") %\n%
			If "!Rand_Col!" == "2" (Set "%%G=%Æ%[34m!E_C!%Æ%[0m") %\n%
			If "!Rand_Col!" == "1" (Set "%%G=%Æ%[91m!E_C!%Æ%[0m") %\n%
		) %\n%
	) Else set Param=
::: }
::: { Display player trail and position.
	Set "@Display.Player=ECHO(%Æ%[!L_Y_Pos!;!L_X_Pos!H%Æ%[38;2;!L_Y_Pos!0;100;!L_X_Pos!0m!Player.Trail!%Æ%[!Y_Pos!;!X_Pos!H!P_C!"
::: }
::: { Macro For testing player XY pos against NPC's to determine Game State changes
	Set @Test.Positions=For %%n in (1 2) do if %%n==2 (%\n%
		For /F "tokens=1 delims=, " %%G in ("!Param!") do (%\n%
			If "!L_X_Pos!" == "!X_Pos!" If "!L_Y_Pos!" == "!Y_Pos!" ECHO(%Æ%[!Y_Pos!;!X_Pos!H!P_C!%\n%
			For /L %%E in (1,1,!Enemies!) Do (%\n%
				If "!X_Pos!" == "!AI%%E_X!" If "!Y_Pos!" == "!AI%%E_Y!" (%\n%
					ECHO(%Æ%[!Y_Pos!;!X_Pos!H%Æ%[31m%Æ%[7mX%Æ%[0m%\n%
					Set "G_Over=%Æ%[!Instructions!;1H%Æ%[K%Æ%[31mYou were Nommed by an Ampersand" ^& Set "Died=True" ^& Goto :end%\n%
				) %\n%
			)%\n%
			If "!X_Pos!" == "!T_X!" If "!Y_Pos!" == "!T_Y!" (%\n%
				ECHO(%Æ%[!Y_Pos!;!X_Pos!H%Æ%[31m%Æ%[7mX%Æ%[0m%\n%
				Set "G_Over=%Æ%[!Instructions!;1H%Æ%[K%Æ%[31mYou Hit the Trap" ^& Set "Died=True" ^& Goto :end%\n%
			) %\n%
			If "!X_Pos!" == "!E_X!" If "!Y_Pos!" == "!E_Y!" (%\n%
				ECHO(%Æ%[!Y_Pos!;!X_Pos!H%Æ%[36m%Æ%[7m!Esc!%Æ%[0m%\n%
				For /L %%E in (1,1,!Enemies!) Do ECHO(%Æ%[!AI%%E_Y!;!AI%%E_X!H!space!%\n%
				Set /A Score+=EndScore%\n%
				Set "G_Over=%Æ%[!Instructions!;1H%Æ%[K%Æ%[36mYou've Escaped with !Score! points" ^& Goto :end%\n%
			) %\n%
		) %\n%
	) Else set Param=
::: }
::: { Macro For calculating the Players movement
	Set @Calculate.Move=For %%n in (1 2) do if %%n==2 (%\n%
		For /F "tokens=1 delims=, " %%G in ("!Param!") do (%\n%
			If /I "%%G" == "!Up!" If NOT "!Y_Pos!" == "!Limit_Axis_Min!" (Set /A "Y_Pos-=1" ^>nul)%\n%
			If /I "%%G" == "!Down!" If NOT "!Y_Pos!" == "!Limit_Y_Max!" (Set /A "Y_Pos+=1" ^>nul)%\n%
			If /I "%%G" == "!Left!" If NOT "!X_Pos!" == "!Limit_Axis_Min!" (Set /A "X_Pos-=1" ^>nul)%\n%
			If /I "%%G" == "!Right!" If NOT "!X_Pos!" == "!Limit_X_Max!" (Set /A "X_Pos+=1" ^>nul)%\n%
			If /I "%%G" == "!Pause!" (%\n%
				TITLE Paused%\n%
				Pause ^> Nul%\n%
			)%\n%
		) %\n%
	) Else set Param=
::: }
::: { Macro's For calculating NPC's Moves
	Set @AI.Move.1=For %%n in (1 2) do if %%n==2 (%\n%
		For /F "tokens=1 delims=, " %%G in ("!Param!") do (%\n%
			If "!%%G_X!" == "!X_Pos!" (%\n%
				If !%%G_Y! LSS !Y_POS! (Set /A "%%G_Y+=1" ^>nul)%\n%
				If !%%G_Y! GTR !Y_POS! (Set /A "%%G_Y-=1" ^>nul)%\n%
			) Else (%\n%
				If !%%G_X! LSS !X_POS! (Set /A "%%G_X+=1" ^>nul)%\n%
				If !%%G_X! GTR !X_POS! (Set /A "%%G_X-=1" ^>nul)%\n%
			)%\n%
		) %\n%
	) Else set Param=
	Set @AI.Move.2=For %%n in (1 2) do if %%n==2 (%\n%
		For /F "tokens=1 delims=, " %%G in ("!Param!") do (%\n%
			If "!%%G_Y!" == "!Y_Pos!" (%\n%
				If !%%G_X! LSS !X_POS! (Set /A "%%G_X+=1" ^>nul)%\n%
				If !%%G_X! GTR !X_POS! (Set /A "%%G_X-=1" ^>nul)%\n%
			) Else (%\n%
				If !%%G_Y! LSS !Y_POS! (Set /A "%%G_Y+=1" ^>nul)%\n%
				If !%%G_Y! GTR !Y_POS! (Set /A "%%G_Y-=1" ^>nul)%\n%
			)%\n%
		) %\n%
	) Else set Param=
	Set @AI.Teleport=For %%n in (1 2) do if %%n==2 (%\n%
		For /F "tokens=1 delims=, " %%G in ("!Param!") do (%\n%
			For %%P in (X Y) Do Set /A "%%G_%%P=!random! %% ( !Limit_%%P_Max! - !Limit_Axis_Min! ) + !Limit_Axis_Min!" ^>nul%\n%
			If "!X_Pos!" == "!%%G_X!" If "!Y_Pos!" == "!%%G_Y!" Set /A "%%G_X=!random! %% ( !Limit_X_Max! - !Limit_Axis_Min! ) + !Limit_Axis_Min!" ^>nul%\n%
		) %\n%
	) Else set Param=
::: }
::: { Macro For updating variables used to show changes in gamestate
	Set @LastPos.Update=For %%n in (1 2) do if %%n==2 (%\n%
		For /F "tokens=1 delims=, " %%G in ("!Param!") do (%\n%
			Set /A %%G+=1 ^>nul%\n%
			For %%P in (X Y) Do For /L %%E in (1,1,!Enemies!) Do Set "L_AI%%E_%%P=!AI%%E_%%P!"%\n%
			Set "L_Y_Pos=!Y_Pos!"%\n%
			Set "L_X_Pos=!X_Pos!"%\n%
		) %\n%
	) Else set Param=
::: }
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: End Macro Definitions }
::: { Prepare the environment For Building Arrays and Expanding Macros.
	Setlocal EnableDelayedExpansion
::: }
	%@Load.Title%
::: { Define Variables For mapping errorlevel to Key.
::: - used For mapping Errorlevel to Key - Differs by Case
:::  ** Numbers within the below ranges must not be assigned as other variable Names **
	Set /A Keypos=47 & REM [48 - 57]
	For %%A in (0 1 2 3 4 5 6 7 8 9) Do (
		Set /A Keypos+=1
		Set "!Keypos!=%%A"
	)
	%@Load.Title%
	Set /A Keypos=64 & REM [65 - 90]
	For %%A in (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) Do (
		Set /A Keypos+=1
		Set "!Keypos!=%%A"
	)
	%@Load.Title%
	Set /A Keypos=96 & REM [97 - 122]
	For %%A in (a b c d e f g h i j k l m n o p q r s t u v w x y z) Do (
		Set /A Keypos+=1
		Set "!Keypos!=%%A"
	)
::: }
	%@Load.Title%
::: { Build screen background using For /L loop to Define each line of the Foreground into an Array
::: - to display in a smooth manner. Done within local environment to reduce memory usage during the game loop
	Setlocal EnableDelayedExpansion
:: [ Select random Symbol from defined Foreground Set
	Set /A rndFG=!random! %% !FGSet[i]!
	Set "FGSYMBOL=%Æ%[32m!FGSet:~%rndFG%,1!%Æ%[0m"
:: ]
:: [ Prepare Playfield, inner and outer border variables For display
	For /L %%H IN (%Limit_Axis_Min%,1,%Limit_Y_Max%) DO Set "Play.Field%%H=Echo."
	For %%A in (Inner Outer) Do For %%B in (Vert Hor) Do Set "%%A.Border.%%B=Echo."
:: ]
	%@Load.Title%
:: [ Define the Play Field
	For /L %%A IN (%Limit_Axis_Min%,1,%Limit_X_Max%) DO (
		For /L %%B IN (%Limit_Axis_Min%,1,%Limit_Y_Max%) DO (
			Set "Play.Field%%B=!Play.Field%%B!%Æ%[%%B;%%AH!FGSYMBOL!"
		)
	)
:: ]
	%@Load.Title%
:: [ Build the Vertical and Horizontal Edges of the Inner Border
	For /L %%A IN (%Axis_mins%,1,%Y_Axis_Boundary%) DO (
		For %%B IN (%Axis_mins%,%X_Axis_Boundary%) DO (
			Set "Inner.Border.Vert=!Inner.Border.Vert!%Æ%[%%A;%%BH!BORDER.IN.SYMBOL!"
		)
	)
	%@Load.Title%
	For /L %%A IN (%Axis_mins%,1,%X_Axis_Boundary%) DO (
		For %%B IN (%Axis_mins%,%Y_Axis_Boundary%) DO (
			Set "Inner.Border.Hor=!Inner.Border.Hor!%Æ%[%%B;%%AH!BORDER.IN.SYMBOL!"
		)
	)
:: ]
	%@Load.Title%
:: [ Build the Vertical and Horizontal Edges of the Outer Border
	For /L %%A IN (1,1,%Hieght%) DO Set "Outer.Border.Vert=!Outer.Border.Vert!%Æ%[%%A;1H%BORDER.OUT.SYMBOL%%Æ%[%%A;%Width%H!BORDER.OUT.SYMBOL!"
	%@Load.Title%
	For /L %%A IN (1,1,%Width%) DO 	Set "Outer.Border.Hor=!Outer.Border.Hor!%Æ%[1;%%AH%BORDER.OUT.SYMBOL%%Æ%[%hieght%;%%AH!BORDER.OUT.SYMBOL!"
:: ]
	%@Load.Title%
:: [ Display the Borders and the Playfield
	For %%A in (Inner Outer) Do For %%B in (Vert Hor) Do !%%A.Border.%%B!
	For /L %%D in (%Limit_Axis_Min%,1,%Limit_Y_Max%) do !Play.Field%%D!
:: ]
	Endlocal & %@Load.Title%
::: }
::: { Define Starting Positions
:startpos
	%@Load.Title%
	For %%P in (X Y) Do (
		Set /A "%%P_Pos=!random! %% ( Limit_%%P_Max - Limit_Axis_Min ) + Limit_Axis_Min"
		Set "L_%%P_Pos=!%%P_Pos!"
	)
	For /L %%E in (1,1,!Enemies!) Do For %%P in (X,Y) Do (
		Set /A "AI%%E_%%P=!random! %% ( Limit_%%P_Max - Limit_Axis_Min ) + Limit_Axis_Min"
		Set "L_AI%%E_%%P=!AI%%E_%%P!"
	)
	For /L %%E In (1,1,!Enemies!) Do If "%X_Pos%" == "!AI%%E_X!" If "%Y_Pos%" == "!AI%%E_Y!" GOTO :startpos

::: - Initialise Teleport properties. Teleport properties are calculated based on starting score, which is Area based
	Set Teleport=0
	Set /A Teleport_Freq=(Hieght * Width) / 100
	Set /A Teleport_Reset=Teleport_Freq * 5

::: [ define win condition 'Escape' and additional Loss condition 'Trap', ensuring values differ and within 'play field'
:Objects
	%@Load.Title%
::: - %%O Object - %%P positional axis
	For %%O in (E T) Do For %%P in (X,Y) Do Set /A "%%O_%%P=!random! %% ( Limit_%%P_Max - Limit_Axis_Min ) + Limit_Axis_Min"
	For %%O in (E T) Do For %%P in (X,Y) Do If "!%%O_%%P!" == "!%%P_Pos!" GOTO :Objects
	If "%E_X%" == "%T_X%" If "%E_Y%" == "%T_Y%" (GOTO :Objects)
	Set "@Display.Escape=Echo.%Æ%[%E_Y%;%E_X%H%Æ%[48;2;%E_Y%0;100;%E_X%0m!ESCSYMBOL!"
::: ]
::: [ Condition a pause and holding value to intiate a choice option to begin gameplay.
	Set "Key=!Pause!"
	Set "Last.Key=NG"
::: ]
::: }
::: { Game Loop.
:Move
	TITLE !Enemies! Ampersand Hunters pursue you.
	Set /A Teleport+=1 & REM Increment count until next enemy teleport
	%@Color.NPC% ENEMY
::: - Display Player and Enemies
	(
		%@Display.Player%
		%@Display.Escape%& REM The escape is refreshed prior to Displaying Enemy Locations so the player can see all enemy locations
		For /L %%E in (1,1,!Enemies!) do ECHO(%Æ%[!L_AI%%E_Y!;!L_AI%%E_X!H%Æ%[38;2;175;!L_AI%%E_Y!0;!L_AI%%E_X!m!Enemy.Trail!%Æ%[!AI%%E_Y!;!AI%%E_X!H!ENEMY!
	)>"%AmpHunt_Components%\Animate.txt"
	TYPE "%AmpHunt_Components%\Animate.txt"	
	%@Test.Positions% screen
	%@LastPos.Update% Score

	ECHO(%Æ%[%instructions%;1H%Æ%[K%Æ%[37m Escape the Ampersands [%Æ%[33m!MoveKeys!%Æ%[37m]
	ECHO(%Æ%[K %Æ%[33mScore : %Æ%[36m !Score!%Æ%[0m
::: - Pause prior to opening Move.
	If "!Last.Key!" == "NG" (
		TITLE Ampersand Hunters. Level !Enemies! - Select a Move to Start.
		For /F "UsebackQ Tokens=* Delims=" %%K in (`"Choice /N /C:!Up!!Left!!Down!!Right!"`) Do (
			Set "Last.Key=%%K"
			Set "Key=%%K"
		)
	)
::: [ Movement Move.Sound.
	CALL "%Player%" "%Move.Sound%" 15 false > Nul 2> Nul
::: ]
::: [ If you do not wish to download GetKeys.exe, Enable Choice and REM out indicated lines, and the Download If Conditions.]
::: - Returns the Key literal instead of the errorlevel.
	If Not Exist "%GetKeyTest%" For /F "Usebackq Tokens=* Delims=" %%K in (`"Choice /T 1 /N /C:0123456789abcdefghijklmnopqrstuvwxyz /D 0"`) Do Set "Key.In=%%K"
::: - Not used as a macro as the expansion of the choice command within the For Loop is significantly slower than executing the For loop Directly.
::: - Default switch results in a Free Move For the AI
::: ] Else :
::: [ GetKey.exe by Antonio - A.K.A Aacini https://stackoverflow.com/users/778560/aacini
::: - Can be found at: https://www.dostips.com/Forum/viewtopic.php?f=3&t=3428#p17101
	If Exist "%GetKeyTest%"	( 
		Set "Key.In="
		%GetKeys% /N & REM out if GetKey.exe is not downloaded
::: - Assign Errorlevel from Getkey.exe to a variable, test if valid predefined Key.
		Set "Key.In=!Errorlevel!" & REM out if GetKey.exe is not downloaded
::: - Return the Key literal instead of the errorlevel:
		For %%K in ("!Key.In!") do Set "Key.In=!%%~K!" & REM out if GetKey.exe is not downloaded
	)
::: ] End of GetKeys
::: [ Determine  game action dependant on last Key in. Pause handley by Key.In and Key variables
::: - preserves movement direction when pause has completed.
	For %%K in (!keys! !Pause!) Do (If /I "!Key.In!" == "%%K" Set "Key=%%K" & If /I Not "%%K" == "!Pause!" Set Last.Key=%%K)
	%@Calculate.Move% !Key!
	If /I "!Key!" == "!Pause!" Set "Key=!Last.Key!"
::: ]
::: [ Use substring modification and ASCII color codes to display last key press
	For %%K in (!Key!) do Set "MoveKeys=!Keys:%%K=%Æ%[35m%%K%Æ%[33m!"
::: ]
::: [ expand Selected Macro's to calculate enemies movements.
::: - Positioned prior to Teleport so an enemy cant teleport to an adjacent cell and Consume you within the same Move.
	For /L %%E in (1,1,!Enemies!) Do (
		Set /A AI%%E_Move=!random! %%2 + 1
		If "!AI%%E_Move!" == "1" %@AI.Move.1% AI%%E
		If "!AI%%E_Move!" == "2" %@AI.Move.2% AI%%E
	)
::: ]
::: [ Warp the enemy characters to a new position Only if they are not on the same ? axis at defined intervals
	For /L %%A in (%Teleport_Freq%,%Teleport_Freq%,%Teleport_Reset%) Do (
		If "!Teleport!" == "%%A" (
			(CALL "%Player%" "%Teleport.Sound%" 20 false > Nul 2> Nul) || CALL "%Player%" "%Teleport.Sound%" 20 false > Nul 2> Nul
			For /L %%O in (1,2,!Enemies!) Do If Not "!X_Pos!" == "!AI%%O_X!" %@AI.Teleport% AI%%O
			For /L %%E in (2,2,!Enemies!) Do If Not "!Y_Pos!" == "!AI%%E_Y!" %@AI.Teleport% AI%%E
			If "%%A" == "%Teleport_Reset%" (Set /A "Score+=Teleport_Reset" && Set "Teleport=0")
		)
	)
::: ]
::: { Enact Speed Delay
	If !Enemies! LEQ 10 For /L %%A in (0,1,!Speed!) Do REM Delay
::: }
Goto :Move
::: } End of movement Loop
::: { * Script break * }
::: { Subroutine used to create sound related subporgrams }
:Make_Components
::: [ creates a companion batch file that's called with arguments For trackpath, volume and loop tf values
::: Parameters required For Player: [filepath.ext] [0-100] [True-False]
	(
	Echo.@ECHO OFF
	Echo.Set "MusicPath=%%~1"
	Echo.Set "vol=%%~2"
	Echo.Set "Loop_TF=%%~3"
::: - Change to the Directory you want to create the Music Launcher in.
	Echo.PUSHD "%%AmpHunt_Components%%"
::: - Ensure no Conflict with the Previous Script.
	Echo For %%%%A In ^("%%MusicPath%%"^) Do Set "SoundSUFFIX=%%%%~nA"
::: - Creates a vbs Script to Launch the music (Occurs without any visual indication or prompting)
	Echo.^( echo Set Sound = CreateObject^("WMPlayer.OCX.7"^^^)
	Echo.echo Sound.URL = "%%MusicPath%%"
	Echo.echo Sound.settings.volume = %%vol%%
	Echo.echo Sound.settings.setMode "loop", %%Loop_TF%%
	Echo.echo Sound.Controls.play
	Echo.echo While Sound.playState ^^^<^^^> 1
	Echo.echo      WScript.Sleep 10
	Echo.echo Wend
	Echo.^)^>PlayMusic%%SoundSUFFIX%%.vbs
	Echo.start /min PlayMusic%%SoundSUFFIX%%.vbs ^> Nul 2^> Nul
::: -	Return to the Previous Directory
	Echo.POPD
::: -	Exit the Launcher and return to Previous batch program.
	Echo.Goto :EOF
	)>"%Player%"
::: ]
::: [ Monitor checks process status of cmd.exe every 1500 ms and calls StopMusic batch file to taskill all vbs scripts when
::: - cmd.exe process count is 0. Delay is to reduce CPU usage of the WMI service
	(
	ECHO Set objWMIService = GetObject ("winmgmts:"^)
	ECHO Set proc = objWMIService.ExecQuery("select * from Win32_Process Where Name='cmd.exe'"^)
	ECHO DO while proc.count ^> 0
	ECHO Set proc = objWMIService.ExecQuery("select * from Win32_Process Where Name='cmd.exe'"^)
	ECHO if proc.count ^< 1 then exit do
	ECHO wscript.sleep 2000
	ECHO loop
	ECHO Set WshShell=createobject("wscript.shell"^)
	ECHO WshShell.run "%MusicStopper%", 0, true
	)>"%Monitor%"
::: ]
::: [ Simple taskkill script to terminate vbs scripts on program exit
	(
	Echo.@ECHO OFF
	Echo.taskkill /pid WScript.exe /f /t ^>nul 2^> nul
	Echo.Goto :EOF
	)>"%MusicStopper%"
	Timeout 1 >nul
Exit /B
::: } End of Subroutines
::: { Script exit and cleanup
:end
	If /I "!Died!" == "True" (
		CALL "%Player%" "%Death.Sound%" 80 False
		Set /A Enemies-=1
		If "!Score!" GEQ "!EndScore!" Set /A Score-=EndScore
	) Else (
::: [ Increase Speed By reducing For /L Loop Iterations
		If !Speed! GTR 1000 (Set /A Speed-=1000)
		CALL "%Player%" "%Escape.Sound%" 60 False
	)
::: ]
	ECHO(%G_over%
	Set /A Instructions+=1
:ExitMenu
	ECHO(%Æ%[%Instructions%;1H%Æ%[K%Æ%[34m[E]xit [C]onitnue
	For /F "Usebackq Tokens=* Delims=" %%K in (`"Choice /N /C:0123456789abcdefghijklmnopqrstuvwxyz"`) Do (
		If /I "%%K" == "E" (
			CALL "%MusicStopper%"
			Del /Q "%Monitor%"
			Del /Q "%MusicStopper%"
			Del /Q "%Player%"
			Del /Q "%VBSplayer%"
			EXIT
		)
		If /I "%%K" == "C" (
			Endlocal & Set "Score=%Score%" & Set "Enemies=%Enemies%" & Set "Speed=%Speed%"
			GOTO :NewGame
		)
	)
Goto :ExitMenu
::: }

Post Reply