Exemplo n.º 1
0
void CWeaponProjectile::UpdateInterception()
{
	if (target == NULL)
		return;

	CWeaponProjectile* po = dynamic_cast<CWeaponProjectile*>(target);

	if (po == NULL)
		return;

	// we are the interceptor, point us toward the interceptee pos each frame
	// (normally not needed, subclasses handle it directly in their Update()'s
	// *until* our owner dies)
	if (owner() == NULL) {
		targetPos = po->pos + po->speed;
	}

	if (hitscan) {
		if (ClosestPointOnLine(startPos, targetPos, po->pos).SqDistance(po->pos) < Square(weaponDef->collisionSize)) {
			po->Collision();
			Collision();
		}
	} else {
		// FIXME: if (pos.SqDistance(po->pos) < Square(weaponDef->collisionSize)) {
		if (pos.SqDistance(po->pos) < Square(damages->damageAreaOfEffect)) {
			po->Collision();
			Collision();
		}
	}
}
void CProjectileHandler::CheckShieldCollisions(
	CProjectile* p,
	std::vector<CPlasmaRepulser*>& tempRepulsers,
	const float3 ppos0,
	const float3 ppos1)
{
	if (!p->checkCol)
		return;

	if (!p->weapon)
		return;

	CWeaponProjectile* wpro = static_cast<CWeaponProjectile*>(p);
	const WeaponDef* wdef = wpro->GetWeaponDef();

	//Bail early
	if (wdef->interceptedByShieldType == 0)
		return;

	CollisionQuery cq;

	for (CPlasmaRepulser* repulser: tempRepulsers) {
		assert(repulser != nullptr);
		if (!repulser->CanIntercept(wdef->interceptedByShieldType, p->GetAllyteamID()))
			continue;

		if (CCollisionHandler::DetectHit(repulser->owner, &repulser->collisionVolume, repulser->owner->GetTransformMatrix(true), ppos0, ppos1, &cq)) {
			if (!cq.InsideHit() || !repulser->weaponDef->exteriorShield || repulser->IsRepulsing(wpro)) {
				if (repulser->IncomingProjectile(wpro))
					return;
			}
		}
	}
}
Exemplo n.º 3
0
void CProjectileHandler::CheckShieldCollisions(
	CProjectile* p,
	std::vector<CPlasmaRepulser*>& tempRepulsers,
	const float3 ppos0,
	const float3 ppos1)
{
	if (!p->checkCol)
		return;

	if (!p->weapon)
		return;

	CWeaponProjectile* wpro = static_cast<CWeaponProjectile*>(p);
	const WeaponDef* wdef = wpro->GetWeaponDef();

	//Bail early
	if (wdef->interceptedByShieldType == 0)
		return;

	CollisionQuery cq;

	for (CPlasmaRepulser* repulser: tempRepulsers) {
		assert(repulser != nullptr);
		if (!repulser->CanIntercept(wdef->interceptedByShieldType, p->GetAllyteamID()))
			continue;

		// we sometimes get false inside hits due to the movement of the shield
		// a very hacky solution is to increase the ray that's intersecting
		// by the last movement of the shield.
		// it's not 100% accurate so there's a bit of a FIXME here to do a real solution
		// (keep track in the projectile which shields it's in)

		const float3 effectivePPos0 = ppos0 + (ppos0 - ppos1) * repulser->deltaPos.Length();
		if (CCollisionHandler::DetectHit(repulser->owner, &repulser->collisionVolume, repulser->owner->GetTransformMatrix(true), effectivePPos0, ppos1, &cq)) {
			if (!cq.InsideHit() || !repulser->weaponDef->exteriorShield || repulser->IsRepulsing(wpro)) {
				if (repulser->IncomingProjectile(wpro, cq.GetHitPos()))
					return;
			}
		}
	}
}
Exemplo n.º 4
0
CWeaponProjectile::CWeaponProjectile(const ProjectileParams& params)
	: CProjectile(params.pos, params.speed, params.owner, true, true, false, false)

	, damages(nullptr)
	, weaponDef(params.weaponDef)
	, target(params.target)

	, ttl(params.ttl)
	, bounces(0)

	, targeted(false)

	, startPos(params.pos)
	, targetPos(params.end)
{
	projectileType = WEAPON_BASE_PROJECTILE;

	assert(weaponDef != nullptr);

	if (weaponDef->IsHitScanWeapon()) {
		hitscan = true;
		// the else-case (default) is handled in CProjectile::Init
		//
		// ray projectiles must all set this to false because their collision
		// detection is handled by the weapons firing them, ProjectileHandler
		// will skip any tests for these
		checkCol = false;
		// type has not yet been set by derived ctor's at this point
		// useAirLos = (projectileType != WEAPON_LIGHTNING_PROJECTILE);
		useAirLos = true;

		// NOTE:
		//   {BeamLaser, Lightning}Projectile's do NOT actually move (their
		//   speed is never added to pos) and never alter their speed either
		//   they additionally override our ::Update (so CProjectile::Update
		//   is also never called) which means assigning speed a non-zerovec
		//   value should have no side-effects
		SetPosition(startPos);
		SetVelocityAndSpeed(targetPos - startPos);

		// ProjectileDrawer vis-culls by pos == startPos, but we
		// want to see the beam even if camera is near targetPos
		// --> use full distance for drawRadius
		SetRadiusAndHeight((targetPos - startPos).Length(), 0.0f);
	}

	collisionFlags = weaponDef->collisionFlags;
	weaponNum = params.weaponNum;
	alwaysVisible = weaponDef->visuals.alwaysVisible;
	ignoreWater = weaponDef->waterweapon;

	CSolidObject* so = NULL;
	CWeaponProjectile* po = NULL;

	if ((so = dynamic_cast<CSolidObject*>(target)) != NULL) {
		AddDeathDependence(so, DEPENDENCE_WEAPONTARGET);
	}
	if ((po = dynamic_cast<CWeaponProjectile*>(target)) != NULL) {
		po->SetBeingIntercepted(po->IsBeingIntercepted() || weaponDef->interceptSolo);
		AddDeathDependence(po, DEPENDENCE_INTERCEPTTARGET);
	}

	if (params.model != NULL) {
		model = params.model;
	} else {
		model = weaponDef->LoadModel();
	}

	if (params.owner == NULL) {
		// the else-case (default) is handled in CProjectile::Init
		ownerID = params.ownerID;
		teamID = params.teamID;
		allyteamID = teamHandler->IsValidTeam(teamID)? teamHandler->AllyTeam(teamID): -1;
	}

	if (ownerID != -1u && weaponNum != -1u) {
		const CUnit* owner = unitHandler->GetUnit(ownerID);
		if (owner != nullptr && weaponNum < owner->weapons.size()) {
			damages = DynDamageArray::IncRef(owner->weapons[weaponNum]->damages);
		}
	}
	if (damages == nullptr)
		damages = DynDamageArray::IncRef(&weaponDef->damages);

	if (params.cegID != -1u) {
		cegID = params.cegID;
	} else {
		cegID = weaponDef->ptrailExplosionGeneratorID;
	}

	// must happen after setting position and velocity
	projectileHandler->AddProjectile(this);
	quadField->AddProjectile(this);

	ASSERT_SYNCED(id);

	if (weaponDef->targetable) {
		interceptHandler.AddInterceptTarget(this, targetPos);
	}
}
Exemplo n.º 5
0
void CInterceptHandler::Update(bool forced) {
	if (((gs->frameNum % UNIT_SLOWUPDATE_RATE) != 0) && !forced)
		return;

	std::list<CWeapon*>::iterator wit;
	std::map<int, CWeaponProjectile*>::const_iterator pit;

	for (wit = interceptors.begin(); wit != interceptors.end(); ++wit) {
		CWeapon* w = *wit;
		const WeaponDef* wDef = w->weaponDef;
		const CUnit* wOwner = w->owner;
		// const float3& wOwnerPos = wOwner->pos;
		const float3& wPos = w->weaponPos;

		assert(wDef->interceptor || wDef->isShield);

		for (pit = interceptables.begin(); pit != interceptables.end(); ++pit) {
			CWeaponProjectile* p = pit->second;
			const WeaponDef* pDef = p->weaponDef;

			if ((pDef->targetable & wDef->interceptor) == 0)
				continue;
			if (w->incomingProjectiles.find(p->id) != w->incomingProjectiles.end())
				continue;

			const CUnit* pOwner = p->owner();
			const int pAllyTeam = (pOwner != NULL)? pOwner->allyteam: -1;

			if (pAllyTeam != -1 && teamHandler->Ally(wOwner->allyteam, pAllyTeam))
				continue;

			// there are four cases when an interceptor <w> should fire at a projectile <p>:
			//     1. p's target position inside w's interception circle (w's owner can move!)
			//     2. p's current position inside w's interception circle
			//     3. p's projected impact position inside w's interception circle
			//     4. p's trajectory intersects w's interception circle
			//
			// these checks all need to be evaluated periodically, not just
			// when a projectile is created and handed to AddInterceptTarget
			const float interceptDist = w->weaponPos.distance(p->pos);
			const float impactDist = ground->LineGroundCol(p->pos, p->pos + p->dir * interceptDist);

			const float3& pFlightPos = p->pos;
			const float3& pImpactPos = p->pos + p->dir * impactDist;
			const float3& pTargetPos = p->targetPos;

			if ((pTargetPos - wPos).SqLength2D() < Square(wDef->coverageRange)) {
				w->AddDeathDependence(p, CObject::DEPENDENCE_INTERCEPT);
				w->incomingProjectiles[p->id] = p;
				continue; // 1
			}

			if (wDef->interceptor == 1) {
				// <w> is just a static interceptor and fires only at projectiles
				// TARGETED within its current interception area; any projectiles
				// CROSSING its interception area are fired at only if interceptor
				// is >= 2
				continue;
			}

			if ((pFlightPos - wPos).SqLength2D() < Square(wDef->coverageRange)) {
				w->AddDeathDependence(p, CObject::DEPENDENCE_INTERCEPT);
				w->incomingProjectiles[p->id] = p;
				continue; // 2
			}

			if ((pImpactPos - wPos).SqLength2D() < Square(wDef->coverageRange)) {
				const float3 pTargetDir = (pTargetPos - pFlightPos).SafeNormalize();
				const float3 pImpactDir = (pImpactPos - pFlightPos).SafeNormalize();

				// the projected impact position can briefly shift into the covered
				// area during transition from vertical to horizontal flight, so we
				// perform an extra test (NOTE: assumes non-parabolic trajectory)
				if (pTargetDir.dot(pImpactDir) >= 0.999f) {
					w->AddDeathDependence(p, CObject::DEPENDENCE_INTERCEPT);
					w->incomingProjectiles[p->id] = p;
					continue; // 3
				}
			}

			const float3 pCurSeparationVec = wPos - pFlightPos;
			const float pMinSeparationDist = std::max(pCurSeparationVec.dot(p->dir), 0.0f);
			const float3 pMinSeparationPos = pFlightPos + (p->dir * pMinSeparationDist);
			const float3 pMinSeparationVec = wPos - pMinSeparationPos;

			if (pMinSeparationVec.SqLength() < Square(wDef->coverageRange)) {
				w->AddDeathDependence(p, CObject::DEPENDENCE_INTERCEPT);
				w->incomingProjectiles[p->id] = p;
				continue; // 4
			}
		}
	}
}
Exemplo n.º 6
0
void CPlasmaRepulser::Update(void)
{
	const int defHitFrames = weaponDef->visibleShieldHitFrames;
	const bool couldBeVisible = (weaponDef->visibleShield || (defHitFrames > 0));
	const int defRechargeDelay = weaponDef->shieldRechargeDelay;

	rechargeDelay -= (rechargeDelay > 0) ? 1 : 0;

	if (startShowingShield) {
		// one-time iteration when shield first goes online
		// (adds the projectile parts, this assumes owner is
		// not mobile)
		startShowingShield = false;
		if (couldBeVisible) {
			// 32 parts
			for (int y = 0; y < 16; y += 4) {
				for (int x = 0; x < 32; x += 4) {
					visibleShieldParts.push_back(
						new CShieldPartProjectile(owner->pos, x, y, radius,
						                               weaponDef->shieldBadColor,
						                               weaponDef->shieldAlpha,
						                               weaponDef->visuals.texture1, owner)
					);
				}
			}
		}
	}

	if (isEnabled && (curPower < weaponDef->shieldPower) && rechargeDelay <= 0) {
		if (owner->UseEnergy(weaponDef->shieldPowerRegenEnergy * (1.0f / 30.0f))) {
			curPower += weaponDef->shieldPowerRegen * (1.0f / 30.0f);
		}
	}
	weaponPos = owner->pos + (owner->frontdir * relWeaponPos.z)
	                       + (owner->updir    * relWeaponPos.y)
	                       + (owner->rightdir * relWeaponPos.x);

	if (couldBeVisible) {
		float drawAlpha = 0.0f;
		if (hitFrames > 0) {
			drawAlpha += float(hitFrames) / float(defHitFrames);
			hitFrames--;
		}
		if (weaponDef->visibleShield) {
			drawAlpha += 1.0f;
		}
		drawAlpha = std::min(1.0f, drawAlpha * weaponDef->shieldAlpha);
		const bool drawMe = (drawAlpha > 0.0f);

		if (drawMe || wasDrawn) {
			const float colorMix = std::min(1.0f, curPower / std::max(1.0f, weaponDef->shieldPower));
			const float3 color = (weaponDef->shieldGoodColor * colorMix)
					+ (weaponDef->shieldBadColor * (1.0f - colorMix));
			std::list<CShieldPartProjectile*>::iterator si;
			for (si = visibleShieldParts.begin(); si != visibleShieldParts.end(); ++si) {
				(*si)->Actualize(weaponPos, color, isEnabled ? drawAlpha : 0.0f);
			}
		}
		wasDrawn = drawMe;
	}

	if (!isEnabled) {
		return;
	}

	for (std::list<CWeaponProjectile*>::iterator pi = incoming.begin(); pi != incoming.end(); ++pi) {
		CWeaponProjectile* pro = *pi;

		if (!pro->checkCol) {
			continue;
		}

		if ((pro->pos - owner->pos).SqLength() > sqRadius) {
			// projectile does not hit the shield, don't touch it
			continue;
		}

		if (luaRules && luaRules->ShieldPreDamaged(pro, this, owner, weaponDef->shieldRepulser)) {
			// gadget handles the collision event, don't touch the projectile
			continue;
		}

		if (curPower < pro->weaponDef->damages[0]) {
			// shield does not have enough power, don't touch the projectile
			continue;
		}

		if (teamHandler->Team(owner->team)->energy < weaponDef->shieldEnergyUse) {
			// team does not have enough energy, don't touch the projectile
			continue;
		}

		rechargeDelay = defRechargeDelay;

		if (weaponDef->shieldRepulser) {
			// bounce the projectile
			const int type = pro->ShieldRepulse(this, weaponPos,
													weaponDef->shieldForce,
													weaponDef->shieldMaxSpeed);

			if (type == 0) {
				continue;
			} else if (type == 1) {
				owner->UseEnergy(weaponDef->shieldEnergyUse);

				if (weaponDef->shieldPower != 0) {
					curPower -= pro->weaponDef->damages[0];
				}
			} else {
				owner->UseEnergy(weaponDef->shieldEnergyUse / 30.0f);

				if (weaponDef->shieldPower != 0) {
					curPower -= pro->weaponDef->damages[0] / 30.0f;
				}
			}

			if (weaponDef->visibleShieldRepulse) {
				std::list<CWeaponProjectile*>::iterator i;

				for (i = hasGfx.begin(); i != hasGfx.end(); ++i) {
					if (*i == pro) {
						break;
					}
				}

				if (i == hasGfx.end()) {
					hasGfx.insert(hasGfx.end(), pro);

					const float colorMix = std::min(1.0f, curPower / std::max(1.0f, weaponDef->shieldPower));
					const float3 color =
						(weaponDef->shieldGoodColor * colorMix) +
						(weaponDef->shieldBadColor * (1.0f - colorMix));

					new CRepulseGfx(owner, pro, radius, color);
				}
			}

			if (defHitFrames > 0) {
				hitFrames = defHitFrames;
			}
		} else {
			// kill the projectile
			if (owner->UseEnergy(weaponDef->shieldEnergyUse)) {
				if (weaponDef->shieldPower != 0) {
					curPower -= pro->weaponDef->damages[0];
				}

				pro->Collision(owner);

				if (defHitFrames > 0) {
					hitFrames = defHitFrames;
				}
			}
		}
	}

}
Exemplo n.º 7
0
void CPlasmaRepulser::Update()
{
	const int defHitFrames = weaponDef->visibleShieldHitFrames;
	const int defRechargeDelay = weaponDef->shieldRechargeDelay;

	rechargeDelay -= (rechargeDelay > 0) ? 1 : 0;

	if (isEnabled && (curPower < weaponDef->shieldPower) && rechargeDelay <= 0) {
		if (owner->UseEnergy(weaponDef->shieldPowerRegenEnergy * (1.0f / GAME_SPEED))) {
			curPower += weaponDef->shieldPowerRegen * (1.0f / GAME_SPEED);
		}
	}

	if (hitFrames > 0)
		hitFrames--;

	weaponPos = owner->pos + (owner->frontdir * relWeaponPos.z)
	                       + (owner->updir    * relWeaponPos.y)
	                       + (owner->rightdir * relWeaponPos.x);

	if (!isEnabled) {
		return;
	}

	for (std::map<int, CWeaponProjectile*>::iterator pi = incomingProjectiles.begin(); pi != incomingProjectiles.end(); ++pi) {
		assert(ph->GetMapPairBySyncedID(pi->first)); // valid projectile id?

		CWeaponProjectile* pro = pi->second;
		const WeaponDef* proWd = pro->weaponDef;

		if (!pro->checkCol) {
			continue;
		}

		if ((pro->pos - owner->pos).SqLength() > sqRadius) {
			// projectile does not hit the shield, don't touch it
			continue;
		}

		if (luaRules && luaRules->ShieldPreDamaged(pro, this, owner, weaponDef->shieldRepulser)) {
			// gadget handles the collision event, don't touch the projectile
			continue;
		}

		if (curPower < proWd->damages[0]) {
			// shield does not have enough power, don't touch the projectile
			continue;
		}

		if (teamHandler->Team(owner->team)->energy < weaponDef->shieldEnergyUse) {
			// team does not have enough energy, don't touch the projectile
			continue;
		}

		rechargeDelay = defRechargeDelay;

		if (weaponDef->shieldRepulser) {
			// bounce the projectile
			const int type = pro->ShieldRepulse(this, weaponPos,
				weaponDef->shieldForce,
				weaponDef->shieldMaxSpeed);

			if (type == 0) {
				continue;
			} else if (type == 1) {
				owner->UseEnergy(weaponDef->shieldEnergyUse);

				if (weaponDef->shieldPower != 0) {
					//FIXME some weapons do range dependent damage! (mantis #2345)
					curPower -= proWd->damages[0];
				}
			} else {
				//FIXME why do all weapons except LASERs do only (1 / GAME_SPEED) damage???
				owner->UseEnergy(weaponDef->shieldEnergyUse / GAME_SPEED);

				if (weaponDef->shieldPower != 0) {
					curPower -= proWd->damages[0] / GAME_SPEED;
				}
			}

			if (weaponDef->visibleShieldRepulse) {
				bool newlyAdded = hasGfx.insert(pro).second;

				if (newlyAdded) {
					const float colorMix = std::min(1.0f, curPower / std::max(1.0f, weaponDef->shieldPower));
					const float3 color =
						(weaponDef->shieldGoodColor * colorMix) +
						(weaponDef->shieldBadColor * (1.0f - colorMix));

					new CRepulseGfx(owner, pro, radius, color);
				}
			}

			if (defHitFrames > 0) {
				hitFrames = defHitFrames;
			}
		} else {
			// kill the projectile
			if (owner->UseEnergy(weaponDef->shieldEnergyUse)) {
				if (weaponDef->shieldPower != 0) {
					//FIXME some weapons do range dependent damage! (mantis #2345)
					curPower -= proWd->damages[0];
				}

				pro->Collision(owner);

				if (defHitFrames > 0) {
					hitFrames = defHitFrames;
				}
			}
		}
	}

}