⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 unit.cpp

📁 这是整套横扫千军3D版游戏的源码
💻 CPP
📖 第 1 页 / 共 5 页
字号:
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 + -