bool CWeapon::AttackUnit(CUnit *unit,bool userTarget) { if((!userTarget && weaponDef->noAutoTarget)) return false; if(weaponDef->interceptor) return false; weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x; if(weaponPos.y<ground->GetHeight2(weaponPos.x,weaponPos.z)) weaponPos=owner->pos+10; //hope that we are underground because we are a popup weapon and will come above ground later if(!unit) { if(targetType!=Target_Unit) //make the unit be more likely to keep the current target if user start to move it targetType=Target_None; haveUserTarget=false; return false; } float3 targetPos(helper->GetUnitErrorPos(unit,owner->allyteam)); targetPos+=errorVector*(weaponDef->targetMoveError*30*unit->speed.Length()*(1.0-owner->limExperience)); if(!TryTarget(targetPos,userTarget,unit)) return false; if(targetUnit) { DeleteDeathDependence(targetUnit); targetUnit=0; } haveUserTarget=userTarget; targetType=Target_Unit; targetUnit=unit; targetPos=unit->midPos+float3(0,0.3,0)*unit->radius; AddDeathDependence(targetUnit); return true; }
bool CWeapon::AttackGround(float3 pos, bool userTarget) { if (!userTarget && weaponDef->noAutoTarget) { return false; } if (weaponDef->interceptor || !weaponDef->canAttackGround || (weaponDef->onlyTargetCategory != 0xffffffff)) { return false; } if (!weaponDef->waterweapon && (pos.y < 1.0f)) { pos.y = 1.0f; } weaponMuzzlePos=owner->pos+owner->frontdir*relWeaponMuzzlePos.z+owner->updir*relWeaponMuzzlePos.y+owner->rightdir*relWeaponMuzzlePos.x; if(weaponMuzzlePos.y<ground->GetHeight2(weaponMuzzlePos.x,weaponMuzzlePos.z)) weaponMuzzlePos=owner->pos+UpVector*10; //hope that we are underground because we are a popup weapon and will come above ground later if(!TryTarget(pos,userTarget,0)) return false; if(targetUnit){ DeleteDeathDependence(targetUnit); targetUnit=0; } haveUserTarget=userTarget; targetType=Target_Pos; targetPos=pos; return true; }
bool CWeapon::AttackUnit(CUnit* unit, bool userTarget) { if ((!userTarget && weaponDef->noAutoTarget)) return false; if (weaponDef->interceptor) return false; weaponPos = owner->pos + owner->frontdir * relWeaponPos.z + owner->updir * relWeaponPos.y + owner->rightdir * relWeaponPos.x; weaponMuzzlePos = owner->pos + owner->frontdir * relWeaponMuzzlePos.z + owner->updir * relWeaponMuzzlePos.y + owner->rightdir * relWeaponMuzzlePos.x; if (weaponMuzzlePos.y < ground->GetHeightReal(weaponMuzzlePos.x, weaponMuzzlePos.z)) { // hope that we are underground because we are a popup weapon and will come above ground later weaponMuzzlePos = owner->pos + UpVector * 10; } if (!unit) { if (targetType != Target_Unit) { // make the unit be more likely to keep the current target if user starts to move it targetType = Target_None; } haveUserTarget = false; return false; } float3 tempTargetPos = helper->GetUnitErrorPos(unit, owner->allyteam) + errorVector * (weaponDef->targetMoveError * GAME_SPEED * unit->speed.Length() * (1.0f - owner->limExperience)); const float appHeight = ground->GetApproximateHeight(tempTargetPos.x, tempTargetPos.z) + 2.0f; if (tempTargetPos.y < appHeight) tempTargetPos.y = appHeight; if (!TryTarget(tempTargetPos, userTarget, unit)) return false; if (targetUnit) { DeleteDeathDependence(targetUnit, DEPENDENCE_TARGETUNIT); targetUnit = NULL; } haveUserTarget = userTarget; targetType = Target_Unit; targetUnit = unit; targetPos = tempTargetPos; AddDeathDependence(targetUnit, DEPENDENCE_TARGETUNIT); avoidTarget = false; return true; }
bool CWeapon::TryTarget(CUnit* unit, bool userTarget){ float3 tempTargetPos(helper->GetUnitErrorPos(unit,owner->allyteam)); tempTargetPos+=errorVector*(weaponDef->targetMoveError*30*unit->speed.Length()*(1.0f-owner->limExperience)); float appHeight=ground->GetApproximateHeight(tempTargetPos.x,tempTargetPos.z)+2; if(tempTargetPos.y < appHeight){ tempTargetPos.y=appHeight; } return TryTarget(tempTargetPos,userTarget,unit); }
bool CWeapon::TryTargetHeading(short heading, float3 pos, bool userTarget, CUnit* unit) { float3 tempfrontdir(owner->frontdir); float3 temprightdir(owner->rightdir); short tempHeadding = owner->heading; owner->heading = heading; owner->frontdir = GetVectorFromHeading(owner->heading); owner->rightdir = owner->frontdir.cross(owner->updir); weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x; weaponMuzzlePos=owner->pos+owner->frontdir*relWeaponMuzzlePos.z+owner->updir*relWeaponMuzzlePos.y+owner->rightdir*relWeaponMuzzlePos.x; bool val = TryTarget(pos, userTarget, unit); owner->frontdir = tempfrontdir; owner->rightdir = temprightdir; owner->heading = tempHeadding; weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x; weaponMuzzlePos=owner->pos+owner->frontdir*relWeaponMuzzlePos.z+owner->updir*relWeaponMuzzlePos.y+owner->rightdir*relWeaponMuzzlePos.x; return val; }
void CWeapon::SlowUpdate(bool noAutoTargetOverride) { #ifdef TRACE_SYNC tracefile << "Weapon slow update: "; tracefile << owner->id << " " << weaponNum << "\n"; #endif // If we can't get a line of fire from the muzzle, try // the aim piece instead (since the weapon may just be // turned in a wrong way) int weaponPiece = -1; bool weaponAimed = (useWeaponPosForAim == 0); if (!weaponAimed) { weaponPiece = owner->script->QueryWeapon(weaponNum); if (useWeaponPosForAim > 1) useWeaponPosForAim--; } else { weaponPiece = owner->script->AimFromWeapon(weaponNum); } relWeaponMuzzlePos = owner->script->GetPiecePos(weaponPiece); weaponMuzzlePos = owner->pos + owner->frontdir * relWeaponMuzzlePos.z + owner->updir * relWeaponMuzzlePos.y + owner->rightdir * relWeaponMuzzlePos.x; weaponPos = owner->pos + owner->frontdir * relWeaponPos.z + owner->updir * relWeaponPos.y + owner->rightdir * relWeaponPos.x; if (!weaponAimed) { weaponPiece = owner->script->AimFromWeapon(weaponNum); } relWeaponPos = owner->script->GetPiecePos(weaponPiece); if (weaponMuzzlePos.y < ground->GetHeightReal(weaponMuzzlePos.x, weaponMuzzlePos.z)) { // hope that we are underground because we are a popup weapon and will come above ground later weaponMuzzlePos = owner->pos + UpVector * 10; } predictSpeedMod = 1.0f + (gs->randFloat() - 0.5f) * 2 * (1.0f - owner->limExperience); hasCloseTarget = ((targetPos - weaponPos).SqLength() < relWeaponPos.SqLength() * 16); if (targetType != Target_None && !TryTarget(targetPos, haveUserTarget, targetUnit)) { HoldFire(); } if (targetType == Target_Unit) { // stop firing at cloaked targets if (targetUnit != NULL && targetUnit->isCloaked && !(targetUnit->losStatus[owner->allyteam] & (LOS_INLOS | LOS_INRADAR))) HoldFire(); if (!haveUserTarget) { // stop firing at neutral targets (unless in FAW mode) // note: HoldFire sets targetUnit to NULL, so recheck if (targetUnit != NULL && targetUnit->neutral && owner->fireState <= FIRESTATE_FIREATWILL) HoldFire(); // stop firing at allied targets // // this situation (unit keeps attacking its target if the // target or the unit switches to an allied team) should // be handled by /ally processing now if (targetUnit != NULL && teamHandler->Ally(owner->allyteam, targetUnit->allyteam)) HoldFire(); } } if (slavedTo) { // use targets from the thing we are slaved to if (targetUnit) { DeleteDeathDependence(targetUnit, DEPENDENCE_TARGETUNIT); targetUnit = NULL; } targetType = Target_None; if (slavedTo->targetType == Target_Unit) { const float3 tp = helper->GetUnitErrorPos(slavedTo->targetUnit, owner->allyteam) + errorVector * (weaponDef->targetMoveError * GAME_SPEED * slavedTo->targetUnit->speed.Length() * (1.0f - owner->limExperience)); if (TryTarget(tp, false, slavedTo->targetUnit)) { targetType = Target_Unit; targetUnit = slavedTo->targetUnit; targetPos = tp; AddDeathDependence(targetUnit, DEPENDENCE_TARGETUNIT); } } else if (slavedTo->targetType == Target_Pos) { if (TryTarget(slavedTo->targetPos, false, 0)) { targetType = Target_Pos; targetPos = slavedTo->targetPos; } } return; } if (!noAutoTargetOverride && AllowWeaponTargetCheck()) { lastTargetRetry = gs->frameNum; std::multimap<float, CUnit*> targets; std::multimap<float, CUnit*>::const_iterator nextTargetIt; std::multimap<float, CUnit*>::const_iterator lastTargetIt; helper->GenerateWeaponTargets(this, targetUnit, targets); if (!targets.empty()) lastTargetIt = --targets.end(); for (nextTargetIt = targets.begin(); nextTargetIt != targets.end(); ++nextTargetIt) { CUnit* nextTargetUnit = nextTargetIt->second; if (nextTargetUnit->neutral && (owner->fireState <= FIRESTATE_FIREATWILL)) { continue; } // when only one target is available, <nextTarget> can equal <targetUnit> // and we want to attack whether it is in our bad target category or not // (if only bad targets are available and this is the last, just pick it) if (nextTargetUnit != targetUnit && (nextTargetUnit->category & badTargetCategory)) { if (nextTargetIt != lastTargetIt) { continue; } } const float weaponLead = weaponDef->targetMoveError * GAME_SPEED * nextTargetUnit->speed.Length(); const float weaponError = weaponLead * (1.0f - owner->limExperience); float3 nextTargetPos = nextTargetUnit->midPos + (errorVector * weaponError); const float appHeight = ground->GetApproximateHeight(nextTargetPos.x, nextTargetPos.z) + 2.0f; if (nextTargetPos.y < appHeight) { nextTargetPos.y = appHeight; } if (TryTarget(nextTargetPos, false, nextTargetUnit)) { if (targetUnit) { DeleteDeathDependence(targetUnit, DEPENDENCE_TARGETUNIT); } targetType = Target_Unit; targetUnit = nextTargetUnit; targetPos = nextTargetPos; AddDeathDependence(targetUnit, DEPENDENCE_TARGETUNIT); break; } } } if (targetType != Target_None) { owner->haveTarget = true; if (haveUserTarget) { owner->haveUserTarget = true; } } else { // if we can't target anything, try switching aim point if (useWeaponPosForAim == 1) { useWeaponPosForAim = 0; } else { useWeaponPosForAim = 1; } } }
void CWeapon::Update() { if (hasCloseTarget) { int weaponPiece = -1; bool weaponAimed = (useWeaponPosForAim == 0); // if we couldn't get a line of fire from the // muzzle, try if we can get it from the aim // piece if (!weaponAimed) { weaponPiece = owner->script->QueryWeapon(weaponNum); } else { weaponPiece = owner->script->AimFromWeapon(weaponNum); } relWeaponMuzzlePos = owner->script->GetPiecePos(weaponPiece); if (!weaponAimed) { weaponPiece = owner->script->AimFromWeapon(weaponNum); } relWeaponPos = owner->script->GetPiecePos(weaponPiece); } if (targetType == Target_Unit) { if (lastErrorVectorUpdate < gs->frameNum - UNIT_SLOWUPDATE_RATE) { float3 newErrorVector(gs->randVector()); errorVectorAdd = (newErrorVector - errorVector) * (1.0f / UNIT_SLOWUPDATE_RATE); lastErrorVectorUpdate = gs->frameNum; } errorVector += errorVectorAdd; if (predict > 50000) { // to prevent runaway prediction (happens sometimes when a missile // is moving *away* from its target), we may need to disable missiles // in case they fly around too long predict = 50000; } float3 lead = targetUnit->speed * (weaponDef->predictBoost+predictSpeedMod * (1.0f - weaponDef->predictBoost)) * predict; if (weaponDef->leadLimit >= 0.0f && lead.SqLength() > Square(weaponDef->leadLimit + weaponDef->leadBonus * owner->experience)) { lead *= (weaponDef->leadLimit + weaponDef->leadBonus*owner->experience) / (lead.Length() + 0.01f); } targetPos = helper->GetUnitErrorPos(targetUnit, owner->allyteam) + lead + errorVector * (weaponDef->targetMoveError * GAME_SPEED * targetUnit->speed.Length() * (1.0f - owner->limExperience)); const float appHeight = ground->GetApproximateHeight(targetPos.x, targetPos.z) + 2.0f; if (targetPos.y < appHeight) targetPos.y = appHeight; if (!weaponDef->waterweapon && targetPos.y < 1.0f) targetPos.y = 1.0f; } if (weaponDef->interceptor) { CheckIntercept(); } if (targetType != Target_None) { if (onlyForward) { float3 goaldir = (targetPos - owner->pos).Normalize(); angleGood = (owner->frontdir.dot(goaldir) > maxAngleDif); } else if (lastRequestedDir.dot(wantedDir) < maxAngleDif || (lastRequest + 15 < gs->frameNum)) { angleGood = false; lastRequestedDir = wantedDir; lastRequest = gs->frameNum; const float heading = GetHeadingFromVectorF(wantedDir.x, wantedDir.z); const float pitch = math::asin(Clamp(wantedDir.dot(owner->updir), -1.0f, 1.0f)); // for COB, this sets anglegood to return value of aim script when it finished, // for Lua, there exists a callout to set the anglegood member. // FIXME: convert CSolidObject::heading to radians too. owner->script->AimWeapon(weaponNum, ClampRad(heading - owner->heading * TAANG2RAD), pitch); } } if(weaponDef->stockpile && numStockpileQued){ float p=1.0f/stockpileTime; if(teamHandler->Team(owner->team)->metal>=metalFireCost*p && teamHandler->Team(owner->team)->energy>=energyFireCost*p){ owner->UseEnergy(energyFireCost*p); owner->UseMetal(metalFireCost*p); buildPercent+=p; } else { // update the energy and metal required counts teamHandler->Team(owner->team)->energyPull += energyFireCost*p; teamHandler->Team(owner->team)->metalPull += metalFireCost*p; } if(buildPercent>=1){ const int oldCount = numStockpiled; buildPercent=0; numStockpileQued--; numStockpiled++; owner->commandAI->StockpileChanged(this); eventHandler.StockpileChanged(owner, this, oldCount); } } if ((salvoLeft == 0) && (owner->fpsControlPlayer == NULL || owner->fpsControlPlayer->fpsController.mouse1 || owner->fpsControlPlayer->fpsController.mouse2) && (targetType != Target_None) && angleGood && subClassReady && (reloadStatus <= gs->frameNum) && (!weaponDef->stockpile || numStockpiled) && (weaponDef->fireSubmersed || (weaponMuzzlePos.y > 0)) && ((((owner->unitDef->maxFuel == 0) || (owner->currentFuel > 0) || (fuelUsage == 0)) && !isBeingServicedOnPad(owner))) ) { if ((weaponDef->stockpile || (teamHandler->Team(owner->team)->metal >= metalFireCost && teamHandler->Team(owner->team)->energy >= energyFireCost))) { const int piece = owner->script->QueryWeapon(weaponNum); owner->script->GetEmitDirPos(piece, relWeaponMuzzlePos, weaponDir); weaponMuzzlePos = owner->pos + owner->frontdir * relWeaponMuzzlePos.z + owner->updir * relWeaponMuzzlePos.y + owner->rightdir * relWeaponMuzzlePos.x; useWeaponPosForAim = reloadTime / 16 + 8; weaponDir = owner->frontdir * weaponDir.z + owner->updir * weaponDir.y + owner->rightdir * weaponDir.x; weaponDir.SafeNormalize(); if (TryTarget(targetPos, haveUserTarget, targetUnit) && !CobBlockShot(targetUnit)) { if (weaponDef->stockpile) { const int oldCount = numStockpiled; numStockpiled--; owner->commandAI->StockpileChanged(this); eventHandler.StockpileChanged(owner, this, oldCount); } else { owner->UseEnergy(energyFireCost); owner->UseMetal(metalFireCost); owner->currentFuel = std::max(0.0f, owner->currentFuel - fuelUsage); } reloadStatus = gs->frameNum + (int)(reloadTime / owner->reloadSpeed); salvoLeft = salvoSize; nextSalvo = gs->frameNum; salvoError = gs->randVector() * (owner->isMoving? weaponDef->movingAccuracy: accuracy); if (targetType == Target_Pos || (targetType == Target_Unit && !(targetUnit->losStatus[owner->allyteam] & LOS_INLOS))) { // area firing stuff is too effective at radar firing... salvoError *= 1.3f; } owner->lastMuzzleFlameSize = muzzleFlareSize; owner->lastMuzzleFlameDir = wantedDir; owner->script->FireWeapon(weaponNum); } } else { // FIXME -- never reached? if (TryTarget(targetPos, haveUserTarget, targetUnit) && !weaponDef->stockpile) { // update the energy and metal required counts const int minPeriod = std::max(1, (int)(reloadTime / owner->reloadSpeed)); const float averageFactor = 1.0f / (float)minPeriod; teamHandler->Team(owner->team)->energyPull += averageFactor * energyFireCost; teamHandler->Team(owner->team)->metalPull += averageFactor * metalFireCost; } } } if (salvoLeft && nextSalvo <= gs->frameNum) { salvoLeft--; nextSalvo = gs->frameNum + salvoDelay; owner->lastFireWeapon = gs->frameNum; int projectiles = projectilesPerShot; while (projectiles > 0) { --projectiles; // add to the commandShotCount if this is the last salvo, // and it is being directed towards the current target // (helps when deciding if a queued ground attack order has been completed) if (((salvoLeft == 0) && (owner->commandShotCount >= 0) && ((targetType == Target_Pos) && (targetPos == owner->userAttackPos))) || ((targetType == Target_Unit) && (targetUnit == owner->userTarget))) { owner->commandShotCount++; } owner->script->Shot(weaponNum); int piece = owner->script->AimFromWeapon(weaponNum); relWeaponPos = owner->script->GetPiecePos(piece); piece = owner->script->/*AimFromWeapon*/QueryWeapon(weaponNum); owner->script->GetEmitDirPos(piece, relWeaponMuzzlePos, weaponDir); weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x; weaponMuzzlePos=owner->pos+owner->frontdir*relWeaponMuzzlePos.z+owner->updir*relWeaponMuzzlePos.y+owner->rightdir*relWeaponMuzzlePos.x; weaponDir = owner->frontdir * weaponDir.z + owner->updir * weaponDir.y + owner->rightdir * weaponDir.x; weaponDir.SafeNormalize(); if (owner->unitDef->decloakOnFire && (owner->scriptCloak <= 2)) { if (owner->isCloaked) { owner->isCloaked = false; eventHandler.UnitDecloaked(owner); } owner->curCloakTimeout = gs->frameNum + owner->cloakTimeout; } Fire(); } //Rock the unit in the direction of the fireing if (owner->script->HasRockUnit()) { float3 rockDir = wantedDir; rockDir.y = 0; rockDir = -rockDir.Normalize(); owner->script->RockUnit(rockDir); } owner->commandAI->WeaponFired(this); if(salvoLeft==0){ owner->script->EndBurst(weaponNum); } #ifdef TRACE_SYNC tracefile << "Weapon fire: "; tracefile << weaponPos.x << " " << weaponPos.y << " " << weaponPos.z << " " << targetPos.x << " " << targetPos.y << " " << targetPos.z << "\n"; #endif } }
void CWeapon::Update() { if(hasCloseTarget) { std::vector<int> args; args.push_back(0); if(useWeaponPosForAim) { owner->cob->Call(COBFN_QueryPrimary+weaponNum,args); } else { owner->cob->Call(COBFN_AimFromPrimary+weaponNum,args); } relWeaponPos=owner->localmodel->GetPiecePos(args[0]); } if(targetType==Target_Unit) { if(lastErrorVectorUpdate<gs->frameNum-16) { float3 newErrorVector(gs->randVector()); errorVectorAdd=(newErrorVector-errorVector)*(1.0f/16.0f); lastErrorVectorUpdate=gs->frameNum; } errorVector+=errorVectorAdd; if(weaponDef->selfExplode) { //assumes that only flakker like units that need to hit aircrafts has this,change to a separate tag later targetPos=helper->GetUnitErrorPos(targetUnit,owner->allyteam)+targetUnit->speed*(0.5+predictSpeedMod*0.5)*predict; } else { targetPos=helper->GetUnitErrorPos(targetUnit,owner->allyteam)+targetUnit->speed*predictSpeedMod*predict; } targetPos+=errorVector*(weaponDef->targetMoveError*30*targetUnit->speed.Length()*(1.0-owner->limExperience)); if(!weaponDef->waterweapon && targetPos.y<1) targetPos.y=1; } if(weaponDef->interceptor) CheckIntercept(); if(targetType!=Target_None) { if(onlyForward) { float3 goaldir=targetPos-owner->pos; goaldir.Normalize(); angleGood=owner->frontdir.dot(goaldir) > maxAngleDif; } else if(lastRequestedDir.dot(wantedDir)<maxAngleDif || lastRequest+15<gs->frameNum) { angleGood=false; lastRequestedDir=wantedDir; lastRequest=gs->frameNum; short int heading=GetHeadingFromVector(wantedDir.x,wantedDir.z); short int pitch=(short int) (asin(wantedDir.dot(owner->updir))*(32768/PI)); std::vector<int> args; args.push_back(short(heading-owner->heading)); args.push_back(pitch); owner->cob->Call(COBFN_AimPrimary+weaponNum,args,ScriptCallback,this,0); } } if(weaponDef->stockpile && numStockpileQued) { float p=1.0/reloadTime; if(gs->Team(owner->team)->metal>=metalFireCost*p && gs->Team(owner->team)->energy>=energyFireCost*p) { owner->UseEnergy(energyFireCost*p); owner->UseMetal(metalFireCost*p); buildPercent+=p; } if(buildPercent>=1) { buildPercent=0; numStockpileQued--; numStockpiled++; owner->commandAI->StockpileChanged(this); } } if(salvoLeft==0 #ifdef DIRECT_CONTROL_ALLOWED && (!owner->directControl || owner->directControl->mouse1 || owner->directControl->mouse2) #endif && targetType!=Target_None && angleGood && subClassReady && reloadStatus<=gs->frameNum && (weaponDef->stockpile || (gs->Team(owner->team)->metal>=metalFireCost && gs->Team(owner->team)->energy>=energyFireCost)) && (!weaponDef->stockpile || numStockpiled) && (weaponDef->waterweapon || weaponPos.y>0) ) { std::vector<int> args; args.push_back(0); owner->cob->Call(COBFN_QueryPrimary+weaponNum,args); relWeaponPos=owner->localmodel->GetPiecePos(args[0]); weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x; useWeaponPosForAim=reloadTime/16+8; if(TryTarget(targetPos,haveUserTarget,targetUnit)) { if(weaponDef->stockpile) { numStockpiled--; owner->commandAI->StockpileChanged(this); } else { owner->UseEnergy(energyFireCost); owner->UseMetal(metalFireCost); } if(weaponDef->stockpile) reloadStatus=gs->frameNum+60; else reloadStatus=gs->frameNum+(int)(reloadTime/owner->reloadSpeed); salvoLeft=salvoSize; nextSalvo=gs->frameNum; salvoError=gs->randVector()*(owner->isMoving?weaponDef->movingAccuracy:accuracy); if(targetType==Target_Pos || (targetType==Target_Unit && !(targetUnit->losStatus[owner->allyteam] & LOS_INLOS))) //area firing stuff is to effective at radar firing... salvoError*=1.3; owner->lastMuzzleFlameSize=muzzleFlareSize; owner->lastMuzzleFlameDir=wantedDir; owner->cob->Call(COBFN_FirePrimary+weaponNum); } } if(salvoLeft && nextSalvo<=gs->frameNum) { salvoLeft--; nextSalvo=gs->frameNum+salvoDelay; owner->lastFireWeapon=gs->frameNum; std::vector<int> args; args.push_back(0); owner->cob->Call(/*COBFN_AimFromPrimary+weaponNum/*/COBFN_QueryPrimary+weaponNum/**/,args); relWeaponPos=owner->localmodel->GetPiecePos(args[0]); weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x; // info->AddLine("RelPosFire %f %f %f",relWeaponPos.x,relWeaponPos.y,relWeaponPos.z); owner->isCloaked=false; owner->curCloakTimeout=gs->frameNum+owner->cloakTimeout; Fire(); //Rock the unit in the direction of the fireing float3 rockDir = wantedDir; rockDir.y = 0; rockDir = -rockDir.Normalize(); std::vector<int> rockAngles; rockAngles.push_back((int)(500 * rockDir.z)); rockAngles.push_back((int)(500 * rockDir.x)); owner->cob->Call(COBFN_RockUnit, rockAngles); owner->commandAI->WeaponFired(this); if(salvoLeft==0) { owner->cob->Call(COBFN_EndBurst+weaponNum); } #ifdef TRACE_SYNC tracefile << "Weapon fire: "; tracefile << weaponPos.x << " " << weaponPos.y << " " << weaponPos.z << " " << targetPos.x << " " << targetPos.y << " " << targetPos.z << "\n"; #endif } }
void CWeapon::SlowUpdate() { #ifdef TRACE_SYNC tracefile << "Weapon slow update: "; tracefile << owner->id << " " << weaponNum << "\n"; #endif std::vector<int> args; args.push_back(0); if(useWeaponPosForAim) { owner->cob->Call(COBFN_QueryPrimary+weaponNum,args); if(useWeaponPosForAim>1) useWeaponPosForAim--; } else { owner->cob->Call(COBFN_AimFromPrimary+weaponNum,args); } relWeaponPos=owner->localmodel->GetPiecePos(args[0]); weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x; if(weaponPos.y<ground->GetHeight2(weaponPos.x,weaponPos.z)) weaponPos=owner->pos+10; //hope that we are underground because we are a popup weapon and will come above ground later predictSpeedMod=1+(gs->randFloat()-0.5)*2*(1-owner->limExperience); if((targetPos-weaponPos).SqLength() < relWeaponPos.SqLength()*16) hasCloseTarget=true; else hasCloseTarget=false; if(targetType!=Target_None && !TryTarget(targetPos,haveUserTarget,targetUnit)) { HoldFire(); } if(targetType==Target_Unit && targetUnit->isCloaked && !(targetUnit->losStatus[owner->allyteam] & (LOS_INLOS | LOS_INRADAR))) HoldFire(); if(slavedTo) { //use targets from the thing we are slaved to if(targetUnit) { DeleteDeathDependence(targetUnit); targetUnit=0; } targetType=Target_None; if(slavedTo->targetType==Target_Unit) { float3 tp=helper->GetUnitErrorPos(slavedTo->targetUnit,owner->allyteam); tp+=errorVector*(weaponDef->targetMoveError*30*slavedTo->targetUnit->speed.Length()*(1.0-owner->limExperience)); if(TryTarget(tp,false,slavedTo->targetUnit)) { targetType=Target_Unit; targetUnit=slavedTo->targetUnit; targetPos=tp; AddDeathDependence(targetUnit); } } else if(slavedTo->targetType==Target_Pos) { if(TryTarget(slavedTo->targetPos,false,0)) { targetType=Target_Pos; targetPos=slavedTo->targetPos; } } return; } if(!weaponDef->noAutoTarget) { if(owner->fireState==2 && !haveUserTarget && (targetType==Target_None || (targetType==Target_Unit && (targetUnit->category & badTargetCategory)) || gs->frameNum>lastTargetRetry+65)) { std::map<float,CUnit*> targets; helper->GenerateTargets(this,targetUnit,targets); for(std::map<float,CUnit*>::iterator ti=targets.begin(); ti!=targets.end(); ++ti) { if(targetUnit && (ti->second->category & badTargetCategory)) continue; float3 tp(ti->second->midPos); tp+=errorVector*(weaponDef->targetMoveError*30*ti->second->speed.Length()*(1.0-owner->limExperience)); if(TryTarget(tp,false,ti->second)) { if(targetUnit) { DeleteDeathDependence(targetUnit); } targetType=Target_Unit; targetUnit=ti->second; targetPos=tp; AddDeathDependence(targetUnit); break; } } } } if(targetType!=Target_None) { owner->haveTarget=true; if(haveUserTarget) owner->haveUserTarget=true; } else { //if we cant target anything try switching aim point if(useWeaponPosForAim && useWeaponPosForAim==1) { useWeaponPosForAim=0; } else { useWeaponPosForAim=1; } } }
void CWeapon::SlowUpdate(bool noAutoTargetOverride) { #ifdef TRACE_SYNC tracefile << "Weapon slow update: "; tracefile << owner->id << " " << weaponNum << "\n"; #endif std::vector<int> args; args.push_back(0); if(useWeaponPosForAim){ //If we can't get a line of fire from the muzzle try the aim piece instead since the weapon may just be turned in a wrong way owner->cob->Call(COBFN_QueryPrimary+weaponNum,args); if(useWeaponPosForAim>1) useWeaponPosForAim--; } else { owner->cob->Call(COBFN_AimFromPrimary+weaponNum,args); } relWeaponMuzzlePos=owner->localmodel->GetPiecePos(args[0]); weaponMuzzlePos=owner->pos+owner->frontdir*relWeaponMuzzlePos.z+owner->updir*relWeaponMuzzlePos.y+owner->rightdir*relWeaponMuzzlePos.x; owner->cob->Call(COBFN_AimFromPrimary+weaponNum,args); relWeaponPos=owner->localmodel->GetPiecePos(args[0]); weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x; if(weaponMuzzlePos.y<ground->GetHeight2(weaponMuzzlePos.x,weaponMuzzlePos.z)) weaponMuzzlePos=owner->pos+UpVector*10; //hope that we are underground because we are a popup weapon and will come above ground later predictSpeedMod=1+(gs->randFloat()-0.5f)*2*(1-owner->limExperience); if((targetPos-weaponPos).SqLength() < relWeaponPos.SqLength()*16) hasCloseTarget=true; else hasCloseTarget=false; if(targetType!=Target_None && !TryTarget(targetPos,haveUserTarget,targetUnit)){ HoldFire(); } if(targetType==Target_Unit && targetUnit->isCloaked && !(targetUnit->losStatus[owner->allyteam] & (LOS_INLOS | LOS_INRADAR))) HoldFire(); if (targetType==Target_Unit && !haveUserTarget && targetUnit->neutral && owner->fireState < 3) HoldFire(); //happens if the target or the unit has switched teams if (targetType==Target_Unit && !haveUserTarget && targetUnit->allyteam == owner->allyteam) HoldFire(); if(slavedTo){ //use targets from the thing we are slaved to if(targetUnit){ DeleteDeathDependence(targetUnit); targetUnit=0; } targetType=Target_None; if(slavedTo->targetType==Target_Unit){ float3 tp=helper->GetUnitErrorPos(slavedTo->targetUnit,owner->allyteam); tp+=errorVector*(weaponDef->targetMoveError*30*slavedTo->targetUnit->speed.Length()*(1.0f-owner->limExperience)); if(TryTarget(tp,false,slavedTo->targetUnit)){ targetType=Target_Unit; targetUnit=slavedTo->targetUnit; targetPos=tp; AddDeathDependence(targetUnit); } } else if(slavedTo->targetType==Target_Pos){ if(TryTarget(slavedTo->targetPos,false,0)){ targetType=Target_Pos; targetPos=slavedTo->targetPos; } } return; } /* owner->fireState>=2 && !haveUserTarget && if (!weaponDef->noAutoTarget && !noAutoTargetOverride) { ((targetType == Target_None) || ((targetType == Target_Unit) && ((targetUnit->category & badTargetCategory) || (targetUnit->neutral && (owner->fireState < 3)))) || (gs->frameNum > lastTargetRetry + 65))) { */ if (!noAutoTargetOverride && ShouldCheckForNewTarget()) { lastTargetRetry = gs->frameNum; std::map<float, CUnit*> targets; helper->GenerateTargets(this, targetUnit, targets); for (std::map<float,CUnit*>::iterator ti=targets.begin();ti!=targets.end();++ti) { if (ti->second->neutral && (owner->fireState < 3)) { continue; } if (targetUnit && (ti->second->category & badTargetCategory)) { continue; } float3 tp(ti->second->midPos); tp+=errorVector*(weaponDef->targetMoveError*30*ti->second->speed.Length()*(1.0f-owner->limExperience)); float appHeight=ground->GetApproximateHeight(tp.x,tp.z)+2; if (tp.y < appHeight) { tp.y = appHeight; } if (TryTarget(tp, false, ti->second)) { if (targetUnit) { DeleteDeathDependence(targetUnit); } targetType = Target_Unit; targetUnit = ti->second; targetPos = tp; AddDeathDependence(targetUnit); break; } } } if (targetType != Target_None) { owner->haveTarget = true; if (haveUserTarget) { owner->haveUserTarget = true; } } else { //if we cant target anything try switching aim point if (useWeaponPosForAim && (useWeaponPosForAim == 1)) { useWeaponPosForAim = 0; } else { useWeaponPosForAim = 1; } } }
void CWeapon::Update() { if(hasCloseTarget){ std::vector<int> args; args.push_back(0); if(useWeaponPosForAim){ //if we couldn't get a line of fire from the muzzle try if we can get it from the aim piece owner->cob->Call(COBFN_QueryPrimary+weaponNum,args); } else { owner->cob->Call(COBFN_AimFromPrimary+weaponNum,args); } relWeaponMuzzlePos=owner->localmodel->GetPiecePos(args[0]); owner->cob->Call(COBFN_AimFromPrimary+weaponNum,args); relWeaponPos=owner->localmodel->GetPiecePos(args[0]); } if(targetType==Target_Unit){ if(lastErrorVectorUpdate<gs->frameNum-16){ float3 newErrorVector(gs->randVector()); errorVectorAdd=(newErrorVector-errorVector)*(1.0f/16.0f); lastErrorVectorUpdate=gs->frameNum; } errorVector+=errorVectorAdd; if (predict > 50000) { /* to prevent runaway prediction (happens sometimes when a missile is moving *away* from it's target), we may need to disable missiles in case they fly around too long */ predict = 50000; } float3 lead = targetUnit->speed * (weaponDef->predictBoost+predictSpeedMod * (1.0f - weaponDef->predictBoost)) * predict; if (weaponDef->leadLimit >= 0.0f && lead.Length() > weaponDef->leadLimit + weaponDef->leadBonus * owner->experience) { lead *= (weaponDef->leadLimit + weaponDef->leadBonus*owner->experience) / (lead.Length() + 0.01f); } targetPos = helper->GetUnitErrorPos(targetUnit, owner->allyteam) + lead; targetPos += errorVector * (weaponDef->targetMoveError * 30 * targetUnit->speed.Length() * (1.0f - owner->limExperience)); float appHeight = ground->GetApproximateHeight(targetPos.x, targetPos.z) + 2; if (targetPos.y < appHeight) targetPos.y = appHeight; if (!weaponDef->waterweapon && targetPos.y < 1.0f) targetPos.y = 1.0f; } if (weaponDef->interceptor) { CheckIntercept(); } if (targetType != Target_None){ if (onlyForward) { float3 goaldir = targetPos - owner->pos; goaldir.Normalize(); angleGood = (owner->frontdir.dot(goaldir) > maxAngleDif); } else if (lastRequestedDir.dot(wantedDir) < maxAngleDif || lastRequest + 15 < gs->frameNum) { angleGood=false; lastRequestedDir=wantedDir; lastRequest=gs->frameNum; short int heading=GetHeadingFromVector(wantedDir.x,wantedDir.z); short int pitch=(short int) (asin(wantedDir.dot(owner->updir))*(32768/PI)); std::vector<int> args; args.push_back(short(heading - owner->heading)); args.push_back(pitch); owner->cob->Call(COBFN_AimPrimary+weaponNum,args,ScriptCallback,this,0); } } if(weaponDef->stockpile && numStockpileQued){ float p=1.0f/stockpileTime; if(gs->Team(owner->team)->metal>=metalFireCost*p && gs->Team(owner->team)->energy>=energyFireCost*p){ owner->UseEnergy(energyFireCost*p); owner->UseMetal(metalFireCost*p); buildPercent+=p; } else { // update the energy and metal required counts gs->Team(owner->team)->energyPull += energyFireCost*p; gs->Team(owner->team)->metalPull += metalFireCost*p; } if(buildPercent>=1){ const int oldCount = numStockpiled; buildPercent=0; numStockpileQued--; numStockpiled++; owner->commandAI->StockpileChanged(this); eventHandler.StockpileChanged(owner, this, oldCount); } } if ((salvoLeft == 0) #ifdef DIRECT_CONTROL_ALLOWED && (!owner->directControl || owner->directControl->mouse1 || owner->directControl->mouse2) #endif && (targetType != Target_None) && angleGood && subClassReady && (reloadStatus <= gs->frameNum) && (!weaponDef->stockpile || numStockpiled) && (weaponDef->fireSubmersed || (weaponMuzzlePos.y > 0)) && ((owner->unitDef->maxFuel == 0) || (owner->currentFuel > 0)) ) { if ((weaponDef->stockpile || (gs->Team(owner->team)->metal >= metalFireCost && gs->Team(owner->team)->energy >= energyFireCost))) { std::vector<int> args; args.push_back(0); owner->cob->Call(COBFN_QueryPrimary + weaponNum, args); owner->localmodel->GetEmitDirPos(args[0], relWeaponMuzzlePos, weaponDir); weaponMuzzlePos = owner->pos + owner->frontdir * relWeaponMuzzlePos.z + owner->updir * relWeaponMuzzlePos.y + owner->rightdir * relWeaponMuzzlePos.x; useWeaponPosForAim = reloadTime / 16 + 8; weaponDir = owner->frontdir * weaponDir.z + owner->updir * weaponDir.y + owner->rightdir * weaponDir.x; weaponDir.Normalize(); if (TryTarget(targetPos,haveUserTarget,targetUnit) && !CobBlockShot(targetUnit)) { if(weaponDef->stockpile){ const int oldCount = numStockpiled; numStockpiled--; owner->commandAI->StockpileChanged(this); eventHandler.StockpileChanged(owner, this, oldCount); } else { owner->UseEnergy(energyFireCost); owner->UseMetal(metalFireCost); owner->currentFuel = std::max(0.0f, owner->currentFuel - fuelUsage); } reloadStatus=gs->frameNum+(int)(reloadTime/owner->reloadSpeed); salvoLeft=salvoSize; nextSalvo=gs->frameNum; salvoError=gs->randVector()*(owner->isMoving?weaponDef->movingAccuracy:accuracy); if(targetType==Target_Pos || (targetType==Target_Unit && !(targetUnit->losStatus[owner->allyteam] & LOS_INLOS))) //area firing stuff is to effective at radar firing... salvoError*=1.3f; owner->lastMuzzleFlameSize=muzzleFlareSize; owner->lastMuzzleFlameDir=wantedDir; owner->cob->Call(COBFN_FirePrimary+weaponNum); } } else { // FIXME -- never reached? if (TryTarget(targetPos,haveUserTarget,targetUnit) && !weaponDef->stockpile) { // update the energy and metal required counts const int minPeriod = std::max(1, (int)(reloadTime / owner->reloadSpeed)); const float averageFactor = 1.0f / (float)minPeriod; gs->Team(owner->team)->energyPull += averageFactor * energyFireCost; gs->Team(owner->team)->metalPull += averageFactor * metalFireCost; } } } if(salvoLeft && nextSalvo<=gs->frameNum ){ salvoLeft--; nextSalvo=gs->frameNum+salvoDelay; owner->lastFireWeapon=gs->frameNum; int projectiles = projectilesPerShot; while(projectiles > 0) { --projectiles; // add to the commandShotCount if this is the last salvo, // and it is being directed towards the current target // (helps when deciding if a queued ground attack order has been completed) if ((salvoLeft == 0) && (owner->commandShotCount >= 0) && ((targetType == Target_Pos) && (targetPos == owner->userAttackPos)) || ((targetType == Target_Unit) && (targetUnit == owner->userTarget))) { owner->commandShotCount++; } std::vector<int> args; args.push_back(0); owner->cob->Call(COBFN_Shot+weaponNum,0); owner->cob->Call(COBFN_AimFromPrimary+weaponNum,args); relWeaponPos=owner->localmodel->GetPiecePos(args[0]); owner->cob->Call(/*COBFN_AimFromPrimary+weaponNum*/COBFN_QueryPrimary+weaponNum/**/,args); owner->localmodel->GetEmitDirPos(args[0], relWeaponMuzzlePos, weaponDir); weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x; weaponMuzzlePos=owner->pos+owner->frontdir*relWeaponMuzzlePos.z+owner->updir*relWeaponMuzzlePos.y+owner->rightdir*relWeaponMuzzlePos.x; weaponDir = owner->frontdir * weaponDir.z + owner->updir * weaponDir.y + owner->rightdir * weaponDir.x; weaponDir.Normalize(); // logOutput.Print("RelPosFire %f %f %f",relWeaponPos.x,relWeaponPos.y,relWeaponPos.z); if (owner->unitDef->decloakOnFire && (owner->scriptCloak <= 2)) { if (owner->isCloaked) { owner->isCloaked = false; eventHandler.UnitDecloaked(owner); } owner->curCloakTimeout = gs->frameNum + owner->cloakTimeout; } Fire(); } //Rock the unit in the direction of the fireing float3 rockDir = wantedDir; rockDir.y = 0; rockDir = -rockDir.Normalize(); std::vector<int> rockAngles; rockAngles.push_back((int)(500 * rockDir.z)); rockAngles.push_back((int)(500 * rockDir.x)); owner->cob->Call(COBFN_RockUnit, rockAngles); owner->commandAI->WeaponFired(this); if(salvoLeft==0){ owner->cob->Call(COBFN_EndBurst+weaponNum); } #ifdef TRACE_SYNC tracefile << "Weapon fire: "; tracefile << weaponPos.x << " " << weaponPos.y << " " << weaponPos.z << " " << targetPos.x << " " << targetPos.y << " " << targetPos.z << "\n"; #endif } }
void CWeapon::Update() { if(hasCloseTarget){ std::vector<int> args; args.push_back(0); if(useWeaponPosForAim){ owner->animator->AnimAction(ANIMFN_QueryPrimary+weaponNum,args); } else { owner->animator->AnimAction(ANIMFN_AimFromPrimary+weaponNum,args); } relWeaponPos=owner->localmodel->GetPiecePos(args[0]); } if(targetType==Target_Unit){ if(lastErrorVectorUpdate<gs->frameNum-16){ float3 newErrorVector(gs->randVector()); errorVectorAdd=(newErrorVector-errorVector)*(1.0f/16.0f); lastErrorVectorUpdate=gs->frameNum; } errorVector+=errorVectorAdd; if (predict > 50000) { /* to prevent runaway prediction (happens sometimes when a missile is moving *away* from it's target), we may need to disable missiles in case they fly around too long */ predict = 50000; } if(weaponDef->selfExplode){ //assumes that only flakker like units that need to hit aircrafts has this,change to a separate tag later targetPos=helper->GetUnitErrorPos(targetUnit,owner->allyteam)+targetUnit->speed*(0.5+predictSpeedMod*0.5)*predict; } else { targetPos=helper->GetUnitErrorPos(targetUnit,owner->allyteam)+targetUnit->speed*predictSpeedMod*predict; } targetPos+=errorVector*(weaponDef->targetMoveError*30*targetUnit->speed.Length()*(1.0-owner->limExperience)); float appHeight=ground->GetApproximateHeight(targetPos.x,targetPos.z)+2; if(targetPos.y < appHeight) targetPos.y=appHeight; if(!weaponDef->waterweapon && targetPos.y<1) targetPos.y=1; } if(weaponDef->interceptor) CheckIntercept(); if(targetType!=Target_None){ if(onlyForward){ float3 goaldir=targetPos-owner->pos; goaldir.Normalize(); angleGood=owner->frontdir.dot(goaldir) > maxAngleDif; } else if(lastRequestedDir.dot(wantedDir)<maxAngleDif || lastRequest+15<gs->frameNum){ angleGood=false; lastRequestedDir=wantedDir; lastRequest=gs->frameNum; short int heading=GetHeadingFromVector(wantedDir.x,wantedDir.z); short int pitch=(short int) (asin(wantedDir.dot(owner->updir))*(32768/PI)); std::vector<int> args; args.push_back(short(heading-owner->heading)); args.push_back(pitch); owner->animator->AnimAction(ANIMFN_AimPrimary+weaponNum,args,ScriptCallback,this,0); } } if(weaponDef->stockpile && numStockpileQued){ float p=1.0/reloadTime; if(gs->Team(owner->team)->metal>=metalFireCost*p && gs->Team(owner->team)->energy>=energyFireCost*p){ owner->UseEnergy(energyFireCost*p); owner->UseMetal(metalFireCost*p); buildPercent+=p; } else { // update the energy and metal required counts gs->Team(owner->team)->energyPullAmount += energyFireCost*p; gs->Team(owner->team)->metalPullAmount += metalFireCost*p; } if(buildPercent>=1){ buildPercent=0; numStockpileQued--; numStockpiled++; owner->commandAI->StockpileChanged(this); } } if(salvoLeft==0 #ifdef DIRECT_CONTROL_ALLOWED && (!owner->directControl || owner->directControl->mouse1 || owner->directControl->mouse2) #endif && targetType!=Target_None && angleGood && subClassReady && reloadStatus<=gs->frameNum && (!weaponDef->stockpile || numStockpiled) && (weaponDef->waterweapon || weaponPos.y>0) && (owner->unitDef->maxFuel==0 || owner->currentFuel > 0) ){ if ((weaponDef->stockpile || (gs->Team(owner->team)->metal>=metalFireCost && gs->Team(owner->team)->energy>=energyFireCost))) { std::vector<int> args; args.push_back(0); owner->animator->AnimAction(ANIMFN_QueryPrimary+weaponNum,args); relWeaponPos=owner->localmodel->GetPiecePos(args[0]); weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x; useWeaponPosForAim=reloadTime/16+8; if(TryTarget(targetPos,haveUserTarget,targetUnit)){ if(weaponDef->stockpile){ numStockpiled--; owner->commandAI->StockpileChanged(this); } else { owner->UseEnergy(energyFireCost); owner->UseMetal(metalFireCost); owner->currentFuel = max(0.0f, owner->currentFuel - fuelUsage); } if(weaponDef->stockpile) reloadStatus=gs->frameNum+60; else reloadStatus=gs->frameNum+(int)(reloadTime/owner->reloadSpeed); salvoLeft=salvoSize; nextSalvo=gs->frameNum; salvoError=gs->randVector()*(owner->isMoving?weaponDef->movingAccuracy:accuracy); if(targetType==Target_Pos || (targetType==Target_Unit && !(targetUnit->losStatus[owner->allyteam] & LOS_INLOS))) //area firing stuff is to effective at radar firing... salvoError*=1.3; owner->lastMuzzleFlameSize=muzzleFlareSize; owner->lastMuzzleFlameDir=wantedDir; owner->animator->AnimAction(ANIMFN_FirePrimary+weaponNum); } } else { if (TryTarget(targetPos,haveUserTarget,targetUnit) && !weaponDef->stockpile) { // update the energy and metal required counts const int minPeriod = max(1, (int)(reloadTime / owner->reloadSpeed)); const float averageFactor = 1.0f / (float)minPeriod; gs->Team(owner->team)->energyPullAmount += averageFactor * energyFireCost; gs->Team(owner->team)->metalPullAmount += averageFactor * metalFireCost; } } } if(salvoLeft && nextSalvo<=gs->frameNum){ salvoLeft--; nextSalvo=gs->frameNum+salvoDelay; owner->lastFireWeapon=gs->frameNum; // add a shot count if it helps to end the current 'attack ground' command // (the 'salvoLeft' check is done because the positions may already have been adjusted) if ((salvoLeft == (salvoSize - 1)) && ((targetType == Target_Pos) && (targetPos == owner->userAttackPos)) || ((targetType == Target_Unit) && (targetUnit == owner->userTarget))) { owner->commandShotCount++; } std::vector<int> args; args.push_back(0); owner->animator->AnimAction(/*ANIMFN_AimFromPrimary+weaponNum/*/ANIMFN_QueryPrimary+weaponNum/**/,args); relWeaponPos=owner->localmodel->GetPiecePos(args[0]); weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x; // info->AddLine("RelPosFire %f %f %f",relWeaponPos.x,relWeaponPos.y,relWeaponPos.z); owner->isCloaked=false; owner->curCloakTimeout=gs->frameNum+owner->cloakTimeout; Fire(); //Rock the unit in the direction of the fireing float3 rockDir = wantedDir; rockDir.y = 0; rockDir = -rockDir.Normalize(); std::vector<int> rockAngles; rockAngles.push_back((int)(500 * rockDir.z)); rockAngles.push_back((int)(500 * rockDir.x)); owner->animator->AnimAction(ANIMFN_RockUnit, rockAngles); owner->commandAI->WeaponFired(this); if(salvoLeft==0){ owner->animator->AnimAction(ANIMFN_EndBurst+weaponNum); } #ifdef TRACE_SYNC tracefile << "Weapon fire: "; tracefile << weaponPos.x << " " << weaponPos.y << " " << weaponPos.z << " " << targetPos.x << " " << targetPos.y << " " << targetPos.z << "\n"; #endif } }