// TODO: Consider location as well as direction when both given.
void KnockbackComponent::HandleDamage(float amount, gentity_t* source, Util::optional<Vec3> location,
                                      Util::optional<Vec3> direction, int flags, meansOfDeath_t meansOfDeath) {
	if (!(flags & DAMAGE_KNOCKBACK)) return;
	if (amount <= 0.0f) return;

	if (!direction) {
		knockbackLogger.Warn("Received damage message with knockback flag set but no direction.");
		return;
	}

	if (Math::Length(direction.value()) == 0.0f) {
		knockbackLogger.Warn("Attempt to do knockback with null vector direction.");
		return;
	}

	// TODO: Remove dependency on client.
	gclient_t *client = entity.oldEnt->client;
	assert(client);

	// Check for immunity.
	if (client->noclip) return;
	if (client->sess.spectatorState != SPECTATOR_NOT) return;

	float mass = (float)BG_Class(client->ps.stats[ STAT_CLASS ])->mass;

	if (mass <= 0.0f) {
		knockbackLogger.Warn("Attempt to do knockback against target with no mass, assuming normal mass.");
		mass = KNOCKBACK_NORMAL_MASS;
	}

	float massMod  = Math::Clamp(KNOCKBACK_NORMAL_MASS / mass, KNOCKBACK_MIN_MASSMOD, KNOCKBACK_MAX_MASSMOD);
	float strength = amount * DAMAGE_TO_KNOCKBACK * massMod;

	// Change client velocity.
	Vec3 clientVelocity = Vec3::Load(client->ps.velocity);
	clientVelocity += Math::Normalize(direction.value()) * strength;
	clientVelocity.Store(client->ps.velocity);

	// Set pmove timer so that the client can't cancel out the movement immediately.
	if (!client->ps.pm_time) {
		client->ps.pm_time = KNOCKBACK_PMOVE_TIME;
		client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
	}

	knockbackLogger.Debug("Knockback: client: %i, strength: %.1f (massMod: %.1f).",
	                      entity.oldEnt->s.number, strength, massMod);
}
float ResourceStorageComponent::GetStoredFraction() {
	// TODO: Add TeamComponent and/or Utility::Team.
	team_t team = entity.oldEnt->buildableTeam;

	if (!level.team[team].acquiredBuildPoints) return 1.0f;

	// The stored fraction is equal to the acquired fraction.
	float storedFraction = acquiredBuildPoints / level.team[team].acquiredBuildPoints;

	if (storedFraction < 0.0f || storedFraction > 1.0f + LINE_DISTANCE_EPSILON) {
		resourceStorageLogger.Warn(
			"A resource storage stores an invalid fraction of all build points: %.1f", storedFraction
		);
	}

	return storedFraction;
}
Ejemplo n.º 3
0
void SpawnerComponent::Think(int timeDelta) {
	BuildableComponent *buildableComponent = entity.Get<BuildableComponent>();

	if (buildableComponent && !buildableComponent->Active()) return;

	Entity* blocker = GetBlocker();

	if (blocker) {
		if (!blocker->oldEnt) {
			logger.Warn("Spawn blocking entity has oldEnt == nullptr");
			return;
		}

		// Suicide if blocked by the map.
		if (blocker->oldEnt->s.number == ENTITYNUM_WORLD
		    || blocker->oldEnt->s.eType == entityType_t::ET_MOVER) {
			Entities::Kill(entity, nullptr, MOD_SUICIDE);
		}

		// Free a blocking corpse.
		else if (blocker->oldEnt->s.eType == entityType_t::ET_CORPSE) {
			G_FreeEntity(blocker->oldEnt);
		}

		else if (Entities::OnSameTeam(entity, *blocker)) {
			// Suicide if blocked by own main buildable.
			if (blocker->Get<MainBuildableComponent>()) {
				Entities::Kill(entity, nullptr, MOD_SUICIDE);
			}

			// Kill a friendly blocking buildable.
			else if (blocker->Get<BuildableComponent>()) {
				Entities::Kill(*blocker, nullptr, MOD_SUICIDE);

				// Play an animation so it's clear what destroyed the buildable.
				G_SetBuildableAnim(entity.oldEnt, BANIM_SPAWN1, true);
			}

			// Do periodic damage to a friendly client.
			// TODO: Externalize constants.
			else  if (blocker->Get<ClientComponent>() && g_antiSpawnBlock.integer) {
				blockTime += timeDelta;

				if (blockTime > BLOCKER_GRACE_PERIOD
				    && blockTime - timeDelta <= BLOCKER_GRACE_PERIOD) {
					WarnBlocker(*blocker, false);
				}

				if (blockTime > BLOCKER_GRACE_PERIOD + BLOCKER_WARN_PERIOD) {
					if (blockTime - timeDelta <= BLOCKER_GRACE_PERIOD + BLOCKER_WARN_PERIOD) {
						WarnBlocker(*blocker, true);
					}

					blocker->Damage(
						BLOCKER_DAMAGE * ((float)timeDelta / 1000.0f), entity.oldEnt, {}, {},
						DAMAGE_PURE, MOD_TRIGGER_HURT
					);
				}
			}
		}
	} else if (g_antiSpawnBlock.integer) {
		blockTime = Math::Clamp(blockTime - timeDelta, 0, BLOCKER_GRACE_PERIOD);
	}
}