SpawnerComponent::SpawnerComponent(Entity& entity, TeamComponent& r_TeamComponent,
	ThinkingComponent& r_ThinkingComponent)
	: SpawnerComponentBase(entity, r_TeamComponent, r_ThinkingComponent)
	, dying(false)
	, blockTime(0)
{
	REGISTER_THINKER(Think, ThinkingComponent::SCHEDULER_AVERAGE, 500);
	level.team[GetTeamComponent().Team()].numSpawns++;
}
void SpawnerComponent::OnLoss() {
	TeamComponent::team_t team = GetTeamComponent().Team();

	int newNumSpawns = --level.team[team].numSpawns;

	ASSERT_GE(newNumSpawns, 0);

	// Warn if the last spawn is lost and there is a main buildable.
	if (newNumSpawns == 0 && G_ActiveMainBuildable(team)) {
		G_BroadcastEvent(EV_NO_SPAWNS, 0, team);
	}
}
void BuildableComponent::HandleDie(gentity_t* killer, meansOfDeath_t meansOfDeath) {
	// Note that this->state is adjusted in (Alien|Human)BuildableComponent::HandleDie so they have
	// access to its current value.

	TeamComponent::team_t team = GetTeamComponent().Team();

	// TODO: Move animation code to BuildableComponent.
	G_SetBuildableAnim(entity.oldEnt, Powered() ? BANIM_DESTROY : BANIM_DESTROY_UNPOWERED, true);
	G_SetIdleBuildableAnim(entity.oldEnt, BANIM_DESTROYED);

	entity.oldEnt->killedBy = killer->s.number;

	G_LogDestruction(entity.oldEnt, killer, meansOfDeath);

	// TODO: Handle in TaggableComponent.
	Beacon::DetachTags(entity.oldEnt);

	// Report an attack to the defending team if the buildable was powered and there is a main
	// buildable that can report it. Note that the main buildables itself issues its own warnings.
	if (Powered() && entity.Get<MainBuildableComponent>() == nullptr &&
	    G_IsWarnableMOD(meansOfDeath) && G_ActiveMainBuildable(team)) {
		// Get a nearby location entity.
		gentity_t *location = GetCloseLocationEntity(entity.oldEnt);

		// Fall back to fake location entity if necessary.
		if (!location) location = level.fakeLocation;

		// Warn if there was no warning for this location recently.
		if (level.time > location->warnTimer) {
			bool inBase = G_InsideBase(entity.oldEnt);

			G_BroadcastEvent(EV_WARN_ATTACK, inBase ? 0 : location->s.number, team);
			Beacon::NewArea(BCT_DEFEND, entity.oldEnt->s.origin, team);
			location->warnTimer = level.time + ATTACKWARN_NEARBY_PERIOD;
		}
	}

	// If not deconstructed, add all build points to queue.
	if (meansOfDeath != MOD_DECONSTRUCT && meansOfDeath != MOD_REPLACE) {
		G_FreeBudget(team, 0, BG_Buildable(entity.oldEnt->s.modelindex)->buildPoints);
	}
}
void SpawnerComponent::WarnBlocker(Entity& blocker, bool lastWarning) {
	std::string message = lastWarning ? Color::ToString(Color::Red)
	                                  : Color::ToString(Color::Yellow);

	switch (GetTeamComponent().Team()) {
		case TEAM_ALIENS:
			message += "You are blocking an egg!";
			break;

		case TEAM_HUMANS:
			message += "You are blocking a telenode!";
			break;

		default:
			message += "You are blocking a spawn!";
			break;
	}

	message = "cp \"" + message + "\"";

	trap_SendServerCommand(blocker.oldEnt - g_entities, message.c_str());
}