Right way to set up counter + conditions Created 4 years ago2020-05-29 16:05:30 UTC by abbadon abbadon

Created 4 years ago2020-05-29 16:05:30 UTC by abbadon abbadon

Posted 4 years ago2020-05-29 16:05:30 UTC Post #344317
Hi. I have tried to set a counter that, once certain time is reached do something. It is like this:
	if (condition)
	{
	        while ( m_flstufftime < gpGlobals->time )
				 {
					m_flstufftime += RANDOM_FLOAT (0.025f, 0.01f); // Decrease 1% every fixed random seconds from 1 to 10 if I am not wrong

					if ( m_flstufftime <= 0 )
					{
						dosomething;
		                 	}
				}
}
But it does not work, because once the amount of time is reached (1 to 10 secs) the code is not executed (i can´t see the result).
Any help?

This is the other version of the code. Still no results.
	if (condition)
	{
	       	m_flstufftime = gpGlobals->time + RANDOM_FLOAT (5, 10); ;
					if ( m_flstufftime <= 0 )
					{
						dosomething;
					}
}
Posted 4 years ago2020-05-29 18:56:17 UTC Post #344318
Checking whether m_flstufftime is 0 or less only makes sense if it represents the time that is left until something needs to be done. In that case, you'll need to decrement it periodically by however much time has passed. But you're incrementing it. It's also unclear in what context this code is being executed.

I'm not too familiar with the HL SDK, but it looks like the easiest way to do this is to execute dosomething in a Think function, and to schedule that function by setting pev->nextthink to the current time + however many seconds you want to wait.

If your entity already needs to perform other work periodically, then here's what I would do: when starting the countdown, set m_flstufftime to the current time + the number of seconds to wait. m_flstufftime now represents the time at which dosomething should be executed. Then, in your entity's Think function, check whether the current time has surpassed m_flstufftime. When it has, execute dosomething, and disable or restart the countdown logic to prevent dosomething from being executed continually from that moment on.

Other notes:
  • RANDOM_FLOAT's first argument is the lower bound, so you should swap the arguments in your first sample. I'm not sure why you need randomness though?
  • m_flstufftime is not a very descriptive name. Something like m_flCountdownTime would be more clear.
  • (while) loops are executed as fast as possible, they're not meant for code that should be 'spread across time'.
Posted 4 years ago2020-05-29 21:09:43 UTC Post #344319
Checking whether m_flstufftime is 0 or less only makes sense if it represents the time that is left until something needs to be done.
Yes!, What I need is that, when the if condition is checked, a countdown starts that has to be a random number (from 10 to 5), the entity in this case needs to wait those random numbers that go from 10,9, 8...(and so on) to 5 from where the countdown starts seconds before the dosomething; part is executed.
m_flstufftime is not a very descriptive name. Something like m_flCountdownTime would be more clear.
Yeah, it is named m_flAutoReloadTime;, and it is related to the Bot AI. ;)

What I (obviously) don´t know how to do is a countdown in code that takes values from 10,9,8 (and so on...again) until 5 seconds. That´s it. One if starts the countdown within the brackets, and when it reaches zero, then something is executed. Just that. :)
Posted 4 years ago2020-05-30 00:10:15 UTC Post #344324
Starting the countdown can be done as following (you already did this in your second sample). This will set the next auto-reload time between 5 and 10 seconds in the future:
m_flAutoReloadTime = gpGlobals->time + RANDOM_FLOAT(5, 10);

Then you periodically check whether it's time to do auto-reload. This goes in your entity's Think function:
if (gpGlobals->time >= m_flAutoReloadTime) { autoReload(); restartCountdown(); }

If you don't want to start another countdown after auto-reloading, then you'll have to disable the countdown check. This can be done by adding an extra boolean variable that indicates whether the countdown is enabled:
if (m_bIsCountdownActive && gpGlobals->time >= m_flAutoReloadTime) { autoReload(); m_bIsCountdownActive = false; }
Don't forget to set this variable to true when you start another countdown.
Posted 4 years ago2020-05-30 08:36:28 UTC Post #344328
Ok. One thing, because the specified conditions are what start the countdown, will it not be disabled if the conditions are not active?

