Skip to content

Commit

Permalink
Fluid Shift and Recovery Position fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
mazinskihenry committed Oct 14, 2024
1 parent e2f1ca6 commit acd85e3
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 73 deletions.
73 changes: 48 additions & 25 deletions addons/pharma/functions/fnc_getBloodVolumeChange.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,13 @@ params ["_unit", "_deltaT", "_syncValues"];
private _bloodLoss = [_unit] call ACEFUNC(medical_status,getBloodLoss);
private _internalBleeding = GET_INTERNAL_BLEEDING(_unit);
private _lossVolumeChange = (-_deltaT * ((_bloodLoss + _internalBleeding * (GET_HEART_RATE(_unit) / DEFAULT_HEART_RATE)) / GET_VASOCONSTRICTION(_unit)));
private _enableFluidShift = EGVAR(vitals,enableFluidShift);
private _fluidVolume = GET_BODY_FLUID(_unit);
private _SRBCChange = 0;
_fluidVolume params ["_ECP","_ECB","_SRBC","_ISP","_fullVolume"];
_fluidVolume params ["_ECB","_ECP","_SRBC","_ISP","_fullVolume"];

_ECP = _ECP + (_lossVolumeChange * LITERS_TO_ML) / 2;
_ECB = _ECB + (_lossVolumeChange * LITERS_TO_ML) / 2;

_SRBCChange = if (_SRBC > 100 && _ECB < DEFAULT_ECB) then { ((DEFAULT_ECB - _ECB) min (abs ((_lossVolumeChange * LITERS_TO_ML)) / 2 + 1)) } else { 0 };
_ECB = _ECB + _SRBCChange;

if (!isNil {_unit getVariable [QACEGVAR(medical,ivBags),[]]}) then {
private _bloodBags = _unit getVariable [QACEGVAR(medical,ivBags), []];
private _IVarray = _unit getVariable [QGVAR(IV), [0,0,0,0,0,0]];
Expand Down Expand Up @@ -68,7 +65,16 @@ if (!isNil {_unit getVariable [QACEGVAR(medical,ivBags),[]]}) then {
// Plasma adds to ECP. Saline splits between the ECP and ISP. Blood adds to ECB
switch (true) do {
case(_type == "Plasma"): { _ECP = _ECP + _bagChange; _lossVolumeChange = _lossVolumeChange + (_bagChange / ML_TO_LITERS); };
case(_type == "Saline"): { _ECP = _ECP + _bagChange / 2; _ISP = _ISP + _bagChange / 2; _lossVolumeChange = _lossVolumeChange + (_bagChange / 2000); };
case(_type == "Saline"): {
if (_enableFluidShift) then {
_ECP = _ECP + _bagChange / 2;
_ISP = _ISP + _bagChange / 2;
_lossVolumeChange = _lossVolumeChange + (_bagChange / 2000);
} else {
_ECP = _ECP + _bagChange;
_lossVolumeChange = _lossVolumeChange + (_bagChange / ML_TO_LITERS);
};
};
case(_type == "Blood"): { _ECB = _ECB + _bagChange; _lossVolumeChange = _lossVolumeChange + (_bagChange / ML_TO_LITERS); };
};
};
Expand All @@ -88,7 +94,7 @@ if (!isNil {_unit getVariable [QACEGVAR(medical,ivBags),[]]}) then {
_unit setVariable [QACEGVAR(medical,ivBags), _bloodBags, _syncValues];
};

// Incoming fluids impacting internal temperature
// Incoming fluids impacting internal temperature
if (_hypothermia) then {
{ _fluidHeat = _fluidHeat + _x; } forEach _incomingVolumeChange;

Expand All @@ -102,27 +108,44 @@ if (!isNil {_unit getVariable [QACEGVAR(medical,ivBags),[]]}) then {
};
};

// Movement and recovery of interstital fluid
private _shiftValue = 0;
switch (true) do {
case (((_ECB + _ECP) > (_ISP * 0.6)) && ((_ECB + _ECP) > 4500)): {
// Negative shifts only happen above 4500ml of blood volume, to prevent issues with falling back into arrest/unconsciousness
_shiftValue = (2 min ((_ECP + _ECB) - (_ISP * 0.6)));
_ECP = _ECP - _shiftValue;
_ISP = _ISP + _shiftValue;
};
case ((_ECB + _ECP) < (_ISP * 0.6)): {
_shiftValue = (2 min ((_ISP * 0.6) - (_ECP + _ECB)));
_ECP = _ECP + _shiftValue;
_ISP = _ISP - _shiftValue;
// Movement and recovery of interstital fluid and SRBC collection
private _SRBCChange = 0;

if (_enableFluidShift) then {
private _shiftValue = 0;
private _defaultShift = false;

_SRBCChange = if ((_SRBC > 0) && (_ECB < DEFAULT_ECB)) then { 0.5 } else { 0 };

Check notice on line 118 in addons/pharma/functions/fnc_getBloodVolumeChange.sqf

View workflow job for this annotation

GitHub Actions / build

assignment to if can be replaced with select

use select
_ECB = _ECB + (_SRBCChange * _deltaT);
_SRBC = _SRBC - (_SRBCChange * _deltaT);

switch (true) do {
case (((_ECB + _ECP) > (_ISP * 0.6)) && ((_ECB + _ECP) > 4500)): {
// Negative shifts only happen above 4500ml of blood volume, to prevent patients from falling back into arrest/unconsciousness
_shiftValue = (1 min ((_ECP + _ECB) - (_ISP * 0.6))) * _deltaT;

_ECP = _ECP - _shiftValue;
_ISP = _ISP + _shiftValue;
};
case ((_ECB + _ECP) < (_ISP * 0.6)): {
_shiftValue = (1 min ((_ISP * 0.6) - (_ECP + _ECB))) *_deltaT;

if (_shiftValue < 0.1) exitWith { _defaultShift = true; };

_ECP = _ECP + _shiftValue;
_ISP = _ISP - _shiftValue;
};
default {
_defaultShift = true;
};
};
default {
// If no shift is required, fluids begin returning to baseline in both ISP and SRBC volumes
_ISP = _ISP + ((DEFAULT_ISP - _ISP) min 2);
_SRBC = _SRBC + ((DEFAULT_SRBC - _SRBC) min 1);

if (_defaultShift) then {
_ISP = _ISP + ((((DEFAULT_ISP - _ISP) max -2) min 2) *_deltaT);
_SRBC = _SRBC + ((((DEFAULT_SRBC - _SRBC) max -1) min 1) * _deltaT);
};
};

_unit setVariable [QEGVAR(circulation,bodyFluid), [_ECP, _ECB, (_SRBC - _SRBCChange), _ISP, (_ECP + _ECB)], _syncValues];
_unit setVariable [QEGVAR(circulation,bodyFluid), [_ECB, _ECP, _SRBC, _ISP, (_ECP + _ECB)], _syncValues];

((_lossVolumeChange + GET_BLOOD_VOLUME_LITERS(_unit)) max 0.01)
9 changes: 9 additions & 0 deletions addons/vitals/XEH_preInit.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,13 @@ PREP_RECOMPILE_END;
true
] call CBA_Settings_fnc_init;

[
QGVAR(enableFluidShift),
"CHECKBOX",
LLSTRING(FLUID_SHIFT),
[CBA_SETTINGS_CAT, ELSTRING(GUI,SubCategory_Basic)],
[true],
true
] call CBA_Settings_fnc_init;

ADDON = true;
8 changes: 5 additions & 3 deletions addons/vitals/functions/fnc_handleOxygenFunction.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ if (IN_CRDC_ARRST(_unit)) then {
private _tidalVolume = GET_KAT_SURFACE_AREA(_unit);

// Respiratory Rate is supressed by Opioids
_respiratoryRate = [((_demandVentilation / _tidalVolume) - (_opioidDepression * 10)) min MAXIMUM_RR, 20] select (_unit getVariable [QEGVAR(breathing,BVMInUse), false]);
_respiratoryRate = [((_demandVentilation / _tidalVolume) - (_opioidDepression * 10)) min MAXIMUM_RR, 25] select (_unit getVariable [QEGVAR(breathing,BVMInUse), false]);
_actualVentilation = _tidalVolume * _respiratoryRate;
};

// The greater the imbalance between CO2 explusion and O2 intake, the higher PaCO2 gets
private _paco2 = if ((_demandVentilation / _actualVentilation) == 1) then { _previousCyclePaco2 + (PACO2_MAX_CHANGE min (-PACO2_MAX_CHANGE max ((DEFAULT_PACO2 + ((_anerobicPressure max 1) - 1) * 150) - _previousCyclePaco2))) } else { [ _previousCyclePaco2 - (PACO2_MAX_CHANGE * _deltaT), _previousCyclePaco2 + (PACO2_MAX_CHANGE * _deltaT)] select ((_demandVentilation / _actualVentilation) > 0) };
private _paco2 = if ((_demandVentilation / _actualVentilation) == 1) then { _previousCyclePaco2 + (PACO2_MAX_CHANGE min (-PACO2_MAX_CHANGE max ((DEFAULT_PACO2 + ((_anerobicPressure max 1) - 1) * 150) - _previousCyclePaco2))) } else { [ _previousCyclePaco2 - (PACO2_MAX_CHANGE * _deltaT), _previousCyclePaco2 + (PACO2_MAX_CHANGE * _deltaT)] select ((_demandVentilation / _actualVentilation) > 0) };
// Generated ETCO2 quadratic. Ensures ETCO2 moves with Respiratory Rate and is constantly below PaCO2
private _etco2 = [((_paco2 - 3) - ((-0.0416667 * (_respiratoryRate^2)) + (3.09167 * (_respiratoryRate)) - DEFAULT_ETCO2) max 10), 0] select (IN_CRDC_ARRST(_unit));

Expand All @@ -71,7 +71,9 @@ if (EGVAR(pharma,kidneyAction)) then {

// Fractional Oxygen when breathing normal air is 0.21, 1 when breathing 100% Oxygen, and 0 when no air is being brought into the lungs
private _fio2 = switch (true) do {
case ((_unit getVariable [QEGVAR(airway,occluded), false]) || (_unit getVariable [QEGVAR(airway,obstruction), false])): { 0 };
case ((_unit getVariable [QEGVAR(airway,occluded), false]) || (_unit getVariable [QEGVAR(airway,obstruction), false])): {
[0, DEFAULT_FIO2] select ((_unit getVariable [QEGVAR(airway,recovery), false]) || (_unit getVariable [QEGVAR(airway,overstretch), false]))
};
case ((_unit getVariable [QEGVAR(chemical,airPoisoning), false]) || (_unit getVariable [QEGVAR(breathing,tensionpneumothorax), false]) || (_unit getVariable [QEGVAR(breathing,hemopneumothorax), false])): { 0 };
case (_unit getVariable [QEGVAR(breathing,oxygenTankConnected), false]): { 1 };
default { DEFAULT_FIO2 };
Expand Down
2 changes: 1 addition & 1 deletion addons/vitals/functions/fnc_handleSimpleVitals.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ if !(_adjustments isEqualTo []) then {
private _heartRate = [_unit, _hrTargetAdjustment, _deltaT, _syncValues] call ACEFUNC(medical_vitals,updateHeartRate); //Rename
[_unit, _painSupressAdjustment, _deltaT, _syncValues] call ACEFUNC(medical_vitals,updatePainSuppress); //Leave alone

private _bloodPressure = [120,80];
private _bloodPressure = [80,120];
_unit setVariable [VAR_BLOOD_PRESS, _bloodPressure, _syncValues];

// Statements are ordered by most lethal first.
Expand Down
14 changes: 7 additions & 7 deletions addons/vitals/functions/fnc_handleUnitVitals.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

params ["_unit"];

if (!(isPlayer _unit) && (_unit getVariable [QEGVAR(circulation,simpleMedical), false])) exitWith { [_unit] call FUNC(handleSimpleVitals) };
if (!(isPlayer _unit) && (_unit getVariable [QEGVAR(vitals,simpleMedical), false])) exitWith { [_unit] call FUNC(handleSimpleVitals) };

private _lastTimeUpdated = _unit getVariable [QACEGVAR(medical_vitals,lastTimeUpdated), 0];
private _deltaT = (CBA_missionTime - _lastTimeUpdated) min 10;
Expand Down Expand Up @@ -144,29 +144,29 @@ _unit setVariable [VAR_BLOOD_PRESS, _bloodPressure, _syncValues];
_bloodPressure params ["_bloodPressureL", "_bloodPressureH"];

// Statements are ordered by most lethal first.
// Add SpO2 reactions to switch statement ---------------------------------------------------------------------
switch (true) do {
case (_spo2 < EGVAR(breathing,SpO2_dieValue) && EGVAR(breathing,SpO2_dieActive)): {
switch (true) do {
case ((_spo2 < EGVAR(breathing,SpO2_dieValue)) && EGVAR(breathing,SpO2_dieActive)): {
TRACE_3("O2 Fatal",_unit,EGVAR(breathing,SpO2_dieValue),_spo2);
[QACEGVAR(medical,FatalInjury), _unit] call CBA_fnc_localEvent;
[QACEGVAR(medical,Death), _unit] call CBA_fnc_localEvent; // We have to call the Death state because if FatalInjuries is disabled, the patient won't actually die and just go into arrest
};
case (_bloodVolume < BLOOD_VOLUME_FATAL): {
TRACE_3("BloodVolume Fatal",_unit,BLOOD_VOLUME_FATAL,_bloodVolume);
[QACEGVAR(medical,Bleedout), _unit] call CBA_fnc_localEvent;
};
case (IN_CRDC_ARRST(_unit)): {}; // if in cardiac arrest just break now to avoid throwing unneeded events
case (_spo2 < EGVAR(breathing,SpO2_cardiacValue) && EGVAR(breathing,SpO2_cardiacActive)): {
case ((_spo2 < EGVAR(breathing,SpO2_cardiacValue)) && EGVAR(breathing,SpO2_cardiacActive)): {
[QACEGVAR(medical,FatalVitals), _unit] call CBA_fnc_localEvent;
};
case (_hemorrhage == 4): {
TRACE_3("Class IV Hemorrhage",_unit,_hemorrhage,_bloodVolume);
[QACEGVAR(medical,FatalVitals), _unit] call CBA_fnc_localEvent;
};
case (_heartRate < 20 || {_heartRate > 220}): {
case (_heartRate < 20 || (_heartRate > 220)): {
TRACE_2("heartRate Fatal",_unit,_heartRate);
[QACEGVAR(medical,FatalVitals), _unit] call CBA_fnc_localEvent;
};
case (_bloodPressureL < 20 || {_bloodPressureL > 180}): {
case (_bloodPressureL < 20 || (_bloodPressureL > 180)): {
TRACE_2("bloodPressure L above or below limits",_unit,_bloodPressureL);
[QACEGVAR(medical,CriticalVitals), _unit] call CBA_fnc_localEvent;
};
Expand Down
5 changes: 5 additions & 0 deletions addons/vitals/stringtable.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,9 @@
<Japanese>AI用単純化医療を有効化</Japanese>
</Key>
</Package>
<Package name="Vitals">
<Key ID="STR_KAT_Vitals_FLUID_SHIFT">
<English>Enable Patient Fluid Shift</English>
</Key>
</Package>
</Project>
92 changes: 55 additions & 37 deletions addons/watch/RscTitles.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,27 @@
#define ST_CENTER 2
#define ST_RIGHT 1

#define pixelW (1 / (getResolution select 2))
#define pixelH (1 / (getResolution select 3))
#define pixelScale 0.50

// pixel grids macros
#define UI_GRID_W (pixelW * pixelGridBase)
#define UI_GRID_H (pixelH * pixelGridBase)
#define UI_GUTTER_W (pixelW * 2)
#define UI_GUTTER_H (pixelH * 2)

// sizes for our control
#define BOX_W (UI_GRID_W * 24)
#define BOX_H (UI_GRID_H * 24)

#define KAT_WATCH_RESOLUTION_W (getResolution select 0)
#define KAT_WATCH_RESOLUTION_H (getResolution select 1)

class RscText;
class RscPicture;
class RscButton;
class RscPictureKeepAspect;
class RscTitles
{
class KAT_Katmin
Expand All @@ -31,10 +49,10 @@ class RscTitles
{
idc = 19800;
text = "\x\kat\addons\watch\UI\watch_katmin.paa";
x = "0.0328437 * safezoneW + safezoneX";
y = "0.652 * safezoneH + safezoneY";
w = "0.225 * safezoneW";
h = "0.4 * safezoneH";
x = QUOTE((KAT_WATCH_RESOLUTION_W - 300) / KAT_WATCH_RESOLUTION_W);
y = QUOTE((KAT_WATCH_RESOLUTION_H - 300) / KAT_WATCH_RESOLUTION_H);
w = QUOTE(BOX_W * (1.7777 / (getResolution select 4)));
h = QUOTE(BOX_H);
};
class KatminIcon: KatminImage
{
Expand Down Expand Up @@ -71,13 +89,13 @@ class RscTitles
shadow = 0;
font = "PuristaBold";
text = "12:00"; //--- ToDo: Localize;
x = "0.104 * safezoneW + safezoneX";
y = "0.8328 * safezoneH + safezoneY";
w = "0.061875 * safezoneW";
h = "0.033 * safezoneH";
x = QUOTE((KAT_WATCH_RESOLUTION_W + 28) / KAT_WATCH_RESOLUTION_W);
y = QUOTE((KAT_WATCH_RESOLUTION_H + 58) / KAT_WATCH_RESOLUTION_H);
w = QUOTE((UI_GRID_W * 7) * (1.7777 / (getResolution select 4)));
h = QUOTE(UI_GRID_W * 2.5);
colorBackground[] = {0,0,0,0};
colorText[] = {1,1,1,1};
sizeEx = QUOTE(KAT_POS_H(2));
sizeEx = QUOTE((UI_GRID_W * 4) * (1.7777 / (getResolution select 4)));
};
class KatminMonth: RscText
{
Expand All @@ -87,10 +105,10 @@ class RscTitles
shadow = 0;
font = "PuristaBold";
text = "JAN"; //--- ToDo: Localize;
x = "0.157316 * safezoneW + safezoneX";
y = "0.8394 * safezoneH + safezoneY";
w = "0.0257812 * safezoneW";
h = "0.013 * safezoneH";
x = QUOTE((KAT_WATCH_RESOLUTION_W + 274.8) / KAT_WATCH_RESOLUTION_W);
y = QUOTE((KAT_WATCH_RESOLUTION_H + 65.2) / KAT_WATCH_RESOLUTION_H);
w = "0.0257812 * safezoneW / ((getResolution select 5) * 1.7777)";
h = "0.013 * safezoneH / ((getResolution select 5) * 1.7777)";
colorBackground[] = {0,0,0,0};
colorText[] = {1,1,1,1};
sizeEx = QUOTE(KAT_POS_H(0.8));
Expand All @@ -103,10 +121,10 @@ class RscTitles
shadow = 0;
font = "PuristaBold";
text = "01"; //--- ToDo: Localize;
x = "0.157316 * safezoneW + safezoneX";
y = "0.8525 * safezoneH + safezoneY";
w = "0.0257812 * safezoneW";
h = "0.013 * safezoneH";
x = QUOTE((KAT_WATCH_RESOLUTION_W + 274.8) / KAT_WATCH_RESOLUTION_W);
y = QUOTE((KAT_WATCH_RESOLUTION_H + 97.84) / KAT_WATCH_RESOLUTION_H);
w = "0.0257812 * safezoneW / ((getResolution select 5) * 1.7777)";
h = "0.013 * safezoneH / ((getResolution select 5) * 1.7777)";
colorBackground[] = {0,0,0,0};
colorText[] = {1,1,1,1};
sizeEx = QUOTE(KAT_POS_H(0.8));
Expand All @@ -119,10 +137,10 @@ class RscTitles
shadow = 0;
font = "PuristaBold";
text = "760"; //--- ToDo: Localize;
x = "0.158625 * safezoneW + safezoneX";
y = "0.80844 * safezoneH + safezoneY";
w = "0.020625 * safezoneW";
h = "0.022 * safezoneH";
x = QUOTE((KAT_WATCH_RESOLUTION_W + 283.2) / KAT_WATCH_RESOLUTION_W);
y = QUOTE((KAT_WATCH_RESOLUTION_H + 10.4) / KAT_WATCH_RESOLUTION_H);
w = "0.020625 * safezoneW / ((getResolution select 5) * 1.7777)";
h = "0.022 * safezoneH / ((getResolution select 5) * 1.7777)";
colorBackground[] = {0,0,0,0};
colorText[] = {1,1,1,1};
sizeEx = QUOTE(KAT_POS_H(0.95));
Expand All @@ -135,10 +153,10 @@ class RscTitles
shadow = 0;
font = "PuristaBold";
text = "80"; //--- ToDo: Localize;
x = "0.149375 * safezoneW + safezoneX";
y = "0.8732 * safezoneH + safezoneY";
w = "0.020625 * safezoneW";
h = "0.022 * safezoneH";
x = QUOTE((KAT_WATCH_RESOLUTION_W + 240.8) / KAT_WATCH_RESOLUTION_W);
y = QUOTE((KAT_WATCH_RESOLUTION_H + 131.8) / KAT_WATCH_RESOLUTION_H);
w = "0.020625 * safezoneW / ((getResolution select 5) * 1.7777)";
h = "0.022 * safezoneH / ((getResolution select 5) * 1.7777)";
colorBackground[] = {0,0,0,0};
colorText[] = {1,1,1,1};
sizeEx = QUOTE(KAT_POS_H(1.2));
Expand All @@ -151,10 +169,10 @@ class RscTitles
shadow = 0;
font = "PuristaBold";
text = "97"; //--- ToDo: Localize;
x = "0.118469 * safezoneW + safezoneX";
y = "0.8732 * safezoneH + safezoneY";
w = "0.020625 * safezoneW";
h = "0.022 * safezoneH";
x = QUOTE((KAT_WATCH_RESOLUTION_W + 100.2) / KAT_WATCH_RESOLUTION_W);
y = QUOTE((KAT_WATCH_RESOLUTION_H + 131.8) / KAT_WATCH_RESOLUTION_H);
w = "0.020625 * safezoneW / ((getResolution select 5) * 1.7777)";
h = "0.022 * safezoneH / ((getResolution select 5) * 1.7777)";
colorBackground[] = {0,0,0,0};
colorText[] = {1,1,1,1};
sizeEx = QUOTE(KAT_POS_H(1.2));
Expand All @@ -167,10 +185,10 @@ class RscTitles
shadow = 0;
font = "PuristaBold";
text = "76F"; //--- ToDo: Localize;
x = "0.134937 * safezoneW + safezoneX";
y = "0.8094 * safezoneH + safezoneY";
w = "0.020625 * safezoneW";
h = "0.022 * safezoneH";
x = QUOTE((KAT_WATCH_RESOLUTION_W + 174.2) / KAT_WATCH_RESOLUTION_W);
y = QUOTE((KAT_WATCH_RESOLUTION_H + 10.4) / KAT_WATCH_RESOLUTION_H);
w = "0.020625 * safezoneW / ((getResolution select 5) * 1.7777)";
h = "0.022 * safezoneH / ((getResolution select 5) * 1.7777)";
colorBackground[] = {0,0,0,0};
colorText[] = {1,1,1,1};
sizeEx = QUOTE(KAT_POS_H(1));
Expand All @@ -183,10 +201,10 @@ class RscTitles
shadow = 0;
font = "PuristaBold";
text = "1000";
x = "0.111219 * safezoneW + safezoneX";
y = "0.80844 * safezoneH + safezoneY";
w = "0.020625 * safezoneW";
h = "0.022 * safezoneH";
x = QUOTE((KAT_WATCH_RESOLUTION_W + 67.2) / KAT_WATCH_RESOLUTION_W);
y = QUOTE((KAT_WATCH_RESOLUTION_H + 10.4) / KAT_WATCH_RESOLUTION_H);
w = "0.020625 * safezoneW / ((getResolution select 5) * 1.7777)";
h = "0.022 * safezoneH / ((getResolution select 5) * 1.7777)";
sizeEx = QUOTE(KAT_POS_H(0.95));
};
};
Expand Down

0 comments on commit acd85e3

Please sign in to comment.