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 + -
显示快捷键?