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 } }