Like setting the:
m_flAutoReloadTime = 0;
...to zero, so it can start all over again when the initial conditions trigger the Countdown again.
I'm not too familiar with the HL SDK
Who'd say! :lol:
Posted 4 years ago2020-05-30 14:13:44 UTC Post #344330
That line doesn't start a countdown the way we humans would do a countdown. It's more like looking at the current time (which is, say, 9:30), deciding you want to go out after 5 minutes, and then writing down '9:35'. You only do that at the start - if you did that again at, say, 9:33, then you'd overwrite the original 9:35 with 9:33 + 5 = 9:38, which essentially just postpones things.

The real work happens in Think - that's similar to looking at the clock every minute or so to see whether it's 9:35 already. Is 9:30 at or past 9:35? No. Is 9:31 at or past 9:35? No. Is 9:32 at or past 9:35? No. ... Until finally at 9:35, the answer is yes, and you go out.

Computers are good at following orders, but they're not smart, so you have to be very precise with your instructions. 9:36 is also past 9:35, so a computer will happily decide to go out again. The same happens at 9:37, 9:38, and so on. Overwriting the original 9:35 with 0:00 won't help, because 9:36 is also past 0:00, so that's why you need an extra check: do I need to go outside? We people just 'know' what we want, but for a computer you really need to spell out all those little details.

You can make the code easier to understand by creating a few small functions:
void startAutoReloadTimer(float seconds)
{
    if (!isAutoReloadTimerEnabled())  // Only schedule a new auto-reload if we're not waiting for one already.
        m_flAutoReloadTime = gpGlobals->time + seconds;
}

bool isAutoReloadTimerEnabled() { return m_flAutoReloadTime > 0; }

void stopAutoReloadTimer() { m_flAutoReloadTime = 0; }
and use those throughout your code:
if (isAutoReloadTimerEnabled() && gpGlobals->time >= m_flAutoReloadTime)
{
    autoReload();
    stopAutoReloadTimer();
}
A simpler variant is to just set m_flAutoReloadTime to a really big value, like 'next year'. I prefer the above approach however, because it's more obvious that the timer can be stopped.
Posted 4 years ago2020-05-30 15:01:25 UTC Post #344331
Wow. Well, my function looks like this now:
           //==================================================================
          // Bots usually cannot reload because trolley_monster cannot reach
          // them, so we give them a little ammo.
          //==================================================================
	     if (FBitSet(m_pPlayer->pev->flags, FL_FAKECLIENT)&& (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0)/*Old Code*/
            && (m_flFreeammotime <= 0)) //New code
	      {
		                 m_flFreeammotime = gpGlobals->time + RANDOM_FLOAT(1, 5);// New code
		                 if (gpGlobals->time >= m_flFreeammotime) //New code
			{
		     		UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "AUTORELOAD\n");//New code
				    m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] += 99;//Old code
			}
          }
And, obviously, it does not work. the message "AUTORELOAD" was not displayed because the code is not executed. I´m not good at C++, but even so, I cannot understand why the message is not displayed and thus the giving ammo part either. If I delete all the time logic...
//New code
...If I can call what I did "logical" in any sense :crowbar: the code works.
Posted 4 years ago2020-05-30 15:40:13 UTC Post #344332
What you're doing is only checking if it's time to auto-reload immediately after you decided the next auto-reload time. That's like looking at the clock after you wrote down the time, but then never checking the clock again.

Don't put the if (gpGlobals->time >= m_flFreeammotime) { ... } part inside the block that starts the countdown timer.
Posted 4 years ago2020-05-30 18:47:22 UTC Post #344334
Crap. I should put the code here, right?
			{
                    if (gpGlobals->time >= m_flFreeammotime) //New code
		     		UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "AUTORELOAD\n");//New code
				    m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] += 99;//Old code
			}
Posted 4 years ago2020-05-30 19:25:44 UTC Post #344335
I don't know the rest of your code, so I can't tell whether this is the right place. I'll need to know at least in which function this is located, and in which 'blocks' it's located within that function (inside an if or else block, or part of a for or while loop body, etc.).

Either way, there is a subtle problem with this code: it will give the player ammo even when gpGlobals->time is still less than m_flFreeammotime, because only the UTIL_ClientPrintAll line depends on the if statement. This is how the compiler sees your code:
if (gpGlobals->time >= m_flFreeammotime)
    UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "AUTORELOAD\n");  // Only executed if the above condition is true

m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] += 99;  // Always executed
If you want to have multiple lines of code depend on an if statement, then you have to surround them with curly braces:
if (gpGlobals->time >= m_flFreeammotime)
{
    UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "AUTORELOAD\n");
    m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] += 99;
    // Both of these lines are now only executed if the 'if' condition is true
}
Posted 4 years ago2020-05-30 21:01:54 UTC Post #344337
Well, this code is within the:
void CMjolnir::PrimaryAttack()
function (the name is for one of the weapons), and my code starts right under:
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--;
My code it´s another if and don´t belong to any other code.
If you want to have multiple lines of code depend on an if statement, then you have to surround them with curly braces:
That´s what I did before, but it does not work. :(
 {
		                 m_flFreeammotime = gpGlobals->time + RANDOM_FLOAT(1, 5);// New code
		                 if (gpGlobals->time >= m_flFreeammotime) //New code
			{
		     		UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "AUTORELOAD\n");//New code
				    m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] += 99;//Old code
			}
          }
Unless what you say is:
if (FBitSet(m_pPlayer->pev->flags, FL_FAKECLIENT)&& (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0)/*Old Code*/
            && (m_flFreeammotime <= 0)) //New code
 {            m_flFreeammotime = gpGlobals->time + RANDOM_FLOAT(1, 5);       } //New code

if (gpGlobals->time >= m_flFreeammotime) //New code
			{
		     		UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "AUTORELOAD\n");//New code
				    m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] += 99;//Old code
			}
Looks easy to do a countdown part that activates something, but I swear it´s not. :pwned:

EDIT: Well, doing that the code works, the AUTORELOAD message appears, BUT it does always appear. It´s like if m_flFreeammotime is always 0 even if the condition m_rgAmmo[m_iPrimaryAmmoType] <= 0 is not met. :/
Posted 4 years ago2020-05-30 22:09:45 UTC Post #344338
That's why I said you need to disable the countdown timer, see this post. The following changes should work (in theory - I haven't tested ;) ):
// Always check if we need to start an auto-reload countdown:
if (FBitSet(m_pPlayer->pev->flags, FL_FAKECLIENT) &&          // Is this a bot?
    (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0) &&         // and is it out of ammo?
    (m_flFreeammotime <= 0))                                  // and are we currently not waiting for an auto-reload?
{
    m_flFreeammotime = gpGlobals->time + RANDOM_FLOAT(1, 5);  // Then start waiting for an auto-reload.
}

// Always check if we need to perform an auto-reload:
if (m_flFreeammotime > 0 &&                                   // Are we currently waiting for an auto-reload?       <-- NEW!
    gpGlobals->time >= m_flFreeammotime)                      // and is it finally time to perform the auto-reload?
{
    UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "AUTORELOAD\n");    // Show a debug message.
    m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] += 99;            // Give the bot some ammo.
    m_flFreeammotime = 0;                                     // and stop waiting for an auto-reload.               <-- NEW!
}
Tip: proper indentation of the code makes it easier to see which blocks depend on which conditions.
Posted 4 years ago2020-05-31 11:39:15 UTC Post #344341
You´re as tidy as batman´s Alfred!. Sorry for being so dirty at posting code. I´ll test it and give the results ASAP. :) Aaaannd... IT WORKS!!, I have to reduce a bit of time the RANDOM part, the Bots reload flawlessly, but seemed to have a delay between the time when the ammo is adden and the time they detect that they already have that ammo loaded. For the rest... it is perfect. Thanks Captain P. Time to add your name to one of the Bots and to add you to the credits video!!!. :)

Thanks:
User posted image
Posted 4 years ago2020-06-03 23:08:33 UTC Post #344358
Heh, that's cool. :) Is this a singleplayer or a multiplayer mod?
Posted 4 years ago2020-06-05 14:11:46 UTC Post #344364
It´s multiplayer but can be played with (my very bad coded , with very bad IA and so very dumb) bots. :)
Now the mod its DONE, after 15 years, yesterday I burned a CD with the installer and the new (56 pag) manual and some extras like the CD-DVD cover.
I´m very happy, after 15 years being a noob I finished it, and it looks not very very bad in the end, haha!.
I dont know. Maybe I should do a raffle for a free CD copy just to celebrate this! what do you think? :crowbar:
Posted 4 years ago2020-06-06 22:51:15 UTC Post #344374
A CD and manual even? You're also going to release it on-line, right? ;)

I wonder though, isn't there a risk that you'll get a cease-and-desist letter from whoever owns the Matrix IP?
Posted 4 years ago2020-06-07 00:01:46 UTC Post #344375
No, no, haha!, it is only my personal project, the problem is that I am so nerd that I want the things perfect even in the slight of the details. It has even an EULA!!. Sounds absurd for something that will ever be released, but I am done this way. ;)
You must be logged in to post a response.