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){ //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->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::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 } }