weapon.cpp

来自「这是整套横扫千军3D版游戏的源码」· C++ 代码 · 共 894 行 · 第 1/2 页

CPP
894
字号
// Weapon.cpp: implementation of the CWeapon class.
//
//////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "creg/STL_List.h"
#include "float3.h"
#include "Game/Camera.h"
#include "Game/GameHelper.h"
#include "Game/Player.h"
#include "Game/Team.h"
#include "LogOutput.h"
#include "Lua/LuaCallInHandler.h"
#include "Map/Ground.h"
#include "myMath.h"
#include "Rendering/UnitModels/3DOParser.h"
#include "Sim/Misc/GeometricObjects.h"
#include "Sim/Misc/InterceptHandler.h"
#include "Sim/Misc/LosHandler.h"
#include "Sim/ModInfo.h"
#include "Sim/MoveTypes/TAAirMoveType.h"
#include "Sim/Projectiles/WeaponProjectiles/WeaponProjectile.h"
#include "Sim/Units/COB/CobFile.h"
#include "Sim/Units/COB/CobInstance.h"
#include "Sim/Units/CommandAI/CommandAI.h"
#include "Sim/Units/Unit.h"
#include "Sync/SyncTracer.h"
#include "WeaponDefHandler.h"
#include "Weapon.h"
#include "mmgr.h"

CR_BIND_DERIVED(CWeapon, CObject, (NULL));

CR_REG_METADATA(CWeapon,(
	CR_MEMBER(owner),
	CR_MEMBER(range),
	CR_MEMBER(heightMod),
	CR_MEMBER(reloadTime),
	CR_MEMBER(reloadStatus),
	CR_MEMBER(salvoLeft),
	CR_MEMBER(salvoDelay),
	CR_MEMBER(salvoSize),
	CR_MEMBER(nextSalvo),
	CR_MEMBER(predict),
	CR_MEMBER(targetUnit),
	CR_MEMBER(accuracy),
	CR_MEMBER(projectileSpeed),
	CR_MEMBER(predictSpeedMod),
	CR_MEMBER(metalFireCost),
	CR_MEMBER(energyFireCost),
	CR_MEMBER(targetPos),
	CR_MEMBER(fireSoundId),
	CR_MEMBER(fireSoundVolume),
	CR_MEMBER(cobHasBlockShot),
	CR_MEMBER(hasTargetWeight),
	CR_MEMBER(angleGood),
	CR_MEMBER(avoidTarget),
	CR_MEMBER(maxAngleDif),
	CR_MEMBER(wantedDir),
	CR_MEMBER(lastRequestedDir),
	CR_MEMBER(haveUserTarget),
	CR_MEMBER(subClassReady),
	CR_MEMBER(onlyForward),
	CR_MEMBER(weaponPos),
	CR_MEMBER(weaponMuzzlePos),
	CR_MEMBER(weaponDir),
	CR_MEMBER(lastRequest),
	CR_MEMBER(relWeaponPos),
	CR_MEMBER(relWeaponMuzzlePos),
	CR_MEMBER(muzzleFlareSize),
	CR_MEMBER(lastTargetRetry),
	CR_MEMBER(areaOfEffect),
	CR_MEMBER(badTargetCategory),
	CR_MEMBER(onlyTargetCategory),
	CR_MEMBER(incoming),
//	CR_MEMBER(weaponDef),
	CR_MEMBER(buildPercent),
	CR_MEMBER(numStockpiled),
	CR_MEMBER(numStockpileQued),
	CR_MEMBER(interceptTarget),
	CR_MEMBER(salvoError),
	CR_ENUM_MEMBER(targetType),
	CR_MEMBER(sprayangle),
	CR_MEMBER(useWeaponPosForAim),
	CR_MEMBER(errorVector),
	CR_MEMBER(errorVectorAdd),
	CR_MEMBER(lastErrorVectorUpdate),
	CR_MEMBER(slavedTo),
	CR_MEMBER(mainDir),
	CR_MEMBER(maxMainDirAngleDif),
	CR_MEMBER(hasCloseTarget),
	CR_MEMBER(avoidFriendly),
	CR_MEMBER(avoidFeature),
	CR_MEMBER(targetBorder),
	CR_MEMBER(cylinderTargetting),
	CR_MEMBER(minIntensity),
	CR_MEMBER(heightBoostFactor),
	CR_MEMBER(collisionFlags),
	CR_MEMBER(fuelUsage),
	CR_MEMBER(weaponNum),
	CR_RESERVED(64)
	));

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

static void ScriptCallback(int retCode,void* p1,void* p2)
{
	if(retCode==1)
		((CWeapon*)p1)->ScriptReady();
}

CWeapon::CWeapon(CUnit* owner)
:	targetType(Target_None),
	owner(owner),
	range(1),
	heightMod(0),
	reloadTime(1),
	reloadStatus(0),
	salvoLeft(0),
	salvoDelay(0),
	salvoSize(1),
	nextSalvo(0),
	predict(0),
	targetUnit(0),
	accuracy(0),
	projectileSpeed(1),
	predictSpeedMod(1),
	metalFireCost(0),
	energyFireCost(0),
	targetPos(1,1,1),
	fireSoundId(0),
	fireSoundVolume(0),
	cobHasBlockShot(false),
	hasTargetWeight(false),
	angleGood(false),
	avoidTarget(false),
	maxAngleDif(0),
	wantedDir(0,1,0),
	lastRequestedDir(0,-1,0),
	haveUserTarget(false),
	subClassReady(true),
	onlyForward(false),
	weaponPos(0,0,0),
	weaponMuzzlePos(0,0,0),
	weaponDir(0,0,0),
	lastRequest(0),
	relWeaponPos(0,1,0),
	relWeaponMuzzlePos(0,1,0),
	muzzleFlareSize(1),
	lastTargetRetry(-100),
	areaOfEffect(1),
	badTargetCategory(0),
	onlyTargetCategory(0xffffffff),
	weaponDef(0),
	buildPercent(0),
	numStockpiled(0),
	numStockpileQued(0),
	interceptTarget(0),
	salvoError(0,0,0),
	sprayangle(0),
	useWeaponPosForAim(0),
	errorVector(ZeroVector),
	errorVectorAdd(ZeroVector),
	lastErrorVectorUpdate(0),
	slavedTo(0),
	mainDir(0,0,1),
	maxMainDirAngleDif(-1),
	hasCloseTarget(false),
	avoidFriendly(true),
	avoidFeature(true),
	targetBorder(0.f),
	cylinderTargetting(0.f),
	minIntensity(0.f),
	heightBoostFactor(-1.f),
	collisionFlags(0),
	fuelUsage(0)
{
}


CWeapon::~CWeapon()
{
	if(weaponDef->interceptor)
		interceptHandler.RemoveInterceptorWeapon(this);
}


void CWeapon::SetWeaponNum(int num)
{
	weaponNum = num;

	cobHasBlockShot = owner->cob->FunctionExist(COBFN_BlockShot + weaponNum);
	hasTargetWeight = owner->cob->FunctionExist(COBFN_TargetWeight + weaponNum);
}


inline bool CWeapon::CobBlockShot(const CUnit* targetUnit)
{
	if (!cobHasBlockShot) {
		return false;
	}


	const int unitID = targetUnit ? targetUnit->id : 0;

	std::vector<int> args;

	args.push_back(unitID);
	args.push_back(0); // arg[1], for the return value
	                   // the default is to not block the shot

	owner->cob->Call(COBFN_BlockShot + weaponNum, args);

	return !!args[1];
}


float CWeapon::TargetWeight(const CUnit* targetUnit) const
{
	const int unitID = targetUnit ? targetUnit->id : 0;

	std::vector<int> args;

	args.push_back(unitID);
	args.push_back(COBSCALE); // arg[1], for the return value
	                          // the default is 1.0

	owner->cob->Call(COBFN_TargetWeight + weaponNum, args);

	return (float)args[1] / (float)COBSCALE;
}


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();
		}

		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)
			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.0f/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)->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);
			luaCallIns.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);
					luaCallIns.StockpileChanged(owner, this, oldCount);
				} 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.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 = 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 = weaponDef->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;
					luaCallIns.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){

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?