void detonate_if(const entity_id& id, const transformr& where, const logic_step& step) {
	step.get_cosmos()[id].dispatch_on_having_all<invariants::explosive>([&](const auto typed_handle) {
		detonate({
			step, id, typed_handle.template get<invariants::explosive>(), where
		});
	});
}
void demolitions_system::advance_cascade_explosions(const logic_step step) {
	auto& cosm = step.get_cosmos();
	const auto& clk = cosm.get_clock();

	cosm.for_each_having<components::cascade_explosion>(
		[&](const auto it) {
			const auto& cascade_def = it.template get<invariants::cascade_explosion>();
			auto& cascade = it.template get<components::cascade_explosion>();

			auto& when_next = cascade.when_next_explosion;

			if (clk.now >= when_next) {
				auto rng = cosm.get_nontemporal_rng_for(it);

				{
					const auto next_explosion_in_ms = rng.randval(cascade_def.explosion_interval_ms);
					when_next = clk.now;
					when_next.step += next_explosion_in_ms / clk.dt.in_milliseconds();
				}

				{
					const auto angle_displacement = rng.randval_h(cascade_def.max_explosion_angle_displacement);
					const auto& body = it.template get<components::rigid_body>();

					auto vel = body.get_velocity();
					vel.rotate(angle_displacement, vec2());
					body.set_velocity(vel);
				}

				auto expl_in = cascade_def.explosion;
				expl_in *= rng.randval_vm(1.f, cascade_def.explosion_scale_variation);

				expl_in.instantiate(
					step,
					it.get_logic_transform(),
					damage_cause(it)
				);

				--cascade.explosions_left;

				if (0 == cascade.explosions_left) {
					step.queue_deletion_of(it, "Cascade explosions exhausted");
				}
			}
		}
	);
}
void demolitions_system::detonate_fuses(const logic_step step) {
	auto& cosm = step.get_cosmos();
	const auto& clk = cosm.get_clock();

	cosm.for_each_having<components::hand_fuse>(
		[&](const auto& it) {
			const auto fuse_logic = fuse_logic_provider(it, step);
			fuse_logic.advance_arming_and_defusing();

			auto& fuse = fuse_logic.fuse;
			const auto& fuse_def = fuse_logic.fuse_def;

			if (fuse.armed()) {
				{
					auto& when_beep = fuse.when_last_beep;

					if (!when_beep.was_set()) {
						when_beep = clk.now;
						/* Don't play the beep effect for the first time. */
					}
					else {
						const auto beep = beep_math { fuse, fuse_def, clk };

						if (beep.should_beep_again()) {
							fuse_def.beep_sound.start(
								step,
								sound_effect_start_input::fire_and_forget(fuse_logic.fused_transform),
								always_predictable_v
							);

							when_beep = clk.now;
						}
					}
				}

				const auto when_armed = fuse.when_armed;
				const auto& fuse_def = it.template get<invariants::hand_fuse>();

				if (clk.is_ready(fuse_def.fuse_delay_ms, when_armed)) {
					detonate_if(it, step);
				}
			}
		}
	);
}