Reputation: 105
I'm making a small little game I want a random chance to get certain items (e.g. if the random number is greater than 10 but less than 15 then you will get a certain item). Here's what I've already tried which resulted in a crash.
set /a chance= %random% %%30+1
if %chance% gtr 10 && lss 30 (
set /a %nails%+1
echo You got nails! %chance%
)
This piece right here was just a test, but should give you an idea of what I am going for. This is really the only way I can think of doing it. If you could help, please do! :)
Upvotes: 0
Views: 834
Reputation: 2951
the if condition approach works and all, but is somewhat clunky if your going to be scripting in many loot situations. it is by far easier to use an array setup with a macro that can access ranges within the array to allow you to simply and easily script loot boxes that roll different items by using substring modification to change the index of the array the random number can access. a demonstration:
@Echo off
:new
::: -------------------------------------------------------------------|| MACRO DEFINITIONS
Setlocal DisableDelayedExpansion
(Set \n=^^^
%=DNR=%
)
rem ********************* Display any existing character names for continuation or deletion of characters
If Exist "%TEMP%\%~n0_*_save.bat" (Echo/Your Characters:&Echo/&(For /F "Delims=" %%G in ('Dir "%TEMP%\%~n0_*_save.bat" /B')Do For /F "Tokens=2 Delims=_" %%o in ("%%~nG") Do < Nul Set /P "=[%%o] ")&Echo/)
:character
Set /P "Name=Name: "
If Exist "%TEMP%\%~n0_%Name%_save.bat" (Echo/[C]ontinue / [D]elete?&For /F "Delims=" %%O in ('Choice /N /C:cd')Do If /I "%%O"=="C" (Goto :playon)Else (Del /P "%TEMP%\%~n0_%Name%_save.bat" & Goto :character))
If "%Name%"=="" Goto :character
:playon
rem *** Inventory Macro. Displays all elements for the given group and their current values.
rem ::: Usage: %INV:@=$varname[%
Set "INV=Echo/&(For /F "Tokens=2 Delims==" %%i in ('Set @') Do (Set "VN=%%i"&^< Nul Set /P"=[!VN:$=!:!%%i!] "))&Echo/"
rem *** Autosave macro. Can be incorperated into other macro's
rem ::: Usage: %Save%
Set SAVE=(For /F "Tokens=1 Delims==" %%i in ('Set $') Do (If not "!%%i!"=="" Echo/Set "%%i=!%%i!"))^>"%TEMP%\%~n0_!name!_save.bat"
rem *** Location Display Macro with autosave macro included
rem ::: Usage: %Loc:@=LocationLABEL%
Set "Loc=(Set "$Loc=@"&Title !$Loc:_= !)&%Save%"
rem *** Loot box Macro to generate random loot from specified range of an indexed array
rem *** !random! %%4 + Index# will access an index range between the index # and 4 above the index number.
rem ::: Usage: %Loot:@=index#%
Set "LOOT=(For /F "UsebackQ Delims=" %%i in (`"Set /A i#=!Random! %%4 + @"`) Do For /F "UsebackQ Delims=" %%v in (`"Set /A v#=!Random! %%3 + 1"`) Do (Set "VN=!$Loot[%%i]:$=!"&Echo/You got %%v !VN!&Set /A "!$Loot[%%i]!+=%%v")) 2> Nul & %SAVE%"
rem *** the below macros /I /V and /P are not used in this example. - They are an optional method for defining
rem *** variables prefixed with $ that automatically saves them for reloading
rem ::: usage: %/I:V=Varname%Input Prompt String:
Set "/I=For %%n in (1 2)Do If %%n==2 (Set /P "$V=!$PromptStr:$=!: "&%Save%)Else Set $PromptStr="
rem ::: usage: %/P:V=Varname%VariableValue
Set "/V=For %%n in (1 2)Do If %%n==2 (Set "$V=!str!"&%Save%)Else Set str="
rem ::: usage: %/A:V=Varname%=Equation
Set "/A=For %%n in (1 2)Do If %%n==2 (Set /A "$V!sum!"&%Save%)Else Set sum="
rem *** Wait prompt Macro
rem ::: usage: %Wait:#=Integer value for time in seconds%Wait Prompt String
Set "Wait=For %%n in (1 2)Do If %%n==2 (Timeout # /Nobreak > Nul & (Pause | Echo/!Output!) 2> Nul )Else Set Output="
rem *** Array definition macro. Asigns the element names to an indexed Groupname (Array), With each element being assigned an initial 0 value
Rem ::: Usage: %DefArray%{VarGroupName}{Element names as list}
Set DefArray=For %%n in (1 2) Do if %%n==2 (%\n%
Set "i#=0"%\n%
For /F "Tokens=1,2 Delims={}" %%G in ("!List!") Do (%\n%
For %%i in (%%~H) Do (%\n%
Set "$%%~G[!i#!]=$%%i"%\n%
Set "$%%i=0"%\n%
Set /A i#+=1 ^> Nul%\n%
)%\n%
)%\n%
) Else Set List=
Set Menu=CLS^&Set "Copt="^&For %%n in (1 2) Do if %%n==2 (%\n%
Echo/[E]xit%\n%
For %%G in (!OPTS!)Do (%\n%
Set "opt=@%%~G"%\n%
Set "opt=!opt:_= !"^&Set "Opt=!Opt:~,-1!"%\n%
Set "Copt=!Copt!%%~G"%\n%
Echo/!Opt! [%%~G]%\n%
)%\n%
(For /F "Delims=" %%O in ('Choice /N /C !Copt!E')Do If "%%O"=="E" (Endlocal^&Endlocal^&Set "Name="^&Goto :New) Else (CLS^&Goto :@%%O))%\n%
) Else Set OPTS=
::: -------------------------------------------------------------------|| END MACRO DEFINITIONS
::: -------------------------------------------------------------------|| Example Script
REM // required to be enabled PRIOR to macro Use, AFTER definition.
Setlocal EnableDelayedExpansion
%DefArray%{Loot}{Wood Nails Ore Leather Gold Silver Bronze Jade}
IF Exist "%TEMP%\%~n0_!name!_save.bat" (
Call "%TEMP%\%~n0_!name!_save.bat"
Goto :!$Loc!
)
:Menu
%Loc:@=Menu%
%Menu:@=Loot_Box_% "1" "2"
Goto :Menu
:Loot_Box_1
%Loc:@=Loot_Box_1%
%Loot:@=0%
%INV:@=$Loot[%
%Wait:#=1%
Goto :Menu
:Loot_Box_2
%Loc:@=Loot_Box_2%
%Loot:@=4%
%INV:@=$Loot[%
%Wait:#=1%Demo wait prompt
Goto :Menu
Upvotes: 0
Reputation: 881623
I see a number of problems in that code:
set /a chance= %random% %%30+1
if %chance% gtr 10 && lss 30 (
set /a %nails%+1
echo You got nails! %chance%
)
Going through them:
The if
statement is not valid, &&
is the "execute next command if previous command worked" conjunction, not a general "and" operator. To do what you want would be:if %chance% gtr 10 if %chance% lss 30
.
See here for a way to do and
and or
in cmd
language.
The command set /a %nails%+1
does not actually change nails
in any way, it just evaluates an expression and throws it away. You need an assignment to assign a value, and you don't need the variable markers in this case:set /a "nails += 1"
.
If you're using delayedexpansion
to print out nails
(and you should be), you need a !
both before and after the variable name:echo You got !nails! %chance%
.
As an aside, you'll probably notice I have a penchant for quoting my
set /a
expressions and spacing them nicely - I find this aids readability.
That will fix some specific problems but, to be honest, you're probably better off making a generic function that can give you a yes/no answer for some probability of an event happening. That way, you can reuse it anywhere you need it.
You can use a function like chance
, shown below in a complete program, to decide whether something should happen based on a percentage:
@echo off
goto :main
:chance
setlocal enableextensions enabledelayedexpansion
set retcode=1==0
set /a "value = %random% %% 100"
rem echo %value% rem uncomment for debugging
if %value% lss %2 set retcode=1==1
endlocal && set %1=%retcode%
goto :eof
:main
call :chance result 50
echo %result%
It should be called with both a variable name to put the result into, and the percentage level you want to use. For example, if you wanted to set a variable hasdied
based on a 5% chance, you would call it with:
call :chance hasdied 5
if %hasdied% goto :handlebeingdead
The function contains a number of features which probably bear explanation:
The setlocal
command ensures that no variables escape the scope of this function (but see below), useful for proper encapsulation.
The value
variable is set to some random value between 0
and 99
inclusive. It's not perfectly distributed since %random%
will give you a value up to 32767
so will be slightly skewed toward numbers less than 68
. Said skew is probably not enough to concern yourself with.
This value is then compared with the threshold you provided (the second argument) to decide the return value true or false.
The return value is rather sneaky in that it gives you an expression that you can put into an if
statement without having to do an explicit comparison like:if %hasdied%==1
By returning such an equality comparison directly, you can just use the return value as if it was boolean.
The endlocal
then cleans up any variable changes that have been made in this function, including the return code. However, the fact that the substitutions on this line take place before any of it is executed means that the set
part of it will already have the correct value of retcode
substituted before the endlocal
cleans it up. This is a way to have specific variables "escape" the scope bounded by setlocal/endlocal
. The retcode
value is therefor placed in the parameter whose name you provided as the first argument.
The set %1=
part of that command is a way to allow you to specify what variable should receive the value in the call itself, akin to myvar = function()
. That stops you from having to allocate a hard-coded variable name to each function and then assign it to another variable after the call.
And, of course, the goto :eof
is simply a return
instruction.
Upvotes: 1
Reputation:
You cannot use &&
like that. You need to run the if statement twice to match both gtr
and lss
you can put them one after the other:
@echo off
set /a chance=%random% %%30+1
if %chance% gtr 10 if %chance% lss 30 (
set /a nails+=1
echo You got nails! %chance%
)
Also note the correct way of increasing a variable set /a nails+=1
Upvotes: 0
Reputation: 321
I'm pretty sure the && does not exist in batch. Nested if statements work:
set /a chance= %random% %%30+1
echo %chance%
IF %chance% GTR 10 (IF %chance% LSS 15 (
echo You got nails! %chance%
))
Upvotes: 0