Is reaver's behavior random in melee games?, This is related to scarab too
Polls
Post #2 theleo_ua Aug 13 2012, 6:57 pm
|
Hi SEN community
This topic will be updated to detailed description about this, but now I have some questions. Please look to the next fragments of the vods: Jangbi vs Fantasy: http://www.youtube.com/watch?v=OoPG9gwgFmQ&feature=player_detailpage#t=663s (look at 11:03) Perfectman vs Fantasy: http://www.youtube.com/watch?v=nekBzOGPg6U&feature=player_detailpage#t=948s (look at 15:47) Bisu vs Light: http://www.youtube.com/watch?list=UUAI86CUHDIkKXGA6YpVBhYg&v=Anr6gKLla7c&feature=player_detailpage#t=606s (look at 10:06) The list of main reaver "random" things are: 1) sometimes reaver dont want to shoot (even if you selected it and pressing right mouse button on the enemy unit) 2) sometimes scarab cannot reach the goal: – it "moving like junk yard dog" in enemy units - it cannot bypass building (like in Jangbi vs Fantasy fragment) - it just moving to non-correct place - even without reason 3) sometimes scarab exploding near goal object, but not dealing any damage to it (and scarab life-time are not ran out in this case) Questions: 1) Are reavers and scarab behavior contain "random" function? Especially I want answer to this from BW Engine gosus like Heinermann 2) If not - why reavers sometimes stupid and sometimes not? 3) Is it possible by progamers - to predict all reaver "stupidnesses"? 4) If yes - is it possible by progamers to control reaver in way, that reaver will not be stupid all the time? This mean, that there is some micro techniques, which, for example, will allow Jangbi/Perfectman to make scarab deal damage to goal in VODS listed above. Thanks P.S. Of course, some of the reaver glitches can be predicted and even controlled by the progamer. But not all. So when I say "reaver/scarab is random" - I mean, that there are some situations, when progamer cannot predict, will reaver be "stupid" or not. This post was edited 1 time, last edit by theleo_ua: Aug 14 2012, 5:31 pm. ![]() ![]() ![]() ![]() ![]() ![]() |
Post #3
Lanthanide
Aug 13 2012, 8:50 pm
Post #4
IskatuMesk
Aug 13 2012, 9:14 pm
|
The scarab is a unit, and is subject to ground pathing, collision, and a variety of bugs and oddities that come as a result of these oddities. Quantifying these problems in a logical form as a player is going to be very difficult, because even as a modder for over a decade who spent much time working with the game on a level just above ASM, I still didn't really possess any manner of predicting what happens with stuff like the scarab. Starcraft is riddled in hardcoded goofiness, and Blizzard's programmers are some of the worst in the industry, leading to very strange behavior.
I believe, like Lanthanide said, you can certainly be in a position to predict everything the scarab will ever do. But you need to understand all of the code working with it at a very deep level. |
Post #5 theleo_ua Aug 13 2012, 10:10 pm
|
Example 1: Code: Step 1: get(current time.milliseconds) from bios Step 2: if this value less than 500 - then move scarab to the left, if 500 or greater - then move scarab to the right. Situation: player didnt know exact time, when BW launched, and player has only keyboard and mouse (player has not any devices like clock etc) Question: how, in this example, player can predict, will scarab move to the left or to the right? Example 2: Player 1 has reaver and scarab. Player 2 has 5 SCVs in range "5 cells from reaver" (lets call them SCVs A), 5 SCVs in range "4 cells from reaver" (lets call them SCVs B), 5 SCVs in range "3 cells from reaver" (lets call them SCVs C) etc If player 1 will attack any SCV of SCVs B or C - scarab will not damage them, because of SCVs A will block his path. But: If player 1 will attack any SCV of SCVs A - player 2 can order attacked SCV "move to minerals", so scarab will still cannot reach the attacked SCV because of another SCVs. So, in this case, the chance to damage SCV is depends on "which SCV player 2 will move to minerals". So player 1 has next 2 outcomes: 1) Scarab damaged targetted SCV 2) Scarab not damaged targetted SCV And this outcome does not depend on player skills - this depend only on his opponent's move. So "SC is a computer program" - did not help the player even in this case. So I think, that "SC is a computer program" is not a panacea, and pro gamers cannot predict (and even control to prevent) all reaver's glitches even if they will read sources of BW and fully understood them. This post was edited 1 time, last edit by theleo_ua: Aug 13 2012, 10:30 pm. ![]() ![]() ![]() ![]() ![]() ![]() |
Post #6
Biophysicist
Aug 13 2012, 10:12 pm
Post #7 theleo_ua Aug 13 2012, 10:22 pm
|
One of reaver's glitches is scarab movement, but another is "reaver dont want to shoot even if ordered to shoot". For example, if you drop the reaver from shuttle near opponent's CC and SCVs, reaver sometimes will shoot to them automatically, sometimes will not shoot to them autimatically, and sometimes will not want to shoot even if you order him to do this. My questions are next: 1) Is it possible to predict this simple thing? 2) Is it possible to drop a reaver, which will shoot a scarab with 100% chance? 3) Is it possible to force reaver shoot a scarab, if you pressing right mouse button on enemy unit, and reaver still dont want to shoot scarab, wihout loading reaver to shuttle ? And if some of this possible - how to do this? But this is only one (and most simple) of reaver/scarab glitches. ![]() ![]() ![]() ![]() ![]() ![]() |
Post #9
IskatuMesk
Aug 13 2012, 11:00 pm
|
The problem that lies herein is a lot of this behavior is doubtlessly from buggy or ineffective code. It's very possible that because of the way the Reaver's orders and AI work, they may not always even fire correctly in this example you've provided. The circumstances can get very complex inside the code, and so predicting them as a player can become extremely hard. There are instances where, as a player, the code is very much working against you. For example, when you micro wraiths, there are times when the wraith engine graphics do not appear. This is because there is a bug in the flingy.dat air code where it is possible to make a unit move without actually triggering the move header within iscript.bin (that controls the engine overlays and other animation-related events). This was most noticeable in a mod of mine that had a unit that depended on this walk script to "teleport". If the header was not activated, it would not move. There were instances, especially with very fast movement, where the header would not activate and thus the unit would not move. The reaver problem you describe can be even more complicated than this. 3) Is it possible to force reaver shoot a scarab, if you pressing right mouse button on enemy unit, and reaver still dont want to shoot scarab, wihout loading reaver to shuttle ? I'm going to guess that resetting its orders, e.g. pressing stop, hold command, or telling it to move and then stopping, might reset its orders. But it's possible it already set itself on an internal cooldown or something equally silly. I'm not a programmer, so this is a bit beyond my expertise. |
Post #10
Heinermann
Aug 16 2012, 11:30 pm
|
BWAPI Project Owner
|
In the Unload function, if the unit being unloaded is a Reaver it sets its main order timer to 30.
For any other unit it sets their weapon cooldowns to their default weapon cooldowns and also sets their ability cooldown to 30. There is no cooldown randomization here. The only randomization here is in the target acquisition code and pathing code. Also I don't recommend you using Stop, since doing so will destroy any scarabs that belong to the reaver. |
Post #12
Heinermann
Aug 17 2012, 5:44 pm
|
BWAPI Project Owner
|
The target acquisition code chooses one of X amount of closest same-priority targets randomly.
The pathing code provides some randomization when a scarab goes bonkers, which is why every shot is not exactly the same. (at least I thought so) EDIT: Here is most of the target acquisition code in a BW/BWAPI mashup format. Code//BW Attack priority tree: #define MemZero(x) memset(x, 0, sizeof(x)) // Priority enum namespace Priority { enum Enum { Highest, Higher, High, Low, Lower, Lowest, MAX }; }; // Some globals, probably "static" in the Unit class int g_AtkPriorityCount[Priority::MAX] Unit *g_AtkPriorityList[Priority::MAX][16]; int g_MinWpnRange; int g_MinAcqRange; // functions int Unit::getAttackPriorityFor(Unit *pTarget); void Unit::setAttackPriority(int priority); Unit *Unit::getNewAttackTarget(); // <---- START HERE Unit *Unit::getRandomAttackTarget(); // <---- START HERE for alternative function (both are called under different circumstances) Unit *Unit::getTargetFromPriorityList(int priority); Unit *getRandomTargetFromPriorityList(int priority); bool Unit::isTargetActive(Unit *pTarget); bool __fastcall getStandardAttackTargetCallback(Unit *pTarget, Unit *pThis); // Functions not listed are intended to be obvious, and I converted many to use BWAPI-style members // let me know if anything needs clarification // Returns 0 (highest priority) to 5 (lowest priority) int Unit::getAttackPriorityFor(Unit *pTarget) { Unit *targ = pTarget; // get target replacement or do instant returns if any switch ( pTarget->getType() ) { case UnitTypes::Enum::Terran_Bunker: if ( !pTarget->getLoadedUnits().empty() ) targ = pTarget->getLoadedUnits().front(); break; case UnitTypes::Enum::Zerg_Larva: case UnitTypes::Enum::Zerg_Egg: case UnitTypes::Enum::Zerg_Cocoon: case UnitTypes::Enum::Zerg_Lurker_Egg: return Priority::Lowest; default: break; } int iPriority; UnitType targType = targ->getType(); if ( targType.isWorker() ) iPriority = Priority::High; else if ( targ->canAttack(this, true) ) iPriority = Priority::Highest; else if ( targ->canAttack() ) iPriority = Priority::High; else if ( targType.isBuilding() ) iPriority = Priority::Low; else iPriority = Priority::Lower; // decrease the priority if the unit is incomplete or if the target is a loaded bunker if ( !targ->isCompleted() || targ != pTarget ) ++iPriority; // Unknown, possibly a flag telling that another unit just recently attacked it? if ( iPriority == Priority::Highest && (targ->statusFlags & UNKNOWN1) ) iPriority = Priority::Higher; return iPriority; } // Sets the attack priority void Unit::setAttackPriority(int priority) { int priorityIndex = g_AtkPriorityCount[priority]; if ( priorityIndex < 16 ) { g_AtkPriorityList[priority][priorityIndex] = this; g_AtkPriorityCount[priority]++; } } // Obtain the new attack target (base function) Unit *Unit::getNewAttackTarget() { // Reset global data MemZero(g_AtkPriorityCount); // obtain target acquisition range int targAcquisition = this->getTargetAcquisitionRange(); if ( this->isInBunker() ) targAcquisition += 2; else if ( this->isAI() && this->getType().sightRange() > targAcquisition ) targAcquisition = this->getType().sightRange(); // Set some globals g_MinAcqRange = targAcquisition*32; g_MinAtkRange = this->getMinAttackRange(); // Iterate all units in a box and call whatever callback for each one int range = (targAcquisition + 2)*32; Position p( this->getPosition() ); Broodwar->iterateUnitsInRectangle_1( p.x - range, p.y - range, p.x + range, p.y + range, getStandardAttackTargetCallback, this); // Get the highest priority value with at least one target entry int priority = Priority::Highest; while ( g_AtkPriorityCount[priority] == 0 ) { ++priority; if ( priority >= Priority::MAX ) return nullptr; } return this->getTargetFromPriorityList(priority); } // ALTERNATIVE Obtain the new attack target Unit *Unit::getRandomAttackTarget() { // Reset global data MemZero(g_AtkPriorityCount); // Get target acquisition range int targAcquisition = this->getTargetAcquisitionRange(); if ( this->isInBunker() ) targAcquisition += 2; // Set some globals g_MinAcqRange = targAcquisition*32; g_MinAtkRange = 0; // Iterate all units in a box and call whatever callback for each one int range = (targAcquisition + 2)*32; Position p( this->getPosition() ); Broodwar->iterateUnitsInRectangle_1( p.x - range, p.y - range, p.x + range, p.y + range, getStandardAttackTargetCallback, this); // Get the highest priority value with at least one target entry int priority = Priority::Highest; while ( g_AtkPriorityCount[priority] == 0 ) { ++priority; if ( priority >= Priority::MAX ) return nullptr; } return getRandomTargetFromPriorityList(priority); } Unit *Unit::getTargetFromPriorityList(int priority) { // Localize globals Unit **pList = g_AtkPriorityList[priority]; int listCount = g_AtkPriorityCount[priority]; // If there is only one element, there is nothing to compare to if ( listCount == 1 ) return pList[0]; // The "best" index int bestIndex = 0; // Special zerg scourge AI if ( this->isAI() && this->getType() == UnitTypes::Zerg_Scourge ) { // Get the highest HP+Shields unit int bestHP = pList[0]->getHitPoints() + pList[0]->getShields(); for ( int i = 1; i < listCount; ++i ) { int newHP = pList[i]->getHitPoints() + pList[i]->getShields(); if ( newHP > bestHP ) { bestHP = newHP; bestIndex = i; } } } else // anything else { // Get the closest unit int bestDist = this->getPosition().getDistance( pList[0]->getPosition() ); for ( int i = 1; i < listCount; ++i ) { int newDist = this->getPosition().getDistance( pList[0]->getPosition() ); if ( newDist < bestDist ) { bestDist = newDist; bestIndex = i; } } } return pList[bestIndex]; } Unit *getRandomTargetFromPriorityList(int priority) { // Localize globals Unit **pList = g_AtkPriorityList[priority]; int listCount = g_AtkPriorityCount[priority]; // If there is only one element, then we don't need any more calculation if ( listCount == 1 ) return pList[0]; return pList[ rand() % listCount ]; //pList[ Broodwar->getRand(RANDDBG::TARGET) % listCount ]; } bool Unit::isTargetActive(Unit *pTarget) { if ( this->getPlayer().getType() != PlayerTypes::Computer || this->isInAir() || this->isTargetInWeaponRange(pTarget) ) return false; return (pTarget->statusFlags & UNKNOWN1) != 0; } bool __fastcall getStandardAttackTargetCallback(Unit *pTarget, Unit *pThis) { if ( pTarget == pThis ) // exclude if both are the same return false; if ( !pTarget->isVisibleTo( pThis->getPlayer() ) ) // exclude if invisible return false; if ( !pThis->canAttack(pTarget, true) ) // exclude if not attackable return false; if ( !pTarget->isEnemyOf( pThis->getPlayer() ) ) // exclude if not an enemy return false; if ( g_MinWpnRange > 0 && pThis->isTargetInRange(g_MinWpnRange, pTarget) ) // exclude if within global min weapon range return false; if ( !pThis->isTargetInRange(g_MinAcqRange, pTarget) ) // exclude if not in global acquisition range return false; // Get the subunit, if none exists then just use pThis Unit *unit = pThis->getSubunit(); if ( unit == nullptr ) unit = pThis; // Not sure if ( !(unit->statusFlags & IsAUnit_Unknown) ) { // Discard units outside of the attack angle if ( !unit->isTargetInAttackAngle(pTarget->getPosition(), unit->getType().groundWeapon()) ) return false; unit = pThis; } // Set the global attack priority if target was not attacked?? if ( !unit->isTargetActive(pTarget) ) pTarget->setAttackPriority( unit->getAttackPriorityFor(pTarget) ); return false; } This post was edited 2 times, last edit by Heinermann: Aug 17 2012, 6:17 pm. |
Post #13 theleo_ua Aug 17 2012, 10:58 pm
|
Thank you so much for answering and giving the code.
The only 2 questions left: 1) What do you think - is it possible by progamer to avoid these randomizations? For example by "forcing reaver to choose correct target by right mouse pressess etc" ? 2) What do you think about Jangbi vs Fantasy, Perfectman vs Fantasy and Bisu vs Light fragments listed above - do you think it is possible by progamers to predict these glitches? Do you think it is possible by progamers to control these glitches (I mean - to do special combinations of keyboard and mouse presses on units to avoid any of these glitches) ? Thank you in advance ![]() ![]() ![]() ![]() ![]() ![]() |
0 members in this topic (italic members are currently writing a reply): None
+ guest(s)
+ guest(s)
[07:30 pm]
[07:30 pm]
[07:25 pm]
[07:06 pm]
[07:06 pm]
[07:01 pm]
[06:58 pm]







![[close]](/images/up.gif)