Example #1
0
void CWeaponProjectile::Explode(
	CUnit* hitUnit,
	CFeature* hitFeature,
	float3 impactPos,
	float3 impactDir
) {
	const DamageArray& damageArray = damages->GetDynamicDamages(startPos, impactPos);
	const CExplosionParams params = {
		impactPos,
		impactDir.SafeNormalize(),
		damageArray,
		weaponDef,
		owner(),
		hitUnit,
		hitFeature,
		damages->craterAreaOfEffect,
		damages->damageAreaOfEffect,
		damages->edgeEffectiveness,
		damages->explosionSpeed,
		weaponDef->noExplode? 0.3f: 1.0f,                 // gfxMod
		weaponDef->impactOnly,
		weaponDef->noExplode || weaponDef->noSelfDamage,  // ignoreOwner
		true,                                             // damgeGround
		static_cast<unsigned int>(id)
	};

	helper->Explosion(params);

	if (!weaponDef->noExplode || TraveledRange()) {
		// remove ourselves from the simulation (otherwise
		// keep traveling and generating more explosions)
		CProjectile::Collision();
	}
}
Example #2
0
bool CWeapon::AdjustTargetVectorLength(
	CUnit* targetUnit,
	float3& targetPos,
	float3& targetVec,
	float3& targetDir)
const {
	bool retCode = false;
	const float tbScale = math::fabsf(targetBorder);

	CollisionVolume* cvOld = targetUnit->collisionVolume;
	CollisionVolume  cvNew = CollisionVolume(targetUnit->collisionVolume);
	CollisionQuery   cq;

	// test for "collision" with a temporarily volume
	// (scaled uniformly by the absolute target-border
	// factor)
	cvNew.RescaleAxes(tbScale, tbScale, tbScale);
	cvNew.SetTestType(CollisionVolume::COLVOL_HITTEST_DISC);

	targetUnit->collisionVolume = &cvNew;

	if (CCollisionHandler::DetectHit(targetUnit, weaponMuzzlePos, ZeroVector, NULL)) {
		// our weapon muzzle is inside the target unit's volume; this
		// means we do not need to make any adjustments to targetVec
		targetVec = ZeroVector;
	} else {
		targetDir.SafeNormalize();

		// otherwise, perform a raytrace to find the proper length correction
		// factor for non-spherical coldet volumes based on the ray's ingress
		// (for positive TB values) or egress (for negative TB values) position;
		// this either increases or decreases the length of <targetVec> but does
		// not change its direction
		cvNew.SetTestType(CollisionVolume::COLVOL_HITTEST_CONT);

		// make the ray-segment long enough so it can reach the far side of the
		// scaled collision volume (helps to ensure a ray-intersection is found)
		//
		// note: ray-intersection is NOT guaranteed if the volume itself has a
		// non-zero offset, since here we are "shooting" at the target UNIT's
		// midpoint
		const float3 targetOffset = targetDir * (cvNew.GetBoundingRadius() * 2.0f);
		const float3 targetRayPos = targetPos + targetOffset;

		if (CCollisionHandler::DetectHit(targetUnit, weaponMuzzlePos, targetRayPos, &cq)) {
			if (targetBorder > 0.0f) { targetVec -= (targetDir * ((targetPos - cq.p0).Length())); }
			if (targetBorder < 0.0f) { targetVec += (targetDir * ((cq.p1 - targetPos).Length())); }
		}

		retCode = true;
	}

	targetUnit->collisionVolume = cvOld;

	// true indicates we took the else-branch and targetDir is now normalized
	return retCode;
}
Example #3
0
void CBeamLaser::FireInternal(float3 dir, bool sweepFire)
{
	// fix negative damage when hitting big spheres
	float actualRange = range;
	float rangeMod = 1.0f;

	if (dynamic_cast<CBuilding*>(owner) == NULL) {
		// help units fire while chasing
		rangeMod = 1.3f;
	}

	if (owner->fpsControlPlayer != NULL) {
		rangeMod = 0.95f;
	}

	float maxLength = range * rangeMod;
	float curLength = 0.0f;

	float3 curPos = weaponMuzzlePos;
	float3 hitPos;

	dir +=
		((gs->randVector() * sprayAngle *
		(1.0f - owner->limExperience * weaponDef->ownerExpAccWeight)));
	dir.SafeNormalize();

	bool tryAgain = true;

	// increase range if targets are searched for in a cylinder
	if (cylinderTargetting > 0.01f) {
		const float verticalDist = owner->radius * cylinderTargetting * dir.y;
		const float maxLengthModSq = maxLength * maxLength + verticalDist * verticalDist;

		maxLength = math::sqrt(maxLengthModSq);
	}


	// increase range if targetting edge of hitsphere
	if (targetType == Target_Unit && targetUnit && targetBorder != 0) {
		maxLength += (targetUnit->radius * targetBorder);
	}


	// unit at the end of the beam
	CUnit* hitUnit = NULL;
	CFeature* hitFeature = NULL;
	CPlasmaRepulser* hitShield = NULL;

	for (int tries = 0; tries < 5 && tryAgain; ++tries) {
		tryAgain = false;

		float length = TraceRay::TraceRay(curPos, dir, maxLength - curLength, collisionFlags, owner, hitUnit, hitFeature);

		if (hitUnit && teamHandler->AlliedTeams(hitUnit->team, owner->team) && sweepFire) {
			// never damage friendlies with sweepfire
			lastFireFrame = 0;
			return;
		}

		float3 newDir;
		const float shieldLength = interceptHandler.AddShieldInterceptableBeam(this, curPos, dir, length, newDir, hitShield);

		if (shieldLength < length) {
			length = shieldLength;
			tryAgain = hitShield->BeamIntercepted(this, damageMul); // repulsed
		}

		hitPos = curPos + dir * length;

		const float baseAlpha  = weaponDef->intensity * 255.0f;
		const float startAlpha = (1.0f - (curLength         ) / (range * 1.3f)) * baseAlpha;
		const float endAlpha   = (1.0f - (curLength + length) / (range * 1.3f)) * baseAlpha;

		if (weaponDef->largeBeamLaser) {
			new CLargeBeamLaserProjectile(curPos, hitPos, color, weaponDef->visuals.color2, owner, weaponDef);
		} else {
			new CBeamLaserProjectile(curPos, hitPos, startAlpha, endAlpha, color, owner, weaponDef);
		}

		curPos = hitPos;
		curLength += length;
		dir = newDir;
	}

	if (hitUnit) {
		if (hitUnit->unitDef->usePieceCollisionVolumes) {
			// getting the actual piece here is probably overdoing it
			// TODO change this if we really need propper flanking bonus support
			// for beam-lasers
			hitUnit->SetLastAttackedPiece(hitUnit->localmodel->GetRoot(), gs->frameNum);
		}

		if (targetBorder > 0) {
			actualRange += hitUnit->radius * targetBorder;
		}
	}

	// make it possible to always hit with some minimal intensity (melee weapons have use for that)
	const float hitIntensity = std::max(minIntensity, 1.0f - (curLength) / (actualRange * 2));

	if (curLength < maxLength) {
		// Dynamic Damage
		DamageArray dynDamages;

		if (weaponDef->dynDamageExp > 0) {
			dynDamages = weaponDefHandler->DynamicDamages(
				weaponDef->damages,
				weaponMuzzlePos,
				curPos,
				weaponDef->dynDamageRange > 0?
					weaponDef->dynDamageRange:
					weaponDef->range,
				weaponDef->dynDamageExp,
				weaponDef->dynDamageMin,
				weaponDef->dynDamageInverted
			);
		}

		DamageArray damageArray = weaponDef->dynDamageExp > 0?
			dynDamages * (hitIntensity * damageMul):
			weaponDef->damages * (hitIntensity * damageMul);
		CGameHelper::ExplosionParams params = {
			hitPos,
			dir,
			damageArray,
			weaponDef,
			owner,
			hitUnit,
			hitFeature,
			areaOfEffect,
			weaponDef->edgeEffectiveness,
			weaponDef->explosionSpeed,
			1.0f,                                             // gfxMod
			weaponDef->impactOnly,
			weaponDef->noExplode || weaponDef->noSelfDamage,  // ignoreOwner
			true                                              // damageGround
		};

		helper->Explosion(params);
	}

	if (targetUnit) {
		lastFireFrame = gs->frameNum;
	}
}
Example #4
0
void CBeamLaser::FireInternal(float3 curDir)
{
	float actualRange = range;
	float rangeMod = 1.0f;

	if (!owner->unitDef->IsImmobileUnit()) {
		// help units fire while chasing
		rangeMod = 1.3f;
	}
	if (owner->UnderFirstPersonControl()) {
		rangeMod = 0.95f;
	}

	bool tryAgain = true;
	bool doDamage = true;

	float maxLength = range * rangeMod;
	float curLength = 0.0f;

	float3 curPos = weaponMuzzlePos;
	float3 hitPos;
	float3 newDir;

	// objects at the end of the beam
	CUnit* hitUnit = NULL;
	CFeature* hitFeature = NULL;
	CPlasmaRepulser* hitShield = NULL;
	CollisionQuery hitColQuery;

	if (!sweepFireState.IsSweepFiring()) {
		curDir += (gs->randVector() * SprayAngleExperience());
		curDir.SafeNormalize();

		// increase range if targets are searched for in a cylinder
		if (cylinderTargeting > 0.01f) {
			const float verticalDist = owner->radius * cylinderTargeting * curDir.y;
			const float maxLengthModSq = maxLength * maxLength + verticalDist * verticalDist;

			maxLength = math::sqrt(maxLengthModSq);
		}

		// adjust range if targetting edge of hitsphere
		if (targetType == Target_Unit && targetUnit != NULL && targetBorder != 0.0f) {
			maxLength += (targetUnit->radius * targetBorder);
		}
	} else {
		// restrict the range when sweeping
		maxLength = std::min(maxLength, sweepFireState.GetTargetDist3D() * 1.125f);
	}

	for (int tries = 0; tries < 5 && tryAgain; ++tries) {
		float beamLength = TraceRay::TraceRay(curPos, curDir, maxLength - curLength, collisionFlags, owner, hitUnit, hitFeature, &hitColQuery);

		if (hitUnit != NULL && teamHandler->AlliedTeams(hitUnit->team, owner->team)) {
			if (sweepFireState.IsSweepFiring() && !sweepFireState.DamageAllies()) {
				doDamage = false; break;
			}
		}

		if (!weaponDef->waterweapon) {
			// terminate beam at water surface if necessary
			if ((curDir.y < 0.0f) && ((curPos.y + curDir.y * beamLength) <= 0.0f)) {
				beamLength = curPos.y / -curDir.y;
			}
		}

		// if the beam gets intercepted, this modifies newDir
		//
		// we do more than one trace-iteration and set dir to
		// newDir only in the case there is a shield in our way
		const float shieldLength = interceptHandler.AddShieldInterceptableBeam(this, curPos, curDir, beamLength, newDir, hitShield);

		if (shieldLength < beamLength) {
			beamLength = shieldLength;
			tryAgain = hitShield->BeamIntercepted(this, salvoDamageMult);
		} else {
			tryAgain = false;
		}

		// same as hitColQuery.GetHitPos() if no water or shield in way
		hitPos = curPos + curDir * beamLength;

		{
			const float baseAlpha  = weaponDef->intensity * 255.0f;
			const float startAlpha = (1.0f - (curLength             ) / maxLength);
			const float endAlpha   = (1.0f - (curLength + beamLength) / maxLength);

			ProjectileParams pparams = GetProjectileParams();
			pparams.pos = curPos;
			pparams.end = hitPos;
			pparams.ttl = weaponDef->beamLaserTTL;
			pparams.startAlpha = Clamp(startAlpha * baseAlpha, 0.0f, 255.0f);
			pparams.endAlpha = Clamp(endAlpha * baseAlpha, 0.0f, 255.0f);

			WeaponProjectileFactory::LoadProjectile(pparams);
		}

		curPos = hitPos;
		curDir = newDir;
		curLength += beamLength;
	}

	if (!doDamage)
		return;

	if (hitUnit != NULL) {
		hitUnit->SetLastAttackedPiece(hitColQuery.GetHitPiece(), gs->frameNum);

		if (targetBorder > 0.0f) {
			actualRange += (hitUnit->radius * targetBorder);
		}
	}

	if (curLength < maxLength) {
		const DamageArray& baseDamages = (weaponDef->dynDamageExp <= 0.0f)?
			weaponDef->damages:
			weaponDefHandler->DynamicDamages(
				weaponDef->damages,
				weaponMuzzlePos,
				curPos,
				(weaponDef->dynDamageRange > 0.0f)?
					weaponDef->dynDamageRange:
					weaponDef->range,
				weaponDef->dynDamageExp,
				weaponDef->dynDamageMin,
				weaponDef->dynDamageInverted
			);

		// make it possible to always hit with some minimal intensity (melee weapons have use for that)
		const float hitIntensity = std::max(minIntensity, 1.0f - curLength / (actualRange * 2.0f));

		const DamageArray damages = baseDamages * (hitIntensity * salvoDamageMult);
		const CGameHelper::ExplosionParams params = {
			hitPos,
			curDir,
			damages,
			weaponDef,
			owner,
			hitUnit,
			hitFeature,
			craterAreaOfEffect,
			damageAreaOfEffect,
			weaponDef->edgeEffectiveness,
			weaponDef->explosionSpeed,
			1.0f,                                             // gfxMod
			weaponDef->impactOnly,
			weaponDef->noExplode || weaponDef->noSelfDamage,  // ignoreOwner
			true,                                             // damageGround
			-1u                                               // projectileID
		};

		helper->Explosion(params);
	}
}