ApplyRepair problem in ShapeBase.cc? (TGE 1.5.2)
by Charles Fusner ("Cog3125") · in Torque Game Engine · 12/03/2008 (4:49 pm) · 3 replies
Hi,
While working my way through GPGT, I noticed an oddity in the way the .applyRepair method works, and I think I've tracked it to the source. I don't want to rehash anything so I did a forum search on "applyRepair" but the closest thing I found was a single passing mention that "applyRepair doesn't seem to work." However that article was in the public area and I didn't want to post this b/c I think I found the issue in the engine source. I'm currently working in 1.5.2 on Mac but I doubt it's platform specific.
Basically, I noticed that the console method "applyRepair" doesn't actually trigger any direct repairs but rather sets a ShapeBase member variable called mRepairReserve which, when > 0 is supposed to cause repair to occur within ShapeBase::processTick. It doesn't always, however. Here's the problematic lines from that method...
Notice that if the repair rate within the object's datablock is set to zero, no repairs will ever occur, nor will mRepairReserve ever get reduced. Essentially, a call to applyRepair with any positive argument under these circumstances will simply result in the engine having to reprocess this code block every tick for the remainder of the game without ever achieving the desired effect.
Comparing my own current test project with starter.fps, I gather this was implemented this way in order to cause health bar to animate upward when a health kit is taken rather than suddenly jumping up by the set amount, however, this only works there b/c the PlayerBody datablock in player.cs was set to a repairRate of 0.33, even though the Armor::onAdd then sets self repair to zero.
I realize this is a trival issue, and the fix is so easy even I can spot it () but since there is virtually no occasion on which mRepairReserve would be lower than the repairRate (and almost never any sense to choosing it over the repairRate when it is), it seems that to do what it appears to have been intended to, then entire block of code commented as "Repair management" in ShapeBase::processTick(...) should read more like...
I hope this "glitch-lette" doesn't sound nit picky. I'm just posting because I can't find any other references to the issue, and it didn't seem to me this was doing what it appeared to have been intended to. Also, I consider myself to still be learning Torque, so if my "fix" seems naive for some reason, by all means let me know why.
While working my way through GPGT, I noticed an oddity in the way the .applyRepair method works, and I think I've tracked it to the source. I don't want to rehash anything so I did a forum search on "applyRepair" but the closest thing I found was a single passing mention that "applyRepair doesn't seem to work." However that article was in the public area and I didn't want to post this b/c I think I found the issue in the engine source. I'm currently working in 1.5.2 on Mac but I doubt it's platform specific.
Basically, I noticed that the console method "applyRepair" doesn't actually trigger any direct repairs but rather sets a ShapeBase member variable called mRepairReserve which, when > 0 is supposed to cause repair to occur within ShapeBase::processTick. It doesn't always, however. Here's the problematic lines from that method...
if (mRepairReserve > 0.0)
{
F32 rate = getMin(mDataBlock->repairRate, mRepairReserve);
mDamage -= rate;
mRepairReserve -= rate;
}Notice that if the repair rate within the object's datablock is set to zero, no repairs will ever occur, nor will mRepairReserve ever get reduced. Essentially, a call to applyRepair with any positive argument under these circumstances will simply result in the engine having to reprocess this code block every tick for the remainder of the game without ever achieving the desired effect.
Comparing my own current test project with starter.fps, I gather this was implemented this way in order to cause health bar to animate upward when a health kit is taken rather than suddenly jumping up by the set amount, however, this only works there b/c the PlayerBody datablock in player.cs was set to a repairRate of 0.33, even though the Armor::onAdd then sets self repair to zero.
I realize this is a trival issue, and the fix is so easy even I can spot it (
//// Change to ShapeBase.cc, ShapeBase::processTick(...) starts at this line...
// Repair management
if (mDataBlock->isInvincible == false)
{
F32 store = mDamage;
// see if we're in the middle of an applyRepair at this point...
if (mRepairReserve <= 0.0)
{
mDamage -= mRepairRate; //No? Well, then just regular repair rate, if any.
}
else
{
if (mRepairReserve > mDamage)
mRepairReserve = mDamage; // Move this inside the 1st conditional. no point in
// checking if we aren't concerned about the reserve
F32 rate = getMax(mDataBlock->repairRate, 0.33f); // IOW, impose a minimum repair rate during
// an applyRepair to force that healing
mDamage -= rate;
mRepairReserve -= rate;
}
// And either way, clamp damage for safety.
mDamage = mClampF(mDamage, 0.f, mDataBlock->maxDamage);
// blah blah blah... presume from this point onward everything else is the same...
// ...I hope this "glitch-lette" doesn't sound nit picky. I'm just posting because I can't find any other references to the issue, and it didn't seem to me this was doing what it appeared to have been intended to. Also, I consider myself to still be learning Torque, so if my "fix" seems naive for some reason, by all means let me know why.
#2
I, and many others, haven't had any problems using the applyRepair() after I've reviewed players and even stationary objects datablocks to check on the repair rate and max damage attributes.
12/04/2008 (6:12 am)
As stated by Gary that the applyRepair() and the associated ProcessTick() code do make sense and the only thing you need to do is make a simple adjustment in your datablock for repair rate as far as I can tell. Now your suggested alternative code on the other hand really doesn't make sense to me at all.I, and many others, haven't had any problems using the applyRepair() after I've reviewed players and even stationary objects datablocks to check on the repair rate and max damage attributes.
#3
It initially stumped me because I accidentally ran across a case where a datablock had a zero repairRate, and the method call that should have been healing the object did nothing. Since I was thinking in terms of repairRate was supposed to be the default repair rate, I didn't see how it might have other uses. I suppose if you wanted different objects to repair at variable rates in spite of receiving the same applyRepair call, it could make things more flexible in this sense. Maybe a vehicle that's more complicate than another and therefore recovers damage at a slower rate, or a character who's more magically resistant than another responding differently to a healing spell or something.
Of course, I wouldn't go so far as to say it "makes no sense at all" since...
1.) If the game scripter inadvertently calls applyRepair against an object with a zero repairRate in the datablock, it's going to force the tick to do slight more work every tick for the remainder of the object's lifetime without a meaningful result.
2.) The mRepairReserve (as written) gets max-checked against mDamage every tick, regardless of whether or not there even is an mRepairReserve in effect,
3.) Having different objects react differently to the same method call seems like a specialty case here
Still, I have to admit: All put together it doesn't add up to enough concern to change a core class. On reflection, I see this isn't really a bug, but rather a stylistic decision. And the cool part is: with a license to the code, I can have my own style, so no point in belaboring it for everybody else
Thanks for the elaborations. The logic makes more sense now. In the future, I guess I should post to the engine discussions section and let people knock it around a little first.
12/04/2008 (3:39 pm)
As to the matter of not immediately apply healing until the user "uses" the pickup, well, surely that's a matter for the inventory "onUse" methods. If it's a matter of waiting for the player to use it, you just don't hit applyRepair until that happens. But after considering what both Gary and Nathan have said, and re-thinking, I can see that it isn't a bug, really.It initially stumped me because I accidentally ran across a case where a datablock had a zero repairRate, and the method call that should have been healing the object did nothing. Since I was thinking in terms of repairRate was supposed to be the default repair rate, I didn't see how it might have other uses. I suppose if you wanted different objects to repair at variable rates in spite of receiving the same applyRepair call, it could make things more flexible in this sense. Maybe a vehicle that's more complicate than another and therefore recovers damage at a slower rate, or a character who's more magically resistant than another responding differently to a healing spell or something.
Of course, I wouldn't go so far as to say it "makes no sense at all" since...
1.) If the game scripter inadvertently calls applyRepair against an object with a zero repairRate in the datablock, it's going to force the tick to do slight more work every tick for the remainder of the object's lifetime without a meaningful result.
2.) The mRepairReserve (as written) gets max-checked against mDamage every tick, regardless of whether or not there even is an mRepairReserve in effect,
3.) Having different objects react differently to the same method call seems like a specialty case here
Still, I have to admit: All put together it doesn't add up to enough concern to change a core class. On reflection, I see this isn't really a bug, but rather a stylistic decision. And the cool part is: with a license to the code, I can have my own style, so no point in belaboring it for everybody else
Thanks for the elaborations. The logic makes more sense now. In the future, I guess I should post to the engine discussions section and let people knock it around a little first.
Torque Owner Gary Preston
if (mRepairReserve > 0.0) { F32 rate = getMin(mDataBlock->repairRate, mRepairReserve); mDamage -= rate; mRepairReserve -= rate; }Depending on how you intend to do repairs that code block could make sense.
Consider for example, you allow the player to pick-up repair packs, but don't immediately apply them to the player. The player has instead to "use"/"activate" them.
When the player has no repair packs, or has not yet activated any they do have, they never get a damage reduction, since mRepairReserve is not > 0. If however they "use" a repair pack they've previously picked up causing mRepairRate to become positive, repairs will then start to work.
You may want a repair pack to have a large repair value, but still keep the actual repair to a slow rate. So a given pack may repair 1000 points of damage, but not in one go. So the getMin line ensures you limit how fast a repair can take place. Likewise when the repair reserve eventually drops below repairRate, the next tick will finish off the repair with the last of the repair reserve. This also allows you to switch the player to a faster/slower/no repair rate datablock due to any number of game reasons at run time.
Rather than this been a bug, it looks more like just a case of your game requiring a different style of repairing to what the code provides. Nothing wrong with that of course. :)