Пример #1
0
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;
}
Пример #2
0
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;
}
Пример #3
0
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;
}
Пример #4
0
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);
}
Пример #5
0
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;

}
Пример #6
0
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;
		}
	}
}
Пример #7
0
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
	}
}
Пример #8
0
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
    }
}
Пример #9
0
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;
        }
    }
}
Пример #10
0
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;
		}
	}
}
Пример #11
0
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
	}
}
Пример #12
0
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
	}
}