There are no arrays in cmd/batch. The one and only variable type is STRING (although Lists and Arrays can be simulated to a certain degree). I therefore would change from arrays (independent variables for each element) to lists (one variable containing all elements) and put all "array/list stuff handling" into a subroutine, making it easy in the main code to get a certain element:
@echo off
setlocal enabledelayedexpansion
REM define readable names for indexes (for more readable code later)
set "MaxHealth=2"
set "Damage=1"
set "Dodge=3"
set "Name=4"
set "Goblin=1"
set "Zombie=2"
set "Skeleton=3"
set "Orc=4"
set "Acrobat=5"
REM define a list of properties for each creature
set "_Goblin=5,20,7,little Goblin"
set "_Zombie=7,15,3,angry Zombie"
set "_Skeleton=3,11,10,bony Skeleton"
set "_Orc=7,25,0,furious Orc"
set "_Acrobat=4,15,80,fast Acrobat"
REM define a list of all creatures
set "PossibleEnemies=Goblin,Zombie,Skeleton,Orc,Acrobat"
set /a Enemy1=%random% %%5
set /a Enemy2=%random% %%5
REM get properties
call :GetProperty %Enemy1% Name Enemy1
call :GetProperty %Enemy2% Name Enemy2
call :GetProperty %Enemy1% MaxHealth Enemy1
call :GetProperty %Enemy2% MaxHealth Enemy2
call :GetProperty %Enemy1% Damage Enemy1
call :GetProperty %Enemy2% Damage Enemy2
call :GetProperty %Enemy1% Dodge Enemy1
call :GetProperty %Enemy2% Dodge Enemy2
echo You fight against:
set Enemy
goto :eof
:GetProperty Type Property VarName ; resulting variable = [VarName].[Property]
echo DBG: EnemyType=%1
echo DBG: Property=%2
for /f "tokens=%1 delims=," %%a in ("%PossibleEnemies%") do set "Type=%%a"
echo DBG: Type=%Type%
echo DBG: ReturnVar=%3.%2
set "opt=tokens=!%2! delims=," & REM not possible to use delayed expansion in for /f "[options]"
for /f "%opt%" %%a in ("!_%Type%!") do set "%3.%2=%%a"
echo DBG: Returnval=!%3.%2!
echo DBG: -----
goto :of
Example output (without the DBG lines):
You fight against:
Enemy1=3
Enemy1.Damage=3
Enemy1.Dodge=10
Enemy1.MaxHealth=11
Enemy1.Name=bony Skeleton
Enemy2=1
Enemy2.Damage=5
Enemy2.Dodge=7
Enemy2.MaxHealth=20
Enemy2.Name=little Goblin
If you want, you can put the REM get properties (all those call ...lines) into another subroutine for even more readable main code.
To answer your actual question: you need one layer of expansion per depth-layer. You have a variable containing a variable containing a variable, so you need three layers of parsing (the original layer plus two more. One more layer is usually achieved by delayed expansion; three layers are a bit more complicated: use call for an additional layer. The innermost variable is used "as usual" %c% for the next layer you have to double each %, as one % gets consumed by the additional layer of parsing (the second call), the same is true for the outermost (third) layer (the first call), resulting in four %, because each layer of parsing halves them:
@echo off
setlocal
set a[2]=1000
set b[1]=2
set c=1
call call set d=%%%%a[%%b[%c%]%%]%%%%
echo %d%
result: 1000
Some words on efficiency:
As @Darin already mentioned, call is slow (because it creates another instance of cmd. Of course call call is even slower. Take a look at the times the three loops take (an empty loop, a "call-call" loop and a loop using another loop to force another layer of parsing):
@echo off
setlocal
set a[2]=1000
set b[1]=2
set c=1
echo start empty loop %time%
for /l %%i in (1,1,10000) do (
REM
)
echo start call-call %time%
for /l %%i in (1,1,10000) do (
call call set d=%%%%a[%%b[%c%]%%]%%%%
)
echo (proof it worked: %d%)
echo start speed-optimized %time%
setlocal enabledelayedexpansion
for /l %%i in (1,1,10000) do (
for %%j in (!b[%c%]!) do set "x=!a[%%j]!"
)
echo (proof it worked: %x%)
endlocal
echo End %time%
Output:
start empty loop 16:20:49,05
start call-call 16:20:49,06
(proof it worked: 1000)
start speed-optimized 16:21:02,51
(proof it worked: 1000)
End 16:21:03,06
(~0.1 seconds / ~12 seconds / ~ 0,6 seconds) - YMMV depending on hardware and processor load. Ok, it takes 10000 iterations to get to those numbers, but nevertheless - its a factor of ~20)