identified_danger assess_danger(
	const const_entity_handle& victim, 
	const const_entity_handle& danger
) {
	identified_danger result;

	const auto* const sentience = victim.find<components::sentience>();

	if (!sentience) {
		return result;
	}

	result.danger = danger;

	const auto* const missile = danger.find<invariants::missile>();
	const auto* const attitude = danger.find<components::attitude>();

	if ((!missile && !attitude) || (missile && danger.get<components::sender>().is_sender_subject(victim))) {
		return result;
	}

	const auto victim_pos = victim.get_logic_transform().pos;
	const auto danger_pos = danger.get_logic_transform().pos;
	const auto danger_vel = danger.get_owner_of_colliders().get_effective_velocity();
	const auto danger_dir = (danger_pos - victim_pos);
	const auto danger_distance = danger_dir.length();

	result.recommended_evasion = augs::danger_avoidance(victim_pos, danger_pos, danger_vel);
	result.recommended_evasion.normalize();

	const auto& sentience_def = victim.get<invariants::sentience>();
	const auto comfort_zone = sentience_def.comfort_zone;
	const auto comfort_zone_disturbance_ratio = augs::disturbance(danger_distance, comfort_zone);

	if (missile) {
		result.amount += comfort_zone_disturbance_ratio * missile->damage.base*4;
	}

	if (attitude) {
		const auto att = calc_attitude(danger, victim);
		
		if (is_hostile(att)) {
			result.amount += comfort_zone_disturbance_ratio * sentience_def.danger_amount_from_hostile_attitude;
		}
	}
	
	return result;
}
entity_id get_closest_hostile(
	const const_entity_handle subject,
	const const_entity_handle subject_attitude,
	const real32 radius,
	const b2Filter filter
) {
	const auto& cosm = subject.get_cosmos();
	const auto si = cosm.get_si();

	const auto& physics = cosm.get_solvable_inferred().physics;
	const auto transform = subject.get_logic_transform();

	entity_id closest_hostile;

	auto min_distance = std::numeric_limits<real32>::max();

	if (subject_attitude.alive()) {
		const auto subject_attitude_transform = subject_attitude.get_logic_transform();

		physics.for_each_in_aabb(
			si,
			transform.pos - vec2(radius, radius),
			transform.pos + vec2(radius, radius),
			filter,
			[&](const b2Fixture& fix) {
				const const_entity_handle s = cosm[get_body_entity_that_owns(fix)];

				if (s != subject && s.has<components::attitude>() && !sentient_and_unconscious(s)) {
					const auto calculated_attitude = calc_attitude(s, subject_attitude);

					if (is_hostile(calculated_attitude)) {
						const auto dist = (s.get_logic_transform().pos - subject_attitude_transform.pos).length_sq();

						if (dist < min_distance) {
							closest_hostile = s;
							min_distance = dist;
						}
					}
				}

				return callback_result::CONTINUE;
			}
		);
	}

	return closest_hostile;
}
std::vector<entity_id> get_closest_hostiles(
	const const_entity_handle subject,
	const const_entity_handle subject_attitude,
	const real32 radius,
	const b2Filter filter
) {
	const auto& cosm = subject.get_cosmos();
	const auto si = cosm.get_si();

	const auto& physics = cosm.get_solvable_inferred().physics;
	const auto transform = subject.get_logic_transform();

	struct hostile_entry {
		entity_id s;
		real32 dist = 0.f;

		bool operator<(const hostile_entry& b) const {
			return dist < b.dist;
		}

		bool operator==(const hostile_entry& b) const {
			return s == b.s;
		}

		operator entity_id() const {
			return s;
		}
	};

	std::vector<hostile_entry> hostiles;

	if (subject_attitude.alive()) {
		const auto subject_attitude_transform = subject_attitude.get_logic_transform();

		physics.for_each_in_aabb(
			si,
			transform.pos - vec2(radius, radius),
			transform.pos + vec2(radius, radius),
			filter,
			[&](const b2Fixture& fix) {
				const const_entity_handle s = cosm[get_body_entity_that_owns(fix)];

				if (s != subject && s.has<components::attitude>()) {
					const auto calculated_attitude = calc_attitude(s, subject_attitude);

					if (is_hostile(calculated_attitude)) {
						const auto dist = (s.get_logic_transform().pos - subject_attitude_transform.pos).length_sq();
						
						hostile_entry new_entry;
						new_entry.s = s;
						new_entry.dist = dist;

						hostiles.push_back(new_entry);
					}
				}

				return callback_result::CONTINUE;
			}
		);
	}

	sort_range(hostiles);
	remove_duplicates_from_sorted(hostiles);

	return { hostiles.begin(), hostiles.end() };
}