📄 unit.cpp
字号:
void CUnit::SlowUpdate()
{
--nextPosErrorUpdate;
if (nextPosErrorUpdate == 0) {
float3 newPosError(gs->randVector());
newPosError.y *= 0.2f;
if (posErrorVector.dot(newPosError) < 0.0f) {
newPosError = -newPosError;
}
posErrorDelta = (newPosError - posErrorVector) * (1.0f / 256.0f);
nextPosErrorUpdate = 16;
}
for (int a = 0; a < gs->activeAllyTeams; ++a) {
const int prevLosStatus = losStatus[a];
if (prevLosStatus & LOS_INTEAM) {
continue; // allied, no need to update
}
else if (loshandler->InLos(this, a)) {
if (!(prevLosStatus & LOS_INLOS)) {
if (beingBuilt) {
losStatus[a] |= (LOS_INLOS | LOS_INRADAR);
} else {
losStatus[a] |= (LOS_INLOS | LOS_INRADAR | LOS_PREVLOS | LOS_CONTRADAR);
}
if (!(prevLosStatus & LOS_INRADAR)) {
luaCallIns.UnitEnteredRadar(this, a);
globalAI->UnitEnteredRadar(this, a);
}
luaCallIns.UnitEnteredLos(this, a);
globalAI->UnitEnteredLos(this, a);
}
}
else if (radarhandler->InRadar(this, a)) {
if ((prevLosStatus & LOS_INLOS)) {
luaCallIns.UnitLeftLos(this, a);
globalAI->UnitLeftLos(this, a);
losStatus[a] &= ~LOS_INLOS;
}
else if (!(prevLosStatus & LOS_INRADAR)) {
luaCallIns.UnitEnteredRadar(this, a);
globalAI->UnitEnteredRadar(this, a);
losStatus[a] |= LOS_INRADAR;
}
}
else {
if ((prevLosStatus & LOS_INRADAR)) {
if ((prevLosStatus & LOS_INLOS)) {
luaCallIns.UnitLeftLos(this, a);
luaCallIns.UnitLeftRadar(this, a);
globalAI->UnitLeftLos(this, a);
globalAI->UnitLeftRadar(this, a);
} else {
luaCallIns.UnitLeftRadar(this, a);
globalAI->UnitLeftRadar(this, a);
}
losStatus[a] &= ~(LOS_INLOS | LOS_INRADAR | LOS_CONTRADAR);
}
}
}
if (paralyzeDamage > 0) {
paralyzeDamage -= maxHealth * (16.0f / 30.0f / 40.0f);
if (paralyzeDamage < 0) {
paralyzeDamage = 0;
}
}
if (stunned) {
// de-stun only if we are not (still) inside a non-firebase transport
if (paralyzeDamage < health && !(transporter && !transporter->unitDef->isfireplatform) ) {
stunned = false;
}
const bool oldCloak = isCloaked;
if (!isDead && (scriptCloak >= 4)) {
isCloaked = true;
} else {
isCloaked = false;
}
if (oldCloak != isCloaked) {
if (isCloaked) {
luaCallIns.UnitCloaked(this);
} else {
luaCallIns.UnitDecloaked(this);
}
}
UpdateResources();
return;
}
if (selfDCountdown && !stunned) {
selfDCountdown--;
if (selfDCountdown <= 1) {
if (!beingBuilt) {
KillUnit(true, false, NULL);
} else {
KillUnit(false, true, NULL); //avoid unfinished buildings making an explosion
}
selfDCountdown = 0;
return;
}
ENTER_MIXED;
if ((selfDCountdown & 1) && (team == gu->myTeam)) {
logOutput.Print("%s: Self destruct in %i s",
unitDef->humanName.c_str(), selfDCountdown / 2);
}
ENTER_SYNCED;
}
if (beingBuilt) {
if (modInfo.constructionDecay && lastNanoAdd < gs->frameNum - modInfo.constructionDecayTime) {
const float buildDecay = 1.0f / (buildTime * modInfo.constructionDecaySpeed);
health -= maxHealth * buildDecay;
buildProgress -= buildDecay;
AddMetal(metalCost * buildDecay);
if (health < 0.0f) {
KillUnit(false, true, NULL);
}
UpdateResources();
}
return;
}
//below is stuff that shouldnt be run while being built
lastSlowUpdate=gs->frameNum;
commandAI->SlowUpdate();
moveType->SlowUpdate();
UpdateResources();
// FIXME: scriptMakeMetal ...?
AddMetal(uncondMakeMetal);
AddEnergy(uncondMakeEnergy);
UseMetal(uncondUseMetal);
UseEnergy(uncondUseEnergy);
if (activated) {
if (UseMetal(condUseMetal)) {
AddEnergy(condMakeEnergy);
}
if (UseEnergy(condUseEnergy)) {
AddMetal(condMakeMetal);
}
}
AddMetal(unitDef->metalMake*0.5f);
if (activated) {
if (UseEnergy(unitDef->energyUpkeep * 0.5f)) {
if (unitDef->isMetalMaker) {
AddMetal(unitDef->makesMetal * 0.5f * uh->metalMakerEfficiency);
uh->metalMakerIncome += unitDef->makesMetal;
} else {
AddMetal(unitDef->makesMetal * 0.5f);
}
if (unitDef->extractsMetal > 0.0f) {
AddMetal(metalExtract * 0.5f);
}
}
UseMetal(unitDef->metalUpkeep * 0.5f);
if (unitDef->windGenerator > 0.0f) {
if (wind.GetCurrentStrength() > unitDef->windGenerator) {
AddEnergy(unitDef->windGenerator * 0.5f);
} else {
AddEnergy(wind.GetCurrentStrength() * 0.5f);
}
}
}
AddEnergy(energyTickMake * 0.5f);
if (health<maxHealth) {
health += unitDef->autoHeal;
if (restTime > unitDef->idleTime) {
health += unitDef->idleAutoHeal;
}
if (health > maxHealth) {
health = maxHealth;
}
}
residualImpulse *= 0.6f;
const bool oldCloak = isCloaked;
if (scriptCloak >= 3) {
isCloaked = true;
}
else if (wantCloak || (scriptCloak >= 1)) {
if ((decloakDistance > 0.0f) &&
helper->GetClosestEnemyUnitNoLosTest(midPos, decloakDistance,
allyteam, unitDef->decloakSpherical)) {
curCloakTimeout = gs->frameNum + cloakTimeout;
isCloaked = false;
}
if (isCloaked || (gs->frameNum >= curCloakTimeout)) {
if (scriptCloak >= 2) {
isCloaked = true;
}
else {
float cloakCost = unitDef->cloakCost;
if (speed.SqLength() > 0.2f) {
cloakCost = unitDef->cloakCostMoving;
}
if (UseEnergy(cloakCost * 0.5f)) {
isCloaked = true;
} else {
isCloaked = false;
}
}
} else {
isCloaked = false;
}
} else {
isCloaked = false;
}
if (oldCloak != isCloaked) {
if (isCloaked) {
luaCallIns.UnitCloaked(this);
} else {
luaCallIns.UnitDecloaked(this);
}
}
if (uh->waterDamage) {
bool inWater = (pos.y <= -3);
bool isFloating = (physicalState == CSolidObject::Floating);
bool onGround = (physicalState == CSolidObject::OnGround);
bool waterSquare = (readmap->mipHeightmap[1][int((pos.z / (SQUARE_SIZE * 2)) * gs->hmapx + (pos.x / (SQUARE_SIZE * 2)))] < -1);
// old: "floating or (on ground and height < -3 and mapheight < -1)"
// new: "height < -3 and (floating or on ground) and mapheight < -1"
if (inWater && (isFloating || onGround) && waterSquare) {
DoDamage(DamageArray() * uh->waterDamage, 0, ZeroVector, -1);
}
}
if (unitDef->canKamikaze) {
if (fireState >= 2) {
CUnit* u = helper->GetClosestEnemyUnitNoLosTest(pos, unitDef->kamikazeDist, allyteam, false);
if (u && u->physicalState != CSolidObject::Flying && u->speed.dot(pos - u->pos) <= 0) {
// self destruct when unit start moving away from mine, should maximize damage
KillUnit(true, false, NULL);
}
}
if(userTarget && userTarget->pos.distance(pos)<unitDef->kamikazeDist)
KillUnit(true, false, NULL);
if(userAttackGround && userAttackPos.distance(pos)<unitDef->kamikazeDist)
KillUnit(true, false, NULL);
}
if(!weapons.empty()){
haveTarget=false;
haveUserTarget=false;
// aircraft and ScriptMoveType do not want this
if (moveType->useHeading) {
SetDirectionFromHeading();
}
if(!dontFire){
for(vector<CWeapon*>::iterator wi=weapons.begin();wi!=weapons.end();++wi){
CWeapon* w=*wi;
if(userTarget && !w->haveUserTarget && (haveDGunRequest || !unitDef->canDGun || !w->weaponDef->manualfire))
w->AttackUnit(userTarget,true);
else if(userAttackGround && !w->haveUserTarget && (haveDGunRequest || !unitDef->canDGun || !w->weaponDef->manualfire))
w->AttackGround(userAttackPos,true);
w->SlowUpdate();
if(w->targetType==Target_None && fireState>0 && lastAttacker && lastAttack+200>gs->frameNum)
w->AttackUnit(lastAttacker,false);
}
}
}
if (moveType->progressState == CMoveType::Active) {
if (seismicSignature) {
DoSeismicPing((int)seismicSignature);
}
}
CalculateTerrainType();
UpdateTerrainType();
}
void CUnit::SetDirectionFromHeading(void)
{
frontdir=GetVectorFromHeading(heading);
if(transporter && transporter->unitDef->holdSteady) {
updir = transporter->updir;
rightdir=frontdir.cross(updir);
rightdir.Normalize();
frontdir=updir.cross(rightdir);
} else if(upright || !unitDef->canmove){
updir=UpVector;
rightdir=frontdir.cross(updir);
} else {
updir=ground->GetNormal(pos.x,pos.z);
rightdir=frontdir.cross(updir);
rightdir.Normalize();
frontdir=updir.cross(rightdir);
}
}
void CUnit::DoDamage(const DamageArray& damages, CUnit *attacker,const float3& impulse, int weaponId)
{
if (isDead) {
return;
}
residualImpulse += impulse / mass;
moveType->ImpulseAdded();
float damage = damages[armorType];
if (damage > 0.0f) {
if (attacker) {
SetLastAttacker(attacker);
if (flankingBonusMode) {
const float3 adir = (attacker->pos - pos).Normalize(); // FIXME -- not the impulse direction?
if (flankingBonusMode == 1) { // mode 1 = global coordinates, mobile
flankingBonusDir += adir * flankingBonusMobility;
flankingBonusDir.Normalize();
flankingBonusMobility = 0.0f;
damage *= flankingBonusAvgDamage - adir.dot(flankingBonusDir) * flankingBonusDifDamage;
}
else {
float3 adirRelative;
adirRelative.x = adir.dot(rightdir);
adirRelative.y = adir.dot(updir);
adirRelative.z = adir.dot(frontdir);
if (flankingBonusMode == 2) { // mode 2 = unit coordinates, mobile
flankingBonusDir += adirRelative * flankingBonusMobility;
flankingBonusDir.Normalize();
flankingBonusMobility = 0.0f;
}
// modes 2 and 3 both use this; 3 is unit coordinates, immobile
damage *= flankingBonusAvgDamage - adirRelative.dot(flankingBonusDir) * flankingBonusDifDamage;
}
}
}
damage *= curArmorMultiple;
restTime = 0; // bleeding != resting
}
float3 hitDir = impulse;
hitDir.y = 0.0f;
hitDir = -hitDir.Normalize();
std::vector<int> cobargs;
cobargs.push_back((int)(500 * hitDir.z));
cobargs.push_back((int)(500 * hitDir.x));
if (cob->FunctionExist(COBFN_HitByWeaponId)) {
if (weaponId != -1) {
cobargs.push_back(weaponDefHandler->weaponDefs[weaponId].tdfId);
} else {
cobargs.push_back(-1);
}
cobargs.push_back((int)(100 * damage));
weaponHitMod = 1.0f;
cob->Call(COBFN_HitByWeaponId, cobargs, hitByWeaponIdCallback, this, NULL);
damage = damage * weaponHitMod; // weaponHitMod gets set in callback function
}
else {
cob->Call(COBFN_HitByWeapon, cobargs);
}
float experienceMod = expMultiplier;
const int paralyzeTime = damages.paralyzeDamageTime;
if (paralyzeTime == 0) { // real damage
if (damage > 0.0f) {
// Dont log overkill damage (so dguns/nukes etc dont inflate values)
const float statsdamage = max(0.0f, min(maxHealth - health, damage));
if (attacker) {
gs->Team(attacker->team)->currentStats.damageDealt += statsdamage;
}
gs->Team(team)->currentStats.damageReceived += statsdamage;
health -= damage;
}
else { // healing
health -= damage;
if (health > maxHealth) {
health = maxHealth;
}
if (health > paralyzeDamage) {
stunned = false;
}
}
}
else { // paralyzation
experienceMod *= 0.1f; // reduced experience
if (damage > 0.0f) {
const float maxParaDmg = health + (maxHealth * 0.025f * (float)paralyzeTime);
if (paralyzeDamage >= maxParaDmg) {
experienceMod = 0.0f;
}
else {
if (stunned) {
experienceMod = 0.0f;
}
paralyzeDamage += damage;
if (paralyzeDamage > health) {
stunned = true;
}
paralyzeDamage = min(paralyzeDamage, maxParaDmg);
}
}
else { // paralyzation healing
if (paralyzeDamage <= 0.0f) {
experienceMod = 0.0f;
}
paralyzeDamage += damage;
if (paralyzeDamage < health) {
stunned = false;
if (paralyzeDamage < 0.0f) {
paralyzeDamage = 0.0f;
}
}
}
}
if (damage > 0.0f) {
recentDamage += damage;
if ((attacker != NULL) && !gs->Ally(allyteam, attacker->allyteam)) {
attacker->AddExperience(0.1f * experienceMod
* (power / attacker->power)
* (damage + min(0.0f, health)) / maxHealth);
ENTER_UNSYNCED;
const int warnFrame = (gs->frameNum - 100);
if ((team == gu->myTeam)
&& ((!unitDef->isCommander && (uh->lastDamageWarning < warnFrame)) ||
( unitDef->isCommander && (uh->lastCmdDamageWarning < warnFrame)))
&& !camera->InView(midPos, radius + 50) && !gu->spectatingFullView) {
logOutput.Print("%s is being attacked", unitDef->humanName.c_str());
logOutput.SetLastMsgPos(pos);
if (unitDef->isCommander || uh->lastDamageWarning + 150 < gs->frameNum) {
const int soundIdx = unitDef->sounds.underattack.getRandomIdx();
if (soundIdx >= 0) {
sound->PlaySample(
unitDef->sounds.underattack.getID(soundIdx),
unitDef->isCommander ? 4 : 2);
}
}
minimap->AddNotification(pos, float3(1.0f, 0.3f, 0.3f),
unitDef->isCommander ? 1.0f : 0.5f);
uh->lastDamageWarning = gs->frameNum;
if (unitDef->isCommander) {
uh->lastCmdDamageWarning = gs->frameNum;
}
}
ENTER_SYNCED;
}
}
luaCallIns.UnitDamaged(this, attacker, damage, weaponId, !!damages.paralyzeDamageTime);
globalAI->UnitDamaged(this, attacker, damage);
if (health <= 0.0f) {
KillUnit(false, false, attacker);
if (isDead && (attacker != 0) &&
!gs->Ally(allyteam, attacker->allyteam) && !beingBuilt) {
attacker->AddExperience(expMultiplier * 0.1f * (power / attacker->power));
gs->Team(attacker->team)->currentStats.unitsKilled++;
}
}
// if(attacker!=0 && attacker->team==team)
// logOutput.Print("FF by %i %s on %i %s",attacker->id,attacker->tooltip.c_str(),id,tooltip.c_str());
#ifdef TRACE_SYNC
tracefile << "Damage: ";
tracefile << id << " " << damage << "\n";
#endif
}
void CUnit::Kill(float3& impulse) {
DamageArray da;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -