From e84d72bca39e883b5ec68ded6553638b5561c0d7 Mon Sep 17 00:00:00 2001 From: Julian | DiGii <45559529+AtrixZockt@users.noreply.github.com> Date: Tue, 23 Jun 2026 03:55:21 +0200 Subject: [PATCH 1/7] Add radiation system: dosimeter, module, sources --- addons/chemical/ACE_Medical_Injuries.hpp | 14 + .../ACE_Medical_Treatment_Actions.hpp | 24 +- addons/chemical/CfgEventHandlers.hpp | 5 + addons/chemical/CfgSounds.hpp | 26 ++ addons/chemical/CfgVehicles.hpp | 207 ++++++++- addons/chemical/CfgWeapons.hpp | 37 +- addons/chemical/RscTitles.hpp | 62 +++ addons/chemical/XEH_PREP.hpp | 22 + addons/chemical/XEH_postInit.sqf | 99 +++++ addons/chemical/XEH_preInit.sqf | 1 + addons/chemical/config.cpp | 12 +- .../functions/fnc_AttributeRadModule.sqf | 76 ++++ .../functions/fnc_applyDelayedEffect.sqf | 48 +++ .../functions/fnc_clearContamination.sqf | 3 + .../chemical/functions/fnc_decontaminate.sqf | 2 +- .../chemical/functions/fnc_dropRadSource.sqf | 21 + .../functions/fnc_enableDosimeter.sqf | 51 +++ .../functions/fnc_evaluateRadDose.sqf | 49 +++ .../functions/fnc_exposureWatcherPFH.sqf | 23 + .../chemical/functions/fnc_fullHealLocal.sqf | 21 + .../chemical/functions/fnc_handleRespawn.sqf | 2 + .../chemical/functions/fnc_hasDosimeter.sqf | 20 + .../chemical/functions/fnc_hideDosimeter.sqf | 19 + .../functions/fnc_initRadProfiles.sqf | 38 ++ .../functions/fnc_initRadSourceObject.sqf | 25 ++ addons/chemical/functions/fnc_irradiate.sqf | 109 +++++ .../functions/fnc_pickupRadSource.sqf | 25 ++ .../functions/fnc_radContaminationTickPFH.sqf | 36 ++ .../functions/fnc_radInternalTickPFH.sqf | 45 ++ .../chemical/functions/fnc_radManagerPFH.sqf | 93 +++++ .../chemical/functions/fnc_radProtection.sqf | 51 +++ addons/chemical/functions/fnc_radmodule.sqf | 55 +++ .../fnc_registerContaminatedSource.sqf | 35 ++ .../fnc_scheduleRadSicknessChain.sqf | 49 +++ .../chemical/functions/fnc_showDosimeter.sqf | 63 +++ .../fnc_startRadContaminationTick.sqf | 23 + .../functions/fnc_startRadInternalTick.sqf | 24 ++ .../fnc_treatmentAdvanced_AntiRadLocal.sqf | 30 ++ .../chemical/functions/fnc_ui_radModule.sqf | 70 ++++ addons/chemical/initSettings.inc.sqf | 258 ++++++++++++ addons/chemical/script_component.hpp | 7 + addons/chemical/stringtable.xml | 392 ++++++++++++++++++ addons/chemical/ui/RscAttributes.hpp | 126 ++++++ 43 files changed, 2392 insertions(+), 6 deletions(-) create mode 100644 addons/chemical/functions/fnc_AttributeRadModule.sqf create mode 100644 addons/chemical/functions/fnc_dropRadSource.sqf create mode 100644 addons/chemical/functions/fnc_enableDosimeter.sqf create mode 100644 addons/chemical/functions/fnc_evaluateRadDose.sqf create mode 100644 addons/chemical/functions/fnc_hasDosimeter.sqf create mode 100644 addons/chemical/functions/fnc_hideDosimeter.sqf create mode 100644 addons/chemical/functions/fnc_initRadProfiles.sqf create mode 100644 addons/chemical/functions/fnc_initRadSourceObject.sqf create mode 100644 addons/chemical/functions/fnc_irradiate.sqf create mode 100644 addons/chemical/functions/fnc_pickupRadSource.sqf create mode 100644 addons/chemical/functions/fnc_radContaminationTickPFH.sqf create mode 100644 addons/chemical/functions/fnc_radInternalTickPFH.sqf create mode 100644 addons/chemical/functions/fnc_radManagerPFH.sqf create mode 100644 addons/chemical/functions/fnc_radProtection.sqf create mode 100644 addons/chemical/functions/fnc_radmodule.sqf create mode 100644 addons/chemical/functions/fnc_registerContaminatedSource.sqf create mode 100644 addons/chemical/functions/fnc_scheduleRadSicknessChain.sqf create mode 100644 addons/chemical/functions/fnc_showDosimeter.sqf create mode 100644 addons/chemical/functions/fnc_startRadContaminationTick.sqf create mode 100644 addons/chemical/functions/fnc_startRadInternalTick.sqf create mode 100644 addons/chemical/functions/fnc_treatmentAdvanced_AntiRadLocal.sqf create mode 100644 addons/chemical/functions/fnc_ui_radModule.sqf diff --git a/addons/chemical/ACE_Medical_Injuries.hpp b/addons/chemical/ACE_Medical_Injuries.hpp index 819c5d5e2..11864e2bb 100644 --- a/addons/chemical/ACE_Medical_Injuries.hpp +++ b/addons/chemical/ACE_Medical_Injuries.hpp @@ -12,6 +12,13 @@ class ACE_Medical_Injuries { pain = 0.6; minDamage = 0; }; + // Beta / local high-dose radiation burn. Like a chemical burn but + // weeps less and hurts more (deep tissue damage, slow to heal). + class KAT_RadiationBurn { + bleeding = 0.003; + pain = 0.7; + minDamage = 0; + }; }; class damageTypes { @@ -22,5 +29,12 @@ class ACE_Medical_Injuries { weighting[] = {{0, 1}}; }; }; + class KAT_radiationBurn { + thresholds[] = {{0, 1}}; + selectionSpecific = 0; + class KAT_RadiationBurn { + weighting[] = {{0, 1}}; + }; + }; }; }; diff --git a/addons/chemical/ACE_Medical_Treatment_Actions.hpp b/addons/chemical/ACE_Medical_Treatment_Actions.hpp index 654ac5c30..233f0baeb 100644 --- a/addons/chemical/ACE_Medical_Treatment_Actions.hpp +++ b/addons/chemical/ACE_Medical_Treatment_Actions.hpp @@ -17,7 +17,29 @@ class ACE_Medical_Treatment_Actions { items[] = {"kat_decon_kit"}; treatmentTime = 8; callbackSuccess = QFUNC(clearContamination); - condition = QUOTE((_patient getVariable [ARR_2(QQGVAR(chemicalContamination),'')]) != ''); + condition = QUOTE(((_patient getVariable [ARR_2(QQGVAR(chemicalContamination),'')]) != '') || {(_patient getVariable [ARR_2(QQGVAR(radExternalContam),0)]) > 0}); + icon = QPATHTOF(ui\icon_deconkit.paa); + }; + class KAT_TakeKI: Diagnose { + displayName = CSTRING(TakeKI); + displayNameProgress = CSTRING(TakeKI_Progress); + allowedSelections[] = {"Head"}; + allowSelfTreatment = 1; + items[] = {"kat_potassiumIodide"}; + treatmentTime = 4; + callbackSuccess = QUOTE([ARR_2(_patient,'ki')] call FUNC(treatmentAdvanced_AntiRadLocal)); + condition = "true"; + icon = QPATHTOF(ui\icon_deconkit.paa); + }; + class KAT_TakePrussianBlue: Diagnose { + displayName = CSTRING(TakePrussianBlue); + displayNameProgress = CSTRING(TakePrussianBlue_Progress); + allowedSelections[] = {"Head"}; + allowSelfTreatment = 1; + items[] = {"kat_prussianBlue"}; + treatmentTime = 4; + callbackSuccess = QUOTE([ARR_2(_patient,'prussian')] call FUNC(treatmentAdvanced_AntiRadLocal)); + condition = "true"; icon = QPATHTOF(ui\icon_deconkit.paa); }; }; diff --git a/addons/chemical/CfgEventHandlers.hpp b/addons/chemical/CfgEventHandlers.hpp index e5090d55b..d6d2ac2cd 100644 --- a/addons/chemical/CfgEventHandlers.hpp +++ b/addons/chemical/CfgEventHandlers.hpp @@ -11,6 +11,11 @@ class Extended_Init_EventHandlers { init = QUOTE([ARR_2((_this select 0),false)] call FUNC(init)); }; }; + class kat_radSource { + class ADDON { + init = QUOTE([_this select 0] call FUNC(initRadSourceObject)); + }; + }; }; class Extended_PostInit_EventHandlers { diff --git a/addons/chemical/CfgSounds.hpp b/addons/chemical/CfgSounds.hpp index b450407cc..a14b80bf6 100644 --- a/addons/chemical/CfgSounds.hpp +++ b/addons/chemical/CfgSounds.hpp @@ -157,4 +157,30 @@ class CfgSounds { }; titles[] = {}; }; + + class GVAR(geiger_base) { + name = QGVAR(geiger_base); + sound[] = {QPATHTOF(audio\baseChime.ogg), 1.5, 1.0, 8}; + titles[] = {}; + }; + class GVAR(geiger_slow) { + name = QGVAR(geiger_slow); + sound[] = {QPATHTOF(audio\slowChime.ogg), 1.5, 1.0, 8}; + titles[] = {}; + }; + class GVAR(geiger_normal) { + name = QGVAR(geiger_normal); + sound[] = {QPATHTOF(audio\normalChime.ogg), 1.5, 1.0, 8}; + titles[] = {}; + }; + class GVAR(geiger_fast) { + name = QGVAR(geiger_fast); + sound[] = {QPATHTOF(audio\fastChime.ogg), 1.5, 1.0, 8}; + titles[] = {}; + }; + class GVAR(geiger_rapid) { + name = QGVAR(geiger_rapid); + sound[] = {QPATHTOF(audio\rapidChime.ogg), 1.5, 1.0, 8}; + titles[] = {}; + }; }; diff --git a/addons/chemical/CfgVehicles.hpp b/addons/chemical/CfgVehicles.hpp index c1078f471..3467ab1ea 100644 --- a/addons/chemical/CfgVehicles.hpp +++ b/addons/chemical/CfgVehicles.hpp @@ -47,13 +47,22 @@ class CfgVehicles { class KAT_UseDecon { displayName = CSTRING(UseDecon); - condition = QUOTE('kat_decon_kit' in (items _player) && {(_player getVariable [ARR_2(QQGVAR(chemicalContamination),'')]) != ''}); + condition = QUOTE('kat_decon_kit' in (items _player) && {((_player getVariable [ARR_2(QQGVAR(chemicalContamination),'')]) != '') || {(_player getVariable [ARR_2(QQGVAR(radExternalContam),0)]) > 0}}); exceptions[] = {"isNotSwimming", "isNotInside"}; statement = QUOTE([ARR_2(_player,_player)] call FUNC(decontaminate)); showDisabled = 0; icon = QPATHTOF(ui\icon_deconkit.paa); }; + class KAT_DropRadSource { + displayName = CSTRING(DropRadSource); + condition = QUOTE(!isNull (_player getVariable [ARR_2(QQGVAR(carriedRadSource),objNull)])); + exceptions[] = {"isNotSwimming"}; + statement = QUOTE([_player] call FUNC(dropRadSource)); + showDisabled = 0; + icon = QPATHTOF(ui\icon_deconkit.paa); + }; + class KAT_ChemicalDetector { displayName = CSTRING(ChemicalDetector_Menu); icon = QPATHTOF(ui\kat_chemicalDet_icon.paa); @@ -84,6 +93,45 @@ class CfgVehicles { icon = QPATHTOF(ui\kat_chemicalDet_icon.paa); }; }; + + class KAT_Dosimeter { + displayName = CSTRING(Dosimeter_Menu); + icon = QPATHTOF(ui\kat_chemicalDet_icon.paa); + exceptions[] = {"isNotSwimming"}; + + class KAT_Dosimeter_Enable { + displayName = CSTRING(EnableDosimeter); + condition = QUOTE(([_player] call FUNC(hasDosimeter)) && !(_player getVariable [ARR_2(QQGVAR(dosimeterEnabled),false)])); + exceptions[] = {"isNotSwimming"}; + statement = QUOTE([_player] call FUNC(enableDosimeter)); + showDisabled = 0; + icon = QPATHTOF(ui\kat_chemicalDet_icon.paa); + }; + class KAT_Dosimeter_Disable { + displayName = CSTRING(DisableDosimeter); + condition = QUOTE(([_player] call FUNC(hasDosimeter)) && (_player getVariable [ARR_2(QQGVAR(dosimeterEnabled),false)])); + exceptions[] = {"isNotSwimming"}; + statement = QUOTE(_player setVariable [ARR_3(QQGVAR(dosimeterEnabled),false,true)]); + showDisabled = 0; + icon = QPATHTOF(ui\kat_chemicalDet_icon.paa); + }; + class KAT_Dosimeter_SoundOn { + displayName = CSTRING(EnableDosimeterSound); + condition = QUOTE(!(_player getVariable [ARR_2(QQGVAR(dosimeterSound),false)]) && ([_player] call FUNC(hasDosimeter))); + exceptions[] = {"isNotSwimming"}; + statement = QUOTE(_player setVariable [ARR_3(QQGVAR(dosimeterSound),true,true)]); + showDisabled = 0; + icon = QPATHTOF(ui\kat_chemicalDet_icon.paa); + }; + class KAT_Dosimeter_SoundOff { + displayName = CSTRING(DisableDosimeterSound); + condition = QUOTE((_player getVariable [ARR_2(QQGVAR(dosimeterSound),false)]) && ([_player] call FUNC(hasDosimeter))); + exceptions[] = {"isNotSwimming"}; + statement = QUOTE(_player setVariable [ARR_3(QQGVAR(dosimeterSound),false,true)]); + showDisabled = 0; + icon = QPATHTOF(ui\kat_chemicalDet_icon.paa); + }; + }; }; }; }; @@ -154,6 +202,63 @@ class CfgVehicles { }; }; + class kat_potassiumIodide_Item: Item_Base_F { + scope = 2; + scopeCurator = 2; + displayName = CSTRING(potassiumIodideItem); + author = "DiGii"; + vehicleClass = "Items"; + editorCategory = "EdCat_Equipment"; + editorSubcategory = "EdSubcat_InventoryItems"; + class TransportItems { + class _xx_kat_potassiumIodide { + name = "kat_potassiumIodide"; + count = 1; + }; + }; + }; + + class kat_prussianBlue_Item: Item_Base_F { + scope = 2; + scopeCurator = 2; + displayName = CSTRING(prussianBlueItem); + author = "DiGii"; + vehicleClass = "Items"; + editorCategory = "EdCat_Equipment"; + editorSubcategory = "EdSubcat_InventoryItems"; + class TransportItems { + class _xx_kat_prussianBlue { + name = "kat_prussianBlue"; + count = 1; + }; + }; + }; + + class Land_Canister_Oil_F; + class kat_radSource: Land_Canister_Oil_F { + scope = 2; + scopeCurator = 2; + displayName = CSTRING(radSourceObject); + author = "DiGii"; + editorCategory = "EdCat_Equipment"; + editorSubcategory = "EdSubcat_InventoryItems"; + class ACE_Actions { + class ACE_MainActions { + displayName = CSTRING(radSourceObject); + selection = ""; + distance = 4; + condition = "true"; + class KAT_PickupRadSource { + displayName = CSTRING(PickupRadSource); + distance = 4; + condition = QUOTE(isNull (_player getVariable [ARR_2(QQGVAR(carriedRadSource),objNull)])); + statement = QUOTE([ARR_2(_player,_target)] call FUNC(pickupRadSource)); + icon = QPATHTOF(ui\icon_deconkit.paa); + }; + }; + }; + }; + class Logic; class Module_F: Logic { @@ -250,6 +355,106 @@ class CfgVehicles { isGlobal = 0; }; + class kat_module_radiation: Module_F + { + scope = 2; + side = 7; + displayName = CSTRING(RadModule_Displayname); + category = QEGVAR(zeus,KAM); + function = QFUNC(radmodule); + isTriggerActivated = 0; + functionPriority = 1; + isGlobal = 0; + + class Arguments: AttributesBase + { + class Radius + { + displayName = CSTRING(UI_max_range); + tooltip = CSTRING(RadModule_max_radius_dcs); + typeName = "NUMBER"; + defaultValue = 20; + }; + class Preset + { + displayName = CSTRING(RadModule_preset); + tooltip = CSTRING(RadModule_preset_dcs); + typeName = "NUMBER"; + class values + { + class custom { name = CSTRING(RadPreset_custom); value = 0; default = 1; }; + class cobalt { name = CSTRING(RadPreset_Cobalt60); value = 1; }; + class reactor { name = CSTRING(RadPreset_Reactor); value = 2; }; + class fallout { name = CSTRING(RadPreset_Fallout); value = 3; }; + class alpha { name = CSTRING(RadPreset_AlphaEmitter); value = 4; }; + class dirty { name = CSTRING(RadPreset_DirtyBomb); value = 5; }; + }; + }; + class Alpha + { + displayName = CSTRING(RadModule_alpha); + tooltip = CSTRING(RadModule_strength_dcs); + typeName = "NUMBER"; + defaultValue = 0; + }; + class Beta + { + displayName = CSTRING(RadModule_beta); + tooltip = CSTRING(RadModule_strength_dcs); + typeName = "NUMBER"; + defaultValue = 0; + }; + class Gamma + { + displayName = CSTRING(RadModule_gamma); + tooltip = CSTRING(RadModule_strength_dcs); + typeName = "NUMBER"; + defaultValue = 50; + }; + class Neutron + { + displayName = CSTRING(RadModule_neutron); + tooltip = CSTRING(RadModule_strength_dcs); + typeName = "NUMBER"; + defaultValue = 0; + }; + class PointSource + { + displayName = CSTRING(RadModule_pointSource); + tooltip = CSTRING(RadModule_pointSource_dcs); + typeName = "BOOL"; + defaultValue = 0; + }; + }; + + class ModuleDescription: ModuleDescription { + description = CSTRING(RadModule_description); + sync[] = {"LocationArea_F"}; + + class LocationArea_F { + position = 0; + optional = 1; + duplicate = 1; + synced[] = {"Anything"}; + }; + }; + }; + + class kat_module_zeus_radiation: Module_F + { + scope = 1; + scopeCurator = 2; + side = 7; + curatorCanAttach = 1; + displayName = CSTRING(RadModule_Displayname); + category = QEGVAR(zeus,KAM); + function = QACEFUNC(common,dummy); + curatorInfoType = QGVAR(kat_RscRadModul); + isTriggerActivated = 0; + functionPriority = 1; + isGlobal = 0; + }; + class LandVehicle; class StaticWeapon: LandVehicle { diff --git a/addons/chemical/CfgWeapons.hpp b/addons/chemical/CfgWeapons.hpp index 8d8dde89b..1d651a0b3 100644 --- a/addons/chemical/CfgWeapons.hpp +++ b/addons/chemical/CfgWeapons.hpp @@ -54,16 +54,51 @@ class CfgWeapons { }; }; + class kat_potassiumIodide : ACE_ItemCore { + scope = 2; + author = "DiGii"; + displayName = CSTRING(potassiumIodideItem); + model = "\A3\weapons_F\ammo\mag_univ.p3d"; + picture = QPATHTOF(ui\icon_deconkit.paa); + descriptionShort = CSTRING(potassiumIodideItem_desc); + descriptionUse = CSTRING(potassiumIodideItem_desc); + class ItemInfo: CBA_MiscItem_ItemInfo { + mass = 1; + }; + }; + + class kat_prussianBlue : ACE_ItemCore { + scope = 2; + author = "DiGii"; + displayName = CSTRING(prussianBlueItem); + model = "\A3\weapons_F\ammo\mag_univ.p3d"; + picture = QPATHTOF(ui\icon_deconkit.paa); + descriptionShort = CSTRING(prussianBlueItem_desc); + descriptionUse = CSTRING(prussianBlueItem_desc); + class ItemInfo: CBA_MiscItem_ItemInfo { + mass = 1; + }; + }; + class ItemWatch; class KAT_ChemicalDetector: ItemWatch { ACE_hideItemType = "Watch"; - author = "Mazinski"; + author = "Mazinski"; descriptionShort = CSTRING(ChemicalDetectorDescription); displayName = CSTRING(ChemicalDetectorDisplayName); picture = QPATHTOF(UI\kat_chemicalDet_icon.paa); model = "\a3\Weapons_F_Enoch\Items\ChemicalDetector_01_F.p3d"; }; + class KAT_Dosimeter: ItemWatch { + ACE_hideItemType = "Watch"; + author = "DiGii"; + descriptionShort = CSTRING(DosimeterDescription); + displayName = CSTRING(DosimeterDisplayName); + picture = QPATHTOF(UI\kat_chemicalDet_icon.paa); + model = "\a3\Weapons_F_Enoch\Items\ChemicalDetector_01_F.p3d"; + }; + class CannonCore; class mortar_82mm: CannonCore { // AI-driven mortars get only choking agents (chlorine, phosgene, CS). diff --git a/addons/chemical/RscTitles.hpp b/addons/chemical/RscTitles.hpp index 2d8197edc..0217b621c 100644 --- a/addons/chemical/RscTitles.hpp +++ b/addons/chemical/RscTitles.hpp @@ -238,4 +238,66 @@ class RscTitles }; }; }; + + class KAT_Dosimeter + { + idd = 18836; + enableSimulation = 1; + movingEnable = 0; + fadeIn = 0; + fadeOut = 1; + duration = 10e10; + onLoad = "uiNamespace setVariable ['KAT_Dosimeter', _this select 0];"; + class controls + { + // "DOSIMETER" header, bottom-right corner stack. + class KatDosiHeader: RscText + { + idc = 18813; + style = ST_RIGHT; + shadow = 1; + font = "PuristaBold"; + text = "$STR_KAT_Chemical_Dosimeter_Header"; + x = QUOTE(SAFEZONE_X_RIGHTEDGE - FRAME_W(22)); + y = QUOTE(SAFEZONE_Y_LOWEDGE - FRAME_H(11)); + w = QUOTE(FRAME_W(20)); + h = QUOTE(FRAME_H(3)); + colorBackground[] = {0,0,0,0}; + colorText[] = {0.55,0.85,0.35,0.9}; + sizeEx = QUOTE(FRAME_H(2)); + }; + // Current dose-rate (Gy/h). + class KatDosiRate: RscText + { + idc = 18814; + style = ST_RIGHT; + shadow = 1; + font = "PuristaMedium"; + text = "0.000 Gy/h"; + x = QUOTE(SAFEZONE_X_RIGHTEDGE - FRAME_W(22)); + y = QUOTE(SAFEZONE_Y_LOWEDGE - FRAME_H(8)); + w = QUOTE(FRAME_W(20)); + h = QUOTE(FRAME_H(3)); + colorBackground[] = {0,0,0,0}; + colorText[] = {0.3,0.3,0.3,0.9}; + sizeEx = QUOTE(FRAME_H(2.2)); + }; + // Accumulated whole-body dose (Gy). + class KatDosiDose: RscText + { + idc = 18815; + style = ST_RIGHT; + shadow = 1; + font = "PuristaMedium"; + text = "0.000 Gy"; + x = QUOTE(SAFEZONE_X_RIGHTEDGE - FRAME_W(22)); + y = QUOTE(SAFEZONE_Y_LOWEDGE - FRAME_H(5)); + w = QUOTE(FRAME_W(20)); + h = QUOTE(FRAME_H(3)); + colorBackground[] = {0,0,0,0}; + colorText[] = {0.3,0.3,0.3,0.9}; + sizeEx = QUOTE(FRAME_H(2.2)); + }; + }; + }; }; diff --git a/addons/chemical/XEH_PREP.hpp b/addons/chemical/XEH_PREP.hpp index 099930f0c..aea329e8b 100644 --- a/addons/chemical/XEH_PREP.hpp +++ b/addons/chemical/XEH_PREP.hpp @@ -2,6 +2,7 @@ PREP(addToExposureWatcher); PREP(applyDelayedEffect); PREP(applyMustardDamage); PREP(AttributeRadius); +PREP(AttributeRadModule); PREP(breathing); PREP(canPutGasMask); PREP(canPutOnGasMask); @@ -16,6 +17,7 @@ PREP(createSealAction); PREP(csGrenade); PREP(decontaminate); PREP(displayGasMaskDur); +PREP(dropRadSource); PREP(effect_chlorine); PREP(effect_cs); PREP(effect_mustard); @@ -24,6 +26,8 @@ PREP(effect_sarin); PREP(effect_vx); PREP(emitSmellHint); PREP(enableChemDetector); +PREP(enableDosimeter); +PREP(evaluateRadDose); PREP(exposureWatcherPFH); PREP(fullHealLocal); PREP(gasManagerPFH); @@ -38,22 +42,40 @@ PREP(handleGasMaskDur); PREP(handleRespawn); PREP(hasCBRNProtection); PREP(hasDetector); +PREP(hasDosimeter); PREP(hasGasmask); PREP(hasGasMaskON); PREP(hideChemDetector); +PREP(hideDosimeter); PREP(init); PREP(initGasRegistry); PREP(initModule); +PREP(initRadProfiles); +PREP(initRadSourceObject); +PREP(irradiate); +PREP(pickupRadSource); PREP(poison); PREP(putOnGasMask); +PREP(radContaminationTickPFH); +PREP(radInternalTickPFH); +PREP(radManagerPFH); +PREP(radmodule); +PREP(radProtection); +PREP(registerContaminatedSource); PREP(scheduleAirPoisoningOnset); PREP(scheduleNerveSymptomChain); +PREP(scheduleRadSicknessChain); PREP(showChemDetector); +PREP(showDosimeter); PREP(spawnSmoke); PREP(startContaminationTick); +PREP(startRadContaminationTick); +PREP(startRadInternalTick); PREP(takeOffGasMask); PREP(throwGrenade); +PREP(treatmentAdvanced_AntiRadLocal); PREP(treatmentAdvanced_AtnaaLocal); PREP(treatmentAdvanced_AtropineLocal); PREP(ui_gasModule); +PREP(ui_radModule); PREP(useM8Paper); diff --git a/addons/chemical/XEH_postInit.sqf b/addons/chemical/XEH_postInit.sqf index b363f365c..f87efa428 100644 --- a/addons/chemical/XEH_postInit.sqf +++ b/addons/chemical/XEH_postInit.sqf @@ -5,6 +5,23 @@ //Events ["ace_glassesChanged", LINKFUNC(breathing)] call CBA_fnc_addEventHandler; [QGVAR(poison), LINKFUNC(poison)] call CBA_fnc_addEventHandler; +[QGVAR(irradiate), LINKFUNC(irradiate)] call CBA_fnc_addEventHandler; + +[QGVAR(attachRadSource), { + params ["_source", "_player"]; + if (isNull _source || {isNull _player}) exitWith {}; + detach _source; + _source attachTo [_player, [0, 0.1, 0.1]]; +}] call CBA_fnc_addEventHandler; + +[QGVAR(detachRadSource), { + params ["_source", "_player"]; + if (isNull _source) exitWith {}; + detach _source; + if (isServer && {!isNull _player}) then { + _source setPosATL (_player modelToWorld [0, 1, 0]); + }; +}] call CBA_fnc_addEventHandler; [QGVAR(handleGasMaskDur), LINKFUNC(handleGasMaskDur)] call CBA_fnc_addEventHandler; [QGVAR(addSealAction), LINKFUNC(createSealAction)] call CBA_fnc_addEventHandler; @@ -52,6 +69,18 @@ missionNamespace setVariable [QGVAR(availGasmaskList), _array, true]; true }, { false }, [24, [false, false, false]], false] call CBA_fnc_addKeybind; +[CBA_SETTINGS_CAT, QGVAR(showDosimeter), LLSTRING(ShowDosimeter), { + if (!([ACE_player, objNull, ["isNotEscorting", "isNotInside"]] call ACEFUNC(common,canInteractWith)) || {!('KAT_Dosimeter' in assignedItems ACE_player)}) exitWith { false }; + + if !(GETMVAR(GVAR(DosimeterActive),false)) then { + [ACE_player] call FUNC(showDosimeter); + } else { + call FUNC(hideDosimeter); + }; + + true +}, { false }, [25, [false, false, false]], false] call CBA_fnc_addKeybind; + // Client-side particle tracking (all machines) GVAR(clientParticles) = createHashMap; @@ -150,6 +179,75 @@ if (!isServer) exitWith {}; GVAR(gasSources) = createHashMap; GVAR(exposureWatcherUnits) = createHashMap; +GVAR(radSources) = createHashMap; +GVAR(radExposedUnits) = []; + +[QGVAR(addRadSource), { + params [ + ["_source", objNull, [objNull, []]], + ["_radius", 0, [0]], + ["_strengths", [], [[]]], + ["_falloff", "linear", [""]], + ["_key", ""], + ["_condition", {true}, [{}]], + ["_conditionArgs", []] + ]; + + private _isObject = _source isEqualType objNull; + + if !(_isObject || {_source isEqualTypeParams [0, 0, 0]}) exitWith {}; + if (_isObject && {isNull _source}) exitWith {}; + if (_radius == 0) exitWith {}; + if (count _strengths != 4) exitWith {}; + if ((_strengths findIf {_x > 0}) == -1) exitWith {}; + if (_key isEqualTo "") exitWith {}; + + private _hashedKey = hashValue _key; + if (isNil "_hashedKey") exitWith { + ERROR_2("Unsupported key type used: %1 - %2",_key,typeName _key); + }; + + private _sourcePos = if (_isObject) then { + getPosATL _source + } else { + ASLToATL _source + }; + + private _radLogic = createVehicle [QGVAR(logic), _sourcePos, [], 0, "CAN_COLLIDE"]; + + if (_isObject) then { + _radLogic attachTo [_source]; + } else { + _radLogic setPosATL [_sourcePos select 0, _sourcePos select 1, 0]; + }; + + if (_hashedKey in GVAR(radSources)) then { + [QGVAR(removeRadSource), _key] call CBA_fnc_localEvent; + }; + + GVAR(radSources) set [_hashedKey, [_radLogic, _radius, _strengths, _falloff, _condition, _conditionArgs]]; +}] call CBA_fnc_addEventHandler; + +[QGVAR(removeRadSource), { + params ["_key"]; + private _hashedKey = hashValue _key; + + if (isNil "_hashedKey") exitWith { + ERROR_2("Unsupported key type used: %1 - %2",_key,typeName _key); + }; + + (GVAR(radSources) deleteAt _hashedKey) params [["_radLogic", objNull]]; + + detach _radLogic; + deleteVehicle _radLogic; +}] call CBA_fnc_addEventHandler; + +[QGVAR(serverRegisterContamSource), LINKFUNC(registerContaminatedSource)] call CBA_fnc_addEventHandler; + +[QGVAR(serverRemoveContamSource), { + params ["_unit"]; + [QGVAR(removeRadSource), _unit] call CBA_fnc_localEvent; +}] call CBA_fnc_addEventHandler; // Server-side: register a unit with the exposure watcher PFH (idempotent). // Triggered by FUNC(addToExposureWatcher) on the unit's owner. @@ -257,4 +355,5 @@ GVAR(exposureWatcherUnits) = createHashMap; }] call CBA_fnc_addEventHandler; [LINKFUNC(gasManagerPFH), GAS_MANAGER_PFH_DELAY, []] call CBA_fnc_addPerFrameHandler; +[LINKFUNC(radManagerPFH), RAD_MANAGER_PFH_DELAY, []] call CBA_fnc_addPerFrameHandler; diff --git a/addons/chemical/XEH_preInit.sqf b/addons/chemical/XEH_preInit.sqf index 22a34bd93..a6eea849c 100644 --- a/addons/chemical/XEH_preInit.sqf +++ b/addons/chemical/XEH_preInit.sqf @@ -10,5 +10,6 @@ PREP_RECOMPILE_END; #include "initSettings.inc.sqf" call FUNC(initGasRegistry); +call FUNC(initRadProfiles); ADDON = true; diff --git a/addons/chemical/config.cpp b/addons/chemical/config.cpp index dba5e8394..789539bf0 100644 --- a/addons/chemical/config.cpp +++ b/addons/chemical/config.cpp @@ -8,10 +8,15 @@ class CfgPatches { "kat_sealant_Item", "kat_module_gas", "kat_module_zeus_gas", + "kat_module_radiation", + "kat_module_zeus_radiation", "KAT_Gas_TripWireMine", "kat_gasmaskFilter_Item", "kat_m8paper_Item", - "kat_decon_kit_Item" + "kat_decon_kit_Item", + "kat_potassiumIodide_Item", + "kat_prussianBlue_Item", + "kat_radSource" }; weapons[] = { "kat_sealant", @@ -19,8 +24,11 @@ class CfgPatches { "KAT_CAS_M43_Bomb", "KAT_CAS_R_80mm_GAS_POD", "KAT_ChemicalDetector", + "KAT_Dosimeter", "kat_m8paper", - "kat_decon_kit" + "kat_decon_kit", + "kat_potassiumIodide", + "kat_prussianBlue" }; ammo[] = { "KAT_Gas_TripWireMine_Ammo", diff --git a/addons/chemical/functions/fnc_AttributeRadModule.sqf b/addons/chemical/functions/fnc_AttributeRadModule.sqf new file mode 100644 index 000000000..c1cc9f7b0 --- /dev/null +++ b/addons/chemical/functions/fnc_AttributeRadModule.sqf @@ -0,0 +1,76 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Wires the radiation Zeus module attribute inputs (radius, preset, per-type + * strengths, point source). Selecting a preset fills the four strength edits. + * Writes ui_radius / ui_radStrengths / ui_radPointSource onto the display for + * fnc_ui_radModule to read on confirm. + * + * Arguments: + * 0: Control + * + * Return Value: + * None + * + * Example: + * [_control] call kat_chemical_fnc_AttributeRadModule; + * + * Public: No + */ + +params ["_control"]; + +private _display = ctrlParent _control; +_control ctrlRemoveAllEventHandlers "SetFocus"; + +private _presetCombo = _display displayCtrl 1624; + +(_display displayCtrl 1621) ctrlSetText "20"; +(_display displayCtrl 1625) ctrlSetText "0"; +(_display displayCtrl 1626) ctrlSetText "0"; +(_display displayCtrl 1627) ctrlSetText "50"; +(_display displayCtrl 1628) ctrlSetText "0"; + +private _presetNames = ["custom", "Cobalt60", "Reactor", "Fallout", "AlphaEmitter", "DirtyBomb"]; +private _presetLabels = [LLSTRING(RadPreset_custom), LLSTRING(RadPreset_Cobalt60), LLSTRING(RadPreset_Reactor), LLSTRING(RadPreset_Fallout), LLSTRING(RadPreset_AlphaEmitter), LLSTRING(RadPreset_DirtyBomb)]; +lbClear _presetCombo; +{ _presetCombo lbAdd _x } forEach _presetLabels; +_presetCombo lbSetCurSel 0; + +_display setVariable [QGVAR(ui_presetNames), _presetNames]; +_display setVariable [QGVAR(ui_radius), 20]; +_display setVariable [QGVAR(ui_radStrengths), [0, 0, 50, 0]]; +_display setVariable [QGVAR(ui_radPointSource), false]; + +private _fnc_onKeyUp = { + params ["_display"]; + _display setVariable [QGVAR(ui_radius), parseNumber (ctrlText (_display displayCtrl 1621))]; + _display setVariable [QGVAR(ui_radStrengths), [ + parseNumber (ctrlText (_display displayCtrl 1625)), + parseNumber (ctrlText (_display displayCtrl 1626)), + parseNumber (ctrlText (_display displayCtrl 1627)), + parseNumber (ctrlText (_display displayCtrl 1628)) + ]]; +}; + +private _fnc_onPreset = { + params ["_combo"]; + private _display = ctrlParent _combo; + private _name = (_display getVariable [QGVAR(ui_presetNames), ["custom"]]) param [lbCurSel _combo, "custom"]; + if (_name isEqualTo "custom") exitWith {}; + private _s = +(GVAR(radPresets) getOrDefault [_name, [0, 0, 0, 0]]); + (_display displayCtrl 1625) ctrlSetText str (_s select 0); + (_display displayCtrl 1626) ctrlSetText str (_s select 1); + (_display displayCtrl 1627) ctrlSetText str (_s select 2); + (_display displayCtrl 1628) ctrlSetText str (_s select 3); + _display setVariable [QGVAR(ui_radStrengths), _s]; +}; + +private _fnc_onCheck = { + params ["_check"]; + (ctrlParent _check) setVariable [QGVAR(ui_radPointSource), cbChecked _check]; +}; + +_display displayAddEventHandler ["KeyUp", _fnc_onKeyUp]; +_presetCombo ctrlAddEventHandler ["LBSelChanged", _fnc_onPreset]; +(_display displayCtrl 1623) ctrlAddEventHandler ["CheckedChanged", _fnc_onCheck]; diff --git a/addons/chemical/functions/fnc_applyDelayedEffect.sqf b/addons/chemical/functions/fnc_applyDelayedEffect.sqf index 90c9fb103..dda704353 100644 --- a/addons/chemical/functions/fnc_applyDelayedEffect.sqf +++ b/addons/chemical/functions/fnc_applyDelayedEffect.sqf @@ -59,4 +59,52 @@ switch (_effect) do { case "cardiac": { [QACEGVAR(medical,FatalVitals), _unit] call CBA_fnc_localEvent; }; + + case "radProdromal": { + if ((_unit getVariable [QGVAR(radTimeToVomit), -1]) < 0) then { + _unit setVariable [QGVAR(radTimeToVomit), CBA_missionTime, true]; + }; + + private _sounds = [ + QPATHTOEF_SOUND(airway,sounds\puking1.wav), + QPATHTOEF_SOUND(airway,sounds\puking2.wav), + QPATHTOEF_SOUND(airway,sounds\puking1.wav) + ]; + playSound3D [selectRandom _sounds, _unit, false, getPosASL _unit, 8, 1, 15]; + + private _pain = _unit getVariable [VAR_PAIN, 0]; + _unit setVariable [VAR_PAIN, (_pain + 0.1) min 1, true]; + + if (GVAR(rad_showSickness) && {_unit == ACE_player}) then { + [LLSTRING(radHint_nausea), 1.5, _unit] call ACEFUNC(common,displayTextStructured); + }; + }; + case "radHema": { + private _pain = _unit getVariable [VAR_PAIN, 0]; + _unit setVariable [VAR_PAIN, (_pain + 0.15) min 1, true]; + + if (GVAR(rad_showSickness) && {_unit == ACE_player}) then { + [LLSTRING(radHint_weak), 1.5, _unit] call ACEFUNC(common,displayTextStructured); + }; + }; + case "radGI": { + private _sounds = [ + QPATHTOEF_SOUND(airway,sounds\puking1.wav), + QPATHTOEF_SOUND(airway,sounds\puking2.wav) + ]; + playSound3D [selectRandom _sounds, _unit, false, getPosASL _unit, 8, 1, 15]; + + private _pain = _unit getVariable [VAR_PAIN, 0]; + _unit setVariable [VAR_PAIN, (_pain + 0.25) min 1, true]; + + if (GVAR(rad_showSickness) && {_unit == ACE_player}) then { + [LLSTRING(radHint_gi), 1.5, _unit] call ACEFUNC(common,displayTextStructured); + }; + }; + case "radCNS": { + [_unit, true] call ACEFUNC(medical,setUnconscious); + }; + case "radCardiac": { + [QACEGVAR(medical,FatalVitals), _unit] call CBA_fnc_localEvent; + }; }; diff --git a/addons/chemical/functions/fnc_clearContamination.sqf b/addons/chemical/functions/fnc_clearContamination.sqf index b7fdbf7aa..8f89b66b8 100644 --- a/addons/chemical/functions/fnc_clearContamination.sqf +++ b/addons/chemical/functions/fnc_clearContamination.sqf @@ -23,3 +23,6 @@ _patient setVariable [QGVAR(chemicalContamination), "", true]; _patient setVariable [QGVAR(mustardDeadline_eye), 0, true]; _patient setVariable [QGVAR(mustardDeadline_burn), 0, true]; _patient setVariable [QGVAR(mustardBurnActive), false, true]; + +_patient setVariable [QGVAR(radExternalContam), 0, true]; +[QGVAR(serverRemoveContamSource), [_patient]] call CBA_fnc_serverEvent; diff --git a/addons/chemical/functions/fnc_decontaminate.sqf b/addons/chemical/functions/fnc_decontaminate.sqf index 439a4a5f8..d8f0d6baa 100644 --- a/addons/chemical/functions/fnc_decontaminate.sqf +++ b/addons/chemical/functions/fnc_decontaminate.sqf @@ -47,7 +47,7 @@ private _duration = missionNamespace getVariable [QGVAR(deconActionTime), 8]; params ["_args"]; _args params ["_player", "_target"]; ([_player, _player, ["kat_decon_kit"]] call ACEFUNC(medical_treatment,hasItem)) && - {(_target getVariable [QGVAR(chemicalContamination), ""]) != ""} + {((_target getVariable [QGVAR(chemicalContamination), ""]) != "") || {(_target getVariable [QGVAR(radExternalContam), 0]) > 0}} }, ["isNotInside"] ] call ACEFUNC(common,progressBar); diff --git a/addons/chemical/functions/fnc_dropRadSource.sqf b/addons/chemical/functions/fnc_dropRadSource.sqf new file mode 100644 index 000000000..7695eb90a --- /dev/null +++ b/addons/chemical/functions/fnc_dropRadSource.sqf @@ -0,0 +1,21 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Drops the radioactive source the player is carrying back onto the ground. + * + * Arguments: + * 0: Player + * + * Return Value: + * None + * + * Public: No + */ + +params ["_player"]; + +private _source = _player getVariable [QGVAR(carriedRadSource), objNull]; +if (isNull _source) exitWith {}; + +_player setVariable [QGVAR(carriedRadSource), objNull, true]; +[QGVAR(detachRadSource), [_source, _player]] call CBA_fnc_globalEvent; diff --git a/addons/chemical/functions/fnc_enableDosimeter.sqf b/addons/chemical/functions/fnc_enableDosimeter.sqf new file mode 100644 index 000000000..c9d5c2189 --- /dev/null +++ b/addons/chemical/functions/fnc_enableDosimeter.sqf @@ -0,0 +1,51 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Starts the dosimeter audio loop. While the dosimeter sound is enabled it + * emits geiger clicks whose tier scales with the current dose-rate, and only + * while the unit is actually being irradiated. Single-instance; self-terminates + * when the dosimeter is disabled, the unit dies, or the item is removed. + * + * Arguments: + * 0: Unit + * + * Return Value: + * None + * + * Example: + * [player] call kat_chemical_fnc_enableDosimeter + * + * Public: No + */ + +params ["_unit"]; + +_unit setVariable [QGVAR(dosimeterEnabled), true, true]; + +if (_unit getVariable [QGVAR(dosimeterAudioActive), false]) exitWith {}; +_unit setVariable [QGVAR(dosimeterAudioActive), true, true]; + +[{ + params ["_args", "_idPFH"]; + _args params ["_unit"]; + + if (!alive _unit || {!("KAT_Dosimeter" in assignedItems _unit)} || {!(_unit getVariable [QGVAR(dosimeterEnabled), false])}) exitWith { + _unit setVariable [QGVAR(dosimeterEnabled), false, true]; + _unit setVariable [QGVAR(dosimeterAudioActive), false, true]; + _idPFH call CBA_fnc_removePerFrameHandler; + }; + + if !(_unit getVariable [QGVAR(dosimeterSound), false]) exitWith {}; + + private _rate = _unit getVariable [QGVAR(radDoseRate), 0]; + if (_rate < 0.01) exitWith {}; + + private _tone = switch (true) do { + case (_rate > 10): { QGVAR(geiger_rapid) }; + case (_rate > 1): { QGVAR(geiger_fast) }; + case (_rate > 0.1): { QGVAR(geiger_normal) }; + default { QGVAR(geiger_slow) }; + }; + + [QGVAR(playTone), [_unit, _tone], _unit] call CBA_fnc_targetEvent; +}, 5, [_unit]] call CBA_fnc_addPerFrameHandler; diff --git a/addons/chemical/functions/fnc_evaluateRadDose.sqf b/addons/chemical/functions/fnc_evaluateRadDose.sqf new file mode 100644 index 000000000..dc21e2684 --- /dev/null +++ b/addons/chemical/functions/fnc_evaluateRadDose.sqf @@ -0,0 +1,49 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Maps a unit's accumulated whole-body dose to an Acute Radiation Syndrome + * (ARS) severity tier, sets the marrow/immune suppression factors for that + * tier, and (when the tier rises) schedules the staged sickness chain. + * + * Tiers: 0 none, 1 mild, 2 moderate (hematopoietic), 3 severe (GI), + * 4 lethal (neurovascular/CNS). + * + * Idempotent per tier: a chain is only (re)scheduled when the tier increases, + * so this is safe to call every exposure tick. + * + * Arguments: + * 0: Unit + * + * Return Value: + * None + * + * Public: No + */ + +params ["_unit"]; + +private _dose = (_unit getVariable [QGVAR(radDoseWB), 0]) + (_unit getVariable [QGVAR(radInternalBurden), 0]); + +private _tier = switch (true) do { + case (_dose >= GVAR(rad_doseThreshold_lethal)): {4}; + case (_dose >= GVAR(rad_doseThreshold_severe)): {3}; + case (_dose >= GVAR(rad_doseThreshold_moderate)): {2}; + case (_dose >= GVAR(rad_doseThreshold_mild)): {1}; + default {0}; +}; + +private _currentTier = _unit getVariable [QGVAR(radSicknessTier), 0]; + +private _suppression = [1, 0.85, 0.6, 0.35, 0.1] select _tier; +if (_tier > _currentTier) then { + _unit setVariable [QGVAR(radMarrowFactor), _suppression, true]; + _unit setVariable [QGVAR(radImmuneFactor), _suppression, true]; +}; + +if (_tier <= _currentTier) exitWith {}; + +_unit setVariable [QGVAR(radSicknessTier), _tier, true]; + +private _scale = [1, 1, 0.7, 0.45, 0.2] select _tier; + +[_unit, _tier, _scale] call FUNC(scheduleRadSicknessChain); diff --git a/addons/chemical/functions/fnc_exposureWatcherPFH.sqf b/addons/chemical/functions/fnc_exposureWatcherPFH.sqf index e953f8eab..63337d8b7 100644 --- a/addons/chemical/functions/fnc_exposureWatcherPFH.sqf +++ b/addons/chemical/functions/fnc_exposureWatcherPFH.sqf @@ -105,6 +105,29 @@ private _toRemove = []; }; }; + private _radTier = _unit getVariable [QGVAR(radSicknessTier), 0]; + + { + _x params ["_var", "_effect", "_minTier"]; + private _deadline = _unit getVariable [_var, 0]; + if (_deadline > 0) then { + if (_now >= _deadline) then { + if (_radTier >= _minTier) then { + [QGVAR(applyDelayedEffect), [_unit, _effect], _unit] call CBA_fnc_targetEvent; + }; + _unit setVariable [_var, -1, true]; + } else { + _hasPending = true; + }; + }; + } forEach [ + [QGVAR(radDeadline_prodromal), "radProdromal", 1], + [QGVAR(radDeadline_hema), "radHema", 2], + [QGVAR(radDeadline_gi), "radGI", 3], + [QGVAR(radDeadline_cns), "radCNS", 4], + [QGVAR(radDeadline_cardiac), "radCardiac", 4] + ]; + if (!_hasPending) then { _toRemove pushBack _netId; }; diff --git a/addons/chemical/functions/fnc_fullHealLocal.sqf b/addons/chemical/functions/fnc_fullHealLocal.sqf index 9a006faaf..dd9325eea 100644 --- a/addons/chemical/functions/fnc_fullHealLocal.sqf +++ b/addons/chemical/functions/fnc_fullHealLocal.sqf @@ -39,3 +39,24 @@ _patient setVariable [QGVAR(mustardBurnActive), false, true]; _patient setVariable [QGVAR(nerveDeadline_vomit), 0, true]; _patient setVariable [QGVAR(nerveDeadline_unconscious), 0, true]; _patient setVariable [QGVAR(nerveDeadline_cardiac), 0, true]; + +_patient setVariable [QGVAR(radDoseWB), 0, true]; +_patient setVariable [QGVAR(radDoseLimb), [0, 0, 0, 0, 0, 0], true]; +_patient setVariable [QGVAR(radInternalBurden), 0, true]; +_patient setVariable [QGVAR(radExternalContam), 0, true]; +_patient setVariable [QGVAR(radDoseRate), 0, true]; +_patient setVariable [QGVAR(radSicknessTier), 0, true]; +_patient setVariable [QGVAR(radMarrowFactor), 1, true]; +_patient setVariable [QGVAR(radImmuneFactor), 1, true]; +_patient setVariable [QGVAR(radInfection), false, true]; +_patient setVariable [QGVAR(radProtectiveWindow), 0, true]; +_patient setVariable [QGVAR(radChelationWindow), 0, true]; +_patient setVariable [QGVAR(radDominantType), "", true]; +_patient setVariable [QGVAR(radInternalPFHActive), false, true]; +_patient setVariable [QGVAR(radContamPFHActive), false, true]; +_patient setVariable [QGVAR(radTimeToVomit), -1, true]; +_patient setVariable [QGVAR(radDeadline_prodromal), 0, true]; +_patient setVariable [QGVAR(radDeadline_hema), 0, true]; +_patient setVariable [QGVAR(radDeadline_gi), 0, true]; +_patient setVariable [QGVAR(radDeadline_cns), 0, true]; +_patient setVariable [QGVAR(radDeadline_cardiac), 0, true]; diff --git a/addons/chemical/functions/fnc_handleRespawn.sqf b/addons/chemical/functions/fnc_handleRespawn.sqf index 2f60a8c9b..bce845c15 100644 --- a/addons/chemical/functions/fnc_handleRespawn.sqf +++ b/addons/chemical/functions/fnc_handleRespawn.sqf @@ -26,3 +26,5 @@ params ["_unit"]; [_unit] call FUNC(fullHealLocal); _unit setVariable [QGVAR(detectorSound), false, true]; +_unit setVariable [QGVAR(dosimeterEnabled), false, true]; +_unit setVariable [QGVAR(dosimeterSound), false, true]; diff --git a/addons/chemical/functions/fnc_hasDosimeter.sqf b/addons/chemical/functions/fnc_hasDosimeter.sqf new file mode 100644 index 000000000..c99703484 --- /dev/null +++ b/addons/chemical/functions/fnc_hasDosimeter.sqf @@ -0,0 +1,20 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Whether the unit has a dosimeter assigned (in the watch slot). + * + * Arguments: + * 0: Unit + * + * Return Value: + * Bool + * + * Example: + * [player] call kat_chemical_fnc_hasDosimeter; + * + * Public: No + */ + +params ["_unit"]; + +"KAT_Dosimeter" in assignedItems _unit diff --git a/addons/chemical/functions/fnc_hideDosimeter.sqf b/addons/chemical/functions/fnc_hideDosimeter.sqf new file mode 100644 index 000000000..945a61b54 --- /dev/null +++ b/addons/chemical/functions/fnc_hideDosimeter.sqf @@ -0,0 +1,19 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Removes the dosimeter HUD from the screen. + * + * Arguments: + * None + * + * Return Value: + * None + * + * Example: + * call kat_chemical_fnc_hideDosimeter + * + * Public: No + */ + +GVAR(DosimeterActive) = false; +"KAT_Dosimeter" cutText ["", "PLAIN", 0, true]; diff --git a/addons/chemical/functions/fnc_initRadProfiles.sqf b/addons/chemical/functions/fnc_initRadProfiles.sqf new file mode 100644 index 000000000..7bf141dd8 --- /dev/null +++ b/addons/chemical/functions/fnc_initRadProfiles.sqf @@ -0,0 +1,38 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Builds the radiation type-profile and source-preset registries. + * + * GVAR(radProfiles): type-id -> [wbWeight, skinWeight, inhaleWeight, qualityFactor, [maskF, cbrnF, radGearF, vehicleF]] + * wbWeight - fraction of shielded external rate that becomes whole-body dose + * skinWeight - fraction that becomes local skin/limb dose (beta burns) + * inhaleWeight - fraction of raw rate that becomes internal burden when unmasked + * qualityFactor - biological weighting on whole-body dose (neutron/alpha high) + * [..F] - per-gear external absorbed-dose multipliers (0 = blocks fully) + * + * GVAR(radPresets): preset-id -> [alpha, beta, gamma, neutron] dose-rate (Gy/h) + * + * Arguments: + * None + * + * Return Value: + * None + * + * Public: No + */ + +GVAR(radProfiles) = createHashMapFromArray [ + ["alpha", [0, 0.05, 1.0, 20, [0, 0, 0, 0 ]]], + ["beta", [0.1, 1.0, 0.3, 1, [0.7, 0.1, 0.1, 0.2 ]]], + ["gamma", [1.0, 0.05, 0.05, 1, [0.97, 0.9, 0.4, 0.5 ]]], + ["neutron", [1.2, 0, 0, 10, [1, 0.97, 0.7, 0.7 ]]] +]; + +GVAR(radPresets) = createHashMapFromArray [ + ["custom", [0, 0, 0, 0 ]], + ["Cobalt60", [0, 0, 50, 0 ]], + ["Reactor", [0, 0, 40, 20]], + ["Fallout", [0, 30, 20, 0 ]], + ["AlphaEmitter", [50, 5, 0, 0 ]], + ["DirtyBomb", [20, 30, 25, 0 ]] +]; diff --git a/addons/chemical/functions/fnc_initRadSourceObject.sqf b/addons/chemical/functions/fnc_initRadSourceObject.sqf new file mode 100644 index 000000000..9cd2069d3 --- /dev/null +++ b/addons/chemical/functions/fnc_initRadSourceObject.sqf @@ -0,0 +1,25 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Registers a placed radioactive-source object as an inverse-square point + * source (default Cobalt-60 profile). Runs once per object on the server. + * + * Arguments: + * 0: Source object + * + * Return Value: + * None + * + * Public: No + */ + +params ["_obj"]; + +if (!isServer || {isNull _obj}) exitWith {}; + +private _strengths = +(GVAR(radPresets) getOrDefault ["Cobalt60", [0, 0, 50, 0]]); + +[QGVAR(addRadSource), [_obj, 15, _strengths, "inverseSquare", _obj, { + params ["_obj"]; + !isNull _obj +}, [_obj]]] call CBA_fnc_localEvent; diff --git a/addons/chemical/functions/fnc_irradiate.sqf b/addons/chemical/functions/fnc_irradiate.sqf new file mode 100644 index 000000000..7d2656c40 --- /dev/null +++ b/addons/chemical/functions/fnc_irradiate.sqf @@ -0,0 +1,109 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Per-unit radiation exposure handler. Routes each radiation type's incident + * dose-rate (after type-specific shielding) into whole-body absorbed dose, + * local skin/limb dose (beta burns), inhaled internal burden, and surface + * contamination, then re-evaluates radiation sickness. + * + * Runs local to the unit's owner (fired via QGVAR(irradiate) target event). + * The geiger reading (radDoseRate) is owned by fnc_radManagerPFH, not here. + * + * Arguments: + * 0: Unit + * 1: Incident dose-rates by type [alpha,beta,gamma,neutron] (Gy/h) + * 2: Source logic + * + * Return Value: + * None + * + * Public: No + */ + +params ["_unit", "_ratesByType", "_radLogic"]; + +if (!GVAR(rad_enable)) exitWith {}; + +if (!local _unit) exitWith { + TRACE_1("rad unit not local",_unit); +}; + +if (getNumber (configOf _unit >> "isPlayableLogic") == 1 || {!(_unit isKindOf "CAManBase")}) exitWith { + TRACE_1("rad unit is virtual or not a man",_unit); +}; + +if !(isDamageAllowed _unit && {_unit getVariable [QACEGVAR(medical,allowDamage), true]}) exitWith { + TRACE_1("rad unit is invulnerable",_unit); +}; + +private _dt = RAD_MANAGER_PFH_DELAY; +private _hasMask = [_unit] call FUNC(hasGasMaskON); +private _kiActive = CBA_missionTime < (_unit getVariable [QGVAR(radProtectiveWindow), 0]); +private _veryClose = (_radLogic distance _unit) < GVAR(rad_localProximity); +private _selfContamSource = (_radLogic getVariable [QGVAR(contamSourceOwner), objNull]) isEqualTo _unit; + +private _wbAdd = 0; +private _internalAdd = 0; +private _contamAdd = 0; +private _skinByPart = [0, 0, 0, 0, 0, 0]; + +{ + private _type = _x; + private _rate = _ratesByType select _forEachIndex; + if (_rate <= 0) then { continue }; + + (GVAR(radProfiles) get _type) params ["_wbW", "_skinW", "_inhaleW", "_qf"]; + private _ext = _rate * ([_unit, _type] call FUNC(radProtection)); + + _wbAdd = _wbAdd + (_ext * _wbW * _qf); + + private _skin = _ext * _skinW; + if (_skin > 0) then { + if (_veryClose) then { + _skinByPart set [2, (_skinByPart select 2) + (_skin * 0.5)]; + _skinByPart set [3, (_skinByPart select 3) + (_skin * 0.5)]; + } else { + _skinByPart = _skinByPart apply { _x + (_skin / 6) }; + }; + }; + + if (!_hasMask && {_inhaleW > 0}) then { + private _inhale = _rate * _inhaleW; + if (_kiActive) then { _inhale = _inhale * GVAR(rad_kiFactor); }; + _internalAdd = _internalAdd + _inhale; + }; + + if (_type != "neutron" && {!_selfContamSource}) then { + _contamAdd = _contamAdd + _rate; + }; +} forEach RAD_TYPES; + +if (_wbAdd > 0) then { + _unit setVariable [QGVAR(radDoseWB), (_unit getVariable [QGVAR(radDoseWB), 0]) + (_wbAdd * _dt), true]; +}; + +private _limb = _unit getVariable [QGVAR(radDoseLimb), [0, 0, 0, 0, 0, 0]]; +private _thr = GVAR(rad_skinBurnThreshold); +private _parts = RAD_BODY_PARTS; +{ + private _new = (_limb select _forEachIndex) + ((_skinByPart select _forEachIndex) * _dt); + if (_new >= _thr) then { + [_unit, 0.1 + random 0.3, _parts select _forEachIndex, "KAT_radiationBurn", _unit] call ACEFUNC(medical,addDamageToUnit); + _new = _new - _thr; + }; + _limb set [_forEachIndex, _new]; +} forEach _limb; +_unit setVariable [QGVAR(radDoseLimb), _limb, true]; + +if (_internalAdd > 0) then { + _unit setVariable [QGVAR(radInternalBurden), (_unit getVariable [QGVAR(radInternalBurden), 0]) + (_internalAdd * _dt), true]; + [_unit] call FUNC(startRadInternalTick); +}; + +if (_contamAdd > 0) then { + private _contam = ((_unit getVariable [QGVAR(radExternalContam), 0]) + (_contamAdd * GVAR(rad_contaminationDeposition) * _dt)) min 1; + _unit setVariable [QGVAR(radExternalContam), _contam, true]; + [_unit] call FUNC(startRadContaminationTick); +}; + +[_unit] call FUNC(evaluateRadDose); diff --git a/addons/chemical/functions/fnc_pickupRadSource.sqf b/addons/chemical/functions/fnc_pickupRadSource.sqf new file mode 100644 index 000000000..dd6417a05 --- /dev/null +++ b/addons/chemical/functions/fnc_pickupRadSource.sqf @@ -0,0 +1,25 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Picks up a radioactive-source object: attaches it to the player (globally, so + * the server-side source follows them and doses point-blank). The attached + * source keeps its registration; local skin/limb dose comes from the proximity + * branch in fnc_irradiate. + * + * Arguments: + * 0: Player + * 1: Source object + * + * Return Value: + * None + * + * Public: No + */ + +params ["_player", "_source"]; + +if (isNull _source) exitWith {}; +if !(isNull (_player getVariable [QGVAR(carriedRadSource), objNull])) exitWith {}; + +_player setVariable [QGVAR(carriedRadSource), _source, true]; +[QGVAR(attachRadSource), [_source, _player]] call CBA_fnc_globalEvent; diff --git a/addons/chemical/functions/fnc_radContaminationTickPFH.sqf b/addons/chemical/functions/fnc_radContaminationTickPFH.sqf new file mode 100644 index 000000000..e04f538c0 --- /dev/null +++ b/addons/chemical/functions/fnc_radContaminationTickPFH.sqf @@ -0,0 +1,36 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Owner-local per-unit tick for external (surface) radioactive contamination. + * Decays contamination by weathering and keeps the server-side + * cross-contamination source in sync (so the unit doses itself and nearby + * people and reads on geigers). Self-terminates and removes the source when + * contamination is cleared (decon or full decay) or the unit dies. + * + * Arguments: + * 0: [_unit] + * 1: PFH handle + * + * Return Value: + * None + * + * Public: No + */ + +params ["_args", "_pfhHandle"]; +_args params ["_unit"]; + +if (isNull _unit || {!alive _unit} || {(_unit getVariable [QGVAR(radExternalContam), 0]) <= 0.01}) exitWith { + if (!isNull _unit) then { + _unit setVariable [QGVAR(radExternalContam), 0, true]; + _unit setVariable [QGVAR(radContamPFHActive), false, true]; + [QGVAR(serverRemoveContamSource), [_unit]] call CBA_fnc_serverEvent; + }; + _pfhHandle call CBA_fnc_removePerFrameHandler; +}; + +private _interval = 5; +private _contam = (_unit getVariable [QGVAR(radExternalContam), 0]) * (0 max (1 - (GVAR(rad_contaminationDecay) * _interval))); +_unit setVariable [QGVAR(radExternalContam), _contam, true]; + +[QGVAR(serverRegisterContamSource), [_unit, _contam]] call CBA_fnc_serverEvent; diff --git a/addons/chemical/functions/fnc_radInternalTickPFH.sqf b/addons/chemical/functions/fnc_radInternalTickPFH.sqf new file mode 100644 index 000000000..5e0b82b7a --- /dev/null +++ b/addons/chemical/functions/fnc_radInternalTickPFH.sqf @@ -0,0 +1,45 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Owner-local per-unit tick that turns internal radionuclide burden into + * committed whole-body dose and decays the burden (biological elimination, + * accelerated while a chelation window is active). Self-terminates when the + * burden is cleared or the unit dies. + * + * Arguments: + * 0: [_unit] + * 1: PFH handle + * + * Return Value: + * None + * + * Public: No + */ + +params ["_args", "_pfhHandle"]; +_args params ["_unit"]; + +if (isNull _unit || {!alive _unit}) exitWith { + if (!isNull _unit) then { _unit setVariable [QGVAR(radInternalPFHActive), false, true]; }; + _pfhHandle call CBA_fnc_removePerFrameHandler; +}; + +private _burden = _unit getVariable [QGVAR(radInternalBurden), 0]; + +if (_burden < 0.001) exitWith { + _unit setVariable [QGVAR(radInternalBurden), 0, true]; + _unit setVariable [QGVAR(radInternalPFHActive), false, true]; + _pfhHandle call CBA_fnc_removePerFrameHandler; +}; + +private _interval = 5; + +_unit setVariable [QGVAR(radDoseWB), (_unit getVariable [QGVAR(radDoseWB), 0]) + (_burden * GVAR(rad_internalConversionRate) * _interval), true]; + +private _decay = GVAR(rad_internalDecayRate); +if (CBA_missionTime < (_unit getVariable [QGVAR(radChelationWindow), 0])) then { + _decay = _decay * GVAR(rad_chelationFactor); +}; +_unit setVariable [QGVAR(radInternalBurden), _burden * (0 max (1 - (_decay * _interval))), true]; + +[_unit] call FUNC(evaluateRadDose); diff --git a/addons/chemical/functions/fnc_radManagerPFH.sqf b/addons/chemical/functions/fnc_radManagerPFH.sqf new file mode 100644 index 000000000..e5279b691 --- /dev/null +++ b/addons/chemical/functions/fnc_radManagerPFH.sqf @@ -0,0 +1,93 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Radiation analogue of fnc_gasManagerPFH. Walks the registered radiation + * sources, removes any whose anchor or condition is gone, and for every unit + * in range computes the per-type incident dose-rate (by geometry) and fires + * the QGVAR(irradiate) target event on the unit's owner. + * + * Owns the geiger reading: radDoseRate (total incident, summed across sources) + * and radDominantType, reset to 0/"" for units no longer in any field. + * + * Source tuple: [_logic, _radius, _strengths(=[a,b,g,n]), _falloff, _condition, _conditionArgs] + * + * Arguments: + * None + * + * Return Value: + * None + * + * Example: + * kat_chemical_fnc_radManagerPFH call CBA_fnc_addPerFrameHandler + * + * Public: No + */ + +private _exposedNow = []; +private _rateByUnit = createHashMap; +private _domByUnit = createHashMap; +private _types = RAD_TYPES; + +{ + _y params ["_radLogic", "_radius", "_strengths", ["_falloff", "linear"], ["_condition", {true}], ["_conditionArgs", []]]; + + if (isNull _radLogic) then { + GVAR(radSources) deleteAt _x; + continue; + }; + + if !(_conditionArgs call _condition) then { + TRACE_2("rad condition no longer valid, deleting",_x,_y); + detach _radLogic; + deleteVehicle _radLogic; + GVAR(radSources) deleteAt _x; + continue; + }; + + { + private _unit = _x; + private _distance = (_unit distance _radLogic) min _radius; + + private _falloffFactor = switch (_falloff) do { + case "inverseSquare": { + private _d = _distance max 1; + 1 / (_d * _d) + }; + default { + 1 - (_distance / _radius) + }; + }; + + if (_falloffFactor <= 0) then { continue }; + + private _ratesByType = _strengths apply { _x * _falloffFactor }; + private _total = 0; + { _total = _total + _x } forEach _ratesByType; + if (_total <= 0) then { continue }; + + _exposedNow pushBackUnique _unit; + _rateByUnit set [_unit, (_rateByUnit getOrDefault [_unit, 0]) + _total]; + + private _maxRate = selectMax _ratesByType; + if (_maxRate > ((_domByUnit getOrDefault [_unit, ["", -1]]) select 1)) then { + _domByUnit set [_unit, [_types select (_ratesByType find _maxRate), _maxRate]]; + }; + + [QGVAR(irradiate), [_unit, _ratesByType, _radLogic], _unit] call CBA_fnc_targetEvent; + } forEach nearestObjects [_radLogic, ["CAManBase"], _radius]; +} forEach GVAR(radSources); + +{ + if (isNull _x) then { continue }; + _x setVariable [QGVAR(radDoseRate), _y, true]; + _x setVariable [QGVAR(radDominantType), (_domByUnit getOrDefault [_x, [""]]) select 0, true]; +} forEach _rateByUnit; + +{ + if (!isNull _x && {!(_x in _exposedNow)}) then { + _x setVariable [QGVAR(radDoseRate), 0, true]; + _x setVariable [QGVAR(radDominantType), "", true]; + }; +} forEach GVAR(radExposedUnits); + +GVAR(radExposedUnits) = _exposedNow; diff --git a/addons/chemical/functions/fnc_radProtection.sqf b/addons/chemical/functions/fnc_radProtection.sqf new file mode 100644 index 000000000..94e42e1cf --- /dev/null +++ b/addons/chemical/functions/fnc_radProtection.sqf @@ -0,0 +1,51 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Returns a unit's external shielding multiplier (0..1, 1 = unshielded) for a + * given radiation type. Each type's per-gear coefficients come from its profile + * in GVAR(radProfiles); the rad_protectionFactor* settings are per-gear + * effectiveness knobs (0 = gear does nothing, 1 = full profile protection). + * + * Arguments: + * 0: Unit + * 1: Radiation type id + * + * Return Value: + * Absorbed-dose multiplier + * + * Example: + * [player, "gamma"] call kat_chemical_fnc_radProtection; + * + * Public: No + */ + +params ["_unit", "_type"]; + +private _profile = GVAR(radProfiles) getOrDefault [_type, [0, 0, 0, 1, [1, 1, 1, 1]]]; +(_profile param [4, [1, 1, 1, 1]]) params ["_maskF", "_cbrnF", "_radF", "_vehF"]; + +private _factor = 1; + +private _fnc_apply = { + params ["_gearFactor", "_eff"]; + _factor = _factor * (1 - ((1 - _gearFactor) * _eff)); +}; + +if ([_unit] call FUNC(hasGasMaskON)) then { + [_maskF, GVAR(rad_protectionFactorMask)] call _fnc_apply; +}; + +if ([_unit] call FUNC(hasCBRNProtection)) then { + [_cbrnF, GVAR(rad_protectionFactorCBRN)] call _fnc_apply; +}; + +private _radList = missionNamespace getVariable [QGVAR(availRadProtectionList), []]; +if ((_radList isNotEqualTo []) && {uniform _unit != ""} && {(uniform _unit) in _radList}) then { + [_radF, GVAR(rad_protectionFactorRadGear)] call _fnc_apply; +}; + +if (!isNull objectParent _unit) then { + [_vehF, GVAR(rad_protectionFactorVehicle)] call _fnc_apply; +}; + +_factor diff --git a/addons/chemical/functions/fnc_radmodule.sqf b/addons/chemical/functions/fnc_radmodule.sqf new file mode 100644 index 000000000..59ac31691 --- /dev/null +++ b/addons/chemical/functions/fnc_radmodule.sqf @@ -0,0 +1,55 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Eden module that spawns a radiation source. Resolves a preset (or the + * per-type Custom fields) into a [alpha,beta,gamma,neutron] strength array and + * registers the source. Cannot be called manually. + * + * Arguments: + * 0: Logic + * 1: Affected Units (unused) + * 2: Activated + * + * Return Value: + * None + * + * Public: No + */ + +params ["_logic", "_units", "_activated"]; + +private _radius = _logic getVariable ["Radius", 20]; +private _pointSource = _logic getVariable ["PointSource", false]; +private _presetName = ["custom", "Cobalt60", "Reactor", "Fallout", "AlphaEmitter", "DirtyBomb"] param [_logic getVariable ["Preset", 0], "custom"]; + +private _strengths = if (_presetName isEqualTo "custom") then { + [_logic getVariable ["Alpha", 0], _logic getVariable ["Beta", 0], _logic getVariable ["Gamma", 0], _logic getVariable ["Neutron", 0]] +} else { + +(GVAR(radPresets) getOrDefault [_presetName, [0, 0, 0, 0]]) +}; + +if (count _units == 0) then {_units pushBack _logic;}; + +if (!_activated) exitWith {}; + +if (isServer) then { + [{ + params ["_logic", "_radius", "_strengths", "_pointSource", "_units"]; + + private _falloff = ["linear", "inverseSquare"] select _pointSource; + + { + [QGVAR(addRadSource), [_x, _radius, _strengths, _falloff, _x, { + params ["_endTime", "_logic"]; + + if (isNull _logic) exitWith { + false + }; + + CBA_missionTime < _endTime + }, [CBA_missionTime + 1e10, _logic]]] call CBA_fnc_serverEvent; + + } forEach _units; + + }, [_logic, _radius, _strengths, _pointSource, _units], 1] call CBA_fnc_waitAndExecute; +}; diff --git a/addons/chemical/functions/fnc_registerContaminatedSource.sqf b/addons/chemical/functions/fnc_registerContaminatedSource.sqf new file mode 100644 index 000000000..ad2e96679 --- /dev/null +++ b/addons/chemical/functions/fnc_registerContaminatedSource.sqf @@ -0,0 +1,35 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Server-side: (re)registers a contaminated unit as a weak beta/gamma point + * source so it doses itself and anyone nearby and shows on geigers. Tagged with + * contamSourceOwner so the unit's own exposure handler doesn't re-deposit + * contamination from it (no feedback loop). The source self-removes via its + * condition once the unit's contamination clears. + * + * Arguments: + * 0: Unit + * 1: Contamination level (0..1) + * + * Return Value: + * None + * + * Public: No + */ + +params ["_unit", "_contam"]; + +if (isNull _unit) exitWith {}; + +private _gamma = _contam * GVAR(rad_crossContamStrength); +private _strengths = [0, _gamma * 0.5, _gamma, 0]; + +[QGVAR(addRadSource), [_unit, 4, _strengths, "linear", _unit, { + params ["_unit"]; + !isNull _unit && {alive _unit} && {(_unit getVariable [QGVAR(radExternalContam), 0]) > 0.01} +}, [_unit]]] call CBA_fnc_localEvent; + +private _entry = GVAR(radSources) getOrDefault [hashValue _unit, []]; +if (_entry isNotEqualTo []) then { + (_entry select 0) setVariable [QGVAR(contamSourceOwner), _unit, true]; +}; diff --git a/addons/chemical/functions/fnc_scheduleRadSicknessChain.sqf b/addons/chemical/functions/fnc_scheduleRadSicknessChain.sqf new file mode 100644 index 000000000..c57cb2372 --- /dev/null +++ b/addons/chemical/functions/fnc_scheduleRadSicknessChain.sqf @@ -0,0 +1,49 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Schedules the staged radiation-sickness chain by writing deadline variables, + * which the exposure watcher PFH consumes. Mirrors fnc_scheduleNerveSymptomChain + * but unlocks stages by ARS tier and compresses delays at higher tiers. + * + * Per-stage deadline convention: + * 0 = never scheduled (eligible) + * >0 = pending (fires when CBA_missionTime reaches it) + * -1 = already fired (set by the watcher; not rescheduled) + * + * Arguments: + * 0: Unit + * 1: ARS tier (1..4) + * 2: Delay scale (shorter at higher tiers) + * + * Return Value: + * None + * + * Public: No + */ + +params ["_unit", "_tier", "_scale"]; + +private _now = CBA_missionTime; + +private _fnc_sched = { + params ["_var", "_delay"]; + if ((_unit getVariable [_var, 0]) == 0) then { + _unit setVariable [_var, _now + (_delay * _scale), true]; + }; +}; + +if (_tier >= 1) then { + [QGVAR(radDeadline_prodromal), GVAR(rad_prodromalDelay)] call _fnc_sched; +}; +if (_tier >= 2) then { + [QGVAR(radDeadline_hema), GVAR(rad_hemaDelay)] call _fnc_sched; +}; +if (_tier >= 3) then { + [QGVAR(radDeadline_gi), GVAR(rad_giDelay)] call _fnc_sched; +}; +if (_tier >= 4) then { + [QGVAR(radDeadline_cns), GVAR(rad_cnsDelay)] call _fnc_sched; + [QGVAR(radDeadline_cardiac), GVAR(rad_cardiacDelay)] call _fnc_sched; +}; + +[_unit] call FUNC(addToExposureWatcher); diff --git a/addons/chemical/functions/fnc_showDosimeter.sqf b/addons/chemical/functions/fnc_showDosimeter.sqf new file mode 100644 index 000000000..bbb580b78 --- /dev/null +++ b/addons/chemical/functions/fnc_showDosimeter.sqf @@ -0,0 +1,63 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Displays the dosimeter HUD and runs a per-frame handler that refreshes the + * accumulated whole-body dose (Gy) and, while the dosimeter is enabled, the + * current dose-rate (Gy/h). The rate reads "--" when the dosimeter is off and + * 0 once the unit leaves a field. + * + * Arguments: + * 0: Unit + * + * Return Value: + * None + * + * Example: + * [player] call kat_chemical_fnc_showDosimeter + * + * Public: Yes + */ + +params ["_unit"]; + +"KAT_Dosimeter" cutRsc ["KAT_Dosimeter", "PLAIN", 0, true]; + +if (isNull (uiNamespace getVariable ["KAT_Dosimeter", displayNull])) exitWith {}; + +GVAR(DosimeterActive) = true; + +private _display = uiNamespace getVariable ["KAT_Dosimeter", displayNull]; +private _rate = _display displayCtrl 18814; +private _dose = _display displayCtrl 18815; + +[{ + _this params ["_args", "_pfhID"]; + _args params ["_unit", "_rate", "_dose"]; + + if !(GVAR(DosimeterActive)) exitWith { + _pfhID call CBA_fnc_removePerFrameHandler; + }; + + if !(alive _unit) exitWith { + call FUNC(hideDosimeter); + _pfhID call CBA_fnc_removePerFrameHandler; + }; + + if !("KAT_Dosimeter" in assignedItems _unit) exitWith { + call FUNC(hideDosimeter); + _pfhID call CBA_fnc_removePerFrameHandler; + }; + + private _doseTotal = _unit getVariable [QGVAR(radDoseWB), 0]; + _dose ctrlSetText (format ["%1 Gy", _doseTotal toFixed 3]); + + if (_unit getVariable [QGVAR(dosimeterEnabled), false]) then { + _rate ctrlSetText (format ["%1 Gy/h", (_unit getVariable [QGVAR(radDoseRate), 0]) toFixed 3]); + } else { + _rate ctrlSetText "--"; + }; +}, 1, [ + _unit, + _rate, + _dose +]] call CBA_fnc_addPerFrameHandler; diff --git a/addons/chemical/functions/fnc_startRadContaminationTick.sqf b/addons/chemical/functions/fnc_startRadContaminationTick.sqf new file mode 100644 index 000000000..ccf17a0dd --- /dev/null +++ b/addons/chemical/functions/fnc_startRadContaminationTick.sqf @@ -0,0 +1,23 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Starts the owner-local external-contamination tick for a unit if not already + * running. Decays surface contamination over time and keeps the unit's + * cross-contamination source (server-side) in sync. Idempotent. + * + * Arguments: + * 0: Unit + * + * Return Value: + * None + * + * Public: No + */ + +params ["_unit"]; + +if (isNull _unit) exitWith {}; +if (_unit getVariable [QGVAR(radContamPFHActive), false]) exitWith {}; +_unit setVariable [QGVAR(radContamPFHActive), true, true]; + +[LINKFUNC(radContaminationTickPFH), 5, [_unit]] call CBA_fnc_addPerFrameHandler; diff --git a/addons/chemical/functions/fnc_startRadInternalTick.sqf b/addons/chemical/functions/fnc_startRadInternalTick.sqf new file mode 100644 index 000000000..e861aed84 --- /dev/null +++ b/addons/chemical/functions/fnc_startRadInternalTick.sqf @@ -0,0 +1,24 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Starts the owner-local internal-burden tick for a unit if not already + * running. Converts internalised radionuclides into committed whole-body dose + * over time and decays the burden (faster while a chelation window is active). + * Idempotent. + * + * Arguments: + * 0: Unit + * + * Return Value: + * None + * + * Public: No + */ + +params ["_unit"]; + +if (isNull _unit) exitWith {}; +if (_unit getVariable [QGVAR(radInternalPFHActive), false]) exitWith {}; +_unit setVariable [QGVAR(radInternalPFHActive), true, true]; + +[LINKFUNC(radInternalTickPFH), 5, [_unit]] call CBA_fnc_addPerFrameHandler; diff --git a/addons/chemical/functions/fnc_treatmentAdvanced_AntiRadLocal.sqf b/addons/chemical/functions/fnc_treatmentAdvanced_AntiRadLocal.sqf new file mode 100644 index 000000000..f245435a5 --- /dev/null +++ b/addons/chemical/functions/fnc_treatmentAdvanced_AntiRadLocal.sqf @@ -0,0 +1,30 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Applies an anti-radiation drug to a patient. Both effects are timestamp + * windows read elsewhere, so they are locality-safe to set from any machine: + * "ki" - potassium iodide: opens a prophylactic uptake-reduction window + * (read in fnc_irradiate via rad_kiFactor). + * "prussian" - Prussian blue: opens a chelation window that accelerates + * internal-burden elimination (read in fnc_radInternalTickPFH). + * + * Arguments: + * 0: Patient + * 1: Mode + * + * Return Value: + * None + * + * Public: No + */ + +params ["_patient", "_mode"]; + +switch (_mode) do { + case "ki": { + _patient setVariable [QGVAR(radProtectiveWindow), CBA_missionTime + GVAR(rad_kiDuration), true]; + }; + case "prussian": { + _patient setVariable [QGVAR(radChelationWindow), CBA_missionTime + GVAR(rad_chelationDuration), true]; + }; +}; diff --git a/addons/chemical/functions/fnc_ui_radModule.sqf b/addons/chemical/functions/fnc_ui_radModule.sqf new file mode 100644 index 000000000..fbd16ce30 --- /dev/null +++ b/addons/chemical/functions/fnc_ui_radModule.sqf @@ -0,0 +1,70 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Confirm/cancel handler for the radiation Zeus module attributes dialog. + * On confirm reads the radius/strength/point-source inputs and registers a + * radiation source; on cancel deletes the placed logic. Mirrors fnc_ui_gasModule. + * + * Arguments: + * 0: Control + * + * Return Value: + * None + * + * Example: + * [_control] call kat_chemical_fnc_ui_radModule; + * + * Public: No + */ + +params ["_control"]; + +private _display = ctrlParent _control; +private _ctrlButtonOK = _display displayCtrl 1; +private _logic = GETMVAR(BIS_fnc_initCuratorAttributes_target,objNull); + +_control ctrlRemoveAllEventHandlers "SetFocus"; + +private _fnc_onUnload = { + params ["_display"]; + private _logic = GETMVAR(BIS_fnc_initCuratorAttributes_target,objNull); + if (isNull _logic) exitWith {}; + if !(_display getVariable [QGVAR(Confirmed), false]) then { + deleteVehicle _logic; + }; +}; + +private _fnc_onConfirm = { + params [["_ctrlButtonOK", controlNull, [controlNull]]]; + + private _display = ctrlParent _ctrlButtonOK; + if (isNull _display) exitWith {}; + + private _logic = GETMVAR(BIS_fnc_initCuratorAttributes_target,objNull); + if (isNull _logic) exitWith {}; + + private _radius = _display getVariable [QGVAR(ui_radius), 20]; + private _strengths = _display getVariable [QGVAR(ui_radStrengths), [0, 0, 50, 0]]; + private _pointSource = _display getVariable [QGVAR(ui_radPointSource), false]; + private _falloff = ["linear", "inverseSquare"] select _pointSource; + + private _center = _logic; + if !(isNull attachedTo _logic) then { + _center = attachedTo _logic; + }; + + [QGVAR(addRadSource), [_center, _radius, _strengths, _falloff, _logic, { + params ["_endTime", "_logic"]; + + if (isNull _logic) exitWith { + false + }; + + CBA_missionTime < _endTime + }, [CBA_missionTime + 1e10, _logic]]] call CBA_fnc_serverEvent; + + _display setVariable [QGVAR(Confirmed), true]; +}; + +_display displayAddEventHandler ["Unload", _fnc_onUnload]; +_ctrlButtonOK ctrlAddEventHandler ["ButtonClick", _fnc_onConfirm]; diff --git a/addons/chemical/initSettings.inc.sqf b/addons/chemical/initSettings.inc.sqf index 4fe01e504..4ac7f53b4 100644 --- a/addons/chemical/initSettings.inc.sqf +++ b/addons/chemical/initSettings.inc.sqf @@ -273,3 +273,261 @@ [1, 600, 8], true ] call CBA_fnc_addSetting; + +// =============== Radiation =============== +[ + QGVAR(rad_enable), + "CHECKBOX", + [LLSTRING(SETTING_rad_enable), LLSTRING(SETTING_rad_enable_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [true], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_showSickness), + "CHECKBOX", + [LLSTRING(SETTING_rad_showSickness), LLSTRING(SETTING_rad_showSickness_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [true], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_doseThreshold_mild), + "SLIDER", + [LLSTRING(SETTING_rad_doseThreshold_mild), LLSTRING(SETTING_rad_doseThreshold_mild_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 30, 1, 2], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_doseThreshold_moderate), + "SLIDER", + [LLSTRING(SETTING_rad_doseThreshold_moderate), LLSTRING(SETTING_rad_doseThreshold_moderate_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 30, 2, 2], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_doseThreshold_severe), + "SLIDER", + [LLSTRING(SETTING_rad_doseThreshold_severe), LLSTRING(SETTING_rad_doseThreshold_severe_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 30, 4, 2], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_doseThreshold_lethal), + "SLIDER", + [LLSTRING(SETTING_rad_doseThreshold_lethal), LLSTRING(SETTING_rad_doseThreshold_lethal_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 30, 6, 2], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_prodromalDelay), + "TIME", + [LLSTRING(SETTING_rad_prodromalDelay), LLSTRING(SETTING_rad_prodromalDelay_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 3600, 120], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_hemaDelay), + "TIME", + [LLSTRING(SETTING_rad_hemaDelay), LLSTRING(SETTING_rad_hemaDelay_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 7200, 600], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_giDelay), + "TIME", + [LLSTRING(SETTING_rad_giDelay), LLSTRING(SETTING_rad_giDelay_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 7200, 480], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_cnsDelay), + "TIME", + [LLSTRING(SETTING_rad_cnsDelay), LLSTRING(SETTING_rad_cnsDelay_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 3600, 180], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_cardiacDelay), + "TIME", + [LLSTRING(SETTING_rad_cardiacDelay), LLSTRING(SETTING_rad_cardiacDelay_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 3600, 300], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_protectionFactorMask), + "SLIDER", + [LLSTRING(SETTING_rad_protectionFactorMask), LLSTRING(SETTING_rad_protectionFactorMask_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 1, 1, 2], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_protectionFactorCBRN), + "SLIDER", + [LLSTRING(SETTING_rad_protectionFactorCBRN), LLSTRING(SETTING_rad_protectionFactorCBRN_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 1, 1, 2], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_protectionFactorRadGear), + "SLIDER", + [LLSTRING(SETTING_rad_protectionFactorRadGear), LLSTRING(SETTING_rad_protectionFactorRadGear_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 1, 1, 2], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_protectionFactorVehicle), + "SLIDER", + [LLSTRING(SETTING_rad_protectionFactorVehicle), LLSTRING(SETTING_rad_protectionFactorVehicle_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 1, 1, 2], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(availRadProtection), + "EDITBOX", + [LLSTRING(SETTING_availRadProtection), LLSTRING(SETTING_availRadProtection_DISC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + "", + 1, + { + private _array = [_this, "CfgWeapons"] call FUNC(getList); + missionNamespace setVariable [QGVAR(availRadProtectionList), _array, true]; + }, + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_kiFactor), + "SLIDER", + [LLSTRING(SETTING_rad_kiFactor), LLSTRING(SETTING_rad_kiFactor_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 1, 0.5, 2], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_kiDuration), + "TIME", + [LLSTRING(SETTING_rad_kiDuration), LLSTRING(SETTING_rad_kiDuration_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 14400, 3600], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_inhalationFactor), + "SLIDER", + [LLSTRING(SETTING_rad_inhalationFactor), LLSTRING(SETTING_rad_inhalationFactor_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 2, 0.1, 2], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_internalConversionRate), + "SLIDER", + [LLSTRING(SETTING_rad_internalConversionRate), LLSTRING(SETTING_rad_internalConversionRate_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 1, 0.001, 4], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_internalDecayRate), + "SLIDER", + [LLSTRING(SETTING_rad_internalDecayRate), LLSTRING(SETTING_rad_internalDecayRate_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 1, 0.0005, 4], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_chelationDuration), + "TIME", + [LLSTRING(SETTING_rad_chelationDuration), LLSTRING(SETTING_rad_chelationDuration_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 14400, 600], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_chelationFactor), + "SLIDER", + [LLSTRING(SETTING_rad_chelationFactor), LLSTRING(SETTING_rad_chelationFactor_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [1, 50, 5, 1], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_contaminationDeposition), + "SLIDER", + [LLSTRING(SETTING_rad_contaminationDeposition), LLSTRING(SETTING_rad_contaminationDeposition_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 1, 0.02, 3], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_contaminationDecay), + "SLIDER", + [LLSTRING(SETTING_rad_contaminationDecay), LLSTRING(SETTING_rad_contaminationDecay_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 1, 0.002, 4], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_crossContamStrength), + "SLIDER", + [LLSTRING(SETTING_rad_crossContamStrength), LLSTRING(SETTING_rad_crossContamStrength_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 50, 5, 1], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_localProximity), + "SLIDER", + [LLSTRING(SETTING_rad_localProximity), LLSTRING(SETTING_rad_localProximity_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 5, 1.5, 1], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_skinBurnThreshold), + "SLIDER", + [LLSTRING(SETTING_rad_skinBurnThreshold), LLSTRING(SETTING_rad_skinBurnThreshold_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [1, 50, 5, 1], + true +] call CBA_fnc_addSetting; diff --git a/addons/chemical/script_component.hpp b/addons/chemical/script_component.hpp index 93a09c3cc..d76c526cb 100644 --- a/addons/chemical/script_component.hpp +++ b/addons/chemical/script_component.hpp @@ -26,3 +26,10 @@ #define Y_PART(num) (H_PART(num) + (safeZoneY + (safeZoneH - SIZEY) / 2)) #define GAS_MANAGER_PFH_DELAY 1 +#define RAD_MANAGER_PFH_DELAY 1 + +// Radiation body parts (matches ALL_BODY_PARTS index order used for per-limb dose) +#define RAD_BODY_PARTS ["Head", "Body", "LeftArm", "RightArm", "LeftLeg", "RightLeg"] + +// Radiation types (canonical index order for per-type strength/rate arrays) +#define RAD_TYPES ["alpha", "beta", "gamma", "neutron"] diff --git a/addons/chemical/stringtable.xml b/addons/chemical/stringtable.xml index 602fae72a..041a4509f 100644 --- a/addons/chemical/stringtable.xml +++ b/addons/chemical/stringtable.xml @@ -624,6 +624,398 @@ 9. Identification (Detector & M8 Paper) 9. dentifikation (Detektor & M8-Papier) + + 10. Radiation + 10. Strahlung + + + Enable radiation + Strahlung aktivieren + + + Master switch for the radiation system (sources, dose, sickness). + Hauptschalter für das Strahlungssystem (Quellen, Dosis, Krankheit). + + + Show radiation sickness hints + Hinweise zur Strahlenkrankheit anzeigen + + + Show on-screen hints (nausea, weakness, etc.) to the affected player. + Bildschirmhinweise (Übelkeit, Schwäche usw.) für den betroffenen Spieler anzeigen. + + + Dose threshold: mild (Gy) + Dosisschwelle: leicht (Gy) + + + Accumulated whole-body dose (Gray) at which mild radiation sickness begins. + Akkumulierte Ganzkörperdosis (Gray), ab der eine leichte Strahlenkrankheit beginnt. + + + Dose threshold: moderate (Gy) + Dosisschwelle: mäßig (Gy) + + + Dose at which the hematopoietic (bone-marrow) syndrome sets in. + Dosis, ab der das hämatopoetische (Knochenmark-) Syndrom einsetzt. + + + Dose threshold: severe (Gy) + Dosisschwelle: schwer (Gy) + + + Dose at which the gastrointestinal syndrome sets in. + Dosis, ab der das gastrointestinale Syndrom einsetzt. + + + Dose threshold: lethal (Gy) + Dosisschwelle: tödlich (Gy) + + + Dose at which the neurovascular/CNS syndrome and cardiac arrest occur. + Dosis, ab der das neurovaskuläre/ZNS-Syndrom und Herzstillstand auftreten. + + + Prodromal onset (s) + Prodromalbeginn (s) + + + Base delay before the prodromal phase (nausea/vomiting). Shorter at higher doses. + Basisverzögerung bis zur Prodromalphase (Übelkeit/Erbrechen). Kürzer bei höheren Dosen. + + + Hematopoietic onset (s) + Hämatopoetischer Beginn (s) + + + Base delay before hematopoietic syndrome onset (marrow suppression). + Basisverzögerung bis zum Beginn des hämatopoetischen Syndroms (Knochenmarksuppression). + + + Gastrointestinal onset (s) + Gastrointestinaler Beginn (s) + + + Base delay before gastrointestinal syndrome onset. + Basisverzögerung bis zum Beginn des gastrointestinalen Syndroms. + + + CNS collapse onset (s) + ZNS-Kollaps-Beginn (s) + + + Base delay before neurovascular/CNS collapse at lethal doses. + Basisverzögerung bis zum neurovaskulären/ZNS-Kollaps bei tödlichen Dosen. + + + Cardiac arrest onset (s) + Herzstillstand-Beginn (s) + + + Base delay before cardiac arrest at lethal doses. + Basisverzögerung bis zum Herzstillstand bei tödlichen Dosen. + + + Shielding: gas mask + Abschirmung: Gasmaske + + + Gas-mask shielding effectiveness (0 = no protection, 1 = full per-type protection). + Wirksamkeit der Gasmasken-Abschirmung (0 = kein Schutz, 1 = voller typabhängiger Schutz). + + + Shielding: CBRN suit + Abschirmung: CBRN-Anzug + + + CBRN-suit shielding effectiveness (0 = no protection, 1 = full per-type protection). + Wirksamkeit der CBRN-Anzug-Abschirmung (0 = kein Schutz, 1 = voller typabhängiger Schutz). + + + Shielding: radiation gear + Abschirmung: Strahlenschutzausrüstung + + + Radiation-gear shielding effectiveness (0 = no protection, 1 = full per-type protection). + Wirksamkeit der Strahlenschutzausrüstung (0 = kein Schutz, 1 = voller typabhängiger Schutz). + + + Shielding: vehicle + Abschirmung: Fahrzeug + + + Vehicle shielding effectiveness (0 = no protection, 1 = full per-type protection). + Wirksamkeit der Fahrzeug-Abschirmung (0 = kein Schutz, 1 = voller typabhängiger Schutz). + + + Radiation-protective uniforms + Strahlenschutz-Uniformen + + + Comma-separated uniform classnames that count as dedicated radiation protection. + Kommagetrennte Uniform-Klassennamen, die als spezieller Strahlenschutz gelten. + + + Potassium iodide protection factor + Kaliumiodid-Schutzfaktor + + + Absorbed-dose multiplier during the potassium-iodide protective window. + Dosismultiplikator während des Kaliumiodid-Schutzfensters. + + + Dosimeter + Dosimeter + + + Personal radiation dosimeter. Worn in the watch slot; shows dose-rate and accumulated dose. + Persönliches Strahlungsdosimeter. Im Uhrenslot getragen; zeigt Dosisleistung und akkumulierte Dosis. + + + DOSIMETER + DOSIMETER + + + Dosimeter + Dosimeter + + + Show Dosimeter + Dosimeter anzeigen + + + Enable Dosimeter + Dosimeter aktivieren + + + Disable Dosimeter + Dosimeter deaktivieren + + + Enable dosimeter sound + Dosimeter-Ton aktivieren + + + Disable dosimeter sound + Dosimeter-Ton deaktivieren + + + Radiation Source + Strahlungsquelle + + + Radius of the radiation field in meters. + Radius des Strahlungsfeldes in Metern. + + + Dose-rate in Gray per hour at the reference point (centre for zones, 1 m for point sources). + Dosisleistung in Gray pro Stunde am Referenzpunkt (Zentrum bei Zonen, 1 m bei Punktquellen). + + + Point source + Punktquelle + + + If enabled, dose falls off with the inverse-square law (a hot point) instead of linearly (an area). + Wenn aktiviert, fällt die Dosis nach dem Abstandsquadratgesetz ab (heißer Punkt) statt linear (Fläche). + + + Creates a radiation source. Sync a LocationArea for a zone, or enable Point source for a hot spot. + Erzeugt eine Strahlungsquelle. Synchronisiere ein LocationArea für eine Zone oder aktiviere Punktquelle für einen Hotspot. + + + You feel nauseous and start to vomit. + Dir ist übel und du beginnst zu erbrechen. + + + You feel weak and feverish. + Du fühlst dich schwach und fiebrig. + + + Severe cramps and vomiting wrack your body. + Schwere Krämpfe und Erbrechen schütteln deinen Körper. + + + Potassium Iodide (KI) + Kaliumiodid (KI) + + + Prophylactic anti-radiation tablets. Taken before/early exposure to reduce radionuclide uptake. + Prophylaktische Anti-Strahlungs-Tabletten. Vor/früh bei Exposition eingenommen, um die Aufnahme von Radionukliden zu verringern. + + + Prussian Blue + Berliner Blau + + + Chelation agent. Speeds elimination of internalised radionuclides (internal burden). + Chelatbildner. Beschleunigt die Ausscheidung internalisierter Radionuklide (innere Belastung). + + + Radioactive Source + Radioaktive Quelle + + + Pick up radioactive source + Radioaktive Quelle aufnehmen + + + Drop radioactive source + Radioaktive Quelle ablegen + + + Take Potassium Iodide + Kaliumiodid einnehmen + + + Taking Potassium Iodide... + Nehme Kaliumiodid ein... + + + Administer Prussian Blue + Berliner Blau verabreichen + + + Administering Prussian Blue... + Verabreiche Berliner Blau... + + + Preset + Vorlage + + + Source profile. Non-Custom presets fill the per-type strengths; Custom uses the four fields below. + Quellenprofil. Nicht-benutzerdefinierte Vorlagen füllen die typabhängigen Stärken; Benutzerdefiniert nutzt die vier Felder unten. + + + Alpha (Gy/h) + Alpha (Gy/h) + + + Beta (Gy/h) + Beta (Gy/h) + + + Gamma (Gy/h) + Gamma (Gy/h) + + + Neutron (Gy/h) + Neutron (Gy/h) + + + Custom + Benutzerdefiniert + + + Cobalt-60 (gamma) + Cobalt-60 (Gamma) + + + Reactor (gamma + neutron) + Reaktor (Gamma + Neutron) + + + Fallout (beta + gamma) + Fallout (Beta + Gamma) + + + Alpha emitter + Alpha-Strahler + + + Dirty bomb (alpha + beta + gamma) + Schmutzige Bombe (Alpha + Beta + Gamma) + + + Potassium iodide duration (s) + Kaliumiodid-Dauer (s) + + + How long the potassium-iodide uptake-reduction window lasts. + Wie lange das Kaliumiodid-Aufnahmereduktionsfenster anhält. + + + Inhalation factor + Inhalationsfaktor + + + Scales how much airborne radioactivity becomes internal burden when no gas mask is worn. + Skaliert, wie viel luftgetragene Radioaktivität ohne Gasmaske zur inneren Belastung wird. + + + Internal dose conversion rate + Interne Dosis-Umwandlungsrate + + + Fraction of internal burden delivered as committed whole-body dose each second. + Anteil der inneren Belastung, der pro Sekunde als festgelegte Ganzkörperdosis abgegeben wird. + + + Internal burden elimination rate + Ausscheidungsrate der inneren Belastung + + + Biological elimination of internal burden per second (chelation multiplies this). + Biologische Ausscheidung der inneren Belastung pro Sekunde (Chelattherapie vervielfacht dies). + + + Chelation duration (s) + Chelat-Dauer (s) + + + How long a Prussian blue chelation window accelerates internal-burden elimination. + Wie lange ein Berliner-Blau-Chelatfenster die Ausscheidung der inneren Belastung beschleunigt. + + + Chelation speed multiplier + Chelat-Geschwindigkeitsmultiplikator + + + How much faster internal burden is eliminated while a chelation window is active. + Wie viel schneller die innere Belastung während eines Chelatfensters ausgeschieden wird. + + + Contamination deposition rate + Kontaminations-Ablagerungsrate + + + How quickly surface contamination builds up while exposed to particulate radiation. + Wie schnell sich Oberflächenkontamination bei Exposition gegenüber Partikelstrahlung aufbaut. + + + Contamination weathering rate + Kontaminations-Verwitterungsrate + + + How quickly surface contamination weathers away on its own per second. + Wie schnell Oberflächenkontamination pro Sekunde von selbst verwittert. + + + Cross-contamination strength (Gy/h) + Kreuzkontaminations-Stärke (Gy/h) + + + Dose-rate emitted by a fully contaminated person (doses themselves and nearby people). + Dosisleistung einer vollständig kontaminierten Person (bestrahlt sich selbst und Personen in der Nähe). + + + Local exposure distance (m) + Lokale Expositionsdistanz (m) + + + Within this distance of a point source, skin dose concentrates on the arms/hands (handling burns). + Innerhalb dieser Distanz zu einer Punktquelle konzentriert sich die Hautdosis auf Arme/Hände (Handhabungsverbrennungen). + + + Skin burn threshold (Gy) + Hautverbrennungsschwelle (Gy) + + + Local skin dose per radiation burn wound applied to a body part. + Lokale Hautdosis pro Strahlenverbrennungswunde, die auf ein Körperteil angewendet wird. + Accepted gas masks Povolené plynové masky diff --git a/addons/chemical/ui/RscAttributes.hpp b/addons/chemical/ui/RscAttributes.hpp index 65478c0cd..efcb1ff43 100644 --- a/addons/chemical/ui/RscAttributes.hpp +++ b/addons/chemical/ui/RscAttributes.hpp @@ -125,3 +125,129 @@ class GVAR(kat_RscGasModul): RscDisplayAttributes { class ButtonCancel: ButtonCancel {}; }; }; + +class GVAR(kat_RscAttributeRadModule): RscControlsGroupNoScrollbars { + onSetFocus = QUOTE(_this call FUNC(AttributeRadModule)); + idc = 26423; + x = 0; + y = 0; + w = QUOTE(W_PART(26)); + h = QUOTE(H_PART(8)); + class controls { + class Title1: RscText { + idc = 16110; + text = CSTRING(UI_max_range); + toolTip = CSTRING(RadModule_max_radius_dcs); + x = 0; + y = 0; + w = QUOTE(W_PART(10)); + h = QUOTE(H_PART(1)); + colorBackground[] = {0,0,0,0.5}; + }; + class radius_max: RscEdit { + idc = 1621; + x = QUOTE(W_PART(10.1)); + y = 0; + w = QUOTE(W_PART(15.9)); + h = QUOTE(H_PART(1)); + }; + class Title2: Title1 { + idc = -1; + text = CSTRING(RadModule_preset); + toolTip = CSTRING(RadModule_preset_dcs); + y = QUOTE(H_PART(1.1)); + }; + class preset_combo: RscCombo { + idc = 1624; + x = QUOTE(W_PART(10.1)); + y = QUOTE(H_PART(1.1)); + w = QUOTE(W_PART(15.9)); + h = QUOTE(H_PART(1)); + colorBackground[] = {0, 0, 0, 0.7}; + }; + class Title3: Title1 { + idc = -1; + text = CSTRING(RadModule_alpha); + toolTip = CSTRING(RadModule_strength_dcs); + y = QUOTE(H_PART(2.2)); + }; + class alpha_edit: RscEdit { + idc = 1625; + x = QUOTE(W_PART(10.1)); + y = QUOTE(H_PART(2.2)); + w = QUOTE(W_PART(15.9)); + h = QUOTE(H_PART(1)); + }; + class Title4: Title1 { + idc = -1; + text = CSTRING(RadModule_beta); + toolTip = CSTRING(RadModule_strength_dcs); + y = QUOTE(H_PART(3.3)); + }; + class beta_edit: RscEdit { + idc = 1626; + x = QUOTE(W_PART(10.1)); + y = QUOTE(H_PART(3.3)); + w = QUOTE(W_PART(15.9)); + h = QUOTE(H_PART(1)); + }; + class Title5: Title1 { + idc = -1; + text = CSTRING(RadModule_gamma); + toolTip = CSTRING(RadModule_strength_dcs); + y = QUOTE(H_PART(4.4)); + }; + class gamma_edit: RscEdit { + idc = 1627; + x = QUOTE(W_PART(10.1)); + y = QUOTE(H_PART(4.4)); + w = QUOTE(W_PART(15.9)); + h = QUOTE(H_PART(1)); + }; + class Title6: Title1 { + idc = -1; + text = CSTRING(RadModule_neutron); + toolTip = CSTRING(RadModule_strength_dcs); + y = QUOTE(H_PART(5.5)); + }; + class neutron_edit: RscEdit { + idc = 1628; + x = QUOTE(W_PART(10.1)); + y = QUOTE(H_PART(5.5)); + w = QUOTE(W_PART(15.9)); + h = QUOTE(H_PART(1)); + }; + class Title7: Title1 { + idc = -1; + text = CSTRING(RadModule_pointSource); + toolTip = CSTRING(RadModule_pointSource_dcs); + y = QUOTE(H_PART(6.6)); + }; + class pointSource_check: RscCheckBox { + idc = 1623; + x = QUOTE(W_PART(10.1)); + y = QUOTE(H_PART(6.6)); + w = QUOTE(W_PART(1)); + h = QUOTE(H_PART(1)); + }; + }; +}; + +class GVAR(kat_RscRadModul): RscDisplayAttributes { + onLoad = QUOTE([ARR_3('onLoad',_this,QQGVAR(kat_RscRadModul))] call EFUNC(zeus,zeusAttributes)); + onUnload = QUOTE([ARR_3('onUnload',_this,QQGVAR(kat_RscRadModul))] call EFUNC(zeus,zeusAttributes)); + + class Controls: Controls { + class Background: Background {}; + class Title: Title {}; + class Content: Content { + class Controls { + class radius: GVAR(kat_RscAttributeRadModule) {}; + }; + }; + class ButtonOK: ButtonOK { + onSetFocus = QUOTE(_this call FUNC(ui_radModule)); + }; + class ButtonCancel: ButtonCancel {}; + }; +}; From bb4580d7624ed140cd177d8ccf97711c7fb85123 Mon Sep 17 00:00:00 2001 From: Julian | DiGii <45559529+AtrixZockt@users.noreply.github.com> Date: Wed, 24 Jun 2026 00:41:56 +0200 Subject: [PATCH 2/7] Add radiation physiology tick and visuals --- addons/chemical/CfgVehicles.hpp | 4 +- addons/chemical/CfgWeapons.hpp | 4 +- addons/chemical/XEH_PREP.hpp | 2 + .../functions/fnc_evaluateRadDose.sqf | 22 ++-- .../chemical/functions/fnc_fullHealLocal.sqf | 8 ++ addons/chemical/functions/fnc_irradiate.sqf | 14 ++- .../functions/fnc_radInternalTickPFH.sqf | 4 +- .../chemical/functions/fnc_radManagerPFH.sqf | 23 ++-- .../functions/fnc_radPhysiologyPFH.sqf | 103 +++++++++++++++++ .../functions/fnc_startRadPhysiologyTick.sqf | 24 ++++ addons/chemical/initSettings.inc.sqf | 63 ++++++++++ addons/chemical/stringtable.xml | 108 +++++++++++++++--- addons/chemical/ui/kat_potassiumIodide.paa | Bin 0 -> 811766 bytes addons/chemical/ui/kat_prussianBlue.paa | Bin 0 -> 674602 bytes .../functions/fnc_getBloodPressure.sqf | 4 +- addons/feedback/XEH_PREP.hpp | 1 + .../functions/fnc_effectRadiation.sqf | 33 ++++++ addons/feedback/functions/fnc_initEffects.sqf | 7 ++ addons/pharma/functions/fnc_coagRegen.sqf | 2 +- .../functions/fnc_getBloodVolumeChange.sqf | 2 +- .../fnc_handleTemperatureFunction.sqf | 2 +- 21 files changed, 378 insertions(+), 52 deletions(-) create mode 100644 addons/chemical/functions/fnc_radPhysiologyPFH.sqf create mode 100644 addons/chemical/functions/fnc_startRadPhysiologyTick.sqf create mode 100644 addons/chemical/ui/kat_potassiumIodide.paa create mode 100644 addons/chemical/ui/kat_prussianBlue.paa create mode 100644 addons/feedback/functions/fnc_effectRadiation.sqf diff --git a/addons/chemical/CfgVehicles.hpp b/addons/chemical/CfgVehicles.hpp index 3467ab1ea..7e9ed854b 100644 --- a/addons/chemical/CfgVehicles.hpp +++ b/addons/chemical/CfgVehicles.hpp @@ -234,8 +234,8 @@ class CfgVehicles { }; }; - class Land_Canister_Oil_F; - class kat_radSource: Land_Canister_Oil_F { + class Land_CanisterFuel_F; + class kat_radSource: Land_CanisterFuel_F { scope = 2; scopeCurator = 2; displayName = CSTRING(radSourceObject); diff --git a/addons/chemical/CfgWeapons.hpp b/addons/chemical/CfgWeapons.hpp index 1d651a0b3..58474fed6 100644 --- a/addons/chemical/CfgWeapons.hpp +++ b/addons/chemical/CfgWeapons.hpp @@ -59,7 +59,7 @@ class CfgWeapons { author = "DiGii"; displayName = CSTRING(potassiumIodideItem); model = "\A3\weapons_F\ammo\mag_univ.p3d"; - picture = QPATHTOF(ui\icon_deconkit.paa); + picture = QPATHTOF(ui\kat_potassiumIodide.paa); descriptionShort = CSTRING(potassiumIodideItem_desc); descriptionUse = CSTRING(potassiumIodideItem_desc); class ItemInfo: CBA_MiscItem_ItemInfo { @@ -72,7 +72,7 @@ class CfgWeapons { author = "DiGii"; displayName = CSTRING(prussianBlueItem); model = "\A3\weapons_F\ammo\mag_univ.p3d"; - picture = QPATHTOF(ui\icon_deconkit.paa); + picture = QPATHTOF(ui\kat_prussianBlue.paa); descriptionShort = CSTRING(prussianBlueItem_desc); descriptionUse = CSTRING(prussianBlueItem_desc); class ItemInfo: CBA_MiscItem_ItemInfo { diff --git a/addons/chemical/XEH_PREP.hpp b/addons/chemical/XEH_PREP.hpp index aea329e8b..5e392f022 100644 --- a/addons/chemical/XEH_PREP.hpp +++ b/addons/chemical/XEH_PREP.hpp @@ -60,6 +60,7 @@ PREP(radContaminationTickPFH); PREP(radInternalTickPFH); PREP(radManagerPFH); PREP(radmodule); +PREP(radPhysiologyPFH); PREP(radProtection); PREP(registerContaminatedSource); PREP(scheduleAirPoisoningOnset); @@ -71,6 +72,7 @@ PREP(spawnSmoke); PREP(startContaminationTick); PREP(startRadContaminationTick); PREP(startRadInternalTick); +PREP(startRadPhysiologyTick); PREP(takeOffGasMask); PREP(throwGrenade); PREP(treatmentAdvanced_AntiRadLocal); diff --git a/addons/chemical/functions/fnc_evaluateRadDose.sqf b/addons/chemical/functions/fnc_evaluateRadDose.sqf index dc21e2684..32d1daa09 100644 --- a/addons/chemical/functions/fnc_evaluateRadDose.sqf +++ b/addons/chemical/functions/fnc_evaluateRadDose.sqf @@ -22,28 +22,30 @@ params ["_unit"]; -private _dose = (_unit getVariable [QGVAR(radDoseWB), 0]) + (_unit getVariable [QGVAR(radInternalBurden), 0]); +private _severity = _unit getVariable [QGVAR(radSeverity), 0]; private _tier = switch (true) do { - case (_dose >= GVAR(rad_doseThreshold_lethal)): {4}; - case (_dose >= GVAR(rad_doseThreshold_severe)): {3}; - case (_dose >= GVAR(rad_doseThreshold_moderate)): {2}; - case (_dose >= GVAR(rad_doseThreshold_mild)): {1}; + case (_severity >= GVAR(rad_doseThreshold_lethal)): {4}; + case (_severity >= GVAR(rad_doseThreshold_severe)): {3}; + case (_severity >= GVAR(rad_doseThreshold_moderate)): {2}; + case (_severity >= GVAR(rad_doseThreshold_mild)): {1}; default {0}; }; private _currentTier = _unit getVariable [QGVAR(radSicknessTier), 0]; private _suppression = [1, 0.85, 0.6, 0.35, 0.1] select _tier; -if (_tier > _currentTier) then { - _unit setVariable [QGVAR(radMarrowFactor), _suppression, true]; - _unit setVariable [QGVAR(radImmuneFactor), _suppression, true]; +_unit setVariable [QGVAR(radMarrowFactor), _suppression, true]; +_unit setVariable [QGVAR(radImmuneFactor), _suppression, true]; + +_unit setVariable [QGVAR(radSicknessTier), _tier, true]; + +if (_tier >= 1) then { + [_unit] call FUNC(startRadPhysiologyTick); }; if (_tier <= _currentTier) exitWith {}; -_unit setVariable [QGVAR(radSicknessTier), _tier, true]; - private _scale = [1, 1, 0.7, 0.45, 0.2] select _tier; [_unit, _tier, _scale] call FUNC(scheduleRadSicknessChain); diff --git a/addons/chemical/functions/fnc_fullHealLocal.sqf b/addons/chemical/functions/fnc_fullHealLocal.sqf index dd9325eea..9bdad30f5 100644 --- a/addons/chemical/functions/fnc_fullHealLocal.sqf +++ b/addons/chemical/functions/fnc_fullHealLocal.sqf @@ -41,6 +41,7 @@ _patient setVariable [QGVAR(nerveDeadline_unconscious), 0, true]; _patient setVariable [QGVAR(nerveDeadline_cardiac), 0, true]; _patient setVariable [QGVAR(radDoseWB), 0, true]; +_patient setVariable [QGVAR(radSeverity), 0, true]; _patient setVariable [QGVAR(radDoseLimb), [0, 0, 0, 0, 0, 0], true]; _patient setVariable [QGVAR(radInternalBurden), 0, true]; _patient setVariable [QGVAR(radExternalContam), 0, true]; @@ -54,7 +55,14 @@ _patient setVariable [QGVAR(radChelationWindow), 0, true]; _patient setVariable [QGVAR(radDominantType), "", true]; _patient setVariable [QGVAR(radInternalPFHActive), false, true]; _patient setVariable [QGVAR(radContamPFHActive), false, true]; +_patient setVariable [QGVAR(radPhysiologyPFHActive), false, true]; +_patient setVariable [QGVAR(radFever), 0, true]; +_patient setVariable [QGVAR(radBPDrop), 0, true]; _patient setVariable [QGVAR(radTimeToVomit), -1, true]; + +if (_patient == ACE_player) then { + [false, 0] call EFUNC(feedback,effectRadiation); +}; _patient setVariable [QGVAR(radDeadline_prodromal), 0, true]; _patient setVariable [QGVAR(radDeadline_hema), 0, true]; _patient setVariable [QGVAR(radDeadline_gi), 0, true]; diff --git a/addons/chemical/functions/fnc_irradiate.sqf b/addons/chemical/functions/fnc_irradiate.sqf index 7d2656c40..aff023f46 100644 --- a/addons/chemical/functions/fnc_irradiate.sqf +++ b/addons/chemical/functions/fnc_irradiate.sqf @@ -36,7 +36,7 @@ if !(isDamageAllowed _unit && {_unit getVariable [QACEGVAR(medical,allowDamage), TRACE_1("rad unit is invulnerable",_unit); }; -private _dt = RAD_MANAGER_PFH_DELAY; +private _hours = RAD_MANAGER_PFH_DELAY / 3600; private _hasMask = [_unit] call FUNC(hasGasMaskON); private _kiActive = CBA_missionTime < (_unit getVariable [QGVAR(radProtectiveWindow), 0]); private _veryClose = (_radLogic distance _unit) < GVAR(rad_localProximity); @@ -68,7 +68,7 @@ private _skinByPart = [0, 0, 0, 0, 0, 0]; }; if (!_hasMask && {_inhaleW > 0}) then { - private _inhale = _rate * _inhaleW; + private _inhale = _rate * _inhaleW * GVAR(rad_inhalationFactor); if (_kiActive) then { _inhale = _inhale * GVAR(rad_kiFactor); }; _internalAdd = _internalAdd + _inhale; }; @@ -79,14 +79,16 @@ private _skinByPart = [0, 0, 0, 0, 0, 0]; } forEach RAD_TYPES; if (_wbAdd > 0) then { - _unit setVariable [QGVAR(radDoseWB), (_unit getVariable [QGVAR(radDoseWB), 0]) + (_wbAdd * _dt), true]; + private _add = _wbAdd * _hours; + _unit setVariable [QGVAR(radDoseWB), (_unit getVariable [QGVAR(radDoseWB), 0]) + _add, true]; + _unit setVariable [QGVAR(radSeverity), (_unit getVariable [QGVAR(radSeverity), 0]) + _add, true]; }; private _limb = _unit getVariable [QGVAR(radDoseLimb), [0, 0, 0, 0, 0, 0]]; private _thr = GVAR(rad_skinBurnThreshold); private _parts = RAD_BODY_PARTS; { - private _new = (_limb select _forEachIndex) + ((_skinByPart select _forEachIndex) * _dt); + private _new = (_limb select _forEachIndex) + ((_skinByPart select _forEachIndex) * _hours); if (_new >= _thr) then { [_unit, 0.1 + random 0.3, _parts select _forEachIndex, "KAT_radiationBurn", _unit] call ACEFUNC(medical,addDamageToUnit); _new = _new - _thr; @@ -96,12 +98,12 @@ private _parts = RAD_BODY_PARTS; _unit setVariable [QGVAR(radDoseLimb), _limb, true]; if (_internalAdd > 0) then { - _unit setVariable [QGVAR(radInternalBurden), (_unit getVariable [QGVAR(radInternalBurden), 0]) + (_internalAdd * _dt), true]; + _unit setVariable [QGVAR(radInternalBurden), (_unit getVariable [QGVAR(radInternalBurden), 0]) + (_internalAdd * _hours), true]; [_unit] call FUNC(startRadInternalTick); }; if (_contamAdd > 0) then { - private _contam = ((_unit getVariable [QGVAR(radExternalContam), 0]) + (_contamAdd * GVAR(rad_contaminationDeposition) * _dt)) min 1; + private _contam = ((_unit getVariable [QGVAR(radExternalContam), 0]) + (_contamAdd * GVAR(rad_contaminationDeposition) * _hours)) min 1; _unit setVariable [QGVAR(radExternalContam), _contam, true]; [_unit] call FUNC(startRadContaminationTick); }; diff --git a/addons/chemical/functions/fnc_radInternalTickPFH.sqf b/addons/chemical/functions/fnc_radInternalTickPFH.sqf index 5e0b82b7a..77d5a048b 100644 --- a/addons/chemical/functions/fnc_radInternalTickPFH.sqf +++ b/addons/chemical/functions/fnc_radInternalTickPFH.sqf @@ -34,7 +34,9 @@ if (_burden < 0.001) exitWith { private _interval = 5; -_unit setVariable [QGVAR(radDoseWB), (_unit getVariable [QGVAR(radDoseWB), 0]) + (_burden * GVAR(rad_internalConversionRate) * _interval), true]; +private _add = _burden * GVAR(rad_internalConversionRate) * _interval; +_unit setVariable [QGVAR(radDoseWB), (_unit getVariable [QGVAR(radDoseWB), 0]) + _add, true]; +_unit setVariable [QGVAR(radSeverity), (_unit getVariable [QGVAR(radSeverity), 0]) + _add, true]; private _decay = GVAR(rad_internalDecayRate); if (CBA_missionTime < (_unit getVariable [QGVAR(radChelationWindow), 0])) then { diff --git a/addons/chemical/functions/fnc_radManagerPFH.sqf b/addons/chemical/functions/fnc_radManagerPFH.sqf index e5279b691..5db12c33a 100644 --- a/addons/chemical/functions/fnc_radManagerPFH.sqf +++ b/addons/chemical/functions/fnc_radManagerPFH.sqf @@ -24,8 +24,7 @@ */ private _exposedNow = []; -private _rateByUnit = createHashMap; -private _domByUnit = createHashMap; +private _accum = createHashMap; private _types = RAD_TYPES; { @@ -66,22 +65,28 @@ private _types = RAD_TYPES; if (_total <= 0) then { continue }; _exposedNow pushBackUnique _unit; - _rateByUnit set [_unit, (_rateByUnit getOrDefault [_unit, 0]) + _total]; + + private _nid = netId _unit; + private _entry = _accum getOrDefault [_nid, [_unit, 0, "", -1]]; + _entry set [1, (_entry select 1) + _total]; private _maxRate = selectMax _ratesByType; - if (_maxRate > ((_domByUnit getOrDefault [_unit, ["", -1]]) select 1)) then { - _domByUnit set [_unit, [_types select (_ratesByType find _maxRate), _maxRate]]; + if (_maxRate > (_entry select 3)) then { + _entry set [2, _types select (_ratesByType find _maxRate)]; + _entry set [3, _maxRate]; }; + _accum set [_nid, _entry]; [QGVAR(irradiate), [_unit, _ratesByType, _radLogic], _unit] call CBA_fnc_targetEvent; } forEach nearestObjects [_radLogic, ["CAManBase"], _radius]; } forEach GVAR(radSources); { - if (isNull _x) then { continue }; - _x setVariable [QGVAR(radDoseRate), _y, true]; - _x setVariable [QGVAR(radDominantType), (_domByUnit getOrDefault [_x, [""]]) select 0, true]; -} forEach _rateByUnit; + _y params ["_unit", "_rate", "_domType"]; + if (isNull _unit) then { continue }; + _unit setVariable [QGVAR(radDoseRate), _rate, true]; + _unit setVariable [QGVAR(radDominantType), _domType, true]; +} forEach _accum; { if (!isNull _x && {!(_x in _exposedNow)}) then { diff --git a/addons/chemical/functions/fnc_radPhysiologyPFH.sqf b/addons/chemical/functions/fnc_radPhysiologyPFH.sqf new file mode 100644 index 000000000..e0455f121 --- /dev/null +++ b/addons/chemical/functions/fnc_radPhysiologyPFH.sqf @@ -0,0 +1,103 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Owner-local per-unit radiation-physiology tick. Drives the dose-dependent + * sub-syndromes by current sickness tier (hematopoietic anemia, GI dehydration + * and acidosis, neurovascular hypotension and seizures, systemic fever and the + * visual feedback) and handles slow spontaneous recovery below the lethal tier. + * + * Each layer is gated on its host KAT subsystem's enable setting so radiation + * never depends on a disabled system; the cross-addon read hooks default to a + * no-op value, so this is inert when the layer is off. + * + * Arguments: + * 0: [_unit] + * 1: PFH handle + * + * Return Value: + * None + * + * Public: No + */ + +params ["_args", "_pfhHandle"]; +_args params ["_unit"]; + +if (isNull _unit || {!alive _unit}) exitWith { + if (!isNull _unit) then { + _unit setVariable [QGVAR(radPhysiologyPFHActive), false, true]; + _unit setVariable [QGVAR(radFever), 0, true]; + _unit setVariable [QGVAR(radBPDrop), 0, true]; + if (_unit == ACE_player) then { [false, 0] call EFUNC(feedback,effectRadiation); }; + }; + _pfhHandle call CBA_fnc_removePerFrameHandler; +}; + +private _interval = 5; +private _tier = _unit getVariable [QGVAR(radSicknessTier), 0]; +private _marrow = _unit getVariable [QGVAR(radMarrowFactor), 1]; + +if (_tier >= 2) then { + private _vol = (1 - _marrow) * GVAR(rad_marrowAnemiaRate) * _interval; + if (_vol > 0) then { REDUCE_TOTAL_BLOOD_VOLUME(_unit,_vol); }; +}; + +if (_tier >= 3) then { + REDUCE_TOTAL_BLOOD_VOLUME(_unit,GVAR(rad_giFluidLossRate) * _interval); + + if (missionNamespace getVariable [QEGVAR(breathing,enable), false]) then { + private _bloodGas = GET_BLOOD_GAS(_unit); + private _drop = GVAR(rad_giAcidosisRate) * _interval; + _bloodGas set [3, ((_bloodGas select 3) - _drop) max 0]; + _bloodGas set [4, ((_bloodGas select 4) - (_drop * 0.02)) max 6.8]; + _unit setVariable [VAR_BLOOD_GAS, _bloodGas, true]; + }; + + if (random 1 < 0.4) then { + playSound3D [QPATHTOEF_SOUND(airway,sounds\puking1.wav), _unit, false, getPosASL _unit, 5, 1, 15]; + _unit setVariable [VAR_PAIN, ((_unit getVariable [VAR_PAIN, 0]) + 0.05) min 1, true]; + }; +}; + +if (_tier >= 4) then { + if (missionNamespace getVariable [QEGVAR(circulation,enable), false]) then { + private _bpMax = GVAR(rad_cnsHypotension); + private _bp = (_unit getVariable [QGVAR(radBPDrop), 0]) + (_bpMax * 0.1 * _interval); + _unit setVariable [QGVAR(radBPDrop), _bp min _bpMax, true]; + }; + _unit setVariable [VAR_PAIN, ((_unit getVariable [VAR_PAIN, 0]) + 0.1) min 1, true]; + if (_unit == ACE_player && {random 1 < 0.3}) then { addCamShake [6, 2, 15]; }; +}; + +if (_tier >= 1 && {missionNamespace getVariable [QEGVAR(hypothermia,hypothermiaActive), false]}) then { + private _fever = ([0, 0.5, 1, 1.5, 2] select _tier) min GVAR(rad_feverMax); + _unit setVariable [QGVAR(radFever), _fever, true]; +}; + +if (_unit == ACE_player) then { + if (GVAR(rad_visualEffect)) then { + private _doseRate = _unit getVariable [QGVAR(radDoseRate), 0]; + private _intensity = ((_tier / 4) max ((_doseRate / 50) min 1)) min 1; + [true, _intensity] call EFUNC(feedback,effectRadiation); + } else { + [false, 0] call EFUNC(feedback,effectRadiation); + }; +}; + +private _doseRate = _unit getVariable [QGVAR(radDoseRate), 0]; +private _burden = _unit getVariable [QGVAR(radInternalBurden), 0]; +if (_tier < 4 && {_doseRate < 0.01} && {_burden < 0.001}) then { + private _severity = (_unit getVariable [QGVAR(radSeverity), 0]) - (GVAR(rad_recoveryRate) * _interval); + _unit setVariable [QGVAR(radSeverity), _severity max 0, true]; + _unit setVariable [QGVAR(radFever), ((_unit getVariable [QGVAR(radFever), 0]) - (0.1 * _interval)) max 0, true]; + _unit setVariable [QGVAR(radBPDrop), ((_unit getVariable [QGVAR(radBPDrop), 0]) - (2 * _interval)) max 0, true]; +}; + +[_unit] call FUNC(evaluateRadDose); + +_tier = _unit getVariable [QGVAR(radSicknessTier), 0]; +if (_tier == 0 && {(_unit getVariable [QGVAR(radFever), 0]) <= 0} && {(_unit getVariable [QGVAR(radBPDrop), 0]) <= 0}) exitWith { + _unit setVariable [QGVAR(radPhysiologyPFHActive), false, true]; + if (_unit == ACE_player) then { [false, 0] call EFUNC(feedback,effectRadiation); }; + _pfhHandle call CBA_fnc_removePerFrameHandler; +}; diff --git a/addons/chemical/functions/fnc_startRadPhysiologyTick.sqf b/addons/chemical/functions/fnc_startRadPhysiologyTick.sqf new file mode 100644 index 000000000..f94cf418e --- /dev/null +++ b/addons/chemical/functions/fnc_startRadPhysiologyTick.sqf @@ -0,0 +1,24 @@ +#include "..\script_component.hpp" +/* + * Author: DiGii + * Starts the owner-local radiation-physiology tick for a unit if not already + * running. Drives the ongoing dose-dependent sub-syndromes (anemia, GI, + * hypotension, fever, visual feedback) and handles spontaneous recovery. + * Idempotent. + * + * Arguments: + * 0: Unit + * + * Return Value: + * None + * + * Public: No + */ + +params ["_unit"]; + +if (isNull _unit) exitWith {}; +if (_unit getVariable [QGVAR(radPhysiologyPFHActive), false]) exitWith {}; +_unit setVariable [QGVAR(radPhysiologyPFHActive), true, true]; + +[LINKFUNC(radPhysiologyPFH), 5, [_unit]] call CBA_fnc_addPerFrameHandler; diff --git a/addons/chemical/initSettings.inc.sqf b/addons/chemical/initSettings.inc.sqf index 4ac7f53b4..e22b02b8e 100644 --- a/addons/chemical/initSettings.inc.sqf +++ b/addons/chemical/initSettings.inc.sqf @@ -531,3 +531,66 @@ [1, 50, 5, 1], true ] call CBA_fnc_addSetting; + +[ + QGVAR(rad_marrowAnemiaRate), + "SLIDER", + [LLSTRING(SETTING_rad_marrowAnemiaRate), LLSTRING(SETTING_rad_marrowAnemiaRate_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 20, 2, 1], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_giFluidLossRate), + "SLIDER", + [LLSTRING(SETTING_rad_giFluidLossRate), LLSTRING(SETTING_rad_giFluidLossRate_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 30, 3, 1], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_giAcidosisRate), + "SLIDER", + [LLSTRING(SETTING_rad_giAcidosisRate), LLSTRING(SETTING_rad_giAcidosisRate_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 1, 0.05, 3], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_cnsHypotension), + "SLIDER", + [LLSTRING(SETTING_rad_cnsHypotension), LLSTRING(SETTING_rad_cnsHypotension_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 100, 40, 0], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_feverMax), + "SLIDER", + [LLSTRING(SETTING_rad_feverMax), LLSTRING(SETTING_rad_feverMax_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 5, 2, 1], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_recoveryRate), + "SLIDER", + [LLSTRING(SETTING_rad_recoveryRate), LLSTRING(SETTING_rad_recoveryRate_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [0, 0.5, 0.01, 3], + true +] call CBA_fnc_addSetting; + +[ + QGVAR(rad_visualEffect), + "CHECKBOX", + [LLSTRING(SETTING_rad_visualEffect), LLSTRING(SETTING_rad_visualEffect_DESC)], + [CBA_SETTINGS_CAT, LSTRING(SubCategory_Radiation)], + [true], + true +] call CBA_fnc_addSetting; diff --git a/addons/chemical/stringtable.xml b/addons/chemical/stringtable.xml index 041a4509f..fbf7073e3 100644 --- a/addons/chemical/stringtable.xml +++ b/addons/chemical/stringtable.xml @@ -589,40 +589,40 @@ Gaz maskesini tak - 1. Protection & Gear - 1. Schutz & Ausrüstung + 01. Protection & Gear + 01. Schutz & Ausrüstung - 2. CS Gas (Level 0) - 2. CS-Gas (Level 0) + 02. CS Gas (Level 0) + 02. CS-Gas (Level 0) - 3. Chlorine (Level 1) - 3. Chlor (Level 1) + 03. Chlorine (Level 1) + 03. Chlor (Level 1) - 4. Phosgene (Level 1) - 4. Phosgen (Level 1) + 04. Phosgene (Level 1) + 04. Phosgen (Level 1) - 5. Mustard Gas (Level 2) - 5. Senfgas (Level 2) + 05. Mustard Gas (Level 2) + 05. Senfgas (Level 2) - 6. Sarin (Level 2) - 6. Sarin (Level 2) + 06. Sarin (Level 2) + 06. Sarin (Level 2) - 7. VX (Level 2) - 7. VX (Level 2) + 07. VX (Level 2) + 07. VX (Level 2) - 8. Contamination & Decontamination - 8. Kontamination & Dekontamination + 08. Contamination & Decontamination + 08. Kontamination & Dekontamination - 9. Identification (Detector & M8 Paper) - 9. dentifikation (Detektor & M8-Papier) + 09. Identification (Detector & M8 Paper) + 09. dentifikation (Detektor & M8-Papier) 10. Radiation @@ -1016,6 +1016,62 @@ Local skin dose per radiation burn wound applied to a body part. Lokale Hautdosis pro Strahlenverbrennungswunde, die auf ein Körperteil angewendet wird. + + Anemia rate (ml/s) + Anämie-Rate (ml/s) + + + Blood-volume loss per second from marrow suppression, scaled by sickness severity. Models progressive anemia at moderate+ tiers. + Blutvolumenverlust pro Sekunde durch Knochenmarkssuppression, skaliert mit der Schwere der Krankheit. Modelliert fortschreitende Anämie ab mittlerer Stufe. + + + GI fluid loss (ml/s) + GI-Flüssigkeitsverlust (ml/s) + + + Blood-volume loss per second from gastrointestinal syndrome (dehydration) at severe+ tiers. + Blutvolumenverlust pro Sekunde durch das gastrointestinale Syndrom (Dehydrierung) ab schwerer Stufe. + + + GI acidosis rate + GI-Azidose-Rate + + + Per-second drop in blood-gas bicarbonate/pH (metabolic acidosis) at severe+ tiers. Requires the Breathing system enabled. + Sekündlicher Abfall von Bikarbonat/pH im Blutgas (metabolische Azidose) ab schwerer Stufe. Erfordert aktiviertes Atmungssystem. + + + CNS hypotension (mmHg) + ZNS-Hypotonie (mmHg) + + + Maximum blood-pressure drop at the lethal/neurovascular tier. Requires the Circulation system enabled. + Maximaler Blutdruckabfall in der tödlichen/neurovaskulären Stufe. Erfordert aktiviertes Kreislaufsystem. + + + Maximum fever (°C) + Maximales Fieber (°C) + + + Maximum body-temperature rise from radiation sickness. Requires the Hypothermia system enabled. + Maximaler Anstieg der Körpertemperatur durch Strahlenkrankheit. Erfordert aktiviertes Hypothermie-System. + + + Recovery rate (Gy/s) + Erholungsrate (Gy/s) + + + How fast sickness severity heals per second once out of radiation (below the lethal tier). The lethal tier does not self-recover. + Wie schnell die Krankheitsschwere pro Sekunde abheilt, sobald keine Strahlung mehr wirkt (unterhalb der tödlichen Stufe). Die tödliche Stufe erholt sich nicht von selbst. + + + Radiation visual effect + Strahlungs-Bildschirmeffekt + + + Show a desaturated screen effect while irradiated/sick (client-side). + Zeigt einen entsättigten Bildschirmeffekt während Bestrahlung/Krankheit (clientseitig). + Accepted gas masks Povolené plynové masky @@ -1763,5 +1819,21 @@ Major Chemical Burn Schwere chemische Verätzung + + Radiation Burn + Strahlenverbrennung + + + Minor Radiation Burn + Leichte Strahlenverbrennung + + + Medium Radiation Burn + Mittlere Strahlenverbrennung + + + Major Radiation Burn + Schwere Strahlenverbrennung + diff --git a/addons/chemical/ui/kat_potassiumIodide.paa b/addons/chemical/ui/kat_potassiumIodide.paa new file mode 100644 index 0000000000000000000000000000000000000000..d54f3fb40a6a73a3134447c001907ce47227850e GIT binary patch literal 811766 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