<commit_before>#include "augs/misc/randomization.h"

#include "game/cosmos/cosmos.h"
#include "game/cosmos/entity_handle.h"
#include "game/cosmos/logic_step.h"
#include "game/cosmos/data_living_one_step.h"
#include "game/cosmos/for_each_entity.h"
#include "game/cosmos/create_entity.hpp"
#include "game/cosmos/delete_entity.h"

#include "game/assets/all_logical_assets.h"
#include "game/messages/intent_message.h"
#include "game/messages/gunshot_message.h"
#include "game/messages/interpolation_correction_request.h"

#include "game/components/rigid_body_component.h"
#include "game/components/missile_component.h"
#include "game/components/container_component.h"
#include "game/components/item_component.h"
#include "game/components/flags_component.h"
#include "game/components/sentience_component.h"
#include "game/components/explosive_component.h"
#include "game/components/sender_component.h"
#include "game/components/transform_component.h"
#include "game/components/gun_component.h"

#include "game/detail/inventory/item_slot_transfer_request.h"
#include "game/detail/inventory/perform_transfer.h"
#include "game/detail/entity_handle_mixins/get_owning_transfer_capability.hpp"
#include "game/detail/gun/gun_math.h"
#include "game/detail/gun/shell_offset.h"
#include "game/detail/entity_handle_mixins/inventory_mixin.hpp"

#include "game/messages/start_sound_effect.h"
#include "game/stateless_systems/gun_system.h"
#include "game/inferred_caches/physics_world_cache.h"

using namespace augs;

#define ENABLE_RECOIL 1

void components::gun::set_chambering_handle_pulling(
	const bool enabled,
	const augs::stepped_timestamp now
) {
	if (!is_chambering_handle_being_pulled) {
		when_began_pulling_chambering_handle = now;
	}

	is_chambering_handle_being_pulled = enabled;
}

static entity_id find_next_cartridge(
	const const_entity_handle gun_entity
) {
	thread_local std::vector<entity_id> next_cartridge_from;
	next_cartridge_from.clear();

	auto& cosm = gun_entity.get_cosmos();

	const auto chamber_magazine_slot = gun_entity[slot_function::GUN_CHAMBER_MAGAZINE];

	if (chamber_magazine_slot.alive()) {
		next_cartridge_from = chamber_magazine_slot.get_items_inside();
	}
	else {
		const auto detachable_magazine_slot = gun_entity[slot_function::GUN_DETACHABLE_MAGAZINE];

		if (detachable_magazine_slot.alive() && detachable_magazine_slot.has_items()) {
			const auto magazine = cosm[detachable_magazine_slot.get_items_inside()[0]];

			if (nullptr == magazine.find_mounting_progress()) {
				next_cartridge_from = magazine[slot_function::ITEM_DEPOSIT].get_items_inside();
			}
		}
	}

	if (next_cartridge_from.size() > 0) {
		return next_cartridge_from.back();
	}

	return {};
}

static void load_next_cartridge(
	const entity_id gun_entity_id,
	const entity_id next_cartridge,
	const logic_step step
) {
	const auto gun_entity = step.get_cosmos()[gun_entity_id];

	item_slot_transfer_request into_chamber_transfer;

	into_chamber_transfer.item = next_cartridge;
	into_chamber_transfer.target_slot = gun_entity[slot_function::GUN_CHAMBER];
	into_chamber_transfer.params.bypass_mounting_requirements = true;
	into_chamber_transfer.params.specified_quantity = 1;
   
	perform_transfer(into_chamber_transfer, step);
}

static void find_and_load_next_cartridge(
	const entity_id gun_entity_id,
	const logic_step step
) {
	auto& cosm = step.get_cosmos();
	const auto gun_entity = cosm[gun_entity_id];

	if (const auto next_cartridge = find_next_cartridge(gun_entity); next_cartridge.is_set()) {
		load_next_cartridge(gun_entity, next_cartridge, step);
	}
}

template <class T>
static void cooldown_gun_heat(
	const logic_step step,
	const transformr& muzzle_transform,
	const T& gun_entity
) {
	auto& gun = gun_entity.template get<components::gun>();
	auto& heat = gun.current_heat;

	const auto& gun_def = gun_entity.template get<invariants::gun>();
	const auto delta = step.get_delta();

	auto& cosm = step.get_cosmos();

	const auto& clk = cosm.get_clock();

	if (clk.is_ready(gun_def.shot_cooldown_ms, gun.when_last_fired)) {
		/* Apply idle cooldown */
		gun.recoil.cooldown(gun_def.recoil, delta.in_milliseconds());
		heat = std::max(0.f, heat - (gun_def.heat_cooldown_speed_mult * delta.in_seconds()) / gun_def.maximum_heat);
		gun.magazine.damp(delta.in_seconds(), 2.f);

		const auto perform_heat = gun.max_heat_after_steam_schedule - gun_def.get_steam_perform_diff();

		if (gun.steam_burst_scheduled && heat <= perform_heat) {
			const auto additional_intensity = gun.max_heat_after_steam_schedule / gun_def.maximum_heat;
			gun.max_heat_after_steam_schedule = 0.f;

			{
				auto chosen_effect = gun_def.steam_burst_sound;

				chosen_effect.modifier.pitch /= additional_intensity;
				chosen_effect.modifier.gain *= additional_intensity;

				const auto owning_capability = gun_entity.get_owning_transfer_capability();
				chosen_effect.start(step, sound_effect_start_input::at_entity(gun_entity).set_listener(owning_capability));
			}

			{
				auto chosen_effect = gun_def.steam_burst_particles;

				chosen_effect.modifier.scale_amounts += additional_intensity;
				chosen_effect.modifier.scale_lifetimes += additional_intensity;

				chosen_effect.start(step, particle_effect_start_input::orbit_absolute(gun_entity, muzzle_transform));
			}

			gun.steam_burst_scheduled = false;
		}
	}
}

void gun_system::launch_shots_due_to_pressed_triggers(const logic_step step) {
	auto& cosm = step.get_cosmos();
	const auto& clk = cosm.get_clock();
	const auto delta = clk.dt;
	const auto& logicals = step.get_logical_assets();

	cosm.for_each_having<components::gun>(
		[&](const auto& gun_entity) {
			const auto gun_transform = gun_entity.get_logic_transform();
			const auto muzzle_transform = ::calc_muzzle_transform(gun_entity, gun_transform);

			const auto capability = gun_entity.get_owning_transfer_capability();

			if (capability.dead()) {
				/* Only process autonomous gun logic */
				cooldown_gun_heat(step, muzzle_transform, gun_entity);
				return;
			}

			capability.template dispatch_on_having_all<components::sentience>([&](const auto& owning_capability) {
				components::sentience& sentience = owning_capability.template get<components::sentience>();

				const auto triggers = [&]() {
					augs::enum_boolset<weapon_action_type> out;

					for (std::size_t i = 0; i < hand_count_v; ++i) {
						const auto& hand_flag = sentience.hand_flags[i];

						if (hand_flag) {
							const auto action = owning_capability.calc_hand_action(i);

							if (action.held_item == gun_entity) {
								out.set(action.type);
							}
						}
					}

					return out;
				}();

				auto& gun = gun_entity.template get<components::gun>();
				const auto& gun_def = gun_entity.template get<invariants::gun>();

				//const bool has_secondary_function = false;

				const bool primary_trigger_pressed = 
					triggers.test(weapon_action_type::PRIMARY)
					//|| (!has_secondary_function && triggers.test(weapon_action_type::SECONDARY))
				;

				bool& primary_just_pressed = 
					gun.just_pressed[weapon_action_type::PRIMARY];
<commit_msg>Fixed chambering progress bug<commit_after>#include "augs/misc/randomization.h"

#include "game/cosmos/cosmos.h"
#include "game/cosmos/entity_handle.h"
#include "game/cosmos/logic_step.h"
#include "game/cosmos/data_living_one_step.h"
#include "game/cosmos/for_each_entity.h"
#include "game/cosmos/create_entity.hpp"
#include "game/cosmos/delete_entity.h"

#include "game/assets/all_logical_assets.h"
#include "game/messages/intent_message.h"
#include "game/messages/gunshot_message.h"
#include "game/messages/interpolation_correction_request.h"

#include "game/components/rigid_body_component.h"
#include "game/components/missile_component.h"
#include "game/components/container_component.h"
#include "game/components/item_component.h"
#include "game/components/flags_component.h"
#include "game/components/sentience_component.h"
#include "game/components/explosive_component.h"
#include "game/components/sender_component.h"
#include "game/components/transform_component.h"
#include "game/components/gun_component.h"

#include "game/detail/inventory/item_slot_transfer_request.h"
#include "game/detail/inventory/perform_transfer.h"
#include "game/detail/entity_handle_mixins/get_owning_transfer_capability.hpp"
#include "game/detail/gun/gun_math.h"
#include "game/detail/gun/shell_offset.h"
#include "game/detail/entity_handle_mixins/inventory_mixin.hpp"

#include "game/messages/start_sound_effect.h"
#include "game/stateless_systems/gun_system.h"
#include "game/inferred_caches/physics_world_cache.h"

using namespace augs;

#define ENABLE_RECOIL 1

void components::gun::set_chambering_handle_pulling(
	const bool enabled,
	const augs::stepped_timestamp now
) {
	if (!is_chambering_handle_being_pulled) {
		when_began_pulling_chambering_handle = now;
	}

	is_chambering_handle_being_pulled = enabled;
}

static entity_id find_next_cartridge(
	const const_entity_handle gun_entity
) {
	thread_local std::vector<entity_id> next_cartridge_from;
	next_cartridge_from.clear();

	auto& cosm = gun_entity.get_cosmos();

	const auto chamber_magazine_slot = gun_entity[slot_function::GUN_CHAMBER_MAGAZINE];

	if (chamber_magazine_slot.alive()) {
		next_cartridge_from = chamber_magazine_slot.get_items_inside();
	}
	else {
		const auto detachable_magazine_slot = gun_entity[slot_function::GUN_DETACHABLE_MAGAZINE];

		if (detachable_magazine_slot.alive() && detachable_magazine_slot.has_items()) {
			const auto magazine = cosm[detachable_magazine_slot.get_items_inside()[0]];

			if (nullptr == magazine.find_mounting_progress()) {
				next_cartridge_from = magazine[slot_function::ITEM_DEPOSIT].get_items_inside();
			}
		}
	}

	if (next_cartridge_from.size() > 0) {
		return next_cartridge_from.back();
	}

	return {};
}

static void load_next_cartridge(
	const entity_id gun_entity_id,
	const entity_id next_cartridge,
	const logic_step step
) {
	const auto gun_entity = step.get_cosmos()[gun_entity_id];

	item_slot_transfer_request into_chamber_transfer;

	into_chamber_transfer.item = next_cartridge;
	into_chamber_transfer.target_slot = gun_entity[slot_function::GUN_CHAMBER];
	into_chamber_transfer.params.bypass_mounting_requirements = true;
	into_chamber_transfer.params.specified_quantity = 1;
   
	perform_transfer(into_chamber_transfer, step);
}

static void find_and_load_next_cartridge(
	const entity_id gun_entity_id,
	const logic_step step
) {
	auto& cosm = step.get_cosmos();
	const auto gun_entity = cosm[gun_entity_id];

	if (const auto next_cartridge = find_next_cartridge(gun_entity); next_cartridge.is_set()) {
		load_next_cartridge(gun_entity, next_cartridge, step);
	}
}

template <class T>
static void cooldown_gun_heat(
	const logic_step step,
	const transformr& muzzle_transform,
	const T& gun_entity
) {
	auto& gun = gun_entity.template get<components::gun>();
	auto& heat = gun.current_heat;

	const auto& gun_def = gun_entity.template get<invariants::gun>();
	const auto delta = step.get_delta();

	auto& cosm = step.get_cosmos();

	const auto& clk = cosm.get_clock();

	if (clk.is_ready(gun_def.shot_cooldown_ms, gun.when_last_fired)) {
		/* Apply idle cooldown */
		gun.recoil.cooldown(gun_def.recoil, delta.in_milliseconds());
		heat = std::max(0.f, heat - (gun_def.heat_cooldown_speed_mult * delta.in_seconds()) / gun_def.maximum_heat);
		gun.magazine.damp(delta.in_seconds(), 2.f);

		const auto perform_heat = gun.max_heat_after_steam_schedule - gun_def.get_steam_perform_diff();

		if (gun.steam_burst_scheduled && heat <= perform_heat) {
			const auto additional_intensity = gun.max_heat_after_steam_schedule / gun_def.maximum_heat;
			gun.max_heat_after_steam_schedule = 0.f;

			{
				auto chosen_effect = gun_def.steam_burst_sound;

				chosen_effect.modifier.pitch /= additional_intensity;
				chosen_effect.modifier.gain *= additional_intensity;

				const auto owning_capability = gun_entity.get_owning_transfer_capability();
				chosen_effect.start(step, sound_effect_start_input::at_entity(gun_entity).set_listener(owning_capability));
			}

			{
				auto chosen_effect = gun_def.steam_burst_particles;

				chosen_effect.modifier.scale_amounts += additional_intensity;
				chosen_effect.modifier.scale_lifetimes += additional_intensity;

				chosen_effect.start(step, particle_effect_start_input::orbit_absolute(gun_entity, muzzle_transform));
			}

			gun.steam_burst_scheduled = false;
		}
	}
}

void gun_system::launch_shots_due_to_pressed_triggers(const logic_step step) {
	auto& cosm = step.get_cosmos();
	const auto& clk = cosm.get_clock();
	const auto delta = clk.dt;
	const auto& logicals = step.get_logical_assets();

	cosm.for_each_having<components::gun>(
		[&](const auto& gun_entity) {
			const auto gun_transform = gun_entity.get_logic_transform();
			const auto muzzle_transform = ::calc_muzzle_transform(gun_entity, gun_transform);

			const auto capability = gun_entity.get_owning_transfer_capability();

			auto& gun = gun_entity.template get<components::gun>();

			if (capability.dead()) {
				/* Only process autonomous gun logic */
				cooldown_gun_heat(step, muzzle_transform, gun_entity);
				gun.chambering_progress_ms = 0.f;
				return;
			}

			capability.template dispatch_on_having_all<components::sentience>([&](const auto& owning_capability) {
				components::sentience& sentience = owning_capability.template get<components::sentience>();

				const auto triggers = [&]() {
					augs::enum_boolset<weapon_action_type> out;

					for (std::size_t i = 0; i < hand_count_v; ++i) {
						const auto& hand_flag = sentience.hand_flags[i];

						if (hand_flag) {
							const auto action = owning_capability.calc_hand_action(i);

							if (action.held_item == gun_entity) {
								out.set(action.type);
							}
						}
					}

					return out;
				}();

				const auto& gun_def = gun_entity.template get<invariants::gun>();

				//const bool has_secondary_function = false;

				const bool primary_trigger_pressed = 
					triggers.test(weapon_action_type::PRIMARY)
					//|| (!has_secondary_function && triggers.test(weapon_action_type::SECONDARY))
				;

				bool& primary_just_pressed = 
					gun.just_pressed[weapon_action_type::PRIMARY];

{'fraction_non_alphanumeric': 0.11006067447438973, 'fraction_numerical': 0.0011993791449132214, 'mean_word_length': 5.714827096162956, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5095899', 'n_tokens_mistral': 5477, 'n_tokens_neox': 4882, 'n_words': 763}