Page 1 of 1

Variable doesn't get overwritten

Posted: 29 Jul 2011 08:21
by KingIsulgard
Hi, I'm busy writing a small RPG game in batch just to lean BATCH scripting a bit more.

But now I have a annoying problem that a variable stays kinda "local".

I have a function to create random numbers.

:RandomNumber
SET /a ValRange=%2-%1+1
SET /a RND=((%random%/3) %% %ValRange%)+%1

GOTO:EOF

Which should be called as CALL :RandomNumber minValue maxValue
This function works great.
I have declared the RND variable at the beginning of the batch file to make it kinda global.

The problem is.

A piece of code uses this RND function to choose which action should happen next.
IF RND==1 CALL action1
IF RND==2 CALL action2

But those actions theirselves also use the RND function

:ACTION1
// GENERATE RND here
// Do something based on the new RND
GOTO:EOF

The problem is that in function ACTION1 or ACTION2, the RND value keeps the same as it was before.
I tried echoing it and the GenerateNumber does make a new funciton, but when action1 tries to use it, it's the old RND again.

Very weird.

Does anyone know how this can happeN,

Re: Variable doesn't get overwritten

Posted: 29 Jul 2011 11:12
by Acy Forsythe
Welcome to the forums!

Without seeing more of your script, it will be hard to tell for sure, but I think you're doing 1 of two things:

1. You're declaring the %RND% variable before doing any SetLocal and expecting that this is now a global variable and all mods to it would then also be global. The problem is that if you then Setlocal, mod the variable and then endlocal, the mod was not global, any changes made to it end with the endlocal.

Or...

2. You're modifying it and then testing for it within the same block of code (IF ELSE or FOR ... DO) You need to use EnableDelayedExpansion for this to work.

Here are some examples:

Code: Select all

@Echo OFF
REM This code will echo Test1

Set Var1=Test1

FOR /F %%a IN ("%Var1%") DO (

SET Var1=Test2
ECHO %Var1%
)


REM This code will echo Test2

Set Var1=Test1

FOR /F %%a IN ("%VAR1%") DO (
SetLocal EnableDelayedExpansion

SET Var1=Test2

Echo !Var1!
)
Endlocal

REM This will Echo Test1

SET Var1=Test1

Setlocal

SET Var1=Test2

Endlocal

Echo %Var1%

pause
exit/b

Re: Variable doesn't get overwritten

Posted: 29 Jul 2011 12:16
by KingIsulgard
The weird thing is, it works the first time. And it keeps working in that section. But it doesn't in another section.

I'll post the code, but hold one because it's very very long already :). Well not extremely long, but long enough to get confusing.

Code: Select all

:: Created by Gilles Lesire
:: Copy, reproduce and edit all you like, as long as you don't infringe copyrights.

@ECHO OFF
SETLOCAL EnableDelayedExpansion
for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do (
  set "DEL=%%a"
)

SET varGameName=HellTown
SET varGameVersion=v0.6

REM Extra calculation variables
SET RND=0

REM Color settings
SET clrTitle=0F
SET clrYou=09
SET clrOther=0D
SET clrLevelUp=06
SET clrAttack=02
SET clrDefend=04
SET clrWin=02
SET clrLose=04

REM Game settings
SET stgIncreaseHP=10
SET stgIncreaseShield=1
SET stgIncreaseDamage=1
SET stgMultiplierXP=2
SET stgPriceInn=25
SET stgWinGold=15
SET stgWinXP=10

REM Default settings
SET dftUsername=unknown
SET dftLevel=1
SET dftXP=0
SET dftMaxXP=100
SET dftHP=20
SET dftMaxHP=20
SET dftCash=50
SET dftDamage=2
SET dftSnacks=0

REM Player settings
SET varLevel=%dftLevel%
SET varXP=%dftXP%
SET varMaxXP=%dftMaxXP%
SET varHP=%dftHP%
SET varMaxHP=%dftMaxHP%
SET varUsername=%dftUsername%
SET varCash=%dftCash%
SET varDamage=%dftDamage%
SET varSnacks=%dftSnacks%

REM Monster settings
SET monsterName=unknown
SET monsterLevel=1
SET monsterHP=10
SET monsterMaxHP=10
SET monsterDamage=1

REM Zone settings
SET exploreZone=unkown

:welcome
   cls

   CALL :ColorText %clrTitle% "Welcome to %varGameName% %varGameVersion%"
   ECHO.
   ECHO.
   ECHO You enter a small town but it looks almost like it's deserted.
   ECHO Everything is closed, all windows nailed down and there is not
   ECHO a living soul in sight.
   ECHO.
   ECHO You see an old man in the distance, you decide to go talk to him.
   ECHO.
   CALL :ColorText %clrOther% "Hi stranger"
   ECHO ^^!
   CALL :ColorText %clrOther% "Welcome to our town"
   ECHO ^^!
   CALL :ColorText %clrOther% "What's your name"
   ECHO ?
   ECHO.

   :whileWelcome
      SET /p varUsername=Type your name:
      IF %varUsername%==%dftUsername% (GOTO:whileWelcome)

   ECHO.
   CALL :ColorText %clrYou% "My name is %varUsername%"
   ECHO .
   ECHO.
   CALL :ColorText %clrOther% "Okay, I'll tell the town about your arrival"
   ECHO .
   ECHO.
   CALL :ColorText %clrYou% "Wait"
   ECHO ^^!
   CALL :ColorText %clrYou% "Where on Earth am I"
   ECHO ?
   ECHO.
   CALL :ColorText %clrOther% "You'll find out soon enough"
   ECHO ^^!
   ECHO.
   ECHO The old man quickly walks off...
   ECHO.
   ECHO Press any key to continue...

   PAUSE>NUL

   GOTO:menu

:menu
   cls

   CALL :ColorText %clrTitle% "%varGameName% main menu"
   ECHO.
   ECHO.
   ECHO You are now playing with the character %varUsername%.
   ECHo.
   ECHO 1. Begin adventure
   ECHO.
   ECHO 2. Change character name
   ECHO 3. Save character
   ECHO 4. Load character
   ECHO 5. Reset character
   ECHO.
   ECHO 8. How to play
   ECHO 9. About
   ECHO.
   ECHO 0. Exit game
   ECHO.

   :whileTown
      SET varAction=100
      SET /p varAction=Choose an action:
   
      IF %varAction%==1 (GOTO:town)
      IF %varAction%==2 (GOTO:changeName)
      IF %varAction%==5 (GOTO:resetCharacter)

      IF %varAction%==8 (GOTO:howto)
      IF %varAction%==9 (GOTO:about)

      IF %varAction%==0 (GOTO:exit)
   GOTO:whileTown

:town
   cls

   CALL :ColorText %clrTitle% "Town square"
   ECHO.
   ECHO.
   ECHO You have %varCash% gold in your pocket and %varHP%/%varMaxHP%HP.
   ECHO.
   ECHO 1. Go exploring
   ECHO 2. Take a rest at the inn
   ECHO 3. Drink from square fountain
   ECHO 4. Go to shop
   ECHO.
   ECHO 0. Back to main menu
   ECHO.

   :whileTown
      SET varAction=100
      SET /p varAction=Choose an action:
   
      IF %varAction%==1 (GOTO:adventure)
      IF %varAction%==2 (GOTO:inn)
      IF %varAction%==3 (GOTO:fountain)

      IF %varAction%==0 (GOTO:menu)
   GOTO:whileTown

:adventure
   cls

   CALL :ColorText %clrTitle% "Exploration - %exploreZone%"
   ECHO.
   ECHO.

   CALL :RandomNumber 1 2

   IF %RND%==1 (
      CALL :Fight
   )
   IF %RND%==2 (
      CALL :Ambushed
   )

   ECHO.
   ECHO Press any key to continue...

   PAUSE>NUL

   GOTO:town

:inn
   cls
   CALL :ColorText %clrTitle% "Take a rest at the inn"
   ECHO.
   ECHO.
   ECHO You walk into the inn and wait at the desk. A chubby old man greets
   ECHO you from behind the counter.
   ECHO.
   CALL :ColorText %clrOther% "Hi there fella, I'm the inn keeper"
   ECHO .
   CALL :ColorText %clrOther% "Can I book you for a room"
   ECHO ?
   CALL :ColorText %clrOther% "It's only %stgPriceInn% gold for one night"
   ECHO ^^!
   ECHO.
   ECHO 1. Yeah sure^^!
   ECHO 2. I don't have enough money...
   ECHO 3. No thanks, I'm A-okay^^!
   ECHO.

   :whileInn
      SET varAction=100
      SET /p varAction=What are you going to do?:

      IF %varAction%==1 (GOTO:innYes)
      IF %varAction%==2 (GOTO:innCheap)
      IF %varAction%==3 (GOTO:innNo)
   GOTO:whileInn
   
:innYes
   ECHO.
   CALL :ColorText %clrYou% "Yes, I'd like a room please"
   ECHO .
   ECHO.

   IF %varCash% LSS %stgPriceInn% (
      ECHO.
      ECHO You look in your pocket and you realise you don't have enough money...
      ECHO.
      ECHO Press any key to continue...

      PAUSE>NUL

      GOTO:innCheap
   )

   SET /a varCash-=%stgPriceInn%
   SET /a varHP=%varMaxHP%

   ECHO The innkeeper provides you with a key to your room.
   ECHO You go up and you see simple room with a bed and a dresser, it's not much
   ECHO but it will do. You go lie on the bed and fall asleep immediatly.
   ECHO.
   ECHO After a long night of sleep you feel completely rested.
   ECHO You have regained all your HP^^!
   ECHO.

   ECHO Press any key to continue...

   PAUSE>NUL

   GOTO:town

:innCheap
   ECHO.
   CALL :ColorText %clrYou% "Oh no"
   ECHO ...
   CALL :ColorText %clrYou% "It looks like I don't have enough gold"
   ECHO .
   ECHO.
   CALL :ColorText %clrOther% "Oh that's no problem my friend but you look awfully tired"
   ECHO ^^!
   CALL :ColorText %clrOther% "If you want to you can sleep on some hay in my barn"
   ECHO .
   CALL :ColorText %clrOther% "It's not that comfy, but at least you'll get some rest"
   ECHO .
   CALL :ColorText %clrOther% "It's not safe to sleep outside these days anyway"
   ECHO ...
   CALL :ColorText %clrOther% "You can sleep there as long as you want"
   ECHO ^^!
   ECHO.
   CALL :ColorText %clrYou% "Great thanks"
   ECHO ^^!
   ECHO.
   ECHO You go inside the barn and you see a bale of hay in the corner.
   ECHO There are some cows stalled. It is not much, but it will have to do...

   GOTO:innCheapSlept

:innCheapSlept

   SET /a increaseHP=%varMaxHP%/10
   SET /a varHP=%varHP%+%increaseHP%

   IF %varMaxHP% LSS %varHP% (
      SET varHP=%varMaxHP%
   )

   ECHO.
   ECHO You wake up the next morning because of the tail of one of the cows
   ECHO slapping you in the face.
   ECHO It was a terrible night but at least you got some rest.
   ECHO You have regained %increaseHP%HP.
   ECHO.
   ECHO Your HP level is now %varHP%/%varMaxHP%HP^^!
   ECHO.

   :whileInnCheapSlept
      SET varAction=100
      SET /p varAction=Do you want to stay another night to get some more rest? (Y/N):

      IF /I %varAction%==y (GOTO:innCheapSlept)
      IF /I %varAction%==n (GOTO:town)
   GOTO:whileInnCheapSlept

:innNo
   ECHO.
   CALL :ColorText %clrOther% "Oh, okay"
   ECHO ...
   CALL :ColorText %clrOther% "See you next time"
   ECHO ^^!
   ECHO.

   ECHO Press any key to continue...

   PAUSE>NUL

   GOTO:town

:fountain
   cls
   CALL :ColorText %clrTitle% "Drink from square fountain"
   ECHO.
   ECHO.
   ECHO The flowing sound of the fountain in the middle of the square
   ECHO draws your attention. You hastly walk towards it.
   ECHO After taking a few doubtful sips you start drinking but when you
   ECHO getting back up, you notice in the reflection of the water your
   ECHO face is full of mud and your hands are still coverd in blood.
   ECHO.
   ECHO After washing the dirt from your face and your hands are clean again
   ECHO you feel refreshed^^!
   ECHO.
   ECHO It's important to keep yourself hydrated... Especially when you are fighting
   ECHO all kind of creatures^^!
   ECHO.
   ECHO Press any key to continue...

   PAUSE>NUL

   GOTO:town

:howto
   cls

   CALL :ColorText %clrTitle% "How to play %varGameName% %varGameVersion%"
   ECHO.
   ECHO.
   ECHO %varGameName% is fairly easy to play. The goal of the game is to slay
   ECHO as many monsters as possible and to explore the world of %varGameName%.
   ECHO.
   ECHO To navigate through the game works the same way as the menu. Every time
   ECHO you'll get a few options and you have to pick one.
   ECHO.
   ECHO Press any key to continue...

   PAUSE>NUL

   GOTO:menu

:about
   cls

   CALL :ColorText %clrTitle% "About %varGameName% %varGameVersion%"
   ECHO.
   ECHO.
   ECHO This game has been written with Batch Scripting Code.
   ECHO.
   ECHO Design and programming by Gilles Lesire.
   ECHO.
   ECHO Press any key to continue...

   PAUSE>NUL

   GOTO:menu

:resetCharacter
   cls

   CALL :ColorText %clrTitle% "Reset character"
   ECHO.
   ECHO.
   ECHO By resetting your character all your experience will be gone^^!
   ECHO.

   :whileReset
      SET varAction=100
      SET /p varAction=Are you sure you want to reset your character? (Y/N):

      IF /I %varAction%==y (
         CALL :ResetSettings

         ECHO.
         ECHO Your character has been resetted!
         ECHO.
         ECHO Press any key to continue...

         PAUSE>NUL

         GOTO:menu
      )
      IF /I %varAction%==n (GOTO:menu)
   GOTO:whileReset

:changeName
   cls

   CALL :ColorText %clrTitle% "Change character name"
   ECHO.
   ECHO.
   ECHO Luckely it's fairly easy for travellers to change their name^^!
   ECHO.
   SET /p varUsername=Choose a new name:
   ECHO.
   ECHO Press any key to continue...

   PAUSE>NUL

   GOTO:menu

:exit
   cls

   CALL :ColorText %clrTitle% "Exit game"
   ECHO.
   ECHO.
   ECHO Your game will not be saved, you need to do this before you quit.
   ECHO.

   :whileExit
      SET varAction=100
      SET /p varAction=Are you sure you want to exit? (Y/N):

      IF /I %varAction%==y (GOTO:EOF)
      IF /I %varAction%==n (GOTO:menu)
   GOTO:whileExit

REM "Functions"

:Ambushed
   ECHO.
   ECHO You got ambushed by a level %monsterLevel% %monsterName%!
   ECHO.
   CALL :Defend
   ECHO.
   ECHO Press any key to continue...

   PAUSE>NUL

   CALL :Battle

   GOTO:EOF

:Fight
   ECHO.
   ECHO You see a level %monsterLevel% %monsterName% in the distance.
   ECHO The %monsterName% hasn't noticed you yet so you could suprise attack it,
   ECHO or you could still safely flee...
   ECHO.
   ECHO What are you going to do?
   ECHO.
   ECHO 1. I'm going to rip that %monsterName% in pieces^^!
   ECHO 2. Run^^!^^!^^!
   ECHO.

   :whileFight
      SET varAction=100
      SET /p varAction=What are you going to do?:

      IF %varAction%==1 (
         CALL :Battle
         GOTO:EOF
      )
      IF %varAction%==2 (
         ECHO.
         ECHO You safely got away^^!
         GOTO:EOF
      )
   GOTO:whileFight

:LevelUp
   SET /a varLevel+=1
   SET /a varMaxHP=%varMaxHP%+%stgIncreaseHP%
   SET /a varDamage+=%stgIncreaseDamage%
   SET /a varMaxXP*=%stgMultiplierXP%
   SET varHP=%varMaxHP%
   
   ECHO.
   CALL :ColorText %clrLevelUp% "You have leveled up"
   ECHO ^^!
   ECHO.
   CALL :ColorText %clrLevelUp% "You are now level %varLevel%"
   ECHO .
   CALL :ColorText %clrLevelUp% "Your HP has been restored to %varHP%HP"
   ECHO ^^!
   ECHO.
   ECHO Press any key to continue...

   PAUSE>NUL

   GOTO:EOF

:FindSnack

   SET /a varSnacks+=1

   CALL :RandomNumber 1 2

   ECHO.

   IF %RND%==1 (
      ECHO You found some mushrooms with healing powers^^!
   )
   IF %RND%==2 (
      ECHO You found an apple, this might give you a little boost!
   )

   ECHO.

   GOTO:EOF

:Battle
   ECHO.
   
   REM Check if monster is dead
   IF %monsterHP% LEQ 0 (
      CALL :WinBattle
      GOTO:EOF
   )

   REM Check if player is dead
   IF %varHP% LEQ 0 (
      CALL :LoseBattle
      GOTO:EOF
   )

   ECHO %varUsername%: %varHP%/%varMaxHP%HP   %monsterName%: %monsterHP%/%monsterMaxHP%HP
   ECHO.
   ECHO 1. Attack
   ECHO 2. Flee
   
   IF %varSnacks% GTR 0 (
      ECHO 3. Eat a snack (%varSnacks% left)
   )

   ECHO.

   :whileBattle
      SET varAction=100
      SET /p varAction=What are you going to do?:

      IF %varAction%==1 (
         ECHO.
         CALL :Attack
         CALL :Defend
         CALL :Battle

         GOTO:EOF
      )
      IF %varAction%==2 (
         IF %varLevel% LSS %monsterLevel% (
            CALL :RandomNumber 1 5
         )
         IF %varLevel% EQU %monsterLevel% (
            CALL :RandomNumber 1 4
         )
         IF %varLevel% GTR %monsterLevel% (
            CALL :RandomNumber 1 3
         )

CALL :RandomNumber 1 5

         ECHO   IF %RND% LEQ 2

         IF %RND% LEQ 2 (
            ECHO You safely got away^^!
            GOTO:EOF
         ) ELSE (
            ECHO You failed to flee!
            ECHO The monster attacked you...
            ECHO.

            CALL :Defend
            CALL :Battle

            GOTO:EOF
         )
      )
      IF %varAction%==3 (
         IF %varSnacks% GTR 0 (
            CALL :EatSnack
            CALL :Battle

            GOTO:EOF
         )
      )

   GOTO:whileBattle

:Defend
   SET inflictedDamage=%monsterDamage%
   SET /a varHP-=%inflictedDamage%

   CALL :ColorText %clrDefend% "The %monsterName% did %inflictedDamage% to %varUsername%"
   ECHO ^^!

   GOTO:EOF

:Attack
   SET inflictedDamage=%varDamage%
   SET /a monsterHP-=%inflictedDamage%

   CALL :ColorText %clrAttack% "%varUsername% inflicted %inflictedDamage% to the %monsterName%"
   ECHO ^^!

   GOTO:EOF

:EatSnack
   ECHO You ate a snack and regained XP!

   GOTO:EOF

:WinBattle
   SET earnedGold=%stgWinGold%
   SET earnedXP=%stgWinXP%

   SET /a varCash+=%earnedGold%
   SET /a varXP+=%earnedXP%

   ECHO.
   CALL :ColorText %clrWin% "%varUsername% defeated the %monsterName%"
   ECHO .
   CALL :ColorText %clrWin% "You have won %earnedGold% gold and %earnedXP%XP"
   ECHO ^^!

   GOTO:EOF

:LoseBattle
   ECHO.
   CALL :ColorText %clrLose% "The %monsterName% has overpowered %varUsername%"
   ECHO ^^!
   CALL :ColorText %clrLose% "%varUsername% collapses on the ground"
   ECHO ...

   GOTO:EOF

:ResetSettings
   SET varLevel=%dftLevel%
   SET varXP=%dftXP%
   SET varMaxXP=%dftMaxXP%
   SET varHP=%dftHP%
   SET varMaxHP=%dftMaxHP%
   SET varCash=%dftCash%
   SET varDamage=%dftDamage%
   SET varSnacks=%dftSnacks%

   GOTO:EOF

:RandomNumber
   SET /a ValRange=%2-%1+1
   SET /a RND=((%random%/3) %% %ValRange%)+%1
ECHO %RND%
   GOTO:EOF

:ColorText
   @ECHO OFF
   
   <NUL SET /p ".=%DEL%" > "%~2"
   FINDSTR /v /x /a:%1 /R "^$" "%~2" NUL
   DEL "%~2" > NUL 2>&1

   GOTO:EOF


So the random works in the :Adventure label piece of code.
But it doesn't work in the :Battle label when the user is trying to flee (option 2)

Basicly, when the user goes to :adventure, the code selects a random step to be taken and then calls the :Battle. When you are in battle and you try to flee, the random is off and keeps the value of the random value selected by the :adventure piece to select what should happen.

The weird thing is, when I go back to adventure after I got back to :town, the random does work, so this might have something to do with the amount of calls or something?

Re: Variable doesn't get overwritten

Posted: 29 Jul 2011 13:20
by dbenham
That is because you are referencing %RND% within an IF block

Code: Select all

     IF %varAction%==2 (
... bunch of code
     )
The value of %RND% is fixed when the IF statement is originally parsed. Everything is parsed at once, including the contents of the ().

Acy correctly suggested you need to SETLOCAL EnableDelayedExpansion. He forgot to tell you to use !RND! when you want to reference a value using delayed expansion. (Actually his example demonstrated the technique, but he didn't spell it out in his text)

The value of %RND% is determined at parse time.
The value of !RND! is determined at execution time (hence the term "delayed expansion").

Dave Benham

Re: Variable doesn't get overwritten

Posted: 29 Jul 2011 13:35
by KingIsulgard
Yes that does work!

Thanks for the information.