Browse Source

update: 1.27 更新

Dcr 2 months ago
parent
commit
7692c5bb1a
100 changed files with 2466 additions and 666 deletions
  1. 1 1
      Scripts/1_core/proto/enconvert.c
  2. 21 0
      Scripts/1_core/proto/enmath.c
  3. 1 1
      Scripts/1_core/proto/enprofiler.c
  4. 4 4
      Scripts/1_core/proto/ensystem.c
  5. 313 0
      Scripts/2_gamelib/tests/testingframework.c
  6. 21 0
      Scripts/3_game/autotest/autotestconfighandler.c
  7. 4 0
      Scripts/3_game/autotest/autotestconfigjson.c
  8. 53 0
      Scripts/3_game/autotest/autotestfixture.c
  9. 217 0
      Scripts/3_game/autotest/autotestrunner.c
  10. 24 0
      Scripts/3_game/cfggameplaydatajson.c
  11. 9 0
      Scripts/3_game/cfggameplayhandler.c
  12. 1 1
      Scripts/3_game/client/onlineservices.c
  13. 55 11
      Scripts/3_game/constants.c
  14. 53 5
      Scripts/3_game/dayzgame.c
  15. 1 9
      Scripts/3_game/dayzplayer.c
  16. 33 12
      Scripts/3_game/debugweatherrpcdata.c
  17. 9 0
      Scripts/3_game/effects/effectparticle/bulletimpactbase/hit_foliage/hit_foliage_conifer.c
  18. 9 0
      Scripts/3_game/effects/effectparticle/bulletimpactbase/hit_foliage/hit_foliage_green.c
  19. 4 1
      Scripts/3_game/effects/effectsound.c
  20. 15 2
      Scripts/3_game/entities/building.c
  21. 15 0
      Scripts/3_game/entities/camera.c
  22. 1 1
      Scripts/3_game/entities/dayzanimal.c
  23. 23 58
      Scripts/3_game/entities/dayzcreatureaitype.c
  24. 120 36
      Scripts/3_game/entities/entityai.c
  25. 3 7
      Scripts/3_game/entities/man.c
  26. 9 6
      Scripts/3_game/entities/object.c
  27. 6 0
      Scripts/3_game/entities/pawn.c
  28. 2 0
      Scripts/3_game/enums/eactions.c
  29. 9 0
      Scripts/3_game/enums/ebuildinglocktypes.c
  30. 6 0
      Scripts/3_game/enums/econsumptionpenaltycontext.c
  31. 2 0
      Scripts/3_game/enums/ediagmenuids.c
  32. 8 0
      Scripts/3_game/enums/eenvironmenttemperaturecomponent.c
  33. 2 0
      Scripts/3_game/enums/erpcs.c
  34. 6 0
      Scripts/3_game/gameplay.c
  35. 51 0
      Scripts/3_game/global/ammotypes.c
  36. 8 0
      Scripts/3_game/global/errormodulehandler/connecterrorclientmodule.c
  37. 57 11
      Scripts/3_game/global/game.c
  38. 17 0
      Scripts/3_game/human.c
  39. 2 0
      Scripts/3_game/impacteffects.c
  40. 1 0
      Scripts/3_game/inputapi/uainput.c
  41. 7 11
      Scripts/3_game/inventoryitemtype.c
  42. 17 3
      Scripts/3_game/objectspawner.c
  43. 17 0
      Scripts/3_game/particles/particlelist.c
  44. 4 0
      Scripts/3_game/particles/particlemanager/particlemanager.c
  45. 20 4
      Scripts/3_game/playerconstants.c
  46. 2 0
      Scripts/3_game/ppemanager/materials/matclasses/ppegaussfilter.c
  47. 3 3
      Scripts/3_game/ppemanager/materials/matparameters/ppematclassparametercolor.c
  48. 9 2
      Scripts/3_game/ppemanager/pperequesterbank.c
  49. 1 1
      Scripts/3_game/ppemanager/requesters/pperequestplatformsbase.c
  50. 9 0
      Scripts/3_game/ppemanager/requesters/pperfeedbackblur.c
  51. 4 2
      Scripts/3_game/ppemanager/requesters/pperhmpghosts.c
  52. 9 0
      Scripts/3_game/ppemanager/requesters/pperlatencyblur.c
  53. 1 3
      Scripts/3_game/services/biosclientservices.c
  54. 10 0
      Scripts/3_game/services/biossessionservice.c
  55. 37 0
      Scripts/3_game/sound.c
  56. 72 0
      Scripts/3_game/surfaceinfo.c
  57. 0 14
      Scripts/3_game/systems/animalcatching/catchingcontextpoissonbase.c
  58. 4 1
      Scripts/3_game/systems/animalcatching/catchingresultbasic.c
  59. 3 0
      Scripts/3_game/systems/animalcatching/catchyielditembase.c
  60. 103 36
      Scripts/3_game/systems/dynamicmusicplayer/dynamicmusicplayer.c
  61. 26 1
      Scripts/3_game/systems/dynamicmusicplayer/dynamicmusicplayerregistry.c
  62. 1 1
      Scripts/3_game/systems/dynamicmusicplayer/dynamicmusicplayerregistryenoch.c
  63. 19 14
      Scripts/3_game/systems/dynamicmusicplayer/dynamicmusicplayerregistrysakhal.c
  64. 11 0
      Scripts/3_game/systems/hfsmbase.c
  65. 63 50
      Scripts/3_game/systems/inventory/handanimatedforceswapping.c
  66. 18 14
      Scripts/3_game/systems/inventory/handanimatedmovingtoatt.c
  67. 12 12
      Scripts/3_game/systems/inventory/handanimatedswapping.c
  68. 20 12
      Scripts/3_game/systems/inventory/handanimatedtakingfromatt.c
  69. 5 7
      Scripts/3_game/systems/inventory/handfsm.c
  70. 1 1
      Scripts/3_game/systems/inventory/handstablestate.c
  71. 11 0
      Scripts/3_game/systems/inventory/humaninventory.c
  72. 15 0
      Scripts/3_game/systems/inventory/humaninventorywithfsm.c
  73. 1 1
      Scripts/3_game/systems/inventory/inventory.c
  74. 1 19
      Scripts/3_game/systems/inventory/inventoryinputuserdata.c
  75. 6 1
      Scripts/3_game/systems/temperatureaccess/temperaturedata.c
  76. 33 39
      Scripts/3_game/tools/debug.c
  77. 32 1
      Scripts/3_game/tools/input.c
  78. 4 6
      Scripts/3_game/tools/jsonfileloader.c
  79. 6 3
      Scripts/3_game/tools/uiscriptedmenu.c
  80. 2 1
      Scripts/3_game/undergroundarealoader.c
  81. 23 0
      Scripts/3_game/vehicles/car.c
  82. 163 7
      Scripts/3_game/vehicles/transport.c
  83. 61 4
      Scripts/3_game/worlddata.c
  84. 1 1
      Scripts/4_world/classes/areadamage/areadamage.c
  85. 1 1
      Scripts/4_world/classes/areadamage/areadamagenew/damagecomponents/areadamagecomponent.c
  86. 1 1
      Scripts/4_world/classes/arrowmanager/arrowmanagerplayer.c
  87. 23 6
      Scripts/4_world/classes/basebuilding/construction.c
  88. 16 0
      Scripts/4_world/classes/basebuilding/constructionactiondata.c
  89. 5 5
      Scripts/4_world/classes/bleedingsources/bleedingsourcesmanagerbase.c
  90. 12 0
      Scripts/4_world/classes/consumeconditiondata.c
  91. 120 126
      Scripts/4_world/classes/cooking/cooking.c
  92. 8 0
      Scripts/4_world/classes/craftingmanager.c
  93. 6 0
      Scripts/4_world/classes/emoteclasses/emoteclasses.c
  94. 11 3
      Scripts/4_world/classes/emotemanager.c
  95. 170 77
      Scripts/4_world/classes/environment/environment.c
  96. 1 1
      Scripts/4_world/classes/explosion.c
  97. 5 0
      Scripts/4_world/classes/foodstage/foodstage.c
  98. 17 0
      Scripts/4_world/classes/heatcomfortanimhandler.c
  99. 4 4
      Scripts/4_world/classes/meleetargeting.c
  100. 3 1
      Scripts/4_world/classes/nutritionalprofile.c

+ 1 - 1
Scripts/1_core/proto/enconvert.c

@@ -99,7 +99,7 @@ class float
 	const float MAX = FLT_MAX;
 	const float LOWEST = -FLT_MAX;
 	
-	proto string ToString();
+	proto string ToString(bool simple = true);
 };
 
 class vector

+ 21 - 0
Scripts/1_core/proto/enmath.c

@@ -727,6 +727,27 @@ class Math
 		return res;
 	}
 	
+	/**
+	\brief Returns given value remaped from input range into output range
+		\param inputMin \p float Minimal value of given input range
+		\param inputMax \p float Maximal value of given input range
+		\param outputMin \p float Minimal value of given output range
+		\param outputMax \p float Maximal value of given input range
+		\param inputValue \p float Value we want to remap
+		\param clampedOutput\p bool If value should stay in that range, otherwise it will be extrapolated 
+		\return \p float - Remapped value
+	*/
+	static float Remap(float inputMin, float inputMax, float outputMin, float outputMax, float inputValue, bool clampedOutput = true)
+	{
+		float tempValue = Math.InverseLerp(inputMin, inputMax, inputValue);
+		float remapped = Math.Lerp(outputMin, outputMax, tempValue);
+		
+		if (clampedOutput)
+			return Math.Clamp(remapped, outputMin, outputMax);
+		
+		return remapped;
+	}
+	
 	static vector CenterOfRectangle(vector min, vector max)
 	{
 		float x = (min[0] + max[0]) * 0.5;

+ 1 - 1
Scripts/1_core/proto/enprofiler.c

@@ -698,7 +698,7 @@ class EnProfiler
 		float timeOfFunc = EnProfiler.GetTimeOfFuncG("ErrorEx", true);
 	@endcode
 	*/
-	static proto float GetTimeOfFuncG(string funct, bool immediate, bool immediate = false);
+	static proto float GetTimeOfFuncG(string funct, bool immediate = false);
 	
 	/**
 	\brief Obtain [SD] or [PD] regarding the amount of times a specific function was called

+ 4 - 4
Scripts/1_core/proto/ensystem.c

@@ -4,7 +4,7 @@
  */
 
 /**
-\brief Returns world time
+\brief Returns system time
 	\param[out] hour \p int Hour
 	\param[out] minute \p int Minute
 	\param[out] second \p int Second
@@ -28,7 +28,7 @@
 proto void GetHourMinuteSecond(out int hour, out int minute, out int second);
 
 /**
-\brief Returns world date
+\brief Returns system date
 	\param[out] year \p int Year
 	\param[out] month \p int Month
 	\param[out] day \p int Day
@@ -52,7 +52,7 @@ proto void GetHourMinuteSecond(out int hour, out int minute, out int second);
 proto void GetYearMonthDay(out int year, out int month, out int day);
 
 /**
-\brief Returns UTC world time
+\brief Returns UTC system time
 	\param[out] hour \p int Hour
 	\param[out] minute \p int Minute
 	\param[out] second \p int Second
@@ -76,7 +76,7 @@ proto void GetYearMonthDay(out int year, out int month, out int day);
 proto void GetHourMinuteSecondUTC(out int hour, out int minute, out int second);
 
 /**
-\brief Returns UTC world date
+\brief Returns UTC system date
 	\param[out] year \p int Year
 	\param[out] month \p int Month
 	\param[out] day \p int Day

+ 313 - 0
Scripts/2_gamelib/tests/testingframework.c

@@ -0,0 +1,313 @@
+/*!
+\defgroup ScriptTestingFramework Script Testing Framework
+\addtogroup ScriptTestingFramework
+@{
+
+\page Page_System_ScriptTestingFramework Script Testing Framework
+\tableofcontents
+
+\section Introduction Introduction
+
+- Provides a unified and simple interface that emphasizes the smallest amount
+of boiler plate code possible.
+
+- The collection and instantiation of its primitives is performed right after
+the script compilation.
+
+- Within the framework a SINGLE test harness derived class can exist. The
+harness runs the tests and contains API to access them.
+
+- The test units are compiled to Suites. These provide additional API for
+environmental control.
+
+\section SimpleTests Simple tests
+
+Can be perfomed in form of annotated <b>free functions</b>.
+
+<em>Note: Notice the Suite name in the attribute.</em>
+
+@code
+[Test("MyFirstTestSuite")]
+TestResultBase MyFooBarTest() { return TestBoolResult(5 > 3); }
+@endcode
+
+\section StatefulTests Stateful tests
+More elaborate tests that need some state and will run for several ticks have
+to be defined as TestBase derived classes. Your logic has to be ran through
+__step methods__.
+
+\subsection StepMethods Step methods
+- You can name your step methods however you like.
+- They have to be annotated by the [Step(Stage)] attribute which pairs the step
+with a stage.
+
+\subsection Stages Stages
+- They divide the steps into groups that express the initialization and
+finalization process.
+- Stages are executed in order Setup -> Main -> TearDown.
+- Methods in stages are executed in order of definition.
+
+\subsection ReturnValues Return values
+- void -> Will get executed only once.
+- bool -> Will get executed every tick until true is returned.
+
+\subsection Result Result
+- Result is set via API member method of TestBase `SetResult(TestResultBase)`.
+- Setting a result which evaluates to failure will terminate the stage.
+
+\subsection FailureUnwind Failure unwind
+- If the Setup stage fails the test only terminates and TearDown is not called.
+- Main stage failure will trigger the TearDown.
+- TearDown failure will do nothing.
+
+\subsection Timeout Timeout
+- The tests won't timeout by default. The value may be specified via Test
+attribute.
+- The timeout counter resets for every stage method.
+- If the stage method times out the TimeoutResult is set (it evaluates to
+failure and the failure unwind process starts).
+
+@code
+[Test("MyFirstTestSuite", timeoutS: 2, timeoutMs: 250)]
+class MyAsyncTest : TestBase
+{
+	// Simple blocking initialization.
+	[Step(EStage.Setup)]
+	void Initialize() { ... }
+
+	// Async test which is waiting for result for several frames.
+	[Step(EStage.Main)]
+	bool Pool() { ... }
+
+	// Finalization process waiting for result for several frames.
+	[Step(EStage.TearDown)]
+	bool FinalizeA() { ... }
+
+	// Simple blocking finalization call.
+	[Step(EStage.TearDown)]
+	void FinalizeB() { ... }
+}
+@endcode
+
+*/
+
+//-----------------------------------------------------------------------------
+//! Attribute used for tests annotation and assignment to Suites.
+class Test
+{
+	string Suite;
+
+	int TimeoutS;
+	int TimeoutMs;
+	int SortOrder;
+
+	//! Defines a suite the test belongs to, its timeout value and order within the suite.
+	void Test(string suite, int timeoutS = 0, int timeoutMs = 0, int sortOrder = 0)
+	{
+		Suite = suite;
+		TimeoutS = timeoutS;
+		TimeoutMs = timeoutMs;
+		SortOrder = sortOrder;
+	}
+}
+
+//-----------------------------------------------------------------------------
+//! Stage definition used in conjunction with Step attribute.
+enum EStage
+{
+	Setup,
+	Main,
+	TearDown
+}
+
+//-----------------------------------------------------------------------------
+//! Attribute which marks a method as part of the testing process.
+class Step
+{
+	EStage Stage;
+
+	void Step(EStage stage = EStage.Main)
+	{
+		Stage = stage;
+	}
+}
+
+//-----------------------------------------------------------------------------
+//! Collection and main interface of the Testing framework.
+class TestHarness : Managed
+{
+	//! Starts the testing process. Returns true when all tests have finished. If
+	//! some of them are still in progress false is reported.
+	proto native static bool Run();
+	//! Generates a xml report.
+	proto static string Report();
+	//! Returns number of test suites.
+	proto native static int GetNSuites();
+	//! Returns a test suite.
+	proto native static TestSuite GetSuite(int handle);
+	//! Returns currently active TestSuite or null when none is active.
+	proto native static TestSuite ActiveSuite();
+	//! Returns true when all tests and suites finished.
+	proto native static bool Finished();
+	//! Starts up the testing process and initializes the structures.
+	proto native static void Begin();
+	//! Finalizes the testing process.
+	proto native static void End();
+}
+
+//-----------------------------------------------------------------------------
+//! Collection of tests.
+class TestSuite : Managed
+{
+	//! Sets the suite result. Failure can result in specialized behavior described
+	//! in TestResultBase.
+	proto native void SetResult(TestResultBase res);
+	//! Returns the number for tests within this suite.
+	proto native int GetNTests();
+	//! Returns a test.
+	proto native TestBase GetTest(int handle);
+	//! Enables/Disables the suites. Disabled suites won't run at all.
+	proto native void SetEnabled(bool val);
+	//! Enabled flag getter.
+	proto native bool IsEnabled();
+	//! Suite class name getter. Strictly for UI porposes!
+	proto string GetName();
+	//! Callback for user defined initialization. Called for all suites during TestHarness.Begin().
+	protected void OnInit();
+}
+
+//-----------------------------------------------------------------------------
+//! Test base class. 
+class TestBase : Managed
+{
+	//! Sets the test result. Failure can result in specialized behavior described
+	//! in TestResultBase.
+	proto native void SetResult(TestResultBase res);
+	//! Result getter.
+	proto native TestResultBase GetResult();
+	//! Enables/Disables the test. Disabled tests won't run at all.
+	proto native void SetEnabled(bool val);
+	//! Enabled flag getter.
+	proto native bool IsEnabled();
+	//! Test name getter. Strictly for UI porposes!
+	proto string GetName();
+}
+
+//-----------------------------------------------------------------------------
+//! Base class for test results. This way you report back to the system.
+//! More complex failure types with descriptions can be reported by
+//! implementation of FailureText in format of junit
+//! [https://llg.cubic.org/docs/junit/].
+class TestResultBase : Managed
+{
+	//! Return true of the result means failure.
+	bool Failure() { return NativeFailure(); } 
+	//! Text used for xml report output.
+	string FailureText() { return NativeFailureText(); }
+
+	// Script forwarding to cpp. Otherwise the script overloading wouldn't be able
+	// to call the native base implementation.
+	// ----------------- vvv ----------------- 
+	proto native bool NativeFailure();
+	proto native string NativeFailureText();
+	// ----------------- ^^^ ----------------- 
+}
+
+/*!
+ * @}
+ */
+
+//-----------------------------------------------------------------------------
+// EXAMPLES
+//-----------------------------------------------------------------------------
+/*
+//-----------------------------------------------------------------------------
+//! Basic test result.
+class TestBoolResult : TestResultBase
+{
+	bool Value;
+
+	void TestBoolResult(bool val) { Value = val; }
+
+	override bool Failure() { return !Value; }
+
+	override string FailureText()
+	{
+		// junit kind of error report. (simple)
+		return "<failure type=\"BoolResult\">Failed</failure>";
+	}
+}
+
+//-----------------------------------------------------------------------------
+class MyHarness : TestHarness
+{
+}
+
+//-----------------------------------------------------------------------------
+class MyTestSuite : TestSuite
+{
+	int cnt;
+
+	[Step(EStage.Setup)]
+	void Prep()
+	{
+		Print("MyTestSuite::Prep");
+		cnt = 3;
+	}
+
+	[Step(EStage.Setup)]
+	bool Count()
+	{
+		--cnt;
+		Print("MyTestSuite::Count: cnt=" + cnt);
+		return cnt == 0;
+	}
+
+	[Step(EStage.TearDown)]
+	bool CountUp()
+	{
+		++cnt;
+		Print("MyTestSuite::CountUp: cnt=" + cnt);
+		return cnt == 10;
+	}
+}
+
+//-----------------------------------------------------------------------------
+[Test("MyTestSuite")]
+TestResultBase MyTest()
+{
+	Print("MyFuncTest");
+	return new TestBoolResult(true);
+}
+
+//-----------------------------------------------------------------------------
+[Test("MyTestSuite")]
+class MyAsyncTest : TestBase
+{
+	int counter;
+
+	[Step(EStage.Main)]
+	void Set()
+	{
+		counter = 10;
+	}
+
+	[Step(EStage.Main)]
+	bool Pool() 
+	{
+		Print("AsyncTest::Pool::counter=" + counter);
+
+		if(counter == 0)
+		{
+			Print("AsyncTest::Pool::Result");
+			SetResult(new TestBoolResult(false));
+			return true;
+		}
+
+		Print("AsyncTest::Pool::Progress");
+
+		counter--;
+		return false;
+	}
+}
+*/

+ 21 - 0
Scripts/3_game/autotest/autotestconfighandler.c

@@ -0,0 +1,21 @@
+class AutotestConfigHandler
+{
+	static ref AutotestConfigJson m_Data = new AutotestConfigJson();
+
+	static bool LoadData(string path)
+	{		
+		string errorMessage;
+		if (!JsonFileLoader<AutotestConfigJson>.LoadFile(path, m_Data, errorMessage))
+		{
+			AutoTestFixture.LogRPT(errorMessage);
+			return false;
+		}
+		
+		return true;
+	}
+	
+	static set<string> GetSuites()
+	{
+		return m_Data.TestSuites;
+	}
+}

+ 4 - 0
Scripts/3_game/autotest/autotestconfigjson.c

@@ -0,0 +1,4 @@
+class AutotestConfigJson
+{
+	ref set<string> TestSuites;
+}

+ 53 - 0
Scripts/3_game/autotest/autotestfixture.c

@@ -0,0 +1,53 @@
+class AutoTestFixture
+{
+	private static const string REPORT_FILE_NAME_TEMPLATE = "$mission:Autotest_%1.xml";
+	private static const string DATETIME_FORMAT = "%1%2%3T%4%5%6Z";
+
+	private static string m_WorldName;
+
+	static bool SaveXMLReport(string data, out string errorMessage)
+	{		
+		int year, month, day, hour, minute, second;
+		GetYearMonthDayUTC(year, month, day);
+		GetHourMinuteSecondUTC(hour, minute, second);
+		string datetimeUTC	= string.Format(DATETIME_FORMAT, year, month.ToStringLen(2), day.ToStringLen(2), hour.ToStringLen(2), minute.ToStringLen(2), second.ToStringLen(2));
+		string filename		= string.Format(REPORT_FILE_NAME_TEMPLATE, datetimeUTC);
+
+		FileHandle handle = OpenFile(filename, FileMode.WRITE);
+		if (handle == 0)
+		{
+			errorMessage = string.Format("Cannot open file \"%1\" for writing", filename);
+			return false;
+		}
+
+		FPrint(handle, data);
+		CloseFile(handle);
+
+		return true;
+	}
+	
+	static EntityAI SpawnEntityAI(string typeName, int flags = ECE_PLACE_ON_SURFACE)
+	{
+		EntityAI entity = EntityAI.Cast(g_Game.CreateObjectEx(typeName, g_Game.GetPlayer().GetPosition(), ECE_PLACE_ON_SURFACE));
+		return entity;
+	}
+	
+	static void SetWorldName()
+	{
+		string worldName = "empty";
+		GetGame().GetWorldName(worldName);
+		worldName.ToLower();
+
+		m_WorldName = worldName;
+	}
+	
+	static string GetWorldName()
+	{
+		return m_WorldName;
+	}
+	
+	static void LogRPT(string message)
+	{
+		PrintToRPT(string.Format("[Autotest] %1", message));
+	}
+}

+ 217 - 0
Scripts/3_game/autotest/autotestrunner.c

@@ -0,0 +1,217 @@
+/*
+	This class is just a convenience wrapper for using the actual TestHarness.
+*/
+class AutotestRunner
+{
+	private static bool m_IsRunning;
+	private static bool m_IsDone;
+	
+	static bool IsRunning()
+    {
+    	return m_IsRunning;
+	}
+        
+	static bool IsDone()
+	{
+		return m_IsDone;
+	}
+        
+	static void Start()
+    {
+		if (m_IsRunning)
+	    {
+	    	ErrorEx("TestAutotest already running!");
+	        return;
+		}
+		
+		AutoTestFixture.SetWorldName();
+	        
+	    // Enabled suites from JSON config provided on CLI (autotest param)
+	    set<string> enabledSuites = new set<string>();
+	    enabledSuites = AutotestConfigHandler.GetSuites();
+	        
+	    // Iterate through all suites and activate 'enabled' ones (list to be provided by config?)
+	    int numSuites = TestHarness.GetNSuites();
+		if (numSuites == 0)
+		{
+			AutoTestFixture.LogRPT("No TestSuites to run.");
+			m_IsDone = true;
+			return;		
+		}
+
+	    for (int i = 0; i < numSuites; ++i)
+	    {
+			TestSuite suite = TestHarness.GetSuite(i);                                              
+			bool isEnabled  = enabledSuites.Find(suite.GetName()) != -1 && suite.IsEnabled();
+			suite.SetEnabled(isEnabled);
+			//AutoTestFixture.LogRPT(string.Format("Suite '%1' activation state set to: %2", suite.GetName(), isEnabled));
+		}
+	        
+	    // Start running active TestSuite
+	    m_IsRunning = true;
+	    TestHarness.Begin();            
+	}
+        
+	static void Update(float deltaTime)
+	{
+		if (!m_IsRunning) 
+			return;
+			
+		if (!TestHarness.Finished())
+		{
+			bool isDone = TestHarness.Run();
+			if (isDone)
+			{
+				string errorMessage;
+				if (!AutoTestFixture.SaveXMLReport(TestHarness.Report(), errorMessage))
+					AutoTestFixture.LogRPT(errorMessage);
+					
+				TestHarness.End();
+				m_IsRunning = false;
+				m_IsDone = true;
+			}
+		}
+	}
+}
+
+/*
+	Script wrapper for TestResultBase that allows script provided kind and message in elegant way.
+*/
+class CustomResult : TestResultBase
+{
+	private bool   m_Success;
+	private string m_FailureText;
+	private string m_FailureKind;
+	
+	override bool Failure()
+	{
+		return !m_Success;
+	}
+	
+	// string FailureTextNativeFormat(string type, string userTxt);
+	// That will return the formatted string
+	override string FailureText()
+	{
+		return string.Format("<failure type=\"%1\">%2</failure>", m_FailureKind, m_FailureText);
+	}
+	
+	void CustomResult(bool success, string text = "User provided error!", string kind = "Failure")
+	{
+		m_Success     = success;
+		m_FailureText = text;
+		m_FailureKind = kind;
+	}
+}
+
+/* Suite is a collection of tests.  */
+/*
+class FooTestSuite : TestSuite
+{
+	// !!!
+	// Be careful, if you leave the suite empty - no error is given and it will just ommit everything.
+	// !!!
+        
+	[Step(EStage.Setup)]
+	void FooSetup()
+	{
+		Print("FooTestSuite is setting up... Tests can commence now..");
+	}
+	
+	[Step(EStage.TearDown)]
+	void FooFinish()
+	{
+		Print("FooTestSuite is finishing up ... Tests are done..");
+	}
+}
+*/
+
+/* Test is registered within suite via the usage of the Test attribute. */
+/*
+[Test("FooTestSuite")]
+class FooTest1 : TestBase
+{
+	private int       m_Value;
+	private const int k_TargetValue = 10;
+	
+	[Step(EStage.Setup)]
+	void Setup()
+	{
+		m_Value = k_TargetValue;
+	}
+	
+	[Step(EStage.Main)]
+	void Main()
+	{
+		if ( m_Value != k_TargetValue )
+		{
+			SetResult( new CustomResult(false, string.Format("Expected value: %1, Actual value: %2", k_TargetValue, m_Value)) );
+		}
+		else
+		{
+			SetResult( new CustomResult(true, "Successfull!") );
+		}
+	}
+	
+	[Step(EStage.TearDown)]
+	void Cleanup()
+	{
+		m_Value = 0;
+	}
+}
+
+/* Test is registered within suite via the usage of the Test attribute. */
+/*
+[Test("FooTestSuite")]
+class FooTest2 : TestBase
+{
+	private int       m_Value;
+	private const int k_TargetValue = 10;
+	
+	[Step(EStage.Setup)]
+	void Setup()
+	{
+		m_Value = 11; // intentionally wrong
+	}
+	
+	[Step(EStage.Main)]
+	void Main()
+	{
+		if ( m_Value != k_TargetValue )
+		{
+			SetResult( new CustomResult(false, string.Format("Expected value: %1, Actual value: %2", k_TargetValue, m_Value)) );
+		}
+		else
+		{
+			SetResult( new CustomResult(true, "Successfull!") );
+		}
+	}
+	
+	[Step(EStage.TearDown)]
+	void Cleanup()
+	{
+		m_Value = 0;
+	}
+}
+
+class BarTestSuite : TestSuite
+{       
+	[Step(EStage.Setup)]
+	void A();
+}
+
+[Test("BarTestSuite")]
+class BarTest1 : TestBase
+{
+	[Step(EStage.Setup)]
+	void Setup();
+	
+	[Step(EStage.Main)]
+	void Main()
+	{
+	        SetResult( new CustomResult(true, "Successfull!") );
+	}
+	
+	[Step(EStage.TearDown)]
+	void Cleanup();
+}
+*/

+ 24 - 0
Scripts/3_game/cfggameplaydatajson.c

@@ -18,6 +18,7 @@ class CfgGameplayJson
 	ref ITEM_BaseBuildingData BaseBuildingData 	= new ITEM_BaseBuildingData;
 	ref ITEM_UIData UIData 						= new ITEM_UIData;
 	ref ITEM_MapData MapData 					= new ITEM_MapData;
+	ref ITEM_VehicleData VehicleData 			= new ITEM_VehicleData;
 	
 };
 
@@ -113,10 +114,14 @@ class ITEM_StaminaData : ITEM_DataBase
 {
 	override void InitServer()
 	{
+		staminaMax = GameConstants.STAMINA_MAX;
 	}
 	
 	override bool ValidateServer()
 	{
+		if (staminaMax == 0.0)
+			return false;
+		
 		return true;
 	}
 	
@@ -239,6 +244,7 @@ class ITEM_HologramData : ITEM_DataBase
 	bool disableHeightPlacementCheck;
 	bool disableIsUnderwaterCheck;
 	bool disableIsInTerrainCheck;
+	bool disableColdAreaPlacementCheck;
 	ref TStringSet disallowedTypesInUnderground;
 };
 
@@ -352,3 +358,21 @@ class ITEM_DrowningData : ITEM_DataBase
 	float healthDepletionSpeed = 10;
 	float shockDepletionSpeed = 10;
 }
+
+//--------------------------------------------------------------------------------------------------------------------------------------------------
+
+class ITEM_VehicleData : ITEM_DataBase
+{
+	override void InitServer()
+	{
+	}
+	
+	override bool ValidateServer()
+	{
+		return true;
+	}
+	
+	//-------------------------------------------------------------------------------------------------
+	//!!! all member variables must correspond with the cfggameplay.json file contents !!!!
+	float boatDecayMultiplier = 1;
+};

+ 9 - 0
Scripts/3_game/cfggameplayhandler.c

@@ -329,6 +329,11 @@ class CfgGameplayHandler
 		return m_Data.BaseBuildingData.ConstructionData.disableDistanceCheck;
 	}
 	//----------------------------------------------------------------------------------
+	static bool GetDisableColdAreaPlacementCheck()
+	{
+		return m_Data.BaseBuildingData.HologramData.disableColdAreaPlacementCheck;
+	}
+	//----------------------------------------------------------------------------------
 	static TStringSet GetDisallowedTypesInUnderground()
 	{
 		return m_Data.BaseBuildingData.HologramData.disallowedTypesInUnderground;
@@ -445,4 +450,8 @@ class CfgGameplayHandler
 		return m_Data.WorldsData.playerRestrictedAreaFiles;
 	}
 	//----------------------------------------------------------------------------------
+	static float GetBoatDecayMultiplier()
+	{
+		return m_Data.VehicleData.boatDecayMultiplier;
+	}
 }

+ 1 - 1
Scripts/3_game/client/onlineservices.c

@@ -7,7 +7,7 @@ class OnlineServices
 	static ref ScriptInvoker												m_MuteUpdateAsyncInvoker	= new ScriptInvoker(); // DEPRECATED
 	static ref ScriptInvoker												m_ServerModLoadAsyncInvoker	= new ScriptInvoker();
 	
-	static ref BiosClientServices											m_ClientServices;
+	static BiosClientServices												m_ClientServices;
 	static ref TrialService													m_TrialService;
 	
 	protected static string													m_InviteServerIP;

+ 55 - 11
Scripts/3_game/constants.c

@@ -143,9 +143,11 @@ const int IDC_MAIN_OPTIONS      	= 102;
 const int IDC_MAIN_MULTIPLAYER  	= 105;
 const int IDC_MAIN_QUIT         	= 106;
 const int IDC_MAIN_CONTINUE			= 114;
+const int IDC_MAIN_TUTORIAL			= 117;
 const int IDC_MAIN_PLAY         	= 142;
 const int IDC_MAIN_CHARACTER		= 143;
 const int IDC_MAIN_ONLINE			= 124;
+const int IDC_MAIN_FEEDBACK			= 125;
 const int IDC_MULTI_REFRESH			= 123;
 const int IDC_MULTI_INVITE			= 126;
 
@@ -210,6 +212,7 @@ const int MENU_CONNECT_ERROR						= 42;
 const int MENU_WARNING_INPUTDEVICE_DISCONNECT		= 43;
 const int MENU_SCRIPTCONSOLE_UNIVERSAL_INFO_DIALOG	= 44;
 const int MENU_MISSION_LOADER						= 45;
+const int MENU_CONNECTION_DIALOGUE					= 46;
 
 
 const int GUI_WINDOW_MISSION_LOADER = 1;
@@ -408,6 +411,24 @@ class EmoteConstants
 	/** @}*/
 }
 
+//! for ItemSoundHandler use, values limited by ItemBase.ITEM_SOUNDS_MAX (networking optimization)
+class SoundConstants
+{
+	const int ITEM_PLACE = 1;
+	const int ITEM_DEPLOY_LOOP = 2;
+	const int ITEM_DEPLOY = 3;
+	
+	const int ITEM_BARREL_OPEN = 20;
+	const int ITEM_BARREL_CLOSE = 21;
+	const int ITEM_TENT_OPEN = 22;
+	const int ITEM_TENT_CLOSE = 23;
+	const int ITEM_TENT_WINDOW_OPEN = 24;
+	const int ITEM_TENT_WINDOW_CLOSE = 25;
+	const int ITEM_EXPLOSIVE_ARM = 26;
+	const int ITEM_EXPLOSIVE_DISARM = 27;
+	const int ITEM_KEY_BREAK = 28;
+}
+
 /**
  * \defgroup ItemGeneratorCfg Configurations for ItemsGenerator class
  * \desc Configurations for ItemsGenerator class
@@ -733,10 +754,18 @@ class GameConstants
 	const float ENVIRO_PLAYER_COMFORT_TEMP				= 24;		//! comfort temperature of environment for the player
 	const float ENVIRO_TEMP_EFFECT_ON_PLAYER			= 40;		//! impact of enviro temperature on player (lower value = higher, cannot be zero or below!)
 	const float ENVIRO_PLAYER_HEATBUFFER_WATEREFFECT	= 20;		//! impact of water contact on player's heatbuffer
-	const float ENVIRO_PLAYER_HEATBUFFER_DECREASE		= 0.03;		//! How much heat buffer decreases per one enviro tick
-	const float ENVIRO_PLAYER_HEATBUFFER_INCREASE		= 0.3;		//! How much heat buffer increases per one enviro tick
-	const float ENVIRO_PLAYER_HEATBUFFER_TEMP_AFFECT	= 0.50;		//! How much heat buffer change rates are affected by temperature
-	const float ENVIRO_PLAYER_HEATBUFFER_CAPACITY_MIN	= 0.3;		//! Minimal heatbuffer capacity of naked character
+	const float ENVIRO_PLAYER_HEATBUFFER_DECREASE		= 0.01;		//! How much heat buffer decreases per one enviro tick
+	const float ENVIRO_PLAYER_HEATBUFFER_INCREASE		= 0.18;		//! How much heat buffer increases per one enviro tick
+	const float ENVIRO_PLAYER_HEATBUFFER_TEMP_AFFECT	= 0.4;		//! How much heat buffer change rates are affected by temperature
+	const float ENVIRO_PLAYER_HEATBUFFER_CAPACITY_MIN	= 0.35;		//! Minimal heatbuffer capacity of naked character
+	
+	//! heatbuffer per stage decrease rate limiting - each entry represents the {min, max} value per HB stage
+	static ref array<ref TFloatArray> ENVIRO_PLAYER_HEATBUFFER_STAGE_RATELIMIT = {
+		{1.0, 1.0},	// not used now
+		{1.82, 4.0},
+		{0.8, 1.6},
+		{1.0, 1.0}, // not used now
+	};
 
 	//! impact of item wetness to the heat isolation
 	const float ENVIRO_ISOLATION_WETFACTOR_DRY			= 1.0;
@@ -778,6 +807,16 @@ class GameConstants
 	
 	/** @}*/
 	
+	/**
+	 * \defgroup Vehicle Vehicle Constants 
+	 * \desc Constants for vehicles
+	 * @{
+	 */
+
+	const float VEHICLE_FLIP_ANGLE_TOLERANCE = 45; //! Angle of the vehicle from the normal of the surface under the vehicle
+	const bool VEHICLE_FLIP_WHEELS_LIMITED = true; //! If the vehicle is not facing directly up, then don't use the "wheels early exit optimization"
+	/** @}*/
+	
 	/**
 	 * \defgroup CarFluidsConstants Cars Fluids 
 	 * \desc Constants for car fluids
@@ -885,6 +924,7 @@ class GameConstants
 	static const float TEMPERATURE_INTERPOLATION_THRESHOLD_MAX_ABS = 300.0; //difference in current - target temperatures
 	
 	static const float TEMPERATURE_FREEZETHAW_LEGACY_COEF = 0.2; //artificially lowers the freeze/thaw progression on reverse-calculated time values
+	static const float TEMPERATURE_FREEZETHAW_ACCELERATION_COEF = 20.0; //accelerates 'lowering' of the FT progress in the oposite direction
 	
 	static const float TEMPERATURE_TIME_OVERHEAT_MIN = 180; //minimal time in seconds to overheat any overheatable entity
 	static const float TEMPERATURE_TIME_FREEZE_MIN = 120; //minimal time in seconds to freeze entity
@@ -897,20 +937,24 @@ class GameConstants
 	
 	static const float TEMPERATURE_SENSITIVITY_THRESHOLD = 0.1; //changes lower than this will usually not be processed (absolute)
 	
-	const float TEMP_COEF_WORLD = 1; //entities on the ground
-	const float TEMP_COEF_INVENTORY = 1;
+	const float TEMP_COEF_WORLD 			= 1; 	//entities on the ground
+	const float TEMP_COEF_INVENTORY 		= 1;
 	const float TEMP_COEF_FIREPLACE_COOLING = 2.0;
 	const float TEMP_COEF_FIREPLACE_HEATING = 2.0;
-	const float TEMP_COEF_GAS_STOVE = 1.0;
-	const float TEMP_COEF_UTS = 6.0; //universal temperature sources
-	const float TEMP_COEF_COOKING_CATCHUP = 3.0; //heating of child items that are below minimal cooking temperature (catching up)
-	const float TEMP_COEF_COOKING_DEFAULT = 3.0;
-	const float TEMP_COEF_COOLING_GLOBAL = 1.0; //one universal coef for item cooling
+	const float TEMP_COEF_GAS_STOVE 		= 1.0;
+	const float TEMP_COEF_UTS 				= 6.0; 	//universal temperature sources
+	const float TEMP_COEF_COOKING_DEFAULT 	= 2.0;
+	const float TEMP_COEF_COOLING_GLOBAL 	= 1.0; 	//one universal coef for item cooling
+	const float TEMP_COEF_SWIMMING 			= 5.0; 	//speed of change for items in player's inventory during swimming
+	
 	
 	const float HEATISO_THRESHOLD_BAD = 0.2;
 	const float HEATISO_THRESHOLD_LOW = 0.4;
 	const float HEATISO_THRESHOLD_MEDIUM = 0.6;
 	const float HEATISO_THRESHOLD_HIGH = 0.8;
+	
+	//Deprecated temperature constants
+	const float TEMP_COEF_COOKING_CATCHUP 	= 3.0; 	//DEPRECEATED, heating of child items that are below minimal cooking temperature (catching up)
 	/** @}*/
 	
 	/**

+ 53 - 5
Scripts/3_game/dayzgame.c

@@ -728,7 +728,7 @@ class LoadingScreen
 			m_ProgressText.Show(GetGame().CommandlineGetParam("loadingTest", tmp));
 		}
 		m_WidgetRoot.FindAnyWidget("notification_root").Show(false);
-		
+
 		#ifdef PLATFORM_CONSOLE
 		#ifdef PLATFORM_XBOX
 		#ifdef BUILD_EXPERIMENTAL
@@ -894,6 +894,8 @@ class DayZGame extends CGame
 	const int MISSION_STATE_GAME = 1;
 	const int MISSION_STATE_FINNISH = 2;
 	
+	bool m_AutotestEnabled;
+	
 	private const int STATS_COUNT = EConnectivityStatType.COUNT;
 	private EConnectivityStatLevel m_ConnectivityStatsStates[STATS_COUNT];
 	
@@ -985,6 +987,7 @@ class DayZGame extends CGame
 	#ifdef DIAG_DEVELOPER
 	ref CameraToolsMenuServer m_CameraToolsMenuServer;
 	#endif
+
 	// CGame override functions
 	void DayZGame()
 	{
@@ -1732,7 +1735,7 @@ class DayZGame extends CGame
 			emh.OnEvent(eventTypeId, params);
 	}
 	
-	protected void SetConnectivityStatState(EConnectivityStatType type, EConnectivityStatLevel level)
+	void SetConnectivityStatState(EConnectivityStatType type, EConnectivityStatLevel level)
 	{
 		if (level != m_ConnectivityStatsStates[type])
 		{
@@ -2078,6 +2081,10 @@ class DayZGame extends CGame
 			{
 				ConnectLaunch();
 			}
+			else if (GetCLIParam("autotest", param))
+			{
+				AutoTestLaunch(param);
+			}
 			else if (GetCLIParam("mission", param))
 			{
 				MissionLaunch();
@@ -2280,7 +2287,6 @@ class DayZGame extends CGame
 		
 		SetGameState(DayZGameState.IN_GAME);
 		SetLoadState(DayZLoadState.MISSION_START);
-		
 
 		#ifndef PLATFORM_WINDOWS
 			#ifdef PLATFORM_CONSOLE			
@@ -2297,6 +2303,24 @@ class DayZGame extends CGame
 		PlayMission(mission);
 	}
 	
+	void AutoTestLaunch(string param)
+	{
+		if (!AutotestConfigHandler.LoadData(param))
+			AutoTestFixture.LogRPT("Failed to load autotest configuration, continue with mission load.");
+		else
+			m_AutotestEnabled = true;
+
+		string mission;
+		GetCLIParam("mission", mission);
+		if (!mission)
+		{
+			AutoTestFixture.LogRPT("Parameter 'mission' is not set on CLI.");
+			RequestExit(IDC_MAIN_QUIT);
+		}
+
+		PlayMission(mission);
+	}
+	
 	void SelectUser(int gamepad = -1)
 	{
 		BiosUserManager user_manager = GetUserManager();
@@ -3223,6 +3247,21 @@ class DayZGame extends CGame
 						
 						if (data.m_SnowfallValue >= 0)
 							GetGame().GetWeather().GetSnowfall().Set(data.m_SnowfallValue, data.m_SnowfallInterpolation, data.m_SnowfallDuration);
+						
+						if (data.m_VolFogDistanceDensity >= 0)
+							GetGame().GetWeather().SetDynVolFogDistanceDensity(data.m_VolFogDistanceDensity, data.m_VolFogDistanceDensityTime);
+						
+						if (data.m_VolFogHeightDensity >= 0)
+							GetGame().GetWeather().SetDynVolFogHeightDensity(data.m_VolFogHeightDensity, data.m_VolFogHeightDensityTime);
+						
+						if (data.m_VolFogHeightBias >= -500)
+							GetGame().GetWeather().SetDynVolFogHeightBias(data.m_VolFogHeightBias, data.m_VolFogHeightBiasTime);
+						
+						if (data.m_WindMagnitudeValue >= 0)
+							GetGame().GetWeather().GetWindMagnitude().Set(data.m_WindMagnitudeValue, data.m_WindDInterpolation, data.m_WindDDuration);
+						
+						if (data.m_WindDirectionValue >= -3.14)
+							GetGame().GetWeather().GetWindDirection().Set(data.m_WindDirectionValue, data.m_WindDInterpolation, data.m_WindDDuration);
 					}
 					else
 					{
@@ -3383,10 +3422,19 @@ class DayZGame extends CGame
 		#ifndef SERVER
 		if (source)
 		{
+			if (GetGame().GetPlayer() == null)
+				return;
 			source.OnExplosionEffects(source, directHit, componentIndex, surface, pos, surfNormal, energyFactor, explosionFactor, isWater, ammoType);
 			
-			if (source.ShootsExplosiveAmmo() && ammoType == "Explosion_40mm_Ammo")
-				ParticleManager.GetInstance().PlayInWorld(ParticleList.EXPLOSION_LANDMINE, pos);
+			if (source.ShootsExplosiveAmmo() )
+			{
+				int particleID = AmmoTypesAPI.GetExplosionParticleID(ammoType, surface);
+				if (particleID > -1)
+				{
+					ParticleManager.GetInstance().PlayInWorld(particleID, pos);
+				}
+			}
+				
 			
 			float distance_to_player = vector.Distance(pos, GetGame().GetPlayer().GetPosition());
 			m_AmmoShakeParams.Load(ammoType);

+ 1 - 9
Scripts/3_game/dayzplayer.c

@@ -1318,16 +1318,8 @@ class DayZPlayer extends Human
 	//! IsPlayerInStance(STANCEMASK_PRONE | STANCEIDX_RAISEDPRONE) returns true if player is in or in prone (both raised or nonraised)
 	//! IsPlayerInStance(STANCEMASK_ALL) returns true always 
 	//! IsPlayerInStance(STANCEMASK_RAISEDERECT | STANCEMASK_RAISEDCROUCH | STANCEMASK_RAISEDPRONE) returns true if player has raised hands
+	proto native 	bool	IsPlayerInStance(int pStanceMask);		// STANCEMASK_ERECT | STANCEMASK_CROUCH 
 	
-	// PSOVIS - move to native
-	bool		IsPlayerInStance(int pStanceMask)		// STANCEMASK_ERECT | STANCEMASK_CROUCH 
-	{
-		HumanMovementState		state = new HumanMovementState;
-		GetMovementState(state);
-		
-		bool ret = ((1 << state.m_iStanceIdx) & pStanceMask) != 0;
-		return ret;
-	}
 	
 	void OnInputForRemote (ParamsReadContext ctx) { }
 	void OnInputFromServer (ParamsReadContext ctx) { }

+ 33 - 12
Scripts/3_game/debugweatherrpcdata.c

@@ -1,17 +1,38 @@
 class DebugWeatherRPCData
 {
-	float m_FogValue 				= -1;
-	float m_OvercastValue 			= -1;
-	float m_RainValue 				= -1;
-	float m_SnowfallValue			= -1;
+	float m_FogValue 					= -1;
+	float m_OvercastValue 				= -1;
+	float m_RainValue 					= -1;
+	float m_SnowfallValue				= -1;
 	
-	float m_FogInterpolation 		= 0;
-	float m_OvercastInterpolation 	= -1;
-	float m_RainInterpolation 		= -1;
-	float m_SnowfallInterpolation	= -1;
+	float m_FogInterpolation 			= 0;
+	float m_OvercastInterpolation 		= -1;
+	float m_RainInterpolation 			= -1;
+	float m_SnowfallInterpolation		= -1;
 	
-	float m_FogDuration 			= -1;
-	float m_OvercastDuration		= -1;
-	float m_RainDuration 			= -1;
-	float m_SnowfallDuration		= -1;
+	float m_FogDuration 				= -1;
+	float m_OvercastDuration			= -1;
+	float m_RainDuration 				= -1;
+	float m_SnowfallDuration			= -1;
+	
+	float m_VolFogDistanceDensity 		= -1;
+	float m_VolFogDistanceDensityTime 	= -1;
+
+	float m_VolFogHeightDensity 		= -1;
+	float m_VolFogHeightDensityTime 	= -1;
+
+	float m_VolFogHeightBias 			= -1;
+	float m_VolFogHeightBiasTime		= -1;
+	
+	float m_StormDensity				= -1;
+	float m_StormThreshold				= -1;
+	float m_StormTimeOut				= -1;
+	
+	float m_WindMagnitudeValue			= -1;
+	float m_WindMInterpolation			= -1;
+	float m_WindMDuration				= -1;
+	
+	float m_WindDirectionValue			= -1;
+	float m_WindDInterpolation			= -1;
+	float m_WindDDuration				= -1;
 }

+ 9 - 0
Scripts/3_game/effects/effectparticle/bulletimpactbase/hit_foliage/hit_foliage_conifer.c

@@ -0,0 +1,9 @@
+class Hit_Foliage_Conifer: Hit_Foliage
+{
+	void Hit_Foliage_Conifer()
+	{
+		SetEnterParticle(ParticleList.IMPACT_FOLIAGE_CONIFER_ENTER);
+		SetExitParticle(ParticleList.IMPACT_FOLIAGE_CONIFER_EXIT);
+		SetRicochetParticle(ParticleList.IMPACT_FOLIAGE_CONIFER_RICOCHET);
+	}
+}

+ 9 - 0
Scripts/3_game/effects/effectparticle/bulletimpactbase/hit_foliage/hit_foliage_green.c

@@ -0,0 +1,9 @@
+class Hit_Foliage_Green : Hit_Foliage
+{
+	void Hit_Foliage_Green()
+	{
+		SetEnterParticle(ParticleList.IMPACT_FOLIAGE_GREEN_ENTER);
+		SetExitParticle(ParticleList.IMPACT_FOLIAGE_GREEN_EXIT);
+		SetRicochetParticle(ParticleList.IMPACT_FOLIAGE_GREEN_RICOCHET);
+	}
+}

+ 4 - 1
Scripts/3_game/effects/effectsound.c

@@ -365,7 +365,10 @@ class EffectSound : Effect
 	protected void ValidateSoundWave()
 	{
 		if (!m_SoundWaveObject)
-			Print(this.ToString() + " SoundWaveObject does not exist, SoundSet: " + m_SoundSetName);
+		{
+			ErrorEx(string.Format("%1 SoundWaveObject is null. SoundSet: %2", this.ToString(), m_SoundSetName));
+			return;
+		}
 		
 		m_SoundWaveLenght = m_SoundWaveObject.GetLength();
 					

+ 15 - 2
Scripts/3_game/entities/building.c

@@ -50,8 +50,8 @@ class Building extends EntityAI
 	//! Locks the door if not already locked, resets the door health. 'force = true' will close the door if open
 	proto native void LockDoor(int index, bool force = false);
 
-	//! Unlocks the door if locked
-	proto native void UnlockDoor(int index);
+	//! Unlocks the door if locked, AJAR animation optional
+	proto native void UnlockDoor(int index, bool animate = true);
 
 	//! Position in world space for where the door sounds are played from
 	proto native vector GetDoorSoundPos(int index);
@@ -151,6 +151,19 @@ class Building extends EntityAI
 		return (!IsDoorOpen(doorIndex) && !IsDoorLocked(doorIndex));
 	}
 	
+	/**
+	\brief Which door is compatible with which key (door idx supplied). Bitwise.
+	@param doorIdx
+	@return bitwise value of all compatible locks
+	\note you can combine the bit values like so:
+	\note return (1 << EBuildingLockType.LOCKPICK) | (1 << EBuildingLockType.SHIP_CONTAINER_1);
+	\note you can also this for each individual door idx
+	*/	
+	int GetLockCompatibilityType(int doorIdx)
+	{
+		return 1 << EBuildingLockType.LOCKPICK; //all doors are lockpickable by default
+	}
+	
 	override void GetDebugActions(out TSelectableActionInfoArrayEx outputList)
 	{
 		outputList.Insert(new TSelectableActionInfoWithColor(SAT_DEBUG_ACTION, EActions.BUILDING_OUTPUT_LOG, "Output Door Log", FadeColors.LIGHT_GREY));

+ 15 - 0
Scripts/3_game/entities/camera.c

@@ -22,6 +22,11 @@ class Camera extends Entity
 		2 = first third - acceleration, second third - linear movement, last third - deceleration
 	*/
 	static proto native void InterpolateTo(Camera targetCamera, float time, int type);
+	
+	/**
+	Checks if the current interpolation reached it's end
+	*/
+	static proto native bool IsInterpolationComplete();
 
 	/*!
 	set near plane of camera
@@ -38,6 +43,16 @@ class Camera extends Entity
 	\brief Sets this camera as active
 	*/
 	proto native void SetActive(bool active);
+	
+	/**
+	\brief Enables the smoothing in interpolation
+	*/
+	proto native void EnableSmooth(bool enable);
+	
+	/**
+	Performs cleanup. Important to call when interpolation run is finished.
+	*/
+	proto native void StopInterpolation();
 
 	/**
 	\brief Is this camera active?

+ 1 - 1
Scripts/3_game/entities/dayzanimal.c

@@ -59,7 +59,7 @@ class DayZCreature extends EntityAI
 	
 	override bool CanBeSkinned()
 	{
-		return true;
+		return !GetIsFrozen();
 	}
 	
 	override bool IsIgnoredByConstruction()

+ 23 - 58
Scripts/3_game/entities/dayzcreatureaitype.c

@@ -1,12 +1,12 @@
 class DayZCreatureAIType
 {
-	ref array<ref AnimSoundEvent> 		m_animSoundEvents;
-	ref array<ref AnimSoundVoiceEvent> 	m_animSoundVoiceEvents;
-	ref array<ref AnimStepEvent> 		m_animStepEvents;
-	ref array<ref AnimDamageEvent> 		m_animDamageEvents;
-
 	string m_CfgPath;
 	string m_AnimEventsCfgPath;
+	
+	private ref map<int, ref AnimSoundEvent> 		m_AnimSoundEvents;
+	private ref map<int, ref AnimStepEvent> 		m_AnimStepEvents;
+	private ref map<int, ref AnimSoundVoiceEvent> 	m_AnimSoundVoiceEvents;
+	private ref map<int, ref AnimDamageEvent> 	m_AnimDamageEvents;
 
 	private void DayZCreatureAIType()
 	{
@@ -15,14 +15,9 @@ class DayZCreatureAIType
 		LoadParams();
 	}
 	
-	private void ~DayZCreatureAIType()
-	{
-		
-	}
-	
 	void LoadAnimSoundEvents()
 	{
-		m_animSoundEvents = new array<ref AnimSoundEvent>;
+		m_AnimSoundEvents = new map<int, ref AnimSoundEvent>();
 		
 		string soundsCfgPath = m_AnimEventsCfgPath + "Sounds ";
 
@@ -34,13 +29,13 @@ class DayZCreatureAIType
 			string soundPath = soundsCfgPath + soundName + " ";
 			AnimSoundEvent soundEvent = new AnimSoundEvent(soundPath);
 			if (soundEvent.IsValid())
-				m_animSoundEvents.Insert(soundEvent);
+				m_AnimSoundEvents.Set(soundEvent.m_iID, soundEvent);
 		}
 	}
 
 	void LoadAnimSoundVoiceEvents()
 	{
-		m_animSoundVoiceEvents = new array<ref AnimSoundVoiceEvent>;
+		m_AnimSoundVoiceEvents = new map<int, ref AnimSoundVoiceEvent>();
 		
 		string soundsCfgPath = m_AnimEventsCfgPath + "SoundVoice ";
 
@@ -52,13 +47,13 @@ class DayZCreatureAIType
 			string soundPath = soundsCfgPath + soundName + " ";
 			AnimSoundVoiceEvent soundEvent = new AnimSoundVoiceEvent(soundPath);
 			if (soundEvent.IsValid())
-				m_animSoundVoiceEvents.Insert(soundEvent);
+				m_AnimSoundVoiceEvents.Set(soundEvent.m_iID, soundEvent);
 		}
 	}
 	
 	void LoadAnimStepEvents()
 	{
-		m_animStepEvents = new array<ref AnimStepEvent>;	
+		m_AnimStepEvents = new map<int, ref AnimStepEvent>();
 		string stepsCfgPath = m_AnimEventsCfgPath + "Steps ";
 		int stepsCount = GetGame().ConfigGetChildrenCount(stepsCfgPath);
 
@@ -68,13 +63,13 @@ class DayZCreatureAIType
 			GetGame().ConfigGetChildName(stepsCfgPath, i, stepName);			
 			string stepPath = stepsCfgPath + stepName + " ";
 			AnimStepEvent stepEvent = new AnimStepEvent(stepPath);
-			m_animStepEvents.Insert(stepEvent);
+			m_AnimStepEvents.Set(stepEvent.m_iID, stepEvent);
 		}
 	}
 	
 	void LoadAnimDamageEvents()
 	{
-		m_animDamageEvents = new array<ref AnimDamageEvent>;
+		m_AnimDamageEvents = new map<int, ref AnimDamageEvent>();
 
 		string damagesCfgPath = m_AnimEventsCfgPath + "Damages ";
 		int damagesCount = GetGame().ConfigGetChildrenCount(damagesCfgPath);
@@ -85,7 +80,7 @@ class DayZCreatureAIType
 			GetGame().ConfigGetChildName(damagesCfgPath, i, damageName);			
 			string damagePath = damagesCfgPath + damageName + " ";
 			AnimDamageEvent damageEvent = new AnimDamageEvent(damagePath);
-			m_animDamageEvents.Insert(damageEvent);
+			m_AnimDamageEvents.Set(damageEvent.m_iID, damageEvent);
 		}
 	}
 
@@ -99,59 +94,29 @@ class DayZCreatureAIType
 
 	AnimStepEvent GetStepEvent(int event_id)
 	{
-		for (int i = 0; i < m_animStepEvents.Count(); i++)
-		{
-			AnimStepEvent stepEvent = m_animStepEvents.Get(i);
-			if (stepEvent.m_iID == event_id)
-			{
-				return stepEvent;
-			}
-		}
-
-		return null;
+		return m_AnimStepEvents.Get(event_id);
 	}
 
 	AnimSoundEvent GetSoundEvent(int event_id)
 	{
-		for (int i = 0; i < m_animSoundEvents.Count(); i++)
-		{
-			AnimSoundEvent soundEvent = m_animSoundEvents.Get(i);
-			if (soundEvent.m_iID == event_id)
-			{
-				return soundEvent;
-			}
-		}
-
-		return null;
+		return m_AnimSoundEvents.Get(event_id);
 	}
 	
 	AnimSoundVoiceEvent GetSoundVoiceEvent(int event_id)
 	{
-		for (int i = 0; i < m_animSoundVoiceEvents.Count(); i++)
-		{
-			AnimSoundVoiceEvent voiceEvent = m_animSoundVoiceEvents.Get(i);
-			if (voiceEvent.m_iID == event_id)
-			{
-				return voiceEvent;
-			}
-		}
-
-		return null;
+		return m_AnimSoundVoiceEvents.Get(event_id);
 	}
 
 	AnimDamageEvent GetDamageEvent(int event_id)
 	{
-		for (int i = 0; i < m_animDamageEvents.Count(); i++)
-		{
-			AnimDamageEvent damageEvent = m_animDamageEvents.Get(i);
-			if (damageEvent.m_iID == event_id)
-			{
-				return damageEvent;
-			}
-		}
-
-		return null;
+		return m_AnimDamageEvents.Get(event_id);
 	}
 
 	proto native owned string GetName();
+
+	//! DEPRECATED
+	ref array<ref AnimSoundEvent> 		m_animSoundEvents;
+	ref array<ref AnimStepEvent> 		m_animStepEvents;
+	ref array<ref AnimSoundVoiceEvent> 	m_animSoundVoiceEvents;
+	ref array<ref AnimDamageEvent> 		m_animDamageEvents;
 }

+ 120 - 36
Scripts/3_game/entities/entityai.c

@@ -191,6 +191,8 @@ class EntityAI extends Entity
 	protected ref ScriptInvoker		m_OnHitByInvoker;	
 	//Called when this entity is killed
 	protected ref ScriptInvoker		m_OnKilledInvoker;
+
+	private ref map<eAgents, float> m_BloodInfectionChanceCached; //! cache blood infection chance (cfgVehicles-><entity>->Skinning->BloodInfectionSettings)
 	
 	#ifdef DEVELOPER
 	float m_LastFTChangeTime;;
@@ -234,6 +236,8 @@ class EntityAI extends Entity
 		m_HiddenSelectionsData = new HiddenSelectionsData( GetType() );
 		
 		GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(DeferredInit,34);
+
+		m_BloodInfectionChanceCached = new map<eAgents, float>();
 	}
 	
 	void ~EntityAI()
@@ -496,6 +500,27 @@ class EntityAI extends Entity
 				return true;
 		return false;
 	}
+	
+	float GetSkinningBloodInfectionChance(eAgents type)
+	{
+		float value = 0.0;
+		if (m_BloodInfectionChanceCached.Find(type, value))
+			return value;
+		
+		return value;
+	}
+	
+	protected void CacheSkinningBloodInfectionChance(eAgents type)
+	{
+		string basePath = string.Format("cfgVehicles %1 Skinning BloodInfectionSettings", GetType());
+		if (g_Game.ConfigIsExisting(basePath))
+		{
+			string agentName = EnumTools.EnumToString(eAgents, type);
+			agentName.ToLower();
+			float value = g_Game.ConfigGetFloat(string.Format("%1 %2 chance", basePath, agentName));
+			m_BloodInfectionChanceCached.Set(type, value);
+		}
+	}
 	///@} Skinning
 
 	// ITEM TO ITEM FIRE DISTRIBUTION
@@ -536,6 +561,11 @@ class EntityAI extends Entity
 		return false;
 	}
 	
+	bool IsCookware()
+	{
+		return false;
+	}
+	
 	//! Should return false if you want to disable hologram rotation
 	bool PlacementCanBeRotated()
 	{
@@ -948,6 +978,16 @@ class EntityAI extends Entity
 			else
 				Error("EntityAI::EEItemLocationChanged - attached, but new_owner is null");
 		}
+		
+		if (oldLoc.GetType() == InventoryLocationType.HANDS)
+		{
+			Man.Cast(oldLoc.GetParent()).OnItemInHandsChanged();
+		}
+		
+		if (newLoc.GetType() == InventoryLocationType.HANDS)
+		{
+			Man.Cast(newLoc.GetParent()).OnItemInHandsChanged();
+		}
 	}
 
 	//! Called from 'IEntity.AddChild'
@@ -1384,41 +1424,7 @@ class EntityAI extends Entity
 	bool CanReceiveAttachment (EntityAI attachment, int slotId)
 	{
 		//generic occupancy check
-		EntityAI currentAtt = GetInventory().FindAttachment(slotId);
-		bool hasInternalConflict = attachment.HasInternalExclusionConflicts(slotId);
-		set<int> diff;
-		InventoryLocation curLoc = new InventoryLocation();
-		if (currentAtt) //probably a swap or same-type swap
-		{
-			diff = attachment.GetAttachmentExclusionMaskAll(slotId);
-			diff.RemoveItems(currentAtt.GetAttachmentExclusionMaskAll(slotId));
-			if (diff.Count() == 0)
-			{
-				return !hasInternalConflict;
-			}
-			else
-			{
-				return !hasInternalConflict && !IsExclusionFlagPresentRecursive(diff,slotId);
-			}
-		}
-		else if (attachment.GetInventory().GetCurrentInventoryLocation(curLoc) && curLoc.GetType() == InventoryLocationType.ATTACHMENT)
-		{
-			EntityAI rootOwner = attachment.GetHierarchyRoot();
-			if (rootOwner && rootOwner == this.GetHierarchyRoot()) //attachment within the same exclusion hierarchy context
-			{
-				diff = attachment.GetAttachmentExclusionMaskAll(slotId);
-				diff.RemoveItems(attachment.GetAttachmentExclusionMaskAll(curLoc.GetSlot()));
-				if (diff.Count() == 0)
-				{
-					return !hasInternalConflict;
-				}
-				else
-				{
-					return !hasInternalConflict && !IsExclusionFlagPresentRecursive(diff,slotId);
-				}
-			}
-		}
-		return !hasInternalConflict && !IsExclusionFlagPresentRecursive(attachment.GetAttachmentExclusionMaskAll(slotId),slotId);
+		return CheckAttachmentReceiveExclusion(attachment,slotId);
 	}
 	
 	/**@fn		CanLoadAsAttachment
@@ -1809,6 +1815,30 @@ class EntityAI extends Entity
 		}
 		return null;
 	}
+	
+	// Check whether attachmnent slot is reserved
+	bool IsSlotReserved(int slotID)
+	{
+		Man player = GetHierarchyRootPlayer();
+		if (!player)
+			return false;
+		
+		HumanInventory inv = player.GetHumanInventory();
+		if (!inv || inv.GetUserReservedLocationCount() == 0)
+			return false;
+		
+		int id = inv.FindFirstUserReservedLocationIndexForContainer(this);
+		InventoryLocation loc = new InventoryLocation();
+		if (id == -1)
+			return false;
+		
+		inv.GetUserReservedLocation(id, loc);
+
+		if (loc.GetSlot() != slotID)
+			return false;
+		
+		return true;		
+	}
 
 	/**@fn		IsLockedInSlot
 	 * @return	true if entity is locked in attachment slot
@@ -2439,6 +2469,12 @@ class EntityAI extends Entity
 		return m_FreezeThawProgress;
 	}
 	
+	//! on server only
+	bool IsFreezeThawProgressFinished()
+	{
+		return m_FreezeThawProgress <= 0.0 || m_FreezeThawProgress >= 1.0;
+	}
+	
 	//! 0->1 when freezing, 1->0 when thawing
 	protected void SetFreezeThawProgress(float val)
 	{
@@ -2537,6 +2573,9 @@ class EntityAI extends Entity
 		}
 		else
 		{
+			if ((progressDelta < 0 && !m_IsFrozen) || (progressDelta > 0 && m_IsFrozen))
+				progressVal = (progressDelta * GameConstants.TEMPERATURE_FREEZETHAW_ACCELERATION_COEF) / changeTime + m_FreezeThawProgress;
+			
 			SetFreezeThawProgress(Math.Clamp(progressVal,0,1));
 		}
 		
@@ -3159,6 +3198,7 @@ class EntityAI extends Entity
 		{
 			floats_out.Insert(m_VarTemperature);
 			floats_out.Insert((float)GetIsFrozen());
+			floats_out.Insert((float)GetFreezeThawProgress());
 		}
 	}
 	
@@ -3179,6 +3219,10 @@ class EntityAI extends Entity
 			bool frozen = Math.Round(floats.Get(index));
 			SetFrozen(frozen);
 			floats.RemoveOrdered(index);
+			
+			float FTProgress = floats.Get(index);
+			SetFreezeThawProgress(FTProgress);
+			floats.RemoveOrdered(index);
 		}
 	}
 	
@@ -3791,7 +3835,7 @@ class EntityAI extends Entity
 			if (CanHaveTemperature() && !IsSelfAdjustingTemperature() && !GetHierarchyRoot().IsSelfAdjustingTemperature())
 			{
 				float target = g_Game.GetMission().GetWorldData().GetBaseEnvTemperatureAtObject(this);
-				if (GetTemperature() != target)
+				if (GetTemperature() != target || !IsFreezeThawProgressFinished())
 				{
 					float heatPermCoef = 1.0;
 					EntityAI ent = this;
@@ -4546,6 +4590,46 @@ class EntityAI extends Entity
 	{
 		AdjustExclusionAccessCondition(occupiedSlot,testedSlot,value,adjustedValue);
 	}
+	
+	//! checks specifically for att. exclusion conflicts before att. receive
+	bool CheckAttachmentReceiveExclusion(EntityAI attachment, int slotId)
+	{
+		EntityAI currentAtt = GetInventory().FindAttachment(slotId);
+		bool hasInternalConflict = attachment.HasInternalExclusionConflicts(slotId);
+		set<int> diff;
+		InventoryLocation curLoc = new InventoryLocation();
+		if (currentAtt) //probably a swap or same-type swap
+		{
+			diff = attachment.GetAttachmentExclusionMaskAll(slotId);
+			diff.RemoveItems(currentAtt.GetAttachmentExclusionMaskAll(slotId));
+			if (diff.Count() == 0)
+			{
+				return !hasInternalConflict;
+			}
+			else
+			{
+				return !hasInternalConflict && !IsExclusionFlagPresentRecursive(diff,slotId);
+			}
+		}
+		else if (attachment.GetInventory().GetCurrentInventoryLocation(curLoc) && curLoc.GetType() == InventoryLocationType.ATTACHMENT)
+		{
+			EntityAI rootOwner = attachment.GetHierarchyRoot();
+			if (rootOwner && rootOwner == this.GetHierarchyRoot()) //attachment within the same exclusion hierarchy context
+			{
+				diff = attachment.GetAttachmentExclusionMaskAll(slotId);
+				diff.RemoveItems(attachment.GetAttachmentExclusionMaskAll(curLoc.GetSlot()));
+				if (diff.Count() == 0)
+				{
+					return !hasInternalConflict;
+				}
+				else
+				{
+					return !hasInternalConflict && !IsExclusionFlagPresentRecursive(diff,slotId);
+				}
+			}
+		}
+		return !hasInternalConflict && !IsExclusionFlagPresentRecursive(attachment.GetAttachmentExclusionMaskAll(slotId),slotId);
+	}
 
 	bool IsManagingArrows()
 	{

+ 3 - 7
Scripts/3_game/entities/man.c

@@ -71,12 +71,8 @@ class Man extends EntityAI
 	void AddItemToDelete(EntityAI item);
 
 	///@{ inventory
-	HumanInventory GetHumanInventory()
-	{
-		HumanInventory i = HumanInventory.Cast(GetInventory());
-		return i;
-	}
-
+	//! Returns player's inventory
+	proto native HumanInventory GetHumanInventory();
 	//Called when an item is removed from the cargo of this item
 	protected ref ScriptInvoker		m_OnItemAddedToHands;
 	//Called when an item is moved around in the cargo of this item
@@ -853,7 +849,7 @@ class Man extends EntityAI
 
 	override bool CanBeSkinned()
 	{
-		return true;
+		return !GetIsFrozen();
 	}
 	
 	override bool DisableVicinityIcon()

+ 9 - 6
Scripts/3_game/entities/object.c

@@ -1055,15 +1055,15 @@ class Object extends IEntity
 	@param zoneName if empty string, sets state of global health
 	@param healthType if empty string, sets state of main health
 	*/
-	void   DecreaseHealth(string zoneName, string healthType, float value, bool auto_delete)
+	void DecreaseHealth(string zoneName, string healthType, float value, bool auto_delete)
 	{
-		DecreaseHealth( zoneName, healthType, value);
+		DecreaseHealth(zoneName, healthType, value);
 		
-		float result_health = GetHealth(zoneName, healthType);
-		
-		if ( auto_delete  &&  result_health <= 0 )
+		if (auto_delete)
 		{
-			GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).Call(GetGame().ObjectDelete, this); 
+			float result_health = GetHealth(zoneName, healthType);
+			if (result_health <= 0)
+				GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).Call(GetGame().ObjectDelete, this); 
 		}
 	}
 	
@@ -1425,6 +1425,9 @@ class Object extends IEntity
 	{
 		return false;
 	}
+	
+	void OnSpawnByObjectSpawner(ITEM_SpawnerObject item)
+	{}
 
 	//Debug
 	//----------------------------------------------

+ 6 - 0
Scripts/3_game/entities/pawn.c

@@ -32,6 +32,12 @@ class PawnOwnerState
 
 	proto native int	GetSimulationTimestamp();
 	
+	proto native void	SetPhysicsTimeStamp(int value);
+	proto native int	GetPhysicsTimeStamp();
+	
+	proto native void	SetWaterTime(float value);
+	proto native float	GetWaterTime();
+	
 	protected event void Write(PawnStateWriter ctx)
 	{
 	}

+ 2 - 0
Scripts/3_game/enums/eactions.c

@@ -27,6 +27,7 @@ enum EActions
 	SPIN,
 	FOOD_STAGE_PREV,
 	FOOD_STAGE_NEXT,
+	FOOD_NUTRITIONS_DATA,
 	MAKE_SPECIAL,
 	DELETE,
 	SEPARATOR,
@@ -65,6 +66,7 @@ enum EActions
 	SPECIALIZED_ACTION4,
 	SPECIALIZED_ACTION5,
 	SPECIALIZED_ACTION6,
+	FLIP_ENTITY,
 	
 	//<----add new individual types above this line
 	

+ 9 - 0
Scripts/3_game/enums/ebuildinglocktypes.c

@@ -0,0 +1,9 @@
+enum EBuildingLockType
+{
+	NONE = 0, //unlockable only via script
+	LOCKPICK,
+	SHIP_CONTAINER_0,
+	SHIP_CONTAINER_1,
+	SHIP_CONTAINER_2,
+	SHIP_CONTAINER_3,
+}

+ 6 - 0
Scripts/3_game/enums/econsumptionpenaltycontext.c

@@ -0,0 +1,6 @@
+enum EConsumptionPenaltyContext
+{
+	NONE 	= 0,
+	DRINK 	= 1,
+	EAT		= 2,
+}

+ 2 - 0
Scripts/3_game/enums/ediagmenuids.c

@@ -5,6 +5,7 @@ enum DiagMenuIDs
 	VEHICLES,
 		VEHICLE_DEBUG_OUTPUT,
 		VEHICLE_DUMP_CRASH_DATA,
+		VEHICLE_DRAW_FLIP_CONTEXT,
 
 	INVENTORY_MENU,
 		INVENTORY_ENTITY_PLACEMENT_CALLBACK_DEBUG,
@@ -95,6 +96,7 @@ enum DiagMenuIDs
 			MISC_HIT_INDICATION_SCATTER,
 			MISC_HIT_INDICATION_DISABLE_PPE,
 		MISC_FREEZE_ENTITY,
+		MISC_FREEZE_PLAYER,
 		MISC_CONNECTION_STATS,
 		MISC_PLAYER_SYMPTOMS_SHOW,
 		MISC_PLAYER_SYMPTOMS_TOGGLE_HMP,

+ 8 - 0
Scripts/3_game/enums/eenvironmenttemperaturecomponent.c

@@ -0,0 +1,8 @@
+enum EEnvironmentTemperatureComponent
+{
+	BASE 			= 0,
+	ALTITUDE		= 1,
+	OVERCAST 		= 2,
+	WIND 			= 4,
+	FOG				= 8,
+}

+ 2 - 0
Scripts/3_game/enums/erpcs.c

@@ -87,6 +87,8 @@ enum ERPCs
 	DEV_RPC_AGENT_RESET,
 	DEV_RPC_STAT_SET,
 	DEV_RPC_HEALTH_SET,
+	DEV_RPC_HORTICULTURE_UPDATE,
+	DEV_RPC_HORTICULTURE_SPEED,
 	DEV_RPC_SERVER_SCRIPT,
 	DEV_RPC_SERVER_SCRIPT_RESULT,
 	RPC_SOUND_CONTAMINATION,

+ 6 - 0
Scripts/3_game/gameplay.c

@@ -127,6 +127,7 @@ class ScriptInputUserData : ParamsWriteContext
 
 	proto native bool CopyFrom(ParamsReadContext other);
 
+	//! Returns true when the channel is free, AND the InputBuffer is NOT full (same as '!IsNetworkInputBufferFull()')
 	proto native static bool CanStoreInputUserData ();
 };
 
@@ -458,6 +459,8 @@ typedef Param1<int> MPConnectionLostEventParams;
 typedef Param2<int, string> MPConnectionCloseEventParams;
 //! Player, "Killer" (Beware: Not necessarily actually the killer, Client doesn't have this info)
 typedef Param2<DayZPlayer, Object> PlayerDeathEventParams;
+//! isFull
+typedef Param1<bool> NetworkInputBufferEventParams;
 
 
 //-----------------------------------------------------------------------------
@@ -566,6 +569,8 @@ const EventType	ConnectingStartEventTypeID;
 const EventType	ConnectingAbortEventTypeID;
 //! params: \ref PlayerDeathEventParams
 const EventType	PlayerDeathEventTypeID;
+//! params: \ref NetworkInputBufferEventParams
+const EventType	NetworkInputBufferEventTypeID;
 	
 //possible in engine events not accessable from script
 //ReloadShadersEvent
@@ -668,6 +673,7 @@ class Hud : Managed
 	void ShowHud(bool show);
 	
 	void OnResizeScreen();
+	void OnPlayerLoaded();	// player connects or respawns
 
 	void SetPermanentCrossHair(bool show) {}
 	

+ 51 - 0
Scripts/3_game/global/ammotypes.c

@@ -1,5 +1,7 @@
 class AmmoTypesAPI
 {
+	static protected ref map<string, ref map<string, int>> m_TypeToSurfaceParticleIDMap;
+	
 	private void AmmoTypesAPI() {}
 	private void ~AmmoTypesAPI() {}
 	
@@ -27,4 +29,53 @@ class AmmoTypesAPI
 			return true;
 		}
 	}
+	
+	static void Init()
+	{
+		if(!m_TypeToSurfaceParticleIDMap)
+		{
+			m_TypeToSurfaceParticleIDMap = new map<string, ref map<string, int>>();
+			
+			AddExplosionParticleEffect("Explosion_40mm_Ammo", "Hit_Snow", ParticleList.EXPLOSION_GRENADE_SNOW);
+			AddExplosionParticleEffect("Explosion_40mm_Ammo", "Hit_Ice", ParticleList.EXPLOSION_GRENADE_ICE);
+			AddExplosionParticleEffect("Explosion_40mm_Ammo", "default", ParticleList.EXPLOSION_LANDMINE);
+		}
+	}
+	
+	static void AddExplosionParticleEffect(string ammoName, string surfaceName, int particleID)
+	{
+		map<string, int>> surfaceNameToEffectIDMap;
+		if (!m_TypeToSurfaceParticleIDMap.Find(ammoName, surfaceNameToEffectIDMap))
+		{
+			surfaceNameToEffectIDMap = new map<string, int>>;
+			m_TypeToSurfaceParticleIDMap.Insert(ammoName, surfaceNameToEffectIDMap);
+		}
+		
+		surfaceNameToEffectIDMap.Insert(surfaceName, particleID);
+	}
+	
+	static int GetExplosionParticleID(string ammoName, string surfaceName)
+	{
+		int result;
+		map<string, int>> surfaceNameToEffectIDMap = m_TypeToSurfaceParticleIDMap.Get(ammoName);
+		if (surfaceNameToEffectIDMap)
+		{
+			if (!surfaceNameToEffectIDMap.Find(surfaceName, result))
+			{
+				if(!surfaceNameToEffectIDMap.Find("default", result))
+				{
+					result = -1;
+				}
+			}
+		}
+		return result;
+	}
+	
+	static void Cleanup()
+	{
+		if(!m_TypeToSurfaceParticleIDMap)
+		{
+			m_TypeToSurfaceParticleIDMap.Clear();
+		}
+	}
 }

+ 8 - 0
Scripts/3_game/global/errormodulehandler/connecterrorclientmodule.c

@@ -18,6 +18,10 @@ enum EConnectErrorClient
 	
 	PASSWORD,					// Server is password protected
 	BE_LICENCE,					// Server is using BE and it has not yet been agreed to
+
+	ALREADY_ON_ANOTHER_SERVER,	// Player is already playing on a different server	
+	COMMUNICATION_TIMED_OUT,	// Communication timed out
+	ALREADY_ON_SERVER,			// Player is already playing on this server
 };
 
 class ConnectErrorClientModule : ErrorHandlerModuleScript
@@ -53,6 +57,10 @@ class ConnectErrorClientModule : ErrorHandlerModuleScript
 		
 		InsertErrorProperties(EConnectErrorClient.PASSWORD);
 		InsertErrorProperties(EConnectErrorClient.BE_LICENCE);
+		
+		InsertDialogueErrorProperties(EConnectErrorClient.ALREADY_ON_ANOTHER_SERVER,"#STR_already_on_another_server");
+		InsertDialogueErrorProperties(EConnectErrorClient.COMMUNICATION_TIMED_OUT,	"#STR_BIOS_CommTimeOutError");
+		InsertDialogueErrorProperties(EConnectErrorClient.ALREADY_ON_SERVER,		"#ps4_already_in_session");
 	}
 
 #ifndef NO_GUI	

+ 57 - 11
Scripts/3_game/global/game.c

@@ -2,7 +2,7 @@
  *  Game Class provide most "world" or global engine API functions.
  */
 
-static int GAME_STORAGE_VERSION = 140;
+static int GAME_STORAGE_VERSION = 141;
 
 class CGame
 {
@@ -11,6 +11,7 @@ class CGame
 
 	ScriptModule GameScript;
 
+	//Obsolete, port [Obsolete()] as well, maybe?
 	private ref array<ref Param> m_ParamCache;
 	
 	//analytics	
@@ -35,6 +36,7 @@ class CGame
 		Math.Randomize(-1);
 		
 		LogManager.Init();
+
 		m_ParamCache = new array<ref Param>;
 		m_ParamCache.Insert(null);
 		
@@ -357,6 +359,14 @@ class CGame
 		@return false, if registration wasn't successful or when object is already registered
 	*/
 	proto native bool		RegisterNetworkStaticObject(Object object);
+
+	/**
+	\brief Returns the state of the buffer for network inputs (UAInterface) 
+		\note See: NetworkInputBufferEventTypeID
+		\note Client side only, returns false on the server always
+		@return true, if the buffer is filled and client simulation must stop
+	*/
+	proto native bool		IsNetworkInputBufferFull();
 	
 	/**
   	\brief Creates spectator object (mostly cameras)
@@ -790,6 +800,47 @@ class CGame
 	*/
 	proto native float		GetFps();
 	
+	/**
+	\brief Returns current framerate.
+	@return Current framerate as frames per second.
+	*/
+	proto native float		GetLastFPS();
+	
+	/**
+	\brief Returns average framerate over last n frames.
+	\param nFrames The number of frames to compute average of, up to 64.
+	@return Average frames per second over last n frames.
+	*/
+	proto native float		GetAvgFPS(int nFrames = 64);
+	
+	/**
+	\brief Returns minimum framerate over last n frames.
+	\param nFrames The number of frames to find minimum of, up to 64.
+	@return Minimum frames per second over last 64 frames.
+	*/
+	proto native float		GetMinFPS(int nFrames = 64);
+	
+	/**
+	\brief Returns maximum framerate over last n frames.
+	\param nFrames The number of frames to find maximum of, up to 64.
+	@return Maximum frames per second over last n frames.
+	*/
+	proto native float		GetMaxFPS(int nFrames = 64);
+	
+	/**
+	\brief Outputs framerate statistics.
+	\param min Outputs minimum framerate over last n frames in frames per second.
+	\param max Outputs maximum framerate over last n frames in frames per second.
+	\param avg Outputs average framerate over last n frames in frames per second.
+	\param nFrames The number of frames to take into account, up to 64. 
+	*/
+	void GetFPSStats(out float min, out float max, out float avg, int nFrames = 64)
+	{
+		min     = GetMinFPS(nFrames);
+		max     = GetMaxFPS(nFrames);
+		avg     = GetAvgFPS(nFrames);
+	}
+	
 	/**
 	\brief Returns current time from start of the game
 	@return time in seconds
@@ -896,18 +947,10 @@ class CGame
 	*/
 	proto native void		RPC(Object target, int rpcType, notnull array<ref Param> params, bool guaranteed,PlayerIdentity recipient = null);
 	//! see CGame.RPC
-	void					RPCSingleParam(Object target, int rpc_type, Param param, bool guaranteed, PlayerIdentity recipient = null)
-	{
-		m_ParamCache.Set(0, param);
-		RPC(target, rpc_type, m_ParamCache, guaranteed, recipient);
-	}
+	proto native void		RPCSingleParam(Object target, int rpc_type, Param param, bool guaranteed, PlayerIdentity recipient = null);
 	//! Not actually an RPC, as it will send it only to yourself
 	proto native void		RPCSelf(Object target, int rpcType, notnull array<ref Param> params);
-	void					RPCSelfSingleParam(Object target, int rpcType, Param param)
-	{
-		m_ParamCache.Set(0, param);
-		RPCSelf(target, rpcType, m_ParamCache);
-	}
+	proto native void		RPCSelfSingleParam(Object target, int rpcType, Param param);
 
 	//! Use for profiling code from start to stop, they must match have same name, look wiki pages for more info: https://confluence.bistudio.com/display/EN/Profiler
 	proto native void		ProfilerStart(string name);
@@ -1048,6 +1091,9 @@ class CGame
 	
 	proto native void		GetPlayerIndentities( out array<PlayerIdentity> identities );
 	
+	//! API for surface detection
+	proto native bool		GetSurface(SurfaceDetectionParameters params, SurfaceDetectionResult result);
+	
 	proto native float		SurfaceY(float x, float z);
 	proto native float		SurfaceRoadY(float x, float z, RoadSurfaceDetection rsd = RoadSurfaceDetection.LEGACY);
 	proto native float		SurfaceRoadY3D(float x, float y, float z, RoadSurfaceDetection rsd);

+ 17 - 0
Scripts/3_game/human.c

@@ -1219,6 +1219,23 @@ class HumanCommandScript
 	proto native 	void 	SetHeading(float yawAngle, float filterDt = -1, float maxYawSpeed = FLT_MAX);
 	proto native 	void 	AddHeadingRelativeTo(HumanRelativeHeadingMode mode, float yawAngle, float filterDt = -1, float maxYawSpeed = FLT_MAX);
 
+	//! Override this to return the current stance of the human.
+	int GetCurrentStance()
+	{
+		return DayZPlayerConstants.STANCEIDX_ERECT;
+	}
+	
+	//! Override this to return the current movement state of the human.
+	int GetCurrentMovement()
+	{
+		return DayZPlayerConstants.MOVEMENT_IDLE;
+	}
+	
+	//! Override this to return the current leaning state of the human. <-1, 1>
+	float GetCurrentLeaning()
+	{
+		return 0.0;
+	}
 
 	//---------------------------------------------------------------
 	// PreAnim Update 

+ 2 - 0
Scripts/3_game/impacteffects.c

@@ -46,6 +46,8 @@ class ImpactMaterials
 	static int GRAVEL 						= RegisterSurface("Hit_Gravel");
 	static int DIRT 						= RegisterSurface("Hit_Dirt");
 	static int FOLIAGE 						= RegisterSurface("Hit_Foliage");
+	static int FOLIAGE_GREEN 				= RegisterSurface("Hit_Foliage_Green");
+	static int FOLIAGE_CONIFER 				= RegisterSurface("Hit_Foliage_Conifer");
 	static int GRASS 						= RegisterSurface("Hit_Grass");
 	static int WOOD 						= RegisterSurface("Hit_Wood");
 	static int METAL 						= RegisterSurface("Hit_Metal");

+ 1 - 0
Scripts/3_game/inputapi/uainput.c

@@ -73,6 +73,7 @@ class UAInput
 
 	proto native void Supress();			// supress press event for next frame (while not pressed ATM - otherwise until release)
 
+	// take care when using these locking methods, if two or more systems un/lock the same input, there is a chance off cross-un/locking it from a wrong place! Use exclude groups instead.
 	proto native bool IsLocked();			// determine if locked (not active ATM)
 	proto native void Lock();				// lock (until unlock called or exclusion is selected)
 	proto native void Unlock();				// unlock exclusively

+ 7 - 11
Scripts/3_game/inventoryitemtype.c

@@ -1,6 +1,6 @@
 class InventoryItemType
 {
-	ref array<ref AnimSoundEvent> m_animSoundEvents;
+	private ref map<int, ref AnimSoundEvent> m_AnimSoundEvents;
 
 	private void InventoryItemType()
 	{
@@ -13,7 +13,7 @@ class InventoryItemType
 	{
 		string cfgPath = "CfgVehicles " + GetName() + " AnimEvents SoundWeapon";
 		
-		m_animSoundEvents = new array<ref AnimSoundEvent>;
+		m_AnimSoundEvents = new map<int, ref AnimSoundEvent>();
 
 		int soundCount = GetGame().ConfigGetChildrenCount(cfgPath);
 		
@@ -42,22 +42,18 @@ class InventoryItemType
 			string soundPath = cfgPath + " " + soundName + " ";
 			AnimSoundEvent soundEvent = new AnimSoundEvent(soundPath);
 			if (soundEvent.IsValid())
-				m_animSoundEvents.Insert(soundEvent);
+				m_AnimSoundEvents.Set(soundEvent.m_iID, soundEvent);
 		}
 	}
 
 
 	AnimSoundEvent GetSoundEvent(int event_id)
 	{
-		foreach (AnimSoundEvent soundEvent : m_animSoundEvents)
-		{
-			if (soundEvent.m_iID == event_id)
-				return soundEvent;
-		}
-
-		return null;
+		return m_AnimSoundEvents.Get(event_id);
 	}
 
-
 	proto native owned string GetName();
+	
+	//! DEPRECATED
+	ref array<ref AnimSoundEvent> m_animSoundEvents;
 }

+ 17 - 3
Scripts/3_game/objectspawner.c

@@ -1,6 +1,12 @@
 class ObjectSpawnerHandler
 {
-	protected static const ref TStringArray VALID_PATHS = {"DZ\\plants","DZ\\plants_bliss","DZ\\rocks","DZ\\rocks_bliss","DZ/plants","DZ/plants_bliss","DZ/rocks","DZ/rocks_bliss"};
+	protected static const ref TStringArray VALID_PATHS = {
+		"DZ\\plants","DZ\\plants_bliss", "DZ\\plants_sakhal",
+		"DZ\\rocks", "DZ\\rocks_bliss", "DZ\\rocks_sakhal",
+		"DZ/plants","DZ/plants_bliss", "DZ/plants_sakhal",
+		"DZ/rocks", "DZ/rocks_bliss", "DZ/rocks_sakhal",
+		};
+
 	//---------------------------------------------------------------------------------------
 	static void SpawnObjects()
 	{
@@ -53,6 +59,8 @@ class ObjectSpawnerHandler
 				object.SetOrientation( vector.ArrayToVec(item.ypr));
 				if (item.scale != 1)
 					object.SetScale(scale);
+				
+				object.OnSpawnByObjectSpawner(item);
 			}
 		}
 		
@@ -62,8 +70,11 @@ class ObjectSpawnerHandler
 	//---------------------------------------------------------------------------------------
 	static void OnGameplayDataHandlerLoad()
 	{
-		SpawnObjects();
-		GetGame().GetWorld().ProcessMarkedObjectsForPathgraphUpdate();
+		if (g_Game && g_Game.IsServer())
+		{
+			SpawnObjects();
+			GetGame().GetWorld().ProcessMarkedObjectsForPathgraphUpdate();
+		}
 	}
 	
 	//---------------------------------------------------------------------------------------
@@ -74,6 +85,8 @@ class ObjectSpawnerHandler
 			if (path.Contains(p))
 				return true;
 		}
+		
+		PrintToRPT("Object spawner: invalid path "+ path);
 		return false;
 	}
 	//---------------------------------------------------------------------------------------
@@ -91,6 +104,7 @@ class ITEM_SpawnerObject
 	float ypr[3];
 	float scale;
 	bool enableCEPersistency;
+	string customString;
 };
 
 

+ 17 - 0
Scripts/3_game/particles/particlelist.c

@@ -131,6 +131,10 @@ class ParticleList
 	static const int RGD5							= RegisterParticle("explosion_RGD5_01");
 	static const int M67							= RegisterParticle("explosion_M67_01");
 	
+	// GRENADE EXPLOSION BY SURFACE
+	static const int EXPLOSION_GRENADE_SNOW			= RegisterParticle("explosion_grenade_snow");
+	static const int EXPLOSION_GRENADE_ICE			= RegisterParticle("explosion_grenade_ice");
+	
 	// ELECTRICITY	        
 	static const int POWER_GENERATOR_SMOKE			= RegisterParticle("smoke_small_generator_01");
 	static const int BARBED_WIRE_SPARKS				= RegisterParticle("electro_shortc2");
@@ -208,6 +212,12 @@ class ParticleList
 	static const int IMPACT_FOLIAGE_ENTER			= RegisterParticle("impacts/hit_foliage_ent_01");
 	static const int IMPACT_FOLIAGE_RICOCHET		= RegisterParticle("impacts/hit_foliage_ric_01");
 	static const int IMPACT_FOLIAGE_EXIT			= RegisterParticle("impacts/hit_foliage_ext_01");
+	static const int IMPACT_FOLIAGE_GREEN_ENTER		= RegisterParticle("impacts/hit_foliage_green_ent_01");
+	static const int IMPACT_FOLIAGE_GREEN_RICOCHET	= RegisterParticle("impacts/hit_foliage_green_ric_01");
+	static const int IMPACT_FOLIAGE_GREEN_EXIT		= RegisterParticle("impacts/hit_foliage_green_ext_01");
+	static const int IMPACT_FOLIAGE_CONIFER_ENTER	= RegisterParticle("impacts/hit_foliage_conifer_ent_01");
+	static const int IMPACT_FOLIAGE_CONIFER_RICOCHET= RegisterParticle("impacts/hit_foliage_conifer_ric_01");
+	static const int IMPACT_FOLIAGE_CONIFER_EXIT	= RegisterParticle("impacts/hit_foliage_conifer_ext_01");
 	static const int IMPACT_GRASS_ENTER				= RegisterParticle("impacts/hit_grass_ent_01");
 	static const int IMPACT_GRASS_RICOCHET			= RegisterParticle("impacts/hit_grass_ric_01");
 	static const int IMPACT_DIRT_ENTER				= RegisterParticle("impacts/hit_dirt_ent_01");
@@ -334,6 +344,7 @@ class ParticleList
 	static const int HOTPSRING_WATERVAPOR			= RegisterParticle("hotspring_watervapor");
 	static const int GEYSER_NORMAL					= RegisterParticle("geyser_normal");
 	static const int GEYSER_STRONG					= RegisterParticle("geyser_strong");
+	static const int GEYSER_BUBBLES					= RegisterParticle("geyser_bubbles");
 	static const int VOLCANO						= RegisterParticle("volcano_smoke");
 	
 	// FISHING
@@ -349,9 +360,15 @@ class ParticleList
 	
 	//TREE FALLING PARTICLES
 	static const int TREE_FALLING_SNOW				= RegisterParticle("tree_falling_snow");//
+	static const int TREE_SOFT_FALLING_SNOW			= RegisterParticle("tree_soft_falling_snow");//
+	static const int TREE_SOFT_LARGE_FALLING_SNOW	= RegisterParticle("tree_soft_large_falling_snow");//
+	static const int TREE_SMALL_FALLING_SNOW		= RegisterParticle("tree_small_falling_snow");//
 	static const int TREE_FALLING_NEEDLE			= RegisterParticle("tree_falling_needle");//
 	static const int TREE_FALLING_LEAF				= RegisterParticle("tree_falling_leaf");//
 	static const int BUSH_FALLING_SNOW				= RegisterParticle("bush_falling_snow");//
+	//TREE PASSING ParticleSource
+	static const int TREE_PASSING_SNOW				= RegisterParticle("tree_passing_snow");//
+	static const int BUSH_PASSING_SNOW				= RegisterParticle("bush_passing_snow");// same as bush_falling_snow
 	
 	
 	static int RegisterParticle(string file_name)

+ 4 - 0
Scripts/3_game/particles/particlemanager/particlemanager.c

@@ -19,7 +19,11 @@ class ParticleManagerConstants
  		Settings applied to the global ParticleManager
 	*/
 	//@{
+#ifdef BULDOZER
+	static const int POOL_SIZE = 1;
+#else
 	static const int POOL_SIZE = 10000;
+#endif
 	static const int FLAGS = ParticleManagerSettingsFlags.NONE;
 	//@}
 }

+ 20 - 4
Scripts/3_game/playerconstants.c

@@ -8,6 +8,11 @@ class PlayerConstants
 	static const float FULL_SPRINT_DELAY_FROM_CROUCH 	= 1.0;	//! [seconds]
 	static const float FULL_SPRINT_DELAY_FROM_PRONE 	= 2.0;	//! [seconds]
 	
+	//approximate head heights
+	static const float HEAD_HEIGHT_ERECT = 1.6;
+	static const float HEAD_HEIGHT_CROUCH = 1.05;
+	static const float HEAD_HEIGHT_PRONE = 0.66;
+	
 	//----------------------------------------------------------
 	//				SHOES DAMAGE/FEET BLEEDING
 	//----------------------------------------------------------
@@ -58,6 +63,9 @@ class PlayerConstants
 	static const float STOMACH_ENERGY_TRANSFERED_PER_SEC 		= 3;	//amount of kcal transfered to energy per second(this should ideally be higher than what player burns under high metabolic load[sprint])
 	static const float STOMACH_WATER_TRANSFERED_PER_SEC 		= 6;	//amount of ml transfered to water per second(this should ideally be higher than what player burns under high metabolic load[sprint])
 	static const float STOMACH_SOLID_EMPTIED_PER_SEC 			= 7;	//amount of g/ml emptied from stomach per second
+	
+	static const float STOMACH_DIGEST_AGENT_RANDOM_MIN = -0.5; 	//! Percents of agents count <-1.0; 1.0>
+	static const float STOMACH_DIGEST_AGENT_RANDOM_MAX = 0.5; 	//! Percents of agents count <-1.0; 1.0>
 
 	static const float LOW_WATER_THRESHOLD 						= SL_WATER_LOW;		//threshold from which water affects health
 
@@ -91,12 +99,14 @@ class PlayerConstants
 	static const float BLOOD_REGEN_RATE_PER_SEC				= 0.3; 	//base amount of blood regenerated per second
 	static const float DAMAGE_ZONE_BLOOD_REGEN_MODIFIER 	= 0.7;
 	
+	static const float UNCONSCIOUS_BLOOD_REGEN_MLTP 		= 2;	//multiplier for blood regen while character has critical blood and uncon
+	
 	static const float BLOOD_REGEN_MODIFIER_ENERGY_LOW		= 0; 	//multiplier for blood regen rate 
-	static const float BLOOD_REGEN_MODIFIER_ENERGY_MID		= 0.5;
+	static const float BLOOD_REGEN_MODIFIER_ENERGY_MID		= 0.65;
 	static const float BLOOD_REGEN_MODIFIER_ENERGY_HIGH		= 1;
 	
 	static const float BLOOD_REGEN_MODIFIER_WATER_LOW		= 0; 	//multiplier for blood regen rate (BLOOD_REGEN_MULTIPLIER_WATER_LOW ?)
-	static const float BLOOD_REGEN_MODIFIER_WATER_MID		= 0.5;
+	static const float BLOOD_REGEN_MODIFIER_WATER_MID		= 0.65;
 	static const float BLOOD_REGEN_MODIFIER_WATER_HIGH		= 1;
 	
 	static const float SALINE_BLOOD_REGEN_PER_SEC			= 3;	//boost for blood regen per second, independent on BLOOD_REGEN_SPEED
@@ -144,8 +154,9 @@ class PlayerConstants
 	static const float UNCONSCIOUS_THRESHOLD				= 25.0;		//player goes unconscious when we get under this threshold
 	static const float CONSCIOUS_THRESHOLD					= 50.0;		//player regains consciousness when he gets above this threshold
 	
-	static const float SHOCK_REFILL_CONSCIOUS_SPEED			= 5;		//shock refill speed when the player is conscious
-	static const float SHOCK_REFILl_UNCONSCIOUS_SPEED		= 1;		//shock refill speed when the player is unconscious
+	static const float SHOCK_REFILL_CONSCIOUS_SPEED				= 5;		//shock refill speed when the player is conscious
+	static const float SHOCK_REFILl_UNCONSCIOUS_SPEED			= 1;		//shock refill speed when the player is unconscious
+	static const float SHOCK_REFILL_UNCON_CRITICAL_BLOOD_MLTP	= 0.8;		//shock refill speed multiplier when the player is unconscious and has critical blood level
 	
 	static const float SHOCK_DAMAGE_BLOOD_THRESHOLD_HIGH	= 3000;		// we start dealing shock damage over time when we get at this value or lower
 	static const float SHOCK_DAMAGE_BLOOD_THRESHOLD_LOW		= 2500; 	// the closer we get to this value, the higher the over time shock damage we deal
@@ -250,4 +261,9 @@ class PlayerConstants
 	static const float	DROWNING_UNCONSCIOUS_THRESHOLD 		= 0.1;	//what water level value does it take for the player to be considered drowning while unconscious -  0.10 seemed a reasonable value based on visual observation and matching it to water depth values(can be adjust more precisely though)
 	static const int	DROWNING_BUBBLE_FREQUENCY_MAX		= 4000; //how often should bubbles appear when the stamina is at its highest,(in ms)
 	static const int	DROWNING_BUBBLE_FREQUENCY_MIN		= 1000; //how often should bubbles appear when the stamina is at its lowest,(in ms)
+	
+	static const float	BLOODY_HANDS_FOOD_POISON_AGENT_INCREMENT = 5;
+	
+	//! DEPRECATED
+	static const float	BLOODY_HANDS_SALMONELLA_CHANCE		= 0.05; //! replaced by EntityAI::m_BloodInfectionChanceCached
 }

+ 2 - 0
Scripts/3_game/ppemanager/materials/matclasses/ppegaussfilter.c

@@ -10,7 +10,9 @@ class PPEGaussFilter: PPEClassBase
 	static const int L_0_INV = 500;
 	static const int L_0_MENU = 600;
 	static const int L_0_CONTROLS = 700;
+	static const int L_0_LATENCY = 750;
 	static const int L_0_TUTORIALS = 800;
+	static const int L_0_FEEDBACK = 850;
 	static const int L_0_SERVER_BROWSER = 900;
 	static const int L_0_DISCONNECT = 1000;
 	

+ 3 - 3
Scripts/3_game/ppemanager/materials/matparameters/ppematclassparametercolor.c

@@ -4,14 +4,14 @@ class ColorValuesData : Managed
 	protected ref array<float> m_Values;
 	protected int m_Operator;
 	
-	void ColorValuesData(ref array<float> values, int operator)
+	void ColorValuesData(array<float> values, int operator)
 	{
 		m_Values = new array<float>;
 		m_Values.Copy(values);
 		m_Operator = operator;
 	}
 	
-	void SetValues(ref array<float> values)
+	void SetValues(array<float> values)
 	{
 		m_Values.Copy(values);
 	}
@@ -279,7 +279,7 @@ class PPEMatClassParameterColor extends PPEMatClassParameterCommandData
 	}
 	
 	//! Fills relative values into arrays/member variables
-	void PrepareLayerInfo(int layer, ref array<float> values, int operator)
+	void PrepareLayerInfo(int layer, array<float> values, int operator)
 	{
 		ColorValuesData tmp;
 		if (m_LayerInfo.Contains(layer))

+ 9 - 2
Scripts/3_game/ppemanager/pperequesterbank.c

@@ -43,6 +43,8 @@ class PPERequesterBank extends Managed
 	static const int REQ_DROWNING				= RegisterRequester(PPERequester_Drowning);
 	//static const int REQ_HMP_LVL3				= RegisterRequester(PPERequester_HeavyMetalPoisoning_3);
 	static const int REQ_HMP_GHOST				= RegisterRequester(PPERequester_HMPGhosts);
+	static const int REQ_LATENCYBLUR			= RegisterRequester(PPERequester_LatencyBlur);
+	static const int REQ_FEEDBACKBLUR			= RegisterRequester(PPERequester_FeedbackBlur);
 	
 	private static ref PPERequesterRegistrations 	m_Registrations; //more registrations to be placed here
 	
@@ -223,10 +225,15 @@ class PPERequesterBank extends Managed
 	}*/
 }
 
-//! Mod this, if you wish to register custom requesters
+//! Mod this, if you wish to register custom requesters inside 'RegisterAdditionalRequesters' override
 class PPERequesterRegistrations extends Managed
 {
 	void PPERequesterRegistrations()
+	{
+		RegisterAdditionalRequesters(); 
+	}
+	
+	protected void RegisterAdditionalRequesters()
 	{
 		//PPERequesterBank.RegisterRequester(TestReq);
 	}
@@ -234,7 +241,7 @@ class PPERequesterRegistrations extends Managed
 
 /*modded class PPERequesterRegistrations extends Managed
 {
-	void PPERequesterRegistrations()
+	override protected void RegisterAdditionalRequesters()
 	{
 		PPERequesterBank.RegisterRequester(TestReq2);
 	}

+ 1 - 1
Scripts/3_game/ppemanager/requesters/pperequestplatformsbase.c

@@ -194,7 +194,7 @@ class PPERequesterBase
 		//DbgPrnt("PPEDebug | PPERequesterBase - SetTargetValueFloatDefault | mat/par/req: " + mat_id + "/" + param_idx + "/" + m_IDX + " | data: " + data);
 	}
 	
-	protected void SetTargetValueColor(int mat_id, int param_idx, ref array<float> val, int priority_layer, int operator = PPOperators.ADD_RELATIVE)
+	protected void SetTargetValueColor(int mat_id, int param_idx, array<float> val, int priority_layer, int operator = PPOperators.ADD_RELATIVE)
 	{
 		if ( !m_RequestDataStructure )
 			return;

+ 9 - 0
Scripts/3_game/ppemanager/requesters/pperfeedbackblur.c

@@ -0,0 +1,9 @@
+class PPERequester_FeedbackBlur extends PPERequester_MenuBase
+{
+	override protected void OnStart(Param par = null)
+	{
+		super.OnStart();
+		
+		SetTargetValueFloat(PostProcessEffectType.GaussFilter,PPEGaussFilter.PARAM_INTENSITY,true,1.0,PPEGaussFilter.L_0_FEEDBACK,PPOperators.SET);
+	}
+}

+ 4 - 2
Scripts/3_game/ppemanager/requesters/pperhmpghosts.c

@@ -56,11 +56,13 @@ class PPERequester_HMPGhosts extends PPERequester_GameplayBase
 			return;
 		}
 		
-		ProcessSimulation(delta);
+		if (m_IsRunning)
+			ProcessSimulation(delta);
 		
 		super.OnUpdate(delta);
 		
-		SetRequesterUpdating(true);
+		if (m_IsRunning)
+			SetRequesterUpdating(true);
 	}
 	
 	protected void ProcessSimulation(float delta)

+ 9 - 0
Scripts/3_game/ppemanager/requesters/pperlatencyblur.c

@@ -0,0 +1,9 @@
+class PPERequester_LatencyBlur extends PPERequester_MenuBase
+{
+	override protected void OnStart(Param par = null)
+	{
+		super.OnStart();
+		
+		SetTargetValueFloat(PostProcessEffectType.GaussFilter,PPEGaussFilter.PARAM_INTENSITY,true,1.0,PPEGaussFilter.L_0_LATENCY,PPOperators.SET);
+	}
+}

+ 1 - 3
Scripts/3_game/services/biosclientservices.c

@@ -9,9 +9,7 @@
 class BiosClientServices
 {
 	private void BiosClientServices() {}
-	void ~BiosClientServices()
-	{
-	}
+	private void ~BiosClientServices() {}
 	
 	proto native BiosPrivacyService GetPrivacyService();
 	proto native BiosSocialService GetSocialService();

+ 10 - 0
Scripts/3_game/services/biossessionservice.c

@@ -260,4 +260,14 @@ class BiosSessionService
 		return ClientData.GetSimplePlayerList();
 	}
 	
+	//! Native callback function to retrieve the session player list.
+	/*!
+		@param outPlayerList Output list of players in the session.
+	*/
+	void GetSessionPlayerListEx(TStringArray outPlayerList)
+	{
+		TStringArray playerList = GetSessionPlayerList();
+		outPlayerList.Copy(playerList);
+	}
+	
 };

+ 37 - 0
Scripts/3_game/sound.c

@@ -13,6 +13,43 @@ enum WaveKind
 	WAVEUI
 }
 
+
+//@}
+
+//----------------------------------------------
+/**
+ * \defgroup SoundController API
+ * @{
+ */
+
+enum SoundControllerAction
+{
+	None,
+	Limit,
+	Overwrite
+}
+
+/*!
+Overrides or limits soundmap value for a sound cotroller
+\param controllerName the sound controller name. One of: rain,night,meadow,trees,hills,houses,windy,deadBody,sea,forest,altitudeGround,altitudeSea,altitudeSurface,daytime,shooting,coast,waterDepth,overcast,fog,snowfall,caveSmall,caveBig
+\param action what action will be used on the normal landscape sound controller value
+\param value the new value of sound controller
+*/
+proto native void SetSoundControllerOverride(string controllerName, float value, SoundControllerAction action);
+
+/*!
+Overrides all the environment controllers to be muted
+*/
+proto native void MuteAllSoundControllers();
+
+/*!
+Removes all the previously set overrides of sound controllers
+*/
+proto native void ResetAllSoundControllers();
+
+//@}
+
+
 class AbstractSoundScene
 {
 	private void AbstractSoundScene() {}

+ 72 - 0
Scripts/3_game/surfaceinfo.c

@@ -3,6 +3,7 @@ typedef int[] SurfaceInfo;
 /*!
 	Unmanaged surface info handle. Provides API to surfaces that are defined as part of 'CfgSurfaces' or exist in an objects .bisurf file.
 	Lifetime is managed in code so don't store handles of this type yourself.
+	Any created 'SurfaceInfo' is destroyed during 'Game' ScriptModule destruction.
 */
 class SurfaceInfo
 {
@@ -48,3 +49,74 @@ class SurfaceInfo
 	proto int GetStepParticleId();
 	proto int GetWheelParticleId();
 };
+
+enum UseObjectsMode
+{
+	//! wait for the data to be ready
+	Wait,
+	
+	//! do not wait for the data, but refresh caches
+	NoWait,
+	
+	//! only return the data we have, do not touch any caches (MT safe mode)
+	NoLock
+};
+
+enum SurfaceDetectionType
+{
+	Scenery,
+	Roadway
+};
+
+/*!
+	Parameters for detecting the surface
+*/
+/*sealed*/ class SurfaceDetectionParameters
+{
+	//! Type of surface to detect
+	SurfaceDetectionType type = SurfaceDetectionType.Scenery;
+	
+	//! 3D position to trace the surface from
+	vector position;
+	
+	//! Include water in the surface detection, will return the water if it is higher than the surface
+	bool includeWater = false;
+	
+	//! See UseObjectsMode, SurfaceTraceType.Roadway only
+	UseObjectsMode syncMode = UseObjectsMode.Wait;
+	
+	//! Object to ignore tracing against, SurfaceTraceType.Roadway only
+	Object ignore = null;
+	
+	//! See RoadSurfaceDetection, SurfaceTraceType.Roadway only
+	RoadSurfaceDetection rsd = RoadSurfaceDetection.ABOVE;
+	
+};
+
+/*!
+	Output of surface detection
+*/
+/*sealed*/ class SurfaceDetectionResult
+{
+	//! Height position
+	float height = 0;
+	
+	//! Right normal 
+	float normalX = 0;
+	
+	//! Up normal - always set to 1, not necessary to expose
+	//float normalY = 1;
+	
+	//! Forward normal
+	float normalZ = 0;
+	
+	//! Returned SurfaceInfo, plain pointer
+	SurfaceInfo surface = null;
+	
+	//! If water was the returned surface
+	bool aboveWater = false;
+	
+	//! SurfaceTraceType.Roadway only
+	Object object = null;
+	
+};

+ 0 - 14
Scripts/3_game/systems/animalcatching/catchingcontextpoissonbase.c

@@ -23,14 +23,6 @@ class CatchingContextPoissonBase : CatchingContextBase
 		return Math.Ceil(m_SignalPoissonMean) + Math.Ceil(m_SignalPoissonMean/5); //TODO?
 	}
 	
-	/*float GetSignalChance()
-	{
-		if (m_SignalCurrent > GetSignalMax() || m_SignalCurrent > 12) //necessary reset
-			m_SignalCurrent = 0;
-		
-		return Math.Poisson(m_SignalPoissonMean,m_SignalCurrent) * GetChanceCoef();
-	}*/
-	
 	override bool ModifySignalProbability(inout float probability)
 	{
 		if (m_SignalCurrent > GetSignalMax() || m_SignalCurrent > 12) //necessary reset
@@ -41,12 +33,6 @@ class CatchingContextPoissonBase : CatchingContextBase
 		return true;
 	}
 	
-	/*float GetQualityModifier()
-	{
-		float ret = m_QualityBaseMod + Math.RandomFloatInclusive(m_QualityDispersionMinMod,m_QualityDispersionMaxMod);//server only, this is fine
-		return ret;
-	}*/
-	
 	override bool RollCatch()
 	{
 		bool ret = super.RollCatch();

+ 4 - 1
Scripts/3_game/systems/animalcatching/catchingresultbasic.c

@@ -104,7 +104,10 @@ class CatchingResultBasic
 			return null;
 		
 		yItemIdx = m_YItem.GetRegistrationIdx();
+		EntityAI ret = EntityAI.Cast(GetGame().CreateObjectEx(m_YItem.GetType(), pos, ECE_PLACE_ON_SURFACE));
+		if (ret)
+			m_YItem.OnEntityYieldSpawned(ret);
 		
-		return EntityAI.Cast(GetGame().CreateObjectEx(m_YItem.GetType(), pos, ECE_PLACE_ON_SURFACE));
+		return ret;
 	}
 };

+ 3 - 0
Scripts/3_game/systems/animalcatching/catchyielditembase.c

@@ -63,6 +63,9 @@ class YieldItemBase
 		return res;
 	}
 	
+	//! called on item spawn
+	void OnEntityYieldSpawned(EntityAI spawn);
+	
 	float GetChanceForYieldItem(CatchingContextBase ctx)
 	{
 		float ret = 1.0;

+ 103 - 36
Scripts/3_game/systems/dynamicmusicplayer/dynamicmusicplayer.c

@@ -8,6 +8,12 @@ class DynamicMusicLocationTypes
 	const int UNDERGROUND = 1;
 }
 
+class DynamicMusicLocationShape
+{
+	const int BOX = 0;
+	const int POLYGON = 1;
+}
+
 class DynamicMusicLocationDynamicData
 {
 	int m_Type = DynamicMusicLocationTypes.NONE;
@@ -38,11 +44,13 @@ class DynamicMusicTrackData
 	bool m_HasPriority		= false;
 	int m_TimeOfDay 		= -1;
 	int m_LocationType 		= DynamicMusicLocationTypes.NONE;
+	int m_Shape				= DynamicMusicLocationShape.BOX;
 	string m_SoundSet;
 
 	EDynamicMusicPlayerCategory m_Category;
 	
-	ref array<ref array<vector>> locationBoundaries = new ref array<ref array<vector>>();
+	ref array<ref array<vector>> locationBoundaries = new array<ref array<vector>>();
+	ref array<vector> vertices = new array<vector>();
 	
 	void InsertLocation(vector min, vector max)
 	{
@@ -124,7 +132,7 @@ class DynamicMusicPlayer
 		m_LastPlayedTrackBufferPerCategory[EDynamicMusicPlayerCategory.LOCATION_STATIC_PRIORITY] 	= new SimpleCircularBuffer<int>(TRACKS_BUFFER_HISTORY_SIZE, -1);
 		m_LastPlayedTrackBufferPerCategory[EDynamicMusicPlayerCategory.LOCATION_DYNAMIC] 			= new SimpleCircularBuffer<int>(TRACKS_BUFFER_HISTORY_SIZE, -1);
 		
-		m_LocationsDynamic = new ref map<int, ref DynamicMusicLocationDynamicData>();
+		m_LocationsDynamic = new map<int, ref DynamicMusicLocationDynamicData>();
 		m_TracksLocationStaticCached = new array<ref DynamicMusicTrackData>();
 		m_TracksLocationStaticPrioritizedCached = new array<ref DynamicMusicTrackData>();
 		
@@ -191,7 +199,7 @@ class DynamicMusicPlayer
 					m_TickPriorityLocationUpdateElapsed = 0.0;
 					
 					//! no playback at all OR playback of non-prioritized category
-					if (!IsPlaybackActive() || (IsPlaybackActive() && !IsPriotitizedCategorySelected()))
+					if ((IsPlaybackActive() && !IsPriotitizedCategorySelected()) || !IsPlaybackActive())
 					{
 						if (PlayerInsideOfLocationFilter(m_LocationsDynamic))
 							OnLocationMatched(EDynamicMusicPlayerCategory.LOCATION_DYNAMIC, true);
@@ -420,7 +428,13 @@ class DynamicMusicPlayer
 			if (!IsPriotitizedCategorySelected())
 			{
 				m_CategorySelected = category;
-				FadeoutTrack(GetPreviousTrackFadeoutSeconds(category));
+				if (m_WaitingForPlayback)
+					ResetWaitingQueue();
+				
+				if (m_SoundPlaying)
+					FadeoutTrack(GetPreviousTrackFadeoutSeconds(category));
+
+				SetCategory(category, isPriorityLocation);
 			}
 			else
 				SetCategory(category, true); //! play prio location track (no fadeout)
@@ -510,7 +524,7 @@ class DynamicMusicPlayer
 		}
 	}
 
-	private bool PlayerInsideOfLocationFilter(ref array<ref DynamicMusicTrackData> locations)
+	private bool PlayerInsideOfLocationFilter(array<ref DynamicMusicTrackData> locations)
 	{
 		m_TracksLocationMatchedPlayerInside.Clear();
 		
@@ -518,17 +532,33 @@ class DynamicMusicPlayer
 		{
 			foreach (DynamicMusicTrackData track : locations)
 			{
-				foreach (int locationId, array<vector> bounds : track.locationBoundaries)
+				switch (track.m_Shape)
 				{
-					if (Math.IsPointInRectangle(bounds[0], bounds[1], m_PlayerPosition))
-					{
-						if (m_TracksLocationMatchedPlayerInside.Find(track) == INDEX_NOT_FOUND)
-							m_TracksLocationMatchedPlayerInside.Insert(track);
-	
-						#ifdef DIAG_DEVELOPER
-						DMPDebugPrint(string.Format("Player inside location <%1, %2>", bounds[0], bounds[1]));
-						#endif
-					}
+					case DynamicMusicLocationShape.BOX:
+						foreach (int locationId, array<vector> bounds : track.locationBoundaries)
+						{
+							if (Math.IsPointInRectangle(bounds[0], bounds[1], m_PlayerPosition))
+							{
+								if (m_TracksLocationMatchedPlayerInside.Find(track) == INDEX_NOT_FOUND)
+									m_TracksLocationMatchedPlayerInside.Insert(track);
+			
+								#ifdef DIAG_DEVELOPER
+								DMPDebugPrint(string.Format("Player inside location <%1, %2>", bounds[0], bounds[1]));
+								#endif
+							}
+						}
+						break;
+					case DynamicMusicLocationShape.POLYGON:
+						if (Math2D.IsPointInPolygonXZ(track.vertices, m_PlayerPosition))
+						{
+							if (m_TracksLocationMatchedPlayerInside.Find(track) == INDEX_NOT_FOUND)
+								m_TracksLocationMatchedPlayerInside.Insert(track);					
+
+							#ifdef DIAG_DEVELOPER
+							DMPDebugPrint(string.Format("Player inside polygon location at <%1>", m_PlayerPosition));
+							#endif	
+						}
+						break;
 				}
 			}
 		}
@@ -536,7 +566,7 @@ class DynamicMusicPlayer
 		return m_TracksLocationMatchedPlayerInside.Count() > 0;
 	}
 
-	private bool PlayerInsideOfLocationFilter(ref map<int, ref DynamicMusicLocationDynamicData> locations)
+	private bool PlayerInsideOfLocationFilter(map<int, ref DynamicMusicLocationDynamicData> locations)
 	{
 		if (locations.Count() > 0)
 		{
@@ -555,7 +585,7 @@ class DynamicMusicPlayer
 		return false;
 	}
 	
-	private bool SetSelectedTrackFromCategory(EDynamicMusicPlayerCategory category, notnull ref array<ref DynamicMusicTrackData> tracklist, int historyLookupType = DynamicMusicPlayerTrackHistoryLookupType.ANY)
+	private bool SetSelectedTrackFromCategory(EDynamicMusicPlayerCategory category, notnull array<ref DynamicMusicTrackData> tracklist, int historyLookupType = DynamicMusicPlayerTrackHistoryLookupType.ANY)
 	{
 		if (tracklist.Count() == 0)
 			return true;
@@ -694,10 +724,13 @@ class DynamicMusicPlayer
 			m_TracksLocationStaticCached.Clear();
 			foreach (DynamicMusicTrackData track : m_DynamicMusicPlayerRegistry.m_TracksLocationStatic)
 			{
-				foreach (array<vector> bounds : track.locationBoundaries)
+				if (track.m_Shape == DynamicMusicLocationShape.BOX)
 				{
-					if (vector.Distance(m_PlayerPosition, Math.CenterOfRectangle(bounds[0], bounds[1])) > LOCATION_DISTANCE_MAX)
-						continue;
+					foreach (array<vector> bounds : track.locationBoundaries)
+					{
+						if (vector.Distance(m_PlayerPosition, Math.CenterOfRectangle(bounds[0], bounds[1])) > LOCATION_DISTANCE_MAX)
+							continue;
+					}
 				}
 	
 				m_TracksLocationStaticCached.Insert(track);
@@ -706,10 +739,13 @@ class DynamicMusicPlayer
 			m_TracksLocationStaticPrioritizedCached.Clear();
 			foreach (DynamicMusicTrackData trackPrio : m_DynamicMusicPlayerRegistry.m_TracksLocationStaticPrioritized)
 			{
-				foreach (array<vector> boundsPrio : trackPrio.locationBoundaries)
+				if (trackPrio.m_Shape == DynamicMusicLocationShape.BOX)
 				{
-					if (vector.Distance(m_PlayerPosition, Math.CenterOfRectangle(boundsPrio[0], boundsPrio[1])) > LOCATION_DISTANCE_MAX)
-						continue;
+					foreach (array<vector> boundsPrio : trackPrio.locationBoundaries)
+					{
+						if (vector.Distance(m_PlayerPosition, Math.CenterOfRectangle(boundsPrio[0], boundsPrio[1])) > LOCATION_DISTANCE_MAX)
+							continue;
+					}
 				}
 				
 				m_TracksLocationStaticPrioritizedCached.Insert(trackPrio);
@@ -718,7 +754,8 @@ class DynamicMusicPlayer
 	}
 	
 	#ifdef DIAG_DEVELOPER
-	private ref array<Shape> m_DebugShapesLocations = new array<Shape>();
+	private ref array<Shape> m_DebugShapesLocations 		= new array<Shape>();
+	private ref array<Shape> m_DebugShapesLocationsVertices = new array<Shape>();
 	private float m_DebugWaitTime = 0;
 
 	private void DisplayDebugStats(bool enabled)
@@ -788,6 +825,9 @@ class DynamicMusicPlayer
 		{
 			if (DbgUI.Button("Stop"))
 				StopTrack();
+
+			if (DbgUI.Button("Reset Waiting"))
+				ResetWaitingQueue();
 			
 			DbgUI.Text("Set Category:\n");
 			if (DbgUI.Button("Time"))
@@ -832,12 +872,15 @@ class DynamicMusicPlayer
 					if (vector.Distance(position, Math.CenterOfRectangle(locationMin, locationMax)) > 2000)
 						continue;
 		
-					CleanupDebugShapes(m_DebugShapesLocations);
+					Debug.CleanupDrawShapes(m_DebugShapesLocations);
 		
-					locationMax[1] = locationMin[1] + 50.0; //! force the height of box for debug
+					locationMax[1] = locationMin[1] + 200.0; //! force the height of box for debug
 					locationMin[1] = locationMin[1] - 50.0;
 					m_DebugShapesLocations.Insert(Debug.DrawBoxEx(locationMin, locationMax, Colors.PURPLE, ShapeFlags.NOZWRITE|ShapeFlags.ONCE));
 				}
+				
+				Debug.CleanupDrawShapes(m_DebugShapesLocationsVertices);
+				DrawPolygonLocation(track);
 			}
 			
 			foreach (DynamicMusicTrackData trackPrio : m_TracksLocationStaticPrioritizedCached)
@@ -850,12 +893,15 @@ class DynamicMusicPlayer
 					if (vector.Distance(position, Math.CenterOfRectangle(locationMin, locationMax)) > 2000)
 						continue;
 
-					CleanupDebugShapes(m_DebugShapesLocations);
+					Debug.CleanupDrawShapes(m_DebugShapesLocations);
 
-					locationMax[1] = locationMin[1] + 50.0; //! force the height of box for debug
+					locationMax[1] = locationMin[1] + 200.0; //! force the height of box for debug
 					locationMin[1] = locationMin[1] - 50.0;
 					m_DebugShapesLocations.Insert(Debug.DrawBoxEx(locationMin, locationMax, Colors.RED, ShapeFlags.NOZWRITE|ShapeFlags.ONCE));
 				}
+				
+				Debug.CleanupDrawShapes(m_DebugShapesLocationsVertices);
+				DrawPolygonLocation(trackPrio);
 			}
 			
 			foreach (DynamicMusicLocationDynamicData locationDynamic : m_LocationsDynamic)
@@ -866,25 +912,39 @@ class DynamicMusicPlayer
 				if (vector.Distance(position, Math.CenterOfRectangle(locationMin, locationMax)) > 2000)
 					continue;
 
-				CleanupDebugShapes(m_DebugShapesLocations);
+				Debug.CleanupDrawShapes(m_DebugShapesLocations);
 
-				locationMax[1] = locationMin[1] + 50.0; //! force the height of box for debug
+				locationMax[1] = locationMin[1] + 200.0; //! force the height of box for debug
 				locationMin[1] = locationMin[1] - 50.0;
 				m_DebugShapesLocations.Insert(Debug.DrawBoxEx(locationMin, locationMax, Colors.YELLOW, ShapeFlags.NOZWRITE|ShapeFlags.ONCE));
 			}
 		}
 		else
-			CleanupDebugShapes(m_DebugShapesLocations);
+		{
+			Debug.CleanupDrawShapes(m_DebugShapesLocations);
+			Debug.CleanupDrawShapes(m_DebugShapesLocationsVertices);
+		}
 	}
-
-	private void CleanupDebugShapes(array<Shape> shapesArr)
+	
+	private void DrawPolygonLocation(notnull DynamicMusicTrackData track)
 	{
-		for ( int it = 0; it < shapesArr.Count(); ++it )
+		vector first, current, last;
+		
+		int count =  track.vertices.Count();
+		foreach (int i, vector vertexPos : track.vertices)
 		{
-			Debug.RemoveShape( shapesArr[it] );
+			vertexPos[1] = vertexPos[1] + 0.5;
+			current = vertexPos;
+
+			if (i == 0)
+				first = vertexPos;
+			else
+				m_DebugShapesLocationsVertices.Insert(Debug.DrawLine(last, current, COLOR_WHITE, ShapeFlags.TRANSP|ShapeFlags.NOZWRITE|ShapeFlags.ONCE));
+			
+			last = current;
 		}
 		
-		shapesArr.Clear();
+		m_DebugShapesLocationsVertices.Insert(Debug.DrawLine(current, first, COLOR_WHITE, ShapeFlags.TRANSP|ShapeFlags.NOZWRITE|ShapeFlags.ONCE));
 	}
 	
 	private void DMPDebugPrint(string message)
@@ -893,6 +953,13 @@ class DynamicMusicPlayer
 		Debug.Log(message);
 		#endif
 	}
+	
+	//!DEPRECATED
+	private void CleanupDebugShapes(array<Shape> shapesArr)
+	{
+		Debug.CleanupDrawShapes(shapesArr);
+	}
+
 	#endif
 }
 

+ 26 - 1
Scripts/3_game/systems/dynamicmusicplayer/dynamicmusicplayerregistry.c

@@ -101,6 +101,7 @@ class DynamicMusicPlayerRegistry
 		RegisterTrackMenu("Music_Menu_2_SoundSet");
 		RegisterTrackMenu("Music_Menu_3_SoundSet");
 		RegisterTrackMenu("Music_Menu_4_SoundSet");
+		RegisterTrackMenu("Music_Menu_subtitles_remake_SoundSet");
 	}
 //____________________________________________Day Time setup___________________________________________
 
@@ -116,6 +117,7 @@ class DynamicMusicPlayerRegistry
 		RegisterTrackTime("Music_time_day_5_SoundSet", DynamicMusicPlayerTimeOfDay.DAY);
 		RegisterTrackTime("Music_time_day_6_SoundSet", DynamicMusicPlayerTimeOfDay.DAY);
 		RegisterTrackTime("Music_time_day_7_SoundSet", DynamicMusicPlayerTimeOfDay.DAY);
+		RegisterTrackTime("Music_time_day_8_SoundSet", DynamicMusicPlayerTimeOfDay.DAY);
 		//NIGHT
 		RegisterTrackTime("Music_time_night_1_SoundSet", DynamicMusicPlayerTimeOfDay.NIGHT);
 		RegisterTrackTime("Music_time_night_2_SoundSet", DynamicMusicPlayerTimeOfDay.NIGHT);
@@ -170,6 +172,7 @@ class DynamicMusicPlayerRegistry
 		DynamicMusicTrackData track = new DynamicMusicTrackData();
 		track.m_SoundSet 	= soundSetName;
 		track.m_TimeOfDay 	= timeOfDay;
+		track.m_Shape		= DynamicMusicLocationShape.BOX;
 		
 		track.InsertLocation(start, end);
 		
@@ -185,11 +188,12 @@ class DynamicMusicPlayerRegistry
 		}
 	}
 	
-	protected void RegisterTrackLocationStaticMultiRectangle(string soundSetName, ref array<ref TVectorArray> locationBoundaries, int timeOfDay = DynamicMusicPlayerTimeOfDay.ANY, bool runImmediately = false)
+	protected void RegisterTrackLocationStaticMultiRectangle(string soundSetName, array<ref TVectorArray> locationBoundaries, int timeOfDay = DynamicMusicPlayerTimeOfDay.ANY, bool runImmediately = false)
 	{
 		DynamicMusicTrackData track = new DynamicMusicTrackData();
 		track.m_SoundSet 	= soundSetName;
 		track.m_TimeOfDay 	= timeOfDay;
+		track.m_Shape		= DynamicMusicLocationShape.BOX;
 		
 		track.locationBoundaries = locationBoundaries;
 		
@@ -206,6 +210,27 @@ class DynamicMusicPlayerRegistry
 		
 	}
 	
+	protected void RegisterTrackLocationStaticPoints(string soundSetName, array<vector> vertices, int timeOfDay = DynamicMusicPlayerTimeOfDay.ANY, bool runImmediately = false)
+	{
+		DynamicMusicTrackData track = new DynamicMusicTrackData();
+		track.m_SoundSet 	= soundSetName;
+		track.m_TimeOfDay 	= timeOfDay;
+		track.m_Shape		= DynamicMusicLocationShape.POLYGON;
+		
+		track.vertices = vertices;
+		
+		if (!runImmediately)
+		{
+			track.m_Category = EDynamicMusicPlayerCategory.LOCATION_STATIC;
+			m_TracksLocationStatic.Insert(track);
+		}
+		else
+		{
+			track.m_Category = EDynamicMusicPlayerCategory.LOCATION_STATIC_PRIORITY;
+			m_TracksLocationStaticPrioritized.Insert(track);
+		}
+	}
+	
 	protected void RegisterTrackLocationDynamic(string soundSetName, int locationType = DynamicMusicLocationTypes.NONE, int timeOfDay = DynamicMusicPlayerTimeOfDay.ANY)
 	{
 		DynamicMusicTrackData track = new DynamicMusicTrackData();

+ 1 - 1
Scripts/3_game/systems/dynamicmusicplayer/dynamicmusicplayerregistryenoch.c

@@ -18,7 +18,7 @@ class DynamicMusicPlayerRegistryEnoch : DynamicMusicPlayerRegistry
 		
 		array<ref TVectorArray> militaryDambog;
 		militaryDambog = {
-			{"591.944336 534.083801 1127.866089", "740.260620 531.480774 1233.501831"},
+			{"591.944336 334.083801 1127.866089", "740.260620 331.480774 1233.501831"},
 		};
 
 		RegisterTrackLocationStaticMultiRectangle("Music_loc_sila_SoundSet", militarySila, DynamicMusicPlayerTimeOfDay.ANY, true);

+ 19 - 14
Scripts/3_game/systems/dynamicmusicplayer/dynamicmusicplayerregistrysakhal.c

@@ -11,21 +11,26 @@ class DynamicMusicPlayerRegistrySakhal : DynamicMusicPlayerRegistry
 
 		array<ref TVectorArray> volcano;
 		volcano = {
-			{"10771.071289 333.252869 11467.150391", "11188.803711 312.930695 11719.018555"},
-			{"9389.848633 168.028839 11451.730469", "10996.273438 305.816284 11758.886719"},
-			{"9325.345703 202.547287 11786.530273", "11026.988281 237.025238 12013.826172"},
-			{"9257.772461 247.768051 12029.183594", "10992.586914 121.198746 12416.230469"},
-			{"9413.806641 201.219055 12407.015625", "10934.227539 86.902298 12643.526367"},
-			{"9773.179688 184.466110 12658.883789", "10639.358398 99.178627 12929.180664"},
-			{"9269.792969 237.340118 11924.660156", "9460.906250 290.912262 12026.143555"},
-			{"9599.116211 127.237610 11272.014648", "9818.397461 214.291824 11425.602539"},
-			{"9907.269531 171.890976 11202.095703", "10434.655273 348.452423 11779.015625"},
-			{"10492.929688 252.155991 11347.783203", "11110.642578 267.198242 12061.648438"},
-			{"10225.749023 220.161255 11173.888672", "10530.322266 335.978149 11753.463867"},
-			{"9721.494141 172.261322 11326.728516", "10212.602539 389.167236 11719.739258"},
-			{"10743.962891 302.046997 11379.742188", "10902.751953 280.287170 11748.366211"},
-			{"10091.279297 189.422806 11175.544922", "10211.166992 236.147644 11222.474609"},
+			//{"10771.071289 333.252869 11467.150391", "11188.803711 312.930695 11719.018555"},
+			{"10125.943359 343.528503 11455.274414", "11188.803711 312.930695 11719.018555"},
+			{"9389.848633 200.028839 11451.730469", "10996.273438 305.816284 11758.886719"},
+			{"9346.515625 210.815308 11741.433594", "11026.988281 237.025238 12013.826172"},
+			{"9257.772461 265.768051 12029.183594", "10992.586914 121.198746 12416.230469"},
+			{"9413.806641 235.219055 12407.015625", "10934.227539 86.902298 12643.526367"},
+			{"9773.179688 150.466110 12658.883789", "10639.358398 20.178627 12929.180664"},
+			{"9269.792969 250.340118 11924.660156", "9460.906250 290.912262 12026.143555"},
+			{"9599.116211 180.237610 11272.014648", "9818.397461 214.291824 11425.602539"},
+			{"9907.269531 190.890976 11202.095703", "10434.655273 348.452423 11779.015625"},
+			{"10492.929688 270.155991 11347.783203", "11110.642578 267.198242 12061.648438"},
+			{"10225.749023 250.161255 11173.888672", "10530.322266 335.978149 11753.463867"},
+			{"9721.494141 190.261322 11326.728516", "10212.602539 389.167236 11719.739258"},
+			{"10743.962891 330.046997 11379.742188", "10902.751953 280.287170 11748.366211"},
+			{"10091.279297 189.422806 11175.544922", "10211.163086 251.937698 11283.309570"},
 			{"9935.606445 486.956635 11941.885742", "10126.136719 505.798462 12129.829102"},
+			//{"10103.692383 231.105469 11273.761719", "10436.062500 348.649872 11788.791992"},
+			//{"10020.197266 350.342590 11662.968750", "10286.432617 429.377930 11918.535156"},
+			//{"10127.908203 292.817322 11474.920898", "10454.188477 338.145111 12013.170898"},
+			//{"9715.170898 365.813385 12181.414063", "10384.988281 266.674225 12403.265625"}
 		};
 		
 		array<ref TVectorArray> AA_base;

+ 11 - 0
Scripts/3_game/systems/hfsmbase.c

@@ -44,6 +44,17 @@ class HFSMBase<Class FSMStateBase, Class FSMEventBase, Class FSMActionBase, Clas
 	{
 		return m_State;
 	}
+	
+	/**@fn		SetCurrentState
+	 * @return	returns currently active state within this machine (i.e. not hierarchic state)
+	 **/
+	void SetCurrentState (FSMStateBase state)
+	{
+		if(m_State != state)
+		{
+			m_State = state;
+		}
+	}	
 	/**@fn		GetOwnerState
 	 * @return	returns state that is owner of this fsm submachine. returns null if this is a root machine.
 	 **/

+ 63 - 50
Scripts/3_game/systems/inventory/handanimatedforceswapping.c

@@ -64,20 +64,31 @@ class HandForceSwappingAnimated_Show extends HandStartAction
 	{
 		if (m_Src1 && m_Src2 && m_Dst1 && m_Dst2)
 		{
-			//InventoryLocation dummy;
-			//dummy.Copy(m_Dst2);			
-			//if (GameInventory.CanForceSwapEntitiesEx(m_Src1.GetItem(), m_Dst1, m_Src2.GetItem(), dummy) && dummy == m_Dst2)
-			//{
-
-				//! TODO(kumarjac): THIS IS WHAT IS BEING CALLED FOR SOME REASON!!!!!
-
-				GameInventory.LocationSwap(m_Src1, m_Src2, m_Dst1, m_Dst2);
-				e.m_Player.OnItemInHandsChanged();
-			//}
-			//else
-			//{
-			//	if (LogManager.IsInventoryHFSMLogEnable()) hndDebugPrint("[hndfsm] HandForceSwapingAnimated_Show - not allowed");
-			//}
+			if (!GetGame().IsMultiplayer())
+			{
+				m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst1.GetItem(), m_Dst1);
+				m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst2.GetItem(), m_Dst2);
+				if (GameInventory.CanForceSwapEntitiesEx(m_Src1.GetItem(), m_Dst1, m_Src2.GetItem(), m_Dst2))
+				{
+					GameInventory.LocationSwap(m_Src1, m_Src2, m_Dst1, m_Dst2);
+					e.m_Player.OnItemInHandsChanged();
+				}
+			}
+			else
+			{
+				if (!GetGame().IsDedicatedServer())
+				{
+					m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst1.GetItem(), m_Dst1);
+					m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst2.GetItem(), m_Dst2);
+					
+					m_Player.GetHumanInventory().PostDeferredForceSwapEntities(InventoryMode.JUNCTURE, m_Dst1.GetItem(), m_Dst2.GetItem(), m_Dst1, m_Dst2);
+				}
+				else
+				{
+					GetGame().ClearJunctureEx(m_Player, m_Dst1.GetItem());
+					GetGame().ClearJunctureEx(m_Player, m_Dst2.GetItem());
+				}
+			}
 		}
 		else
 			Error("[hndfsm] HandForceSwappingAnimated_Show is not properly configured!");
@@ -151,10 +162,11 @@ class HandAnimatedForceSwapping extends HandStateBase
 			{
 				m_Start.m_ActionType = efs.m_AnimationID;
 			
-				m_Src1 = efs.GetSrc();
-				m_Src2 = efs.m_Src2;
-				m_Dst1 = efs.GetDst();
-				m_Dst2 = efs.m_Dst2;
+				m_Src1 = efs.m_Src2;
+				m_Src2 = efs.GetSrc();
+				m_Dst1 = efs.m_Dst2;
+				m_Dst2 = efs.GetDst();
+
 			
 				m_Show.m_ActionType = efs.m_Animation2ID;
 			}
@@ -162,16 +174,17 @@ class HandAnimatedForceSwapping extends HandStateBase
 			{
 				m_Start.m_ActionType = efs.m_Animation2ID;
 			
-				m_Src1 = efs.m_Src2;
-				m_Src2 = efs.GetSrc();
-				m_Dst1 = efs.m_Dst2;
-				m_Dst2 = efs.GetDst();
+				m_Src1 = efs.GetSrc();
+				m_Src2 = efs.m_Src2;
+				m_Dst1 = efs.GetDst();
+				m_Dst2 = efs.m_Dst2;
 			
 				m_Show.m_ActionType = efs.m_AnimationID;
 			}
 			
-			m_Hide.m_Dst = m_Dst1;
-			m_Show.m_Dst = m_Dst2;
+			m_Show.m_Dst = m_Dst1;
+			m_Hide.m_Dst = m_Dst2;
+			
 
 			if (!GetGame().IsDedicatedServer())
 			{
@@ -189,16 +202,16 @@ class HandAnimatedForceSwapping extends HandStateBase
 		{
 			if (m_Dst1)
 			{
-				e.m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst1.GetItem(), m_Dst1);
-				e.m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst2.GetItem(), m_Dst2);
+				m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst1.GetItem(), m_Dst1);
+				m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst2.GetItem(), m_Dst2);
 			}
 		}
 		else
 		{
 			if (m_Dst1)
 			{
-				GetGame().ClearJuncture(e.m_Player, m_Dst1.GetItem());
-				GetGame().ClearJuncture(e.m_Player, m_Dst2.GetItem());
+				GetGame().ClearJunctureEx(m_Player, m_Dst1.GetItem());
+				GetGame().ClearJunctureEx(m_Player, m_Dst2.GetItem());
 			}
 		}
 		
@@ -214,13 +227,13 @@ class HandAnimatedForceSwapping extends HandStateBase
 	{
 		if ( !GetGame().IsDedicatedServer())
 		{
-			e.m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst1.GetItem(), m_Dst1);
-			e.m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst2.GetItem(), m_Dst2);
+			m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst1.GetItem(), m_Dst1);
+			m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst2.GetItem(), m_Dst2);
 		}
 		else
 		{
-			GetGame().ClearJuncture(e.m_Player, m_Dst1.GetItem());
-			GetGame().ClearJuncture(e.m_Player, m_Dst2.GetItem());
+			GetGame().ClearJunctureEx(m_Player, m_Dst1.GetItem());
+			GetGame().ClearJunctureEx(m_Player, m_Dst2.GetItem());
 		}
 		
 		m_Src1 = null;
@@ -271,10 +284,10 @@ class HandAnimatedForceSwapping_Inst extends HandStateBase
 			{
 				m_Start.m_ActionType = efs.m_AnimationID;
 			
-				m_Src1 = efs.GetSrc();
-				m_Src2 = efs.m_Src2;
-				m_Dst1 = efs.GetDst();
-				m_Dst2 = efs.m_Dst2;
+				m_Src1 = efs.m_Src2;
+				m_Src2 = efs.GetSrc();
+				m_Dst1 = efs.m_Dst2;
+				m_Dst2 = efs.GetDst();
 
 				m_Swap.m_ActionType = efs.m_Animation2ID;
 			}
@@ -282,10 +295,10 @@ class HandAnimatedForceSwapping_Inst extends HandStateBase
 			{
 				m_Start.m_ActionType = efs.m_Animation2ID;
 			
-				m_Src1 = efs.m_Src2;
-				m_Src2 = efs.GetSrc();
-				m_Dst1 = efs.m_Dst2;
-				m_Dst2 = efs.GetDst();
+				m_Src1 = efs.GetSrc();
+				m_Src2 = efs.m_Src2;
+				m_Dst1 = efs.GetDst();
+				m_Dst2 = efs.m_Dst2;
 
 				m_Swap.m_ActionType = efs.m_AnimationID;				
 			}
@@ -297,8 +310,8 @@ class HandAnimatedForceSwapping_Inst extends HandStateBase
 			
 			if (!GetGame().IsDedicatedServer())
 			{
-				e.m_Player.GetHumanInventory().AddInventoryReservationEx(m_Dst1.GetItem(), m_Dst1, GameInventory.c_InventoryReservationTimeoutShortMS);
-				e.m_Player.GetHumanInventory().AddInventoryReservationEx(m_Dst2.GetItem(), m_Dst2, GameInventory.c_InventoryReservationTimeoutShortMS);
+				m_Player.GetHumanInventory().AddInventoryReservationEx(m_Dst1.GetItem(), m_Dst1, GameInventory.c_InventoryReservationTimeoutShortMS);
+				m_Player.GetHumanInventory().AddInventoryReservationEx(m_Dst2.GetItem(), m_Dst2, GameInventory.c_InventoryReservationTimeoutShortMS);
 			}
 		}
 
@@ -311,22 +324,22 @@ class HandAnimatedForceSwapping_Inst extends HandStateBase
 			{
 				if (m_Dst1)
 				{
-					e.m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst1.GetItem(), m_Dst1);
+					m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst1.GetItem(), m_Dst1);
 				}
 				if (m_Dst2)
 				{
-					e.m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst2.GetItem(), m_Dst2);
+					m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst2.GetItem(), m_Dst2);
 				}
 			}
 			else
 			{
 				if (m_Dst1)
 				{
-					GetGame().ClearJuncture(e.m_Player, m_Dst1.GetItem());
+					GetGame().ClearJunctureEx(m_Player, m_Dst1.GetItem());
 				}
 				if (m_Dst2)
 				{
-					GetGame().ClearJuncture(e.m_Player, m_Dst2.GetItem());
+					GetGame().ClearJunctureEx(m_Player, m_Dst2.GetItem());
 				}			
 			}
 		
@@ -342,13 +355,13 @@ class HandAnimatedForceSwapping_Inst extends HandStateBase
 	{
 		if ( !GetGame().IsDedicatedServer())
 		{
-			e.m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst1.GetItem(), m_Dst1);
-			e.m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst2.GetItem(), m_Dst2);
+			m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst1.GetItem(), m_Dst1);
+			m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst2.GetItem(), m_Dst2);
 		}
 		else
 		{
-			GetGame().ClearJuncture(e.m_Player, m_Dst1.GetItem());
-			GetGame().ClearJuncture(e.m_Player, m_Dst2.GetItem());
+			GetGame().ClearJunctureEx(m_Player, m_Dst1.GetItem());
+			GetGame().ClearJunctureEx(m_Player, m_Dst2.GetItem());
 		}
 		
 		m_Src1 = null;

+ 18 - 14
Scripts/3_game/systems/inventory/handanimatedmovingtoatt.c

@@ -29,29 +29,33 @@ class HandAnimatedMoveToDst_W4T extends HandStartAction
 
 	override void OnEntry(HandEventBase e)
 	{
-		Man player = e.m_Player;
 		if (m_Dst && m_Dst.IsValid())
 		{
 			EntityAI item = m_Dst.GetItem();
 			InventoryLocation src = new InventoryLocation;
 			if (item.GetInventory().GetCurrentInventoryLocation(src))
 			{
-				if (GameInventory.LocationSyncMoveEntity(src, m_Dst))
+				if (!GetGame().IsDedicatedServer())
 				{
-					player.OnItemInHandsChanged();
+					if (!GetGame().IsMultiplayer())
+					{
+						m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst.GetItem(), m_Dst);
+						GameInventory.LocationSyncMoveEntity(src, m_Dst);
+						m_Player.OnItemInHandsChanged();
+					}
+					else
+					{
+						m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst.GetItem(), m_Dst);
+						m_Player.GetHumanInventory().PostDeferredEventTakeToDst(InventoryMode.JUNCTURE,src, m_Dst);
+					}
 				}
 				else
 				{
-					#ifdef ENABLE_LOGGING
-					if ( LogManager.IsInventoryHFSMLogEnable() )
-					{	
-						Debug.InventoryHFSMLog("[hndfsm] HandAnimatedMoveToDst_W4T - not allowed");
-					}
-					#endif
+					GetGame().ClearJunctureEx(m_Player, m_Dst.GetItem());
 				}
 			}
 			else
-				Error("[hndfsm] " + Object.GetDebugName(e.m_Player) + " STS = " + e.m_Player.GetSimulationTimeStamp() + " HandAnimatedMoveToDst_W4T - item " + item + " has no Inventory or Location, inv=" + item.GetInventory());
+				Error("[hndfsm] " + Object.GetDebugName(m_Player) + " STS = " + m_Player.GetSimulationTimeStamp() + " HandAnimatedMoveToDst_W4T - item " + item + " has no Inventory or Location, inv=" + item.GetInventory());
 		}
 		else
 			Error("[hndfsm] HandAnimatedMoveToDst_W4T - event has no valid m_Dst");
@@ -113,7 +117,7 @@ class HandAnimatedMovingToAtt extends HandStateBase
 			
 			m_ilEntity = m_Show.m_Dst;
 			
-			e.m_Player.GetHumanInventory().AddInventoryReservationEx(m_Entity, m_ilEntity, GameInventory.c_InventoryReservationTimeoutShortMS);
+			m_Player.GetHumanInventory().AddInventoryReservationEx(m_Entity, m_ilEntity, GameInventory.c_InventoryReservationTimeoutShortMS);
 		}
 		
 		super.OnEntry(e); // @NOTE: super at the end (prevent override from submachine start)
@@ -123,11 +127,11 @@ class HandAnimatedMovingToAtt extends HandStateBase
 	{
 		if ( !GetGame().IsDedicatedServer())
 		{
-			e.m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Entity, m_ilEntity);
+			m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Entity, m_ilEntity);
 		}
 		else
 		{
-			GetGame().ClearJunctureEx(e.m_Player, m_Entity);
+			GetGame().ClearJunctureEx(m_Player, m_Entity);
 		}
 		
 		m_Entity = null;
@@ -140,7 +144,7 @@ class HandAnimatedMovingToAtt extends HandStateBase
 	{
 		if ( !GetGame().IsDedicatedServer())
 		{
-			e.m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Entity, m_ilEntity);
+			m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Entity, m_ilEntity);
 		}
 		
 		m_Entity = null;

+ 12 - 12
Scripts/3_game/systems/inventory/handanimatedswapping.c

@@ -36,10 +36,10 @@ class HandAnimatedSwapping extends HandStateBase
 		HandEventSwap es = HandEventSwap.Cast(e);
 		if (es)
 		{
-			m_Src1 = es.GetSrc();
-			m_Src2 = es.m_Src2;
-			m_Dst1 = es.GetDst();
-			m_Dst2 = es.m_Dst2;
+			m_Src1 = es.m_Src2;
+			m_Src2 = es.GetSrc();
+			m_Dst1 = es.m_Dst2;
+			m_Dst2 = es.GetDst();
 
 			m_Show.m_Src1 = m_Src1;
 			m_Show.m_Src2 = m_Src2;
@@ -51,8 +51,8 @@ class HandAnimatedSwapping extends HandStateBase
 			
 			if (!GetGame().IsDedicatedServer())
 			{
-				e.m_Player.GetHumanInventory().AddInventoryReservationEx(m_Dst2.GetItem(), m_Dst2, GameInventory.c_InventoryReservationTimeoutShortMS);
-				e.m_Player.GetHumanInventory().AddInventoryReservationEx(m_Dst1.GetItem(), m_Dst1, GameInventory.c_InventoryReservationTimeoutShortMS);
+				m_Player.GetHumanInventory().AddInventoryReservationEx(m_Dst2.GetItem(), m_Dst2, GameInventory.c_InventoryReservationTimeoutShortMS);
+				m_Player.GetHumanInventory().AddInventoryReservationEx(m_Dst1.GetItem(), m_Dst1, GameInventory.c_InventoryReservationTimeoutShortMS);
 			}
 		}
 
@@ -65,22 +65,22 @@ class HandAnimatedSwapping extends HandStateBase
 		{
 			if (m_Dst2)
 			{
-				e.m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst2.GetItem(), m_Dst2);
+				m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst2.GetItem(), m_Dst2);
 			}
 			if (m_Dst1)
 			{
-				e.m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst1.GetItem(), m_Dst1);
+				m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst1.GetItem(), m_Dst1);
 			}
 		}
 		else
 		{
 			if (m_Dst2)
 			{
-				GetGame().ClearJuncture(e.m_Player, m_Dst2.GetItem());
+				GetGame().ClearJunctureEx(m_Player, m_Dst2.GetItem());
 			}
 			if (m_Dst1)
 			{
-				GetGame().ClearJuncture(e.m_Player, m_Dst1.GetItem());
+				GetGame().ClearJunctureEx(m_Player, m_Dst1.GetItem());
 			}
 		}
 		
@@ -96,8 +96,8 @@ class HandAnimatedSwapping extends HandStateBase
 	{
 		if ( !GetGame().IsDedicatedServer())
 		{
-			e.m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst2.GetItem(), m_Dst2);
-			e.m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst1.GetItem(), m_Dst1);		
+			m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst2.GetItem(), m_Dst2);
+			m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst1.GetItem(), m_Dst1);		
 		}
 		
 		m_Src1 = null;

+ 20 - 12
Scripts/3_game/systems/inventory/handanimatedtakingfromatt.c

@@ -27,15 +27,23 @@ class HandTakingAnimated_Show extends HandStartAction
 				}
 				#endif
 				
-				//if (GameInventory.LocationCanMoveEntity(m_Src, m_Dst))
-				//{
+				if (!GetGame().IsMultiplayer())
+				{
 					GameInventory.LocationSyncMoveEntity(m_Src, m_Dst);
-					e.m_Player.OnItemInHandsChanged();
-				//}
-				//else
-				//{
-				//	if (LogManager.IsInventoryHFSMLogEnable()) hndDebugPrint("[hndfsm] HandTakingAnimated_Show - not allowed");
-				//}
+					m_Player.OnItemInHandsChanged();
+				}
+				else
+				{
+					if (!GetGame().IsDedicatedServer())
+					{
+						m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst.GetItem(), m_Dst);
+						m_Player.GetHumanInventory().PostDeferredEventTakeToDst(InventoryMode.JUNCTURE, m_Src, m_Dst);
+					}
+					else
+					{
+						GetGame().ClearJunctureEx(e.m_Player, m_Dst.GetItem());
+					}
+				}
 			}
 			else
 			{
@@ -101,7 +109,7 @@ class HandAnimatedTakingFromAtt extends HandStateBase
 		m_Hide.m_ActionType = e.GetAnimationID();
 		m_Show.m_ActionType = e.GetAnimationID();
 			
-		e.m_Player.GetHumanInventory().AddInventoryReservationEx(m_Dst.GetItem(), m_Dst, GameInventory.c_InventoryReservationTimeoutShortMS);
+		m_Player.GetHumanInventory().AddInventoryReservationEx(m_Dst.GetItem(), m_Dst, GameInventory.c_InventoryReservationTimeoutShortMS);
 
 		super.OnEntry(e); // @NOTE: super at the end (prevent override from submachine start)
 	}
@@ -116,9 +124,9 @@ class HandAnimatedTakingFromAtt extends HandStateBase
 		#endif
 		if (m_Dst)
 		{
-			e.m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst.GetItem(), m_Dst);
+			m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst.GetItem(), m_Dst);
 			if ( GetGame().IsServer() )
-				GetGame().ClearJuncture(e.m_Player, m_Dst.GetItem());
+				GetGame().ClearJunctureEx(e.m_Player, m_Dst.GetItem());
 		}
 		m_Dst = null;
 
@@ -127,7 +135,7 @@ class HandAnimatedTakingFromAtt extends HandStateBase
 
 	override void OnExit(HandEventBase e)
 	{
-		e.m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst.GetItem(), m_Dst);
+		m_Player.GetHumanInventory().ClearInventoryReservationEx(m_Dst.GetItem(), m_Dst);
 		m_Dst = null;
 
 		super.OnExit(e);

+ 5 - 7
Scripts/3_game/systems/inventory/handfsm.c

@@ -35,13 +35,11 @@ class HandFSM extends HFSMBase<HandStateBase, HandEventBase, HandActionBase, Han
 {
 	int GetCurrentStateID ()
 	{
-		/*if (m_States.Count() == 2)
-		{
-			int s0 = m_States[0].GetCurrentStateID();
-			int s1 = m_States[1].GetCurrentStateID() << 8;
-			return s1 | s0;
-		}*/
-		return 0;
+		HandStableState hState = HandStableState.Cast(m_State);
+		if(hState)
+			return hState.GetCurrentStateID();
+
+		return HandStateID.UNKNOWN;
 	}
 
 	/**@fn			SyncStateFromID

+ 1 - 1
Scripts/3_game/systems/inventory/handstablestate.c

@@ -33,7 +33,7 @@ class HandStableState extends HandStateBase
 
 	override bool IsIdle () { return true; }
 
-	int GetCurrentStateID () { return 0; }
+	int GetCurrentStateID () { return HandStateID.UNKNOWN; }
 
 	/// query for entity in hands
 	bool HasEntityInHands () { return false; }

+ 11 - 0
Scripts/3_game/systems/inventory/humaninventory.c

@@ -613,4 +613,15 @@ class HumanInventory : GameInventory
 		
 		return true; 
 	}
+	
+	bool PostDeferredEventTakeToDst(InventoryMode mode, notnull InventoryLocation src, notnull InventoryLocation dst)
+	{
+		return true;
+	}
+
+	
+	bool PostDeferredForceSwapEntities(InventoryMode mode, notnull EntityAI item1, notnull EntityAI item2, notnull InventoryLocation dst1, notnull InventoryLocation dst2)
+	{
+		return true;
+	}
 }

+ 15 - 0
Scripts/3_game/systems/inventory/humaninventorywithfsm.c

@@ -129,6 +129,21 @@ class HumanInventoryWithFSM : HumanInventory
 			return false;
 		}
 	}
+	
+		
+	void CheckFSMState()
+	{
+		if (GetEntityInHands())
+		{
+			if (!m_FSM.IsRunning() || GetCurrentStateID() == HandStateID.Empty) //forcing when not running or in stable state only
+				m_FSM.SetCurrentState(m_Equipped);
+		}
+		else
+		{
+			if (!m_FSM.IsRunning() || GetCurrentStateID() == HandStateID.Equipped) //forcing when not running or in stable state only
+				m_FSM.SetCurrentState(m_Empty);
+		}
+	}
 
 	override bool OnStoreLoad (ParamsReadContext ctx, int version)
 	{

+ 1 - 1
Scripts/3_game/systems/inventory/inventory.c

@@ -488,7 +488,7 @@ class GameInventory
 
 				if (!src.GetItem() || !dst.GetItem())
 				{
-					LogError("[syncinv] ServerInventoryCommand (cmd=SYNC_MOVE) dropped, item not in bubble");
+					Error("[syncinv] ServerInventoryCommand (cmd=SYNC_MOVE) dropped, item not in bubble");
 					break; // not in bubble
 				}
 

+ 1 - 19
Scripts/3_game/systems/inventory/inventoryinputuserdata.c

@@ -103,23 +103,5 @@ class InventoryInputUserData
 		}
 	}
 	///@} hand
-
-	static void SerializeDestroy(ParamsWriteContext ctx, notnull InventoryLocation src)
-	{
-		ctx.Write(INPUT_UDT_INVENTORY);
-		ctx.Write(InventoryCommandType.DESTROY);
-		src.WriteToContext(ctx);
-	}
-
-	static void SendInputUserDataDestroy(notnull InventoryLocation src)
-	{
-		if (GetGame().IsClient())
-		{
-			if (LogManager.IsSyncLogEnable()) syncDebugPrint("[syncinv] SendInputUserDataDestroy src=" + InventoryLocation.DumpToStringNullSafe(src));
-			ScriptInputUserData ctx = new ScriptInputUserData;
-			SerializeDestroy(ctx, src);
-			ctx.Send();
-		}
-	}
-};
+}
 

+ 6 - 1
Scripts/3_game/systems/temperatureaccess/temperaturedata.c

@@ -1,6 +1,7 @@
 class TemperatureData
 {
 	ETemperatureAccessTypes m_AccessType;
+	bool m_UseGlobalCooling;
 	float m_Value; 					//target or increment, depends on context!
 	float m_AdjustedTarget; 		//actual target of the operation (can be adjusted via over-time interpolation, not necessarily the original target value!)
 	float m_UpdateTimeInfo; 		//if the temperature change was accumulated over some time, pass this info to temperature subsystems
@@ -24,6 +25,7 @@ class TemperatureData
 	
 	protected void Init()
 	{
+		m_UseGlobalCooling = true;
 		m_InterpolatedFraction = 0.0;
 	}
 }
@@ -45,11 +47,14 @@ class TemperatureDataInterpolated : TemperatureData
 			
 			float coef = m_UpdateTimeCoef;
 			float absBaseTempChange = m_UpdateTimeInfo * m_InterpolatedStepSize;
+			
 			if (start > target)
 			{
 				absBaseTempChange *= -1;
-				coef = GameConstants.TEMP_COEF_COOLING_GLOBAL;
+				if (m_UseGlobalCooling)
+					coef = GameConstants.TEMP_COEF_COOLING_GLOBAL;
 			}
+
 			coef *= m_HeatPermeabilityCoef;
 			absBaseTempChange *= coef;
 			

+ 33 - 39
Scripts/3_game/tools/debug.c

@@ -1,45 +1,31 @@
-// TODO:
-// 1. Alredy exist some key in array / map
-// 2. Object is null (check object & log error)
-// 3. Debug version only
-// 4. Destructor of static classes
-// 5. Debug Console Interface:
-//		- Clear Log
-//		- Filter
-//		- Log per frame
-// 6. Per frame log ?
-// 7. Zapis do fajlu
-
 class Debug
 {	
-	static private const string	LOG_DEBUG					= "Debug";
-	static private const string	LOG_DEBUG_ACTION			= "Action";
-	static private const string	LOG_DEBUG_SYMPTOM			= "Symptom";
-	static private const string	LOG_DEBUG_INV_MOVE			= "Inv Move";
-	static private const string	LOG_DEBUG_INV_RESERVATION	= "Inv Rrsv";
-	static private const string	LOG_DEBUG_INV_HFSM			= "HFSM";
-	static private const string	LOG_DEBUG_QUICKBAR			= "Quickbar";
-	static private const string	LOG_DEBUG_BASEBUILDING		= "Base Building";
-	static private const string	LOG_DEBUG_BLEEDING_CHANCES	= "Bleeding";
-	static private const string LOG_DEBUG_TRIGGER			= "Trigger";
-	static private const string LOG_DEBUG_PARTICLE			= "Particle";
-	static private const string LOG_DEBUG_TF				= "TestFramework";
-	static private const string LOG_DEBUG_WEIGHT			= "Weight";
-	static private const string LOG_DEBUG_MELEE				= "Melee";
-	static private const string LOG_DEBUG_WEATHER			= "Weather";
+	private static const string	LOG_DEBUG					= "Debug";
+	private static const string	LOG_DEBUG_ACTION			= "Action";
+	private static const string	LOG_DEBUG_SYMPTOM			= "Symptom";
+	private static const string	LOG_DEBUG_INV_MOVE			= "Inv Move";
+	private static const string	LOG_DEBUG_INV_RESERVATION	= "Inv Rrsv";
+	private static const string	LOG_DEBUG_INV_HFSM			= "HFSM";
+	private static const string	LOG_DEBUG_QUICKBAR			= "Quickbar";
+	private static const string	LOG_DEBUG_BASEBUILDING		= "Base Building";
+	private static const string	LOG_DEBUG_BLEEDING_CHANCES	= "Bleeding";
+	private static const string LOG_DEBUG_TRIGGER			= "Trigger";
+	private static const string LOG_DEBUG_PARTICLE			= "Particle";
+	private static const string LOG_DEBUG_TF				= "TestFramework";
+	private static const string LOG_DEBUG_WEIGHT			= "Weight";
+	private static const string LOG_DEBUG_MELEE				= "Melee";
+	private static const string LOG_DEBUG_WEATHER			= "Weather";
 
-	static private const string	LOG_INFO					= "Info";
-	static private const string	LOG_WARNING					= "Warning";
-	static private const string	LOG_ERROR					= "Error";
-	static private const string	LOG_DEFAULT					= "n/a";
+	private static const string	LOG_INFO					= "Info";
+	private static const string	LOG_WARNING					= "Warning";
+	private static const string	LOG_ERROR					= "Error";
+	private static const string	LOG_DEFAULT					= "n/a";
 	
-	static private ref array<Shape>	m_DebugShapes;
+	private static ref array<Shape>	m_DebugShapes;
 	
 	static Widget m_DebugLayoutCanvas;
 	static CanvasWidget m_CanvasDebug;
 	
-	
-	
 	static string GetDebugName(Managed entity)
 	{
 		if (!entity)
@@ -373,6 +359,14 @@ class Debug
 		return shapes;
 	}
 	
+	static void CleanupDrawShapes(array<Shape> shapes)
+	{
+		foreach (Shape shape : shapes)
+			Debug.RemoveShape(shape);
+		
+		shapes.Clear();
+	}
+	
 	/**
 	DrawLine
 	\nFlags:
@@ -483,9 +477,9 @@ class Debug
 	//---------------------------------------------------------------
 	//-------private
 	
-	static private bool				m_EnabledLogs; //! DEPRECATED
+	private static bool				m_EnabledLogs; //! DEPRECATED
 		
-	static private string LogMessage(string level, string plugin, string entity, string author, string label, string message)
+	private static string LogMessage(string level, string plugin, string entity, string author, string label, string message)
 	{
 		if (GetGame() == null || !LogManager.IsLogsEnable())
 			return string.Empty;
@@ -523,7 +517,7 @@ class Debug
 		return msg;
 	}
 
-	static private void	SaveLog(string log_message)
+	private static void	SaveLog(string log_message)
 	{
 		if (log_message.Length() == 0)
 			return;
@@ -578,7 +572,7 @@ class Debug
 		return CFG_FILE_SCRIPT_LOG_EXT;
 	}
 	
-	static private string GetDate()
+	private static string GetDate()
 	{
 		int year;
 		int month;
@@ -758,7 +752,7 @@ enum WeightDebugType
 
 class WeightDebug
 {
-	static private ref map<EntityAI, ref WeightDebugData> m_WeightDebugData 	= new map<EntityAI, ref WeightDebugData>();
+	private static ref map<EntityAI, ref WeightDebugData> m_WeightDebugData 	= new map<EntityAI, ref WeightDebugData>();
 	static WeightDebugType m_VerbosityFlags;
 	
 	//-------------------------------------------------------------

+ 32 - 1
Scripts/3_game/tools/input.c

@@ -252,6 +252,9 @@ class Input
 	//! callback that is fired when a new gamepad is connected
 	void OnGamepadConnected(int gamepad)
 	{
+		if (!g_Game)
+			return;
+		
 		#ifdef PLATFORM_PS4
 		BiosUser user;
 		GetGamepadUser( gamepad, user );
@@ -276,6 +279,9 @@ class Input
 	//! callback that is fired when gamepad is disconnected
 	void OnGamepadDisconnected(int gamepad)
 	{
+		if (!g_Game)
+			return;
+		
 		if (IsInactiveGamepadOrUserSelected(gamepad))
 		{
 			UpdateConnectedInputDeviceList();
@@ -295,6 +301,9 @@ class Input
 	//! callback that is fired when identification was requested
 	void OnGamepadIdentification(int gamepad)
 	{
+		if (!g_Game)
+			return;
+		
 		if (gamepad > -1)
 		{
 			DayZLoadState state = g_Game.GetLoadState();
@@ -315,7 +324,7 @@ class Input
 	}
 	
 	int GetUserGamepad( BiosUser user )
-	{
+	{		
 		array<int> gamepads = new array<int>;
 		GetGamepadList( gamepads );
 		for( int i = 0; i < gamepads.Count(); i++ )
@@ -330,6 +339,12 @@ class Input
 	
 	bool IsInactiveGamepadOrUserSelected( int gamepad = -1 )
 	{
+		if (!g_Game)
+			return false;
+		
+		#ifdef PLATFORM_CONSOLE
+		if (!GetGame().GetUserManager())
+			return false;
 		#ifdef PLATFORM_XBOX
 		return !IsActiveGamepadSelected();
 		#endif
@@ -338,6 +353,7 @@ class Input
 		GetGamepadUser( gamepad, user );
 		return (user == GetGame().GetUserManager().GetSelectedUser());
 		#endif
+		#endif
 		return false;
 	}
 	
@@ -345,6 +361,9 @@ class Input
 	//! does not fire on PC - mouse/keyboard assumed to always be connected
 	void OnMouseConnected()
 	{
+		if (!g_Game)
+			return;
+		
 		UpdateConnectedInputDeviceList();
 		if (!g_Game.IsLoading() && GetGame().GetMission())
 		{
@@ -360,6 +379,9 @@ class Input
 	//! does not fire on PC - mouse/keyboard assumed to always be connected
 	void OnMouseDisconnected()
 	{
+		if (!g_Game)
+			return;
+		
 		UpdateConnectedInputDeviceList();
 		if (!g_Game.IsLoading() && GetGame().GetMission())
 		{
@@ -375,6 +397,9 @@ class Input
 	//! does not fire on PC - mouse/keyboard assumed to always be connected
 	void OnKeyboardConnected()
 	{
+		if (!g_Game)
+			return;
+		
 		UpdateConnectedInputDeviceList();
 		if (!g_Game.IsLoading() && GetGame().GetMission())
 		{
@@ -390,6 +415,9 @@ class Input
 	//! does not fire on PC - mouse/keyboard assumed to always be connected
 	void OnKeyboardDisconnected()
 	{
+		if (!g_Game)
+			return;
+		
 		UpdateConnectedInputDeviceList();
 		if (!g_Game.IsLoading() && GetGame().GetMission())
 		{
@@ -404,6 +432,9 @@ class Input
 	//! called from code on different input device use
 	void OnLastInputDeviceChanged(EInputDeviceType inputDevice)
 	{
+		if (!g_Game)
+			return;
+		
 		if (GetGame().GetMission())
 		{
 			GetGame().GetMission().GetOnInputDeviceChanged().Invoke(inputDevice);

+ 4 - 6
Scripts/3_game/tools/jsonfileloader.c

@@ -1,5 +1,7 @@
 class JsonFileLoader<Class T>
 {
+	protected static const int READ_FILE_LENGTH = 100000000;
+	
 	protected static ref JsonSerializer m_Serializer = new JsonSerializer();
 	
 	static bool LoadFile(string filename, out T data, out string errorMessage)
@@ -14,12 +16,8 @@ class JsonFileLoader<Class T>
 			}
 			
 			string fileContent;
-			string lineContent;
-			while (FGets(handle, lineContent) >= 0)
-			{
-				fileContent += lineContent;
-			}
-			
+			ReadFile(handle, fileContent, READ_FILE_LENGTH);
+
 			CloseFile(handle);
 			
 			if (!m_Serializer)

+ 6 - 3
Scripts/3_game/tools/uiscriptedmenu.c

@@ -69,6 +69,7 @@ class UIScriptedMenu extends UIMenuPanel
 	private Widget	m_AnimAlphaWidget;
 	private bool	m_AnimAlphaIsIncreasing;
 	private float	m_AnimAlphaValue;
+	private ScriptInvoker m_PlayerDeathInvoker; //DayZPlayer::GetOnDeathStart -> used to keep track of and ensure proper callback handling
 
 	Widget GetLayoutRoot()
 	{
@@ -173,7 +174,8 @@ class UIScriptedMenu extends UIMenuPanel
 		LockControls();
 		if (IsHandlingPlayerDeathEvent() && g_Game && g_Game.GetPlayer())
 		{
-			g_Game.GetPlayer().GetOnDeathStart().Insert(OnPlayerDeath);
+			m_PlayerDeathInvoker = g_Game.GetPlayer().GetOnDeathStart();
+			m_PlayerDeathInvoker.Insert( OnPlayerDeath );
 		}
 	}
 
@@ -181,9 +183,10 @@ class UIScriptedMenu extends UIMenuPanel
 	void OnHide()
 	{
 		UnlockControls();
-		if (IsHandlingPlayerDeathEvent() && g_Game && g_Game.GetPlayer())
+		if (m_PlayerDeathInvoker) // Only ever registered while `IsHandlingPlayerDeathEvent`. Remove callback directly.
 		{
-			g_Game.GetPlayer().GetOnDeathStart().Remove(OnPlayerDeath);
+			m_PlayerDeathInvoker.Remove( OnPlayerDeath, EScriptInvokerRemoveFlags.NONE );
+			m_PlayerDeathInvoker = null;
 		}
 	}
 

+ 2 - 1
Scripts/3_game/undergroundarealoader.c

@@ -39,7 +39,8 @@ class JsonUndergroundAreaTriggerData
 	ref array<float> Size;
 	float  EyeAccommodation;
 	float  InterpolationSpeed;
-	bool UseLinePointFade;	// simple fade between points which are defined using existing breadcrumbs array 
+	bool UseLinePointFade;		// simple fade between points which are defined using existing breadcrumbs array 
+	string AmbientSoundType;	// type of ambient sound which will be played by sound controller
 	
 	ref array<ref JsonUndergroundAreaBreadcrumb> Breadcrumbs;
 	

+ 23 - 0
Scripts/3_game/vehicles/car.c

@@ -146,6 +146,29 @@ class Car extends Transport
 		return shape;
 	}
 	
+	protected bool DetectFlippedUsingWheels(VehicleFlippedContext ctx, bool disallowSide)
+	{
+		if (disallowSide && (vector.Dot(GetDirectionUp(), vector.Up) < 0.7))
+		{
+			// return as "flipped", vehicle isn't pointing enough up to be reasonably certain
+			return true;
+		}
+		
+		int wheelCount = WheelCount();
+		
+		for (int wheelIdx = 0; wheelIdx < wheelCount; wheelIdx++)
+		{
+			if (!WheelHasContact(wheelIdx))
+			{
+				// wheel not in contact, then we could be flipped, we assume there exist other predicates
+				return true;
+			}
+		}
+		
+		// all wheels in contact (or zero registered wheels), then we are in contact
+		return false;
+	}
+	
 //-----------------------------------------------------------------------------
 // controls
 

+ 163 - 7
Scripts/3_game/vehicles/transport.c

@@ -13,12 +13,6 @@ class TransportOwnerState : PawnOwnerState
 
 	proto native void	SetAngularVelocity(vector value);
 	proto native void	GetAngularVelocity(out vector value);
-
-	proto native void	SetPhysicsTimeStamp(int value);
-	proto native int	GetPhysicsTimeStamp();
-	
-	proto native void	SetWaterTime(float value);
-	proto native float	GetWaterTime();
 	
 	proto native void	SetBuoyancySubmerged(float value);
 	proto native float	GetBuoyancySubmerged();
@@ -56,6 +50,9 @@ class Transport extends Pawn
 class Transport extends EntityAI
 #endif
 {
+	//! Shared context across all vehicles for flipping
+	private static ref VehicleFlippedContext m_FlippedContext;
+	
 	ref TIntArray m_SingleUseActions;
 	ref TIntArray m_ContinuousActions;
 	ref TIntArray m_InteractActions;
@@ -201,6 +198,119 @@ class Transport extends EntityAI
 		return ModelToWorld( m_fuelPos );
 	}
 	
+	protected /*sealed*/ VehicleFlippedContext GetFlipContext()
+	{
+		if (!m_FlippedContext)
+		{
+			m_FlippedContext = new VehicleFlippedContext();
+		}
+
+#ifdef DIAG_DEVELOPER
+		m_FlippedContext.Reset(DiagMenu.GetBool(DiagMenuIDs.VEHICLE_DRAW_FLIP_CONTEXT));
+#endif
+		
+		return m_FlippedContext;
+	}
+	
+	protected bool DetectFlippedUsingSurface(VehicleFlippedContext ctx, float angleTolerance)
+	{
+		vector corners[4];
+		GetTightlyPackedCorners(ETransformationAxis.BOTTOM, corners);
+		
+		// compute the average position to find the lowest "center-most" position
+		vector avgPosition = vector.Zero;		
+		for (int i = 0; i < 4; i++)
+		{			
+			avgPosition = avgPosition + corners[i];
+		}
+		
+		avgPosition = avgPosition * 0.25;
+		
+		// get depth of the water to determine if we should use the roadway surface normal or just up vector
+		float depth = GetGame().GetWaterDepth(avgPosition);
+		
+		vector normal = vector.Up;
+		vector dirUp = GetDirectionUp();
+		
+		bool testLand = depth < -1.0;
+		
+		// iterate over the corners to find the average normal
+		if (testLand)
+		{
+			// trace roadway, incase the vehicle is on a rock, or bridge
+			ctx.m_SurfaceParams.type = SurfaceDetectionType.Roadway;
+			
+			// ignore expensive water computation, we already know we are above land
+			ctx.m_SurfaceParams.includeWater = false;
+			
+			// ignore this vehicle, it may have a roadway LOD
+			ctx.m_SurfaceParams.ignore = this;
+			
+			// detect closest to the given point
+			ctx.m_SurfaceParams.rsd = RoadSurfaceDetection.CLOSEST;
+			
+			for (i = 0; i < 4; i++)
+			{
+				ctx.m_SurfaceParams.position = corners[i];
+				
+				GetGame().GetSurface(ctx.m_SurfaceParams, ctx.m_SurfaceResult);
+				
+				corners[i][1] = ctx.m_SurfaceResult.height;
+			}
+			
+			vector d0 = vector.Direction(corners[0], corners[1]);
+			vector d1 = vector.Direction(corners[0], corners[2]);
+			
+			d0.Normalize();
+			d1.Normalize();
+			
+			// cross product the two directions to get the normal vector of the land
+			normal = d0 * d1;
+		}
+				
+		bool isFlipped = vector.Dot(normal, dirUp) < Math.Cos(angleTolerance * Math.DEG2RAD);
+		
+#ifdef DIAG_DEVELOPER
+		if (ctx.IsDebug())
+		{
+			int color = 0xFF00FF00;
+			if (isFlipped)
+				color = 0xFFFF0000;
+			
+			ctx.AddShape(Shape.Create(ShapeType.LINE, color, ShapeFlags.NOZBUFFER, corners[0], corners[0] + normal));
+			ctx.AddShape(Shape.Create(ShapeType.LINE, color, ShapeFlags.NOZBUFFER, corners[1], corners[1] + normal));
+			ctx.AddShape(Shape.Create(ShapeType.LINE, color, ShapeFlags.NOZBUFFER, corners[2], corners[2] + normal));
+			ctx.AddShape(Shape.Create(ShapeType.LINE, color, ShapeFlags.NOZBUFFER, corners[3], corners[3] + normal));
+		}
+#endif
+		
+		return isFlipped;
+	}
+	
+	//! Override based on vehicle implementation (Car, Boat, modded, etc.)
+	protected bool DetectFlipped(VehicleFlippedContext ctx)
+	{
+		return false;
+	}
+	
+	//! Don't override, may change to native for caching 'DetectFlipped' in the future based on active-ness (i.e. only updated when vehicle changes active state)
+	/*sealed*/ bool IsFlipped()
+	{
+		VehicleFlippedContext ctx = GetFlipContext();
+		ctx.m_bIsAction = false;
+		ctx.m_ActionPlayer = null;
+		return DetectFlipped(ctx);
+	}
+	
+	//! Don't override, may change to native for caching 'DetectFlipped' in the future based on active-ness (i.e. only updated when vehicle changes active state)
+	/*sealed*/ bool IsActionFlipped(Man player)
+	{
+		VehicleFlippedContext ctx = GetFlipContext();
+		ctx.m_bIsAction = true;
+		ctx.m_ActionPlayer = player;
+		return DetectFlipped(ctx);
+	}
+	
 	bool IsAnyCrewPresent()
 	{
 		for (int index = 0; index < CrewSize(); ++index)
@@ -457,4 +567,50 @@ class VehicleContactData
 		m_Other		= other;
 		m_Impulse	= impulse;
 	}
-}
+};
+
+class VehicleFlippedContext
+{
+	bool m_bIsAction = false;
+	Man m_ActionPlayer;
+	
+	ref SurfaceDetectionParameters m_SurfaceParams = new SurfaceDetectionParameters();
+	ref SurfaceDetectionResult m_SurfaceResult = new SurfaceDetectionResult();
+	
+#ifdef DIAG_DEVELOPER	
+	private ref array<Shape> m_DebugShapes;
+	private bool m_bIsDebug;
+
+	void VehicleFlippedContext()
+	{
+		m_DebugShapes = new array<Shape>();
+	}
+	
+	void ~VehicleFlippedContext()
+	{
+		Reset();
+	}
+	
+	void Reset(bool isDebug = false)
+	{
+		foreach (Shape shape : m_DebugShapes)
+		{
+			shape.Destroy();
+		}
+		
+		m_DebugShapes.Clear();
+		
+		m_bIsDebug = isDebug;
+	}
+	
+	void AddShape(Shape shape)
+	{
+		m_DebugShapes.Insert(shape);
+	}
+	
+	bool IsDebug()
+	{
+		return m_bIsDebug;
+	}
+#endif
+};

+ 61 - 4
Scripts/3_game/worlddata.c

@@ -7,8 +7,8 @@ class WorldData
 	//! directly accesible (defined/overriden in Init())
 	float m_TemperaturePerHeightReductionModifier; 	//! amount of °C reduced for each 100 meteres of height above water level
 	float m_CloudsTemperatureEffectModifier; 		//! how many % of environment temperature can be lowered by clouds
-	float m_TemperatureInsideBuildingsModifier
-	float m_WaterContactTemperatureModifier
+	float m_TemperatureInsideBuildingsModifier;
+	float m_WaterContactTemperatureModifier;
 
 	protected float SUDDENCHANGE_TIME_MULTIPLIER 	= 0.2;
 	protected float SUDDENCHANGE_LENGTH_MULTIPLIER 	= 0.4;
@@ -46,8 +46,7 @@ class WorldData
 	protected int m_Chance = 50;
 	protected int m_ChoosenWeather = 1;
 	protected int m_LastWeather = 0;
-	
-	
+
 	void WorldData()
 	{
 		Init();
@@ -294,6 +293,64 @@ class WorldData
 		return m_UniversalTemperatureSourceCapModifier;
 	}
 	
+	/*!
+		\brief Return actual temperature of environment based on provided parameters
+		\param object Reference to object that is used mainly for sea height related calculation
+		\param properties Flag made of EEnvironmentTemperatureComponent which will influence the resulting value of temperature based on combination of the parts
+	*/	
+	float GetTemperature(Object object, EEnvironmentTemperatureComponent properties = EEnvironmentTemperatureComponent.BASE)
+	{
+		// EEnvironmentTemperatureComponent.BASE only
+		float temperature = GetBaseEnvTemperature();
+
+		if (object && properties & EEnvironmentTemperatureComponent.ALTITUDE)
+			temperature = GetBaseEnvTemperatureAtObject(object);
+		
+		if (properties & EEnvironmentTemperatureComponent.OVERCAST)
+			temperature += m_Weather.GetOvercast().GetActual() * m_CloudsTemperatureEffectModifier;
+
+		if (properties & EEnvironmentTemperatureComponent.WIND)
+			temperature += WindEffectTemperatureValue(temperature);
+		
+		if (properties & EEnvironmentTemperatureComponent.FOG)
+			temperature += m_Weather.GetFog().GetActual() * GameConstants.ENVIRO_FOG_TEMP_EFFECT;
+
+		return temperature;
+	}
+
+	/*!
+		\brief Return value of queried EEnvironmentTemperatureComponent which can be used in future calculation(s)
+		\param temperature Base temperature which will be used in component calculation (currently WIND only)
+		\param properties Flag made of EEnvironmentTemperatureComponent which will influence the resulting value of temperature based on combination of the parts
+	*/	
+	float GetTemperatureComponentValue(float temperatureIn, EEnvironmentTemperatureComponent properties = 0)
+	{
+		float temperatureOut = 0.0;
+
+		if ((properties & EEnvironmentTemperatureComponent.OVERCAST) == EEnvironmentTemperatureComponent.OVERCAST)
+			temperatureOut = m_Weather.GetOvercast().GetActual() * m_CloudsTemperatureEffectModifier;
+		else if ((properties & EEnvironmentTemperatureComponent.WIND) == EEnvironmentTemperatureComponent.WIND)
+			temperatureOut = WindEffectTemperatureValue(temperatureIn);
+		else if ((properties & EEnvironmentTemperatureComponent.FOG) == EEnvironmentTemperatureComponent.FOG)
+			temperatureOut = m_Weather.GetFog().GetActual() * GameConstants.ENVIRO_FOG_TEMP_EFFECT;
+		else
+		{
+			Debug.Log(string.Format("Only OVERCAST, WIND and FOG parameters are supported"));
+		}
+
+		return temperatureOut;
+	}
+	
+	protected float WindEffectTemperatureValue(float temperatureInput)
+	{
+		float temperatureOutput = 0.0;
+
+		temperatureOutput = (temperatureInput - GameConstants.ENVIRO_WIND_CHILL_LIMIT) / (GameConstants.ENVIRO_WIND_EFFECT_SLOPE - GameConstants.ENVIRO_WIND_CHILL_LIMIT);
+		temperatureOutput = temperatureOutput * m_Weather.GetWindMagnitude().GetActual() * GetWindCoef();
+		
+		return -temperatureOutput;
+	}
+	
 	protected void CalculateWind(int newWeather, bool suddenChange, out float magnitude, out float direction);
 	
 	protected void CalculateVolFog(float lerpValue, float windMagnitude, float changeTime);

+ 1 - 1
Scripts/4_world/classes/areadamage/areadamage.c

@@ -28,7 +28,7 @@ class AreaDamageBase : AreaDamageManager
 		m_OthersDamage		= 0.0;
 		
 		m_AmmoName			= "MeleeDamage";
-		m_DamageType 		= DT_CUSTOM;
+		m_DamageType 		= DamageType.CUSTOM;
 		
 		m_LoopInterval 		= 1.0;
 		m_DeferDuration		= 1.0;

+ 1 - 1
Scripts/4_world/classes/areadamage/areadamagenew/damagecomponents/areadamagecomponent.c

@@ -17,7 +17,7 @@ class AreaDamageComponent : AreaDamageEvents
 	{
 		m_Parent = parent;
 		
-		m_DamageType 		= DT_CUSTOM;
+		m_DamageType 		= DamageType.CUSTOM;
 		m_AmmoName			= "MeleeDamage";
 
 		m_DamageableTypes	= new array<typename>;

+ 1 - 1
Scripts/4_world/classes/arrowmanager/arrowmanagerplayer.c

@@ -2,7 +2,7 @@ class ArrowManagerPlayer : ArrowManagerBase
 {	
 	private static ref map<int,typename> m_TypeHashTable;
 	
-	void ArrowManagerPlayer(PlayerBase player)
+	void ArrowManagerPlayer(EntityAI owner)
 	{
 		if (!m_TypeHashTable)
 		{

+ 23 - 6
Scripts/4_world/classes/basebuilding/construction.c

@@ -476,7 +476,6 @@ class Construction
 	}
 	
 	//checks if construction part has dependent part (that is already built) because of which it cannot be deconstruct
-	//TODO return whole array of dependent parts/dependencies (one or the other), should be used to eventually destroy all dependent parts instead
 	bool HasDependentPart( string part_name )
 	{
 		for ( int i = 0; i < m_ConstructionParts.Count(); ++i )
@@ -840,7 +839,7 @@ class Construction
 								InventoryLocation dst = new InventoryLocation;
 								vector mat[4];
 								attachment.GetTransform(mat);
-								
+								//TODO: why are we spawning and deleting here, instead of moving to location??
 								if ( parent.MemoryPointExists("" + part_name + "_materials") )
 								{
 									vector destination = parent.GetMemoryPointPos("" + part_name + "_materials");
@@ -852,8 +851,17 @@ class Construction
 									float dir[4];
 									inventory_location.GetDir(dir);
 									dst.SetGroundEx(attachment,destination,dir);
-									//Print(dst.DumpToString());
-									MiscGameplayFunctions.CreateItemBasePiles(attachment.GetType(),destination,quantity,health,true);
+									
+									if (player)
+									{
+										vector posHead;
+										MiscGameplayFunctions.GetHeadBonePos(PlayerBase.Cast(player),posHead);
+										MiscGameplayFunctions.CreateItemBasePilesDispersed(attachment.GetType(),posHead,destination,UAItemsSpreadRadius.NARROW,quantity,health,player);
+									}
+									else
+									{
+										MiscGameplayFunctions.CreateItemBasePiles(attachment.GetType(),destination,quantity,health,true);
+									}
 									attachment.AddQuantity( -quantity );
 								}
 								else
@@ -1290,12 +1298,21 @@ class StaticConstructionMethods
 					destination = entity.GetMemoryPointPos(main_part_name);
 					destination = GetGame().ObjectModelToWorld(entity,destination);
 				}
-				//pile_health = GameConstants.DAMAGE_WORN_VALUE * MiscGameplayFunctions.GetTypeMaxGlobalHealth(type);
 				pile_health = entity.GetHealth01(damagezone_name,"Health") * MiscGameplayFunctions.GetTypeMaxGlobalHealth(type);
 				qty_coef =  1 - (entity.GetHealthLevel(damagezone_name) * Construction.DECONSTURCT_MATERIAL_LOSS) - Construction.DECONSTURCT_MATERIAL_LOSS;
 				quantity *= qty_coef;
 				quantity = Math.Max(Math.Floor(quantity),1);
-				MiscGameplayFunctions.CreateItemBasePiles(type,destination,quantity,pile_health,true);
+				
+				if (player)
+				{
+					vector posHead;
+					MiscGameplayFunctions.GetHeadBonePos(PlayerBase.Cast(player),posHead);
+					MiscGameplayFunctions.CreateItemBasePilesDispersed(type,posHead,destination,UAItemsSpreadRadius.NARROW,quantity,pile_health,player);
+				}
+				else
+				{
+					MiscGameplayFunctions.CreateItemBasePiles(type,destination,quantity,pile_health,true);
+				}
 			}
 		}
 	}

+ 16 - 0
Scripts/4_world/classes/basebuilding/constructionactiondata.c

@@ -46,6 +46,22 @@ class ConstructionActionData
 		}
 	}
 	
+	void ~ConstructionActionData()
+	{
+		if (GetGame() && (GetGame().IsClient() || !GetGame().IsMultiplayer()))
+		{
+			if (m_ActionVariantManager)
+			{
+				m_ActionVariantManager.GetOnUpdateInvoker().Remove(OnUpdateActions);
+			}
+			
+			if (m_ActionNoToolVariantManager)
+			{
+				m_ActionNoToolVariantManager.GetOnUpdateInvoker().Remove(OnUpdateActionsNoTool);
+			}
+		}
+	}
+	
 	//************************************************/
 	//  Base building
 	//************************************************/

+ 5 - 5
Scripts/4_world/classes/bleedingsources/bleedingsourcesmanagerbase.c

@@ -21,14 +21,14 @@ class BleedingSourcesManagerBase
 	protected void Init()
 	{
 		//dmgZone_head
-		RegisterBleedingZoneEx("Head",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "0 90 0" , "0 0 0", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_HIGH, "BleedingSourceEffect",InventorySlots.MASK);
+		RegisterBleedingZoneEx("Head",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "95 90 0" , "0.15 -0.08 0.00", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_HIGH, "BleedingSourceEffect",InventorySlots.MASK);
 		//dmgZone_torso
-		RegisterBleedingZoneEx("Neck", PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL,"", "-180 0 0" , "0.02 -0.05 0.05", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_HIGH, "BleedingSourceEffect",InventorySlots.HEADGEAR);
+		RegisterBleedingZoneEx("Neck", PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL,"", "-130 70 0" , "0.02 -0.05 0.05", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_HIGH, "BleedingSourceEffect",InventorySlots.HEADGEAR);
 		RegisterBleedingZoneEx("Pelvis",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "0 -135 0" , "0 0.12 0", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_HIGH, "BleedingSourceEffect",InventorySlots.BODY);
 		RegisterBleedingZoneEx("Spine",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "0 55 0" , "0 -0.1 0", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_HIGH, "BleedingSourceEffect",InventorySlots.BODY);
 		RegisterBleedingZoneEx("Spine1",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "0 155 0" , "0 -0.1 0", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_HIGH, "BleedingSourceEffect",InventorySlots.BODY);
 		RegisterBleedingZoneEx("Spine2",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "0 55 0" , "0 -0.07 0", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_HIGH, "BleedingSourceEffect",InventorySlots.BODY);
-		RegisterBleedingZoneEx("Spine3",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "0 155 0" , "0 -0.05 0", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_HIGH, "BleedingSourceEffect",InventorySlots.BODY);
+		RegisterBleedingZoneEx("Spine3",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "0 90 0" , "0 -0.05 0", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_HIGH, "BleedingSourceEffect",InventorySlots.BODY);
 		//dmgZone_leftArm
 		RegisterBleedingZoneEx("LeftShoulder",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "0 -90 0" , "0 0.07 0", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_MEDIUM, "BleedingSourceEffect", InventorySlots.BODY);
 		RegisterBleedingZoneEx("LeftArm", PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "","0 90 90" , "0 -0.05 0", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_MEDIUM, "BleedingSourceEffect",InventorySlots.BODY);
@@ -47,12 +47,12 @@ class BleedingSourcesManagerBase
 		RegisterBleedingZoneEx("LeftLeg",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "0 90 0" , "0 -0.07 0", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_MEDIUM, "BleedingSourceEffect",InventorySlots.LEGS);
 		RegisterBleedingZoneEx("LeftLegRoll",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "0 90 0" , "0 -0.07 0", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_MEDIUM, "BleedingSourceEffect",InventorySlots.LEGS);
 		RegisterBleedingZoneEx("LeftUpLeg",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "0 90 0" , "0 -0.12 0", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_MEDIUM, "BleedingSourceEffect",InventorySlots.LEGS);
-		RegisterBleedingZoneEx("LeftUpLegRoll",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "0 90 0" , "0 -0.07 0", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_MEDIUM , "BleedingSourceEffect",InventorySlots.LEGS);
+		RegisterBleedingZoneEx("LeftUpLegRoll",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "0 -90 0" , "0 0.1 0", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_MEDIUM , "BleedingSourceEffect",InventorySlots.LEGS);
 		//dmgZone_rightLeg
 		RegisterBleedingZoneEx("RightLeg",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "0 -90 0" , "0 0.06 0", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_MEDIUM, "BleedingSourceEffect",InventorySlots.LEGS);
 		RegisterBleedingZoneEx("RightLegRoll",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "0 -90 0" , "0 0 0", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_MEDIUM, "BleedingSourceEffect",InventorySlots.LEGS);
 		RegisterBleedingZoneEx("RightUpLeg",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "0 -90 0" , "0 0.12 0", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_MEDIUM, "BleedingSourceEffect",InventorySlots.LEGS);
-		RegisterBleedingZoneEx("RightUpLegRoll",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "0 -90 0" , "0 0.06 0", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_MEDIUM, "BleedingSourceEffect",InventorySlots.LEGS);
+		RegisterBleedingZoneEx("RightUpLegRoll",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "0 90 0" , "0 -0.1 0", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_MEDIUM, "BleedingSourceEffect",InventorySlots.LEGS);
 		//dmgZone_leftFoot
 		RegisterBleedingZoneEx("LeftFoot",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "0 180 0" , "0 0 0.035", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_LOW, "BleedingSourceEffectLight", InventorySlots.LEGS);
 		RegisterBleedingZoneEx("LeftToeBase",PlayerConstants.BLEEDING_SOURCE_DURATION_NORMAL, "", "0 -90 0" , "0 0.07 0", PlayerConstants.BLEEDING_SOURCE_FLOW_MODIFIER_LOW, "BleedingSourceEffect",InventorySlots.FEET);

+ 12 - 0
Scripts/4_world/classes/consumeconditiondata.c

@@ -0,0 +1,12 @@
+class ConsumeConditionData
+{
+	EntityAI m_Consumer;
+	ItemBase m_ConsumedItem;
+	//int m_ConsumptionType = -1;
+	
+	void ConsumeConditionData(EntityAI consumer, ItemBase item)
+	{
+		m_Consumer = consumer;
+		m_ConsumedItem = item;
+	}
+}

+ 120 - 126
Scripts/4_world/classes/cooking/cooking.c

@@ -23,8 +23,11 @@ class Cooking
 	static const float FOOD_MAX_COOKING_TEMPERATURE					= 150;	//
 	static const float PARAM_BURN_DAMAGE_COEF						= 0.05;	//value for calculating damage on items located in fireplace CargoGrid
 
-	static const float LIQUID_BOILING_POINT 						= 150;	//boiling point for liquids
-	static const float LIQUID_VAPOR_QUANTITY 						= 2;	//vapor quantity
+	static const float LIQUID_BOILING_POINT 						= 150;	//default boiling point for liquids, overriden by 'liquidBoilingThreshold' in cfgLiquidDefinitions
+	static const float LIQUID_VAPOR_QUANTITY 						= 2;	//vaporization quantity loss
+	static const float SOLID_OVERHEAT_QUANTITY 						= 2;	//solid overheat quantity loss
+	
+	static const float BURNING_WARNING_THRESHOLD 					= 0.75; //! 0..1, validly cooked item will pre-emptively start emitting burning sounds when this close to being burned
 
 	typename COOKING_EQUIPMENT_POT	 					= Pot;
 	typename COOKING_EQUIPMENT_FRYINGPAN				= FryingPan;
@@ -41,10 +44,6 @@ class Cooking
 	void ProcessItemToCook(notnull ItemBase pItem, ItemBase cookingEquip, Param2<CookingMethodType, float> pCookingMethod, out Param2<bool, bool> pStateFlags)
 	{
 		Edible_Base item_to_cook = Edible_Base.Cast(pItem);
-		
-		//! state flags are in order: is_done, is_burned
-		pStateFlags = new Param2<bool, bool>(false, false);
-		
 		if (item_to_cook && item_to_cook.CanBeCooked())
 		{
 			//! update food
@@ -53,44 +52,62 @@ class Cooking
 			//check for done state for boiling and drying
 			if (item_to_cook.IsFoodBoiled() || item_to_cook.IsFoodDried())
 			{
-				pStateFlags.param1 = true;
+				pStateFlags.param1 |= true;
 			}
 			//! check for done state from baking (exclude Lard from baked items)
 			else if (item_to_cook.IsFoodBaked() && item_to_cook.Type() != Lard)
 			{
-				pStateFlags.param1 = true;
+				pStateFlags.param1 |= true;
 			}
 			//! check for burned state
 			else if (item_to_cook.IsFoodBurned())
 			{
-				pStateFlags.param2 = true;
+				pStateFlags.param2 |= true;
 			}
 		}
 		else
 		{
 			//add temperature to item
-			if (pItem != cookingEquip) //already handled by the fireplace directly!
+			if (pItem != cookingEquip) //1st order item already handled by the fireplace directly!
 				AddTemperatureToItem(pItem, null, 0);
 			
-			//damage item that can actually overheat?
+			int liquidType = pItem.GetLiquidType();
+			bool handleLiquid = pItem.IsLiquidContainer() && liquidType != LIQUID_NONE;
+			bool isLiquiBoiling = handleLiquid && pItem.GetTemperature() >= Liquid.GetBoilThreshold(liquidType);
+			
+			//handle items that can actually overheat
 			if (pItem.CanItemOverheat())
 			{
-				if (pItem.IsItemOverheated())
+				//handle qty first
+				if (pItem.HasQuantity() && pItem.GetQuantityNormalized() > 0)
 				{
-					if (pItem.HasQuantity() && pItem.GetQuantityNormalized() > 0)
+					if (handleLiquid)
 					{
-						pItem.AddQuantity(-LIQUID_VAPOR_QUANTITY,!pItem.IsLiquidContainer()); //TODO: use other constant here, or calculate from qtyMax..
+						if (pItem.IsItemOverheated() || isLiquiBoiling) //overheat causes qty loss here!
+							pItem.AddQuantity(-LIQUID_VAPOR_QUANTITY,false);
 					}
-					else
+					else if (pItem.IsItemOverheated())
 					{
-						pItem.DecreaseHealth(PARAM_BURN_DAMAGE_COEF * 100);
+						pItem.AddQuantity(-SOLID_OVERHEAT_QUANTITY,true);
 					}
+				}//next handle damage
+				else if (!pItem.IsCookware() && pItem.IsItemOverheated()) //cookware already damaged by fireplace, skipping
+				{
+					pItem.DecreaseHealth(PARAM_BURN_DAMAGE_COEF * 100);
 				}
 			}
 			else
 			{
-				pItem.DecreaseHealth(PARAM_BURN_DAMAGE_COEF * 100); //pItem.IsKindOf("Grenade_Base")
+				if (!pItem.IsCookware()) //cookware already damaged by fireplace, skipping
+					pItem.DecreaseHealth(PARAM_BURN_DAMAGE_COEF * 100);
+				
+				if (isLiquiBoiling)
+					pItem.AddQuantity(-LIQUID_VAPOR_QUANTITY,false);
 			}
+			
+			//last handle agents
+			if (isLiquiBoiling)
+				pItem.RemoveAllAgentsExcept(eAgents.HEAVYMETAL);
 		}
 	}
 
@@ -99,73 +116,65 @@ class Cooking
 	//Returns 1 if the item changed its cooking stage, 0 if not
 	int CookWithEquipment(ItemBase cooking_equipment, float cooking_time_coef = 1)
 	{
-		bool is_empty;
+		bool is_empty = true;
 		
 		//check cooking conditions
 		if (cooking_equipment == null)
-		{
 			return 0;
-		}
 		
 		if (cooking_equipment.IsRuined())
-		{
 			return 0;
-		}
 		
 		//manage items in cooking equipment
 		Param2<bool, bool> stateFlags = new Param2<bool, bool>(false, false); // 1st - done; 2nd - burned
 		Param2<CookingMethodType, float> cookingMethodWithTime = GetCookingMethodWithTimeOverride(cooking_equipment);
-		
-		//! cooking time coef override
 		if (cooking_time_coef != 1)
-		{
 			cookingMethodWithTime.param2 = cooking_time_coef;
+		
+		//handle the cooking equipment/direct cooking first
+		ProcessItemToCook(cooking_equipment, cooking_equipment, cookingMethodWithTime, stateFlags);
+		
+		//cooking method may have changed due to liquid evaporating, refresh..
+		if (cooking_equipment.IsCookware() && cooking_equipment.IsLiquidContainer())
+		{
+			cookingMethodWithTime = GetCookingMethodWithTimeOverride(cooking_equipment);
+			if (cooking_time_coef != 1)
+				cookingMethodWithTime.param2 = cooking_time_coef;
 		}
 		
+		//handle the cooking inside of a container last
 		CargoBase cargo = cooking_equipment.GetInventory().GetCargo();
 		if (cargo)
 		{
-			is_empty = cargo.GetItemCount() == 0;
+			int count = cargo.GetItemCount();
+			is_empty = count == 0;
 			
 			//process items
-			for (int i = 0; i < cargo.GetItemCount(); i++)
+			for (int i = 0; i < count; i++)
 			{
 				ProcessItemToCook(ItemBase.Cast(cargo.GetItem(i)), cooking_equipment, cookingMethodWithTime, stateFlags);
 			}
 		}
-		else
-		{
-			ProcessItemToCook(cooking_equipment, cooking_equipment, cookingMethodWithTime, stateFlags);
-		}
 		
-		//manage cooking equipment
-		Bottle_Base bottle_base = Bottle_Base.Cast(cooking_equipment);
-		if (bottle_base)
+		//manage equipment EFFECTS
+		int liquidType = cooking_equipment.GetLiquidType();
+		//handle liquid boiling EFFECTS
+		if (cooking_equipment.IsLiquidContainer() && liquidType != LIQUID_NONE)
 		{
-			float cookingEquipmentTemp = bottle_base.GetTemperature();
-			int liquidType = bottle_base.GetLiquidType();
-			
-			//handle water boiling
-			if (liquidType != LIQUID_NONE && cookingEquipmentTemp >= Liquid.GetBoilThreshold(liquidType))
+			if (cooking_equipment.GetTemperature() >= Liquid.GetBoilThreshold(liquidType))
 			{
-				//remove agents
-				bottle_base.RemoveAllAgentsExcept(eAgents.HEAVYMETAL);
-				
-				//vaporize liquid
-				if (bottle_base.IsItemOverheated())
-					bottle_base.AddQuantity(-LIQUID_VAPOR_QUANTITY);
+				//handle boiling audiovisuals for any liquid container
+				cooking_equipment.RefreshAudioVisualsOnClient(cookingMethodWithTime.param1, stateFlags.param1, is_empty, stateFlags.param2);
+			}
+			else
+			{
+				cooking_equipment.RemoveAudioVisualsOnClient();
 			}
-			
-			//handle audio visuals
-			if (bottle_base.Type() == COOKING_EQUIPMENT_POT || bottle_base.Type() == COOKING_EQUIPMENT_CAULDRON)
-				bottle_base.RefreshAudioVisualsOnClient(cookingMethodWithTime.param1, stateFlags.param1, is_empty, stateFlags.param2);
 		}
-		
-		FryingPan frying_pan = FryingPan.Cast(cooking_equipment);
-		if (frying_pan && !bottle_base)
+		else if (cooking_equipment.IsCookware())
 		{
-			//handle audio visuals
-			frying_pan.RefreshAudioVisualsOnClient(cookingMethodWithTime.param1, stateFlags.param1, is_empty, stateFlags.param2);
+			//handle non-boiling audiovisuals for cookware only
+			cooking_equipment.RefreshAudioVisualsOnClient(cookingMethodWithTime.param1, stateFlags.param1, is_empty, stateFlags.param2);
 		}
 		
 		return 1;
@@ -417,82 +426,41 @@ class Cooking
 		
 		return null;
 	}
-
-	//! DEPREACTED
-	protected CookingMethodType GetCookingMethod(ItemBase cooking_equipment)
+	
+	protected Param2<CookingMethodType, float> GetCookingMethodWithTimeOverride(ItemBase cooking_equipment)
 	{
-		if (cooking_equipment.Type() == COOKING_EQUIPMENT_POT || cooking_equipment.Type() == COOKING_EQUIPMENT_CAULDRON)
+		if (cooking_equipment.IsCookware())
 		{
-			//has water, but not petrol dammit X)
-			if (cooking_equipment.GetQuantity() > 0 && cooking_equipment.GetLiquidType() != LIQUID_GASOLINE)
+			if (cooking_equipment.GetQuantity() > 0)
 			{
-				return CookingMethodType.BOILING;
+				if (cooking_equipment.GetLiquidType() == LIQUID_GASOLINE)
+				{
+					//! when cooking in gasoline, jump to drying state(will be burnt then)
+					return new Param2<CookingMethodType, float>(CookingMethodType.DRYING, TIME_WITHOUT_SUPPORT_MATERIAL_COEF);
+				}
+
+				return new Param2<CookingMethodType, float>(CookingMethodType.BOILING, TIME_WITH_SUPPORT_MATERIAL_COEF);
 			}
 			
-			//has lard in cargo
 			if (GetItemTypeFromCargo(COOKING_INGREDIENT_LARD, cooking_equipment))
 			{
-				return CookingMethodType.BAKING;
+				//has lard in cargo, slower process
+				return new Param2<CookingMethodType, float>(CookingMethodType.BAKING, TIME_WITH_SUPPORT_MATERIAL_COEF);
 			}
-			return CookingMethodType.DRYING;
-		}
-		
-		if (cooking_equipment.Type() == COOKING_EQUIPMENT_FRYINGPAN)
-		{
-			if (GetItemTypeFromCargo(COOKING_INGREDIENT_LARD, cooking_equipment))
+
+			if (cooking_equipment.GetInventory().GetCargo().GetItemCount() > 0)
 			{
-				return CookingMethodType.BAKING;
+				return new Param2<CookingMethodType, float>(CookingMethodType.BAKING, TIME_WITHOUT_SUPPORT_MATERIAL_COEF);
 			}
-			return CookingMethodType.DRYING;
+		
+			return new Param2<CookingMethodType, float>(CookingMethodType.NONE, TIME_WITHOUT_SUPPORT_MATERIAL_COEF);
 		}
-
-		return CookingMethodType.NONE;
-	}
-	
-	protected Param2<CookingMethodType, float> GetCookingMethodWithTimeOverride(ItemBase cooking_equipment)
-	{
-		Param2<CookingMethodType, float> val = new Param2<CookingMethodType, float>(CookingMethodType.NONE, TIME_WITH_SUPPORT_MATERIAL_COEF);
-
-		switch (cooking_equipment.Type())
+		else if (cooking_equipment.IsLiquidContainer() && cooking_equipment.GetQuantity() > 0 && cooking_equipment.GetLiquidType() != LIQUID_GASOLINE) //fake 'boiling' on liquid containers, for effects playback
 		{
-			case COOKING_EQUIPMENT_POT:
-			case COOKING_EQUIPMENT_CAULDRON:
-			case COOKING_EQUIPMENT_FRYINGPAN:
-				if (cooking_equipment.GetQuantity() > 0)
-				{
-					if (cooking_equipment.GetLiquidType() == LIQUID_GASOLINE)
-					{
-						//! when cooking in gasoline, jump to drying state(will be burnt then)
-						val = new Param2<CookingMethodType, float>(CookingMethodType.DRYING, TIME_WITHOUT_SUPPORT_MATERIAL_COEF);
-						break;
-					}
-	
-					val = new Param2<CookingMethodType, float>(CookingMethodType.BOILING, TIME_WITH_SUPPORT_MATERIAL_COEF);
-					break;
-				}
-				
-				if (GetItemTypeFromCargo(COOKING_INGREDIENT_LARD, cooking_equipment))
-				{
-					//has lard in cargo, slower process
-					val = new Param2<CookingMethodType, float>(CookingMethodType.BAKING, TIME_WITH_SUPPORT_MATERIAL_COEF);
-					break;
-				}
-
-				if (cooking_equipment.GetInventory().GetCargo().GetItemCount() > 0)
-				{
-					val = new Param2<CookingMethodType, float>(CookingMethodType.BAKING, TIME_WITHOUT_SUPPORT_MATERIAL_COEF);
-					break;
-				}
-				
-				val = new Param2<CookingMethodType, float>(CookingMethodType.NONE, TIME_WITHOUT_SUPPORT_MATERIAL_COEF);
-				break;
-			
-			default:
-				val = new Param2<CookingMethodType, float>(CookingMethodType.BAKING, TIME_WITHOUT_SUPPORT_MATERIAL_COEF);
-				break;
+			return new Param2<CookingMethodType, float>(CookingMethodType.BOILING, TIME_WITH_SUPPORT_MATERIAL_COEF);
 		}
-
-		return val;
+		
+		return new Param2<CookingMethodType, float>(CookingMethodType.BAKING, TIME_WITHOUT_SUPPORT_MATERIAL_COEF);
 	}
 	
 	Edible_Base GetFoodOnStick( ItemBase stick_item )
@@ -537,20 +505,13 @@ class Cooking
 			}
 			
 			//adjust temperature
-			if (targetTemp != itemTemp)
+			if (targetTemp != itemTemp || !cooked_item.IsFreezeThawProgressFinished())
 			{
 				float heatPermCoef = 1.0;
 				if (cooking_equipment)
 					heatPermCoef = cooking_equipment.GetHeatPermeabilityCoef();
 				heatPermCoef *= cooked_item.GetHeatPermeabilityCoef();
-				float tempCoef;
-				
-				if (itemTemp < min_temperature && targetTemp > itemTemp) //heating 'catch-up' only
-					tempCoef = GameConstants.TEMP_COEF_COOKING_CATCHUP;
-				else
-					tempCoef = GameConstants.TEMP_COEF_COOKING_DEFAULT;
-				
-				cooked_item.SetTemperatureEx(new TemperatureDataInterpolated(targetTemp,ETemperatureAccessTypes.ACCESS_COOKING,m_UpdateTime,tempCoef,heatPermCoef));
+				cooked_item.SetTemperatureEx(new TemperatureDataInterpolated(targetTemp,ETemperatureAccessTypes.ACCESS_COOKING,m_UpdateTime,GameConstants.TEMP_COEF_COOKING_DEFAULT,heatPermCoef));
 			}
 		}
 	}
@@ -564,4 +525,37 @@ class Cooking
 			pItem.SetQuantity(quantity);
 		}
 	}
+	
+	////////////////////////////
+	//DEPRECATED cooking stuff
+	//! DEPRECATED
+	protected CookingMethodType GetCookingMethod(ItemBase cooking_equipment)
+	{
+		if (cooking_equipment.Type() == COOKING_EQUIPMENT_POT || cooking_equipment.Type() == COOKING_EQUIPMENT_CAULDRON)
+		{
+			//has water, but not petrol dammit X)
+			if (cooking_equipment.GetQuantity() > 0 && cooking_equipment.GetLiquidType() != LIQUID_GASOLINE)
+			{
+				return CookingMethodType.BOILING;
+			}
+			
+			//has lard in cargo
+			if (GetItemTypeFromCargo(COOKING_INGREDIENT_LARD, cooking_equipment))
+			{
+				return CookingMethodType.BAKING;
+			}
+			return CookingMethodType.DRYING;
+		}
+		
+		if (cooking_equipment.Type() == COOKING_EQUIPMENT_FRYINGPAN)
+		{
+			if (GetItemTypeFromCargo(COOKING_INGREDIENT_LARD, cooking_equipment))
+			{
+				return CookingMethodType.BAKING;
+			}
+			return CookingMethodType.DRYING;
+		}
+
+		return CookingMethodType.NONE;
+	}
 }

+ 8 - 0
Scripts/4_world/classes/craftingmanager.c

@@ -27,6 +27,14 @@ class CraftingManager
 		m_actionVariantManager.GetOnUpdateInvoker().Insert(OnUpdate);
 		m_recipes = new array<int>;
 	}
+	
+	void ~CraftingManager()
+	{
+		if (m_actionVariantManager)
+		{
+			m_actionVariantManager.GetOnUpdateInvoker().Remove(OnUpdate);
+		}
+	}
 
 	void SetRecipeID(int recipeID)
 	{

+ 6 - 0
Scripts/4_world/classes/emoteclasses/emoteclasses.c

@@ -434,6 +434,12 @@ class EmoteSurrender extends EmoteBase
 		m_HideItemInHands = false;
 	}
 	
+	override bool EmoteCondition(int stancemask)
+	{
+		Transport transportEnt; //standing on a Transport-type object...
+		return !m_Player.PhysicsGetLinkedEntity() && !Class.CastTo(transportEnt,m_Player.PhysicsGetFloorEntity());
+	}
+	
 	override bool DetermineOverride(out int callback_ID, out int stancemask, out bool is_fullbody)
 	{
 		stancemask = DayZPlayerConstants.STANCEMASK_ALL;

+ 11 - 3
Scripts/4_world/classes/emotemanager.c

@@ -590,15 +590,17 @@ class EmoteManager
 		
 		if (CanPlayEmote(id))
 		{
+			EmoteBase emote;
+			m_NameEmoteMap.Find(id,emote);
+			
 			if (m_AdminLog)
-				m_AdminLog.LogPrint("[emote] " + Object.GetDebugName(m_Player) + " play emote id=" + id + " IH=" + Object.GetDebugName(m_Player.GetItemInHands()));
+				m_AdminLog.OnEmote(m_Player, emote);
 			
 			m_PreviousGestureID = m_CurrentGestureID;
 			m_CurrentGestureID = id;
 			if (id > 0)
 			{
-				EmoteBase emote;
-				if (m_NameEmoteMap.Find(id,emote))
+				if (emote)
 				{
 					int callback_ID;
 					int stancemask;
@@ -674,6 +676,12 @@ class EmoteManager
 		}
 	}
 	
+	void RequestCommitSuicide()
+	{
+		if (!GetGame().IsClient())
+			CommitSuicide();
+	}
+	
 	protected void CommitSuicide()
 	{
 		Weapon_Base weapon;

+ 170 - 77
Scripts/4_world/classes/environment/environment.c

@@ -6,7 +6,8 @@ enum EEnvironmentHeatcomfortBehaviorCategory
 
 class EnvironmentSnapshotData
 {
-	float m_TargetHeatComfort = 0.0;
+	float m_ClothingHeatComfort;
+	float m_TargetHeatComfort;
 }
 
 class Environment
@@ -57,6 +58,7 @@ class Environment
 	protected bool					m_IsTempSet;
 	//
 	protected float 				m_HeatBufferTimer; //! reused as state toggle
+	protected float 				m_HeatBufferCapPrevious;
 	
 	protected ref array<int> 		m_SlotIdsComplete;
 	protected ref array<int> 		m_SlotIdsUpper;
@@ -67,7 +69,7 @@ class Environment
 	protected ref array<int>		m_BodyParts;
 	protected ref array<int>		m_FeetParts;
 	
-	protected ref WorldData 		m_WorldData;
+	protected WorldData 			m_WorldData;
 
 	protected bool m_HasTemperatureSources;
 	protected float m_UTSAverageTemperature;
@@ -77,8 +79,6 @@ class Environment
 	
 	protected int m_HeatComfortBehaviorCategory;
 	
-	protected ref EnvironmentSnapshotData m_EnvironmentSnapshot; //! used for calculations before the data modification
-	
 	private bool m_Initialized;
 	
 	#ifdef DIAG_DEVELOPER
@@ -179,9 +179,8 @@ class Environment
 			InventorySlots.FEET,
 		};
 		
-		m_HeatComfortBehaviorCategory = EEnvironmentHeatcomfortBehaviorCategory.DEFAULT;
-		
-		SetEnvironmentSnapshotData();
+		m_HeatComfortBehaviorCategory 	= EEnvironmentHeatcomfortBehaviorCategory.DEFAULT;
+		m_EnvironmentSnapshot 			= new EnvironmentSnapshotData();
 		
 		m_Initialized = true;
 	}
@@ -235,7 +234,7 @@ class Environment
 					{
 						ProcessWetnessByWaterLevel(m_WaterLevel);
 					}
-					else if ((IsRaining() || (IsSnowing() && MiscGameplayFunctions.GetCombinedSnowfallWindValue() > SNOWFALL_WIND_COMBINED_THRESHOLD)) && !IsInsideBuilding() && !IsUnderRoof() && !IsInsideVehicle())
+					else if ((IsRaining() || (IsSnowing() && MiscGameplayFunctions.GetCombinedSnowfallWindValue() > SNOWFALL_WIND_COMBINED_THRESHOLD)) && !IsInsideBuilding() && !IsUnderRoof() && !IsChildOfType({Car}))
 					{
 						ProcessItemsWetness(m_SlotIdsComplete);
 					}
@@ -293,6 +292,15 @@ class Environment
 		return m_Player && m_Player.IsInVehicle();
 	}
 	
+	private bool IsChildOfType(array<typename> typenames)
+	{
+		Object parent = Object.Cast(m_Player.GetParent());
+		if (parent)
+			return parent.IsAnyInherited(typenames);
+		
+		return false;
+	}
+	
 	private bool IsUnderRoofBuilding()
 	{
 		return m_IsUnderRoofBuilding;
@@ -310,7 +318,7 @@ class Environment
 
 	protected bool DetermineHeatcomfortBehavior()
 	{
-		if (IsInsideVehicle())
+		if (IsChildOfType({Car}))
 		{
 			CarScript car = CarScript.Cast(m_Player.GetParent());
 			if (car && car.EngineIsOn())
@@ -329,7 +337,7 @@ class Environment
 	protected void CheckUnderRoof()
 	{
 		// if inside vehicle return immediatelly
-		if (IsInsideVehicle())
+		if (IsChildOfType({Car}))
 		{
 			m_IsUnderRoof = false;
 			m_IsUnderRoofBuilding = false;
@@ -350,11 +358,14 @@ class Environment
 	
 	protected void CheckWaterContact(out float pWaterLevel)
 	{
-		
 		string surfType;
 		int liquidType;
 
 		m_IsInWater = false;
+		
+		if (m_Player.PhysicsGetLinkedEntity() || IsChildOfType({Transport}))
+			return;
+		
 		if (m_Player.IsSwimming())
 		{
 			g_Game.SurfaceUnderObjectByBoneCorrectedLiquid(m_Player, SurfaceAnimationBone.RightFrontLimb, surfType, liquidType);
@@ -427,9 +438,7 @@ class Environment
 	// Calculates and return temperature of environment
 	protected float GetEnvironmentTemperature()
 	{
-		float temperature;
-		temperature = m_WorldData.GetBaseEnvTemperatureAtObject(m_Player);
-		temperature += m_Clouds * m_WorldData.m_CloudsTemperatureEffectModifier;
+		float temperature = m_WorldData.GetTemperature(m_Player, EEnvironmentTemperatureComponent.ALTITUDE | EEnvironmentTemperatureComponent.OVERCAST);
 		
 		if (IsWaterContact())
 		{
@@ -443,20 +452,20 @@ class Environment
 		{
 			temperature += m_WorldData.m_TemperatureInsideBuildingsModifier;
 		}
-		else if (IsInsideVehicle())
+		else if (IsChildOfType({Car}))
 		{
 			temperature += Math.AbsFloat(temperature * GameConstants.ENVIRO_TEMPERATURE_INSIDE_VEHICLE_COEF);
 			return temperature;
 		}
 		else if (IsUnderRoof() && !m_IsUnderRoofBuilding)
 		{
-			temperature += m_Fog * GameConstants.ENVIRO_FOG_TEMP_EFFECT;
+			temperature = m_WorldData.GetTemperature(m_Player, EEnvironmentTemperatureComponent.ALTITUDE | EEnvironmentTemperatureComponent.OVERCAST|EEnvironmentTemperatureComponent.FOG);
 			temperature += WindEffectTemperatureValue(temperature) * GetWindModifierPerSurface() * GameConstants.ENVIRO_TEMPERATURE_UNDERROOF_COEF;
 		}
 		else
 		{
-			temperature += m_Fog * GameConstants.ENVIRO_FOG_TEMP_EFFECT;
-			temperature += WindEffectTemperatureValue(temperature) * GetWindModifierPerSurface();
+			temperature = m_WorldData.GetTemperature(m_Player, EEnvironmentTemperatureComponent.ALTITUDE | EEnvironmentTemperatureComponent.OVERCAST|EEnvironmentTemperatureComponent.FOG);
+			temperature += m_WorldData.GetTemperatureComponentValue(temperature, EEnvironmentTemperatureComponent.WIND) * GetWindModifierPerSurface();
 		}
 
 		// incorporate temperature from temperature sources (buffer)
@@ -490,7 +499,7 @@ class Environment
 				wetDelta = 0.33;
 			}
 		}
-		else if (!IsInsideBuilding() && !IsUnderRoof() && !IsInsideVehicle())
+		else if (!IsInsideBuilding() && !IsUnderRoof() && !IsChildOfType({Car}))
 		{
 			if (IsRaining())
 				wetDelta = GameConstants.ENVIRO_WET_INCREMENT * GameConstants.ENVIRO_TICKS_TO_WETNESS_CALCULATION * (m_Rain) * (1 + (GameConstants.ENVIRO_WIND_EFFECT * m_Wind));
@@ -544,7 +553,7 @@ class Environment
 		m_DayOrNight 	= g_Game.GetMission().GetWorldData().GetDaytime();
 		m_Fog 			= weather.GetFog().GetActual();
 		m_Clouds 		= weather.GetOvercast().GetActual();
-		m_Wind 			= g_Game.GetWeather().GetWindMagnitude().GetActual();
+		m_Wind 			= weather.GetWindMagnitude().GetActual();
 
 		SetEnvironmentTemperature();
 		SetAreaGenericColdness();
@@ -562,16 +571,6 @@ class Environment
 		float heigthCorrectedTemp = m_WorldData.GetBaseEnvTemperatureAtObject(m_Player);
 		m_Player.SetInColdArea(heigthCorrectedTemp <= GameConstants.COLD_AREA_TEMPERATURE_THRESHOLD);
 	}
-	
-	protected float WindEffectTemperatureValue(float temperatureInput)
-	{
-		float output = 0.0;
-
-		output = (temperatureInput - GameConstants.ENVIRO_WIND_CHILL_LIMIT) / (GameConstants.ENVIRO_WIND_EFFECT_SLOPE - GameConstants.ENVIRO_WIND_CHILL_LIMIT);
-		output = output * m_Wind * m_WorldData.GetWindCoef();
-		
-		return -output;
-	}
 
 	//! process attachments by water depth
 	protected void ProcessWetnessByWaterLevel(float pWaterLevel)
@@ -702,10 +701,9 @@ class Environment
 				else if (isParentWet && parentItem)
 				{
 					if (pItem.GetWet() < parentItem.GetWet())
-					{
-						soakingCoef = pItem.GetSoakingIncrement("wetParent");
-						LogDryWetProcess(string.Format("%1 (soak coef=%2/s, current wetness=%3) [parent wet]", pItem.GetDisplayName(), soakingCoef / GameConstants.ENVIRO_TICK_RATE, pItem.GetWet()), parentItem != null);
-					}
+						soakingCoef = GetWetDelta();
+
+					LogDryWetProcess(string.Format("%1 (soak coef=%2/s, current wetness=%3) [parent wet]", pItem.GetDisplayName(), soakingCoef / GameConstants.ENVIRO_TICK_RATE, pItem.GetWet()), parentItem != null);
 				}
 				else
 				{
@@ -826,13 +824,10 @@ class Environment
 	
 			if (isParentWet)
 			{
-				if (pItem.GetWet() < parentItem.GetWet())
-				{
-					//! adds wetness to item inside wet parent item
-					dryingCoef = (GameConstants.ENVIRO_TICK_RATE * pItem.GetSoakingIncrement("wetParent")) / pDrynessData.m_TemperatureSourceDistance;
-					LogDryWetProcess(string.Format("%1 (dry coef=%2/s, current wetness=%3) [parent wet]", pItem.GetDisplayName(), dryingCoef / GameConstants.ENVIRO_TICK_RATE, pItem.GetWet()), parentItem != null);
-					pItem.AddWet(dryingCoef);
-				}
+				//! adds wetness to item inside wet parent item
+				dryingCoef = (GameConstants.ENVIRO_TICK_RATE * pItem.GetSoakingIncrement("wetParent")) / pDrynessData.m_TemperatureSourceDistance;
+				LogDryWetProcess(string.Format("%1 (dry coef=%2/s, current wetness=%3) [parent wet]", pItem.GetDisplayName(), dryingCoef / GameConstants.ENVIRO_TICK_RATE, pItem.GetWet()), parentItem != null);
+				pItem.AddWet(dryingCoef);
 			}
 		}
 	}
@@ -880,23 +875,46 @@ class Environment
 		heatItems = hBodyPartTotal;
 		heatComfortSum = hcBodyPartTotal;
 		heatComfortSum += hcPenaltyTotal; //! heatcomfort body parts penalties
-		
+
+		//! Stomach temperature influence to heatcomfort		
 		{
-			float stomachContentTemperature = m_Player.GetStomach().GetStomachTemperature();
-			if (!IsNeutralTemperature(stomachContentTemperature))
+			if (m_Player.GetStomach().GetStomachVolume() > 0.0)
 			{
-				stomachContentTemperature = m_Player.GetStomach().GetStomachTemperature() * GameConstants.ENVIRO_STOMACH_WEIGHT;
-				stomachContentTemperature = Math.Clamp(stomachContentTemperature, -GameConstants.ENVIRO_STOMACH_WEIGHT, GameConstants.ENVIRO_STOMACH_WEIGHT);
-				
-				heatComfortSum += stomachContentTemperature;
+				float stomachContentTemperature = m_Player.GetStomach().GetStomachTemperature();
+				if (stomachContentTemperature < GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT)
+				{
+					stomachContentTemperature = Math.Remap(
+						-10.0,
+						GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT,
+						-GameConstants.ENVIRO_STOMACH_WEIGHT,
+						0.0,
+						stomachContentTemperature,
+					);
+				}
+				else if (stomachContentTemperature > GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT)
+				{
+					stomachContentTemperature = Math.Remap(
+						GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT,
+						70.0,
+						0.0,
+						GameConstants.ENVIRO_STOMACH_WEIGHT,
+						stomachContentTemperature,
+					);
+				}
+				else
+					stomachContentTemperature = 0.0;
+
+				heatComfortSum += stomachContentTemperature * GameConstants.ENVIRO_STOMACH_WEIGHT;
 			}
 		}
 
 		float targetHeatComfort = (heatComfortSum + heatItems + (GetPlayerHeat() / 100)) + EnvTempToCoef(m_EnvironmentTemperature);
-
-		SetEnvironmentSnapshotData();
-		ProcessHeatBuffer(hcBodyPartTotal);
-
+		
+		//! uses the raw targetHeatComfort data
+		m_EnvironmentSnapshot.m_ClothingHeatComfort = hcBodyPartTotal;
+		m_EnvironmentSnapshot.m_TargetHeatComfort 	= targetHeatComfort;
+		ProcessHeatBuffer(m_EnvironmentSnapshot);
+		
 		if (m_Player.GetModifiersManager().IsModifierActive(eModifiers.MDF_HEATBUFFER))
 			targetHeatComfort = Math.Clamp(targetHeatComfort, 0.0, m_Player.GetStatHeatComfort().GetMax());
  		else
@@ -943,26 +961,62 @@ class Environment
  			m_Player.GetStatHeatComfort().Set(dynamicHeatComfort);
 		}
 	}
-	
-	protected void ProcessHeatBuffer(float heatComfortCloths)
+
+	protected void ProcessHeatBuffer(EnvironmentSnapshotData data)
 	{
 		if (m_HeatComfortBehaviorCategory == EEnvironmentHeatcomfortBehaviorCategory.DEFAULT)
 		{
 			float applicableHeatbuffer = GetApplicableHeatbuffer();
-			float originalTargetHeatcomfortMultiplier = 1.0;
 			
 			//! dynamic HB cap based on actual heatcomfort (from cloths)
-			float heatBufferCap = Math.InverseLerp(0.0, GameConstants.ENVIRO_HEATCOMFORT_WEIGHT_SUMMARY, heatComfortCloths);
+			float heatBufferCap = Math.InverseLerp(0.0, GameConstants.ENVIRO_HEATCOMFORT_WEIGHT_SUMMARY, data.m_ClothingHeatComfort);
 			float heatBufferMax = GameConstants.ENVIRO_PLAYER_HEATBUFFER_CAPACITY_MIN + heatBufferCap * (1 - GameConstants.ENVIRO_PLAYER_HEATBUFFER_CAPACITY_MIN);
 			m_Player.SetHeatBufferDynamicMax(heatBufferMax);
+
+			//! deplete the heat buffer if there is difference in HB capacity (eg.: cloths were removed)
+			if (heatBufferCap < m_HeatBufferCapPrevious)
+			{
+				float heatBufferValueCorrection = GameConstants.ENVIRO_PLAYER_HEATBUFFER_INCREASE / (heatBufferMax * ((-GameConstants.ENVIRO_PLAYER_HEATBUFFER_TEMP_AFFECT * data.m_TargetHeatComfort) + 1 ));
+				m_Player.GetStatHeatBuffer().Add(-heatBufferValueCorrection);
+				m_HeatBufferCapPrevious = heatBufferCap;
+			}
 			
 			float increaseRate = 0.0;
 			float decreaseRate = 0.0;
 
-			if (m_EnvironmentSnapshot)
 			{
-				increaseRate = GameConstants.ENVIRO_PLAYER_HEATBUFFER_INCREASE / (heatBufferMax * (( -GameConstants.ENVIRO_PLAYER_HEATBUFFER_TEMP_AFFECT * m_EnvironmentSnapshot.m_TargetHeatComfort ) + 1 ));
-				decreaseRate = GameConstants.ENVIRO_PLAYER_HEATBUFFER_DECREASE / (heatBufferMax * (( GameConstants.ENVIRO_PLAYER_HEATBUFFER_TEMP_AFFECT * m_EnvironmentSnapshot.m_TargetHeatComfort ) + 1 ));
+				increaseRate = GameConstants.ENVIRO_PLAYER_HEATBUFFER_INCREASE / (heatBufferMax * (( -GameConstants.ENVIRO_PLAYER_HEATBUFFER_TEMP_AFFECT * data.m_TargetHeatComfort) + 1 ));
+				decreaseRate = GameConstants.ENVIRO_PLAYER_HEATBUFFER_DECREASE / (heatBufferMax * (( GameConstants.ENVIRO_PLAYER_HEATBUFFER_TEMP_AFFECT * data.m_TargetHeatComfort) + 1 ));				
+				
+				float decreaseRateByHeatBufferStageCoef = 1;
+				
+				if (heatBufferMax > HeatBufferMdfr.STAGE_THRESHOLDS[1])
+				{
+					float heatBufferMaxInversed = Math.InverseLerp(HeatBufferMdfr.STAGE_THRESHOLDS[1], 1.0, heatBufferMax);
+					switch (m_Player.GetHeatBufferStage())
+					{
+						case 2:
+							decreaseRateByHeatBufferStageCoef = Math.Lerp(
+								GameConstants.ENVIRO_PLAYER_HEATBUFFER_STAGE_RATELIMIT[2][0],
+								GameConstants.ENVIRO_PLAYER_HEATBUFFER_STAGE_RATELIMIT[2][1],
+								heatBufferMaxInversed,
+							);
+							break;
+						case 1:
+							decreaseRateByHeatBufferStageCoef = Math.Lerp(
+								GameConstants.ENVIRO_PLAYER_HEATBUFFER_STAGE_RATELIMIT[1][0],
+								GameConstants.ENVIRO_PLAYER_HEATBUFFER_STAGE_RATELIMIT[1][1],
+								heatBufferMaxInversed,
+							);
+							break;
+					}
+				}
+				else
+				{
+					decreaseRateByHeatBufferStageCoef = GameConstants.ENVIRO_PLAYER_HEATBUFFER_STAGE_RATELIMIT[1][0];
+				}
+				
+				decreaseRate *= decreaseRateByHeatBufferStageCoef;
 				
 				if (m_IsInWater)
 					decreaseRate *= GameConstants.ENVIRO_PLAYER_HEATBUFFER_WATEREFFECT * m_WaterLevel;
@@ -991,12 +1045,15 @@ class Environment
 				if (m_HeatComfort > PlayerConstants.THRESHOLD_HEAT_COMFORT_MINUS_WARNING && m_UTSAverageTemperature > 0) // m_UTSAverageTemperature can be negative
 				{
 					if (applicableHeatbuffer < heatBufferMax)
+					{
 						m_Player.GetStatHeatBuffer().Add(increaseRate);
+						m_HeatBufferCapPrevious = heatBufferCap;
+					}
 				}
 				else if (applicableHeatbuffer > 0.0)
 					m_Player.GetStatHeatBuffer().Add(-decreaseRate);
 				else if (applicableHeatbuffer != 0.0 && !m_Player.GetModifiersManager().IsModifierActive(eModifiers.MDF_HEATBUFFER))
-						m_Player.GetStatHeatBuffer().Set(0.0);
+					m_Player.GetStatHeatBuffer().Set(0.0);
 	
 				m_HeatBufferTimer = 0.0;
 			}
@@ -1008,18 +1065,13 @@ class Environment
 		float applicableHeatbuffer = Math.Round((m_Player.GetStatHeatBuffer().Get() / m_Player.GetStatHeatBuffer().GetMax()) * 1000) * 0.001;
 		return applicableHeatbuffer;
 	}
-	
-	
+
 	//! go through all items in player's possession cool/warm them to neutral temperature
 	protected void ProcessItemsTemperature(array<int> pBodyPartIds)
 	{
 		EntityAI attachment;
 		ItemBase item;
 		
-		
-		// TODO:
-		// * based on water level - change the target temperature and speed exchange while swimming
-		
 		int attCount = m_Player.GetInventory().AttachmentCount();
 		for (int attIdx = 0; attIdx < attCount; ++attIdx)
 		{
@@ -1110,8 +1162,28 @@ class Environment
 	
 	protected void SetProcessedItemTemperature(ItemBase item, float heatPermeabilityCoef = 1.0)
 	{
-		if (item.GetTemperature() != GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_MIDDLE)
-			item.SetTemperatureEx(new TemperatureDataInterpolated(GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_MIDDLE,ETemperatureAccessTypes.ACCESS_INVENTORY,GameConstants.ENVIRO_TICK_RATE,m_ItemTemperatureCoef,heatPermeabilityCoef));
+		float targetTemperature = GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_MIDDLE;
+		bool globalCooling = true;
+		if (m_Player.IsSwimming())
+		{
+			SetItemHeatingCoef(GameConstants.TEMP_COEF_SWIMMING);
+			targetTemperature = m_WorldData.GetLiquidTypeEnviroTemperature(m_LiquidType); 
+			globalCooling = false;
+		}
+
+		if (item.GetTemperature() != targetTemperature || !item.IsFreezeThawProgressFinished())
+		{
+			TemperatureDataInterpolated temperatureData = new TemperatureDataInterpolated(
+				targetTemperature,
+				ETemperatureAccessTypes.ACCESS_INVENTORY,
+				GameConstants.ENVIRO_TICK_RATE,
+				m_ItemTemperatureCoef,
+				heatPermeabilityCoef,
+			);
+			temperatureData.m_UseGlobalCooling = globalCooling;
+
+			item.SetTemperatureEx(temperatureData);
+		}
 	}
 	
 	protected float EnvTempToCoef(float pTemp)
@@ -1201,7 +1273,7 @@ class Environment
 	{
 		float penalty = 0.0;
 		
-		if (!IsInsideBuilding() && !IsUnderRoof() && !IsInsideVehicle() && !IsWaterContact())
+		if (!IsInsideBuilding() && !IsUnderRoof() && !IsChildOfType({Car}) && !IsWaterContact())
 		{
 			if (m_Rain > GameConstants.ENVIRO_NAKED_BODY_PENALTY_RAIN_MIN_VALUE || m_Snowfall > GameConstants.ENVIRO_NAKED_BODY_PENALTY_SNOWFALL_MIN_VALUE)
 			{
@@ -1292,14 +1364,6 @@ class Environment
 	protected void OnTemperatureSourcesEnter();	
 	protected void OnTemperatureSourcesLeft();
 	
-	protected void SetEnvironmentSnapshotData()
-	{
-		EnvironmentSnapshotData data = new EnvironmentSnapshotData();
-		data.m_TargetHeatComfort = m_TargetHeatComfort;
-		
-		m_EnvironmentSnapshot = data;
-	}
-	
 	float GetUniversalSourcesTemperageAverage()
 	{
 		return m_UTSAverageTemperature;
@@ -1494,6 +1558,7 @@ class Environment
 	//! DEPRECATED
 	protected float 				m_HeatSourceTemp;	
 	protected ref SimpleMovingAverage<float> m_WindAverageBuffer;
+	protected ref EnvironmentSnapshotData m_EnvironmentSnapshot; //! used for calculations before the data modification
 	
 	void Init(PlayerBase pPlayer)
 	{
@@ -1612,6 +1677,34 @@ class Environment
 		}
 	}
 	
+	protected void SetEnvironmentSnapshotData()
+	{
+		EnvironmentSnapshotData data = new EnvironmentSnapshotData();
+		data.m_TargetHeatComfort = m_TargetHeatComfort;
+		
+		m_EnvironmentSnapshot = data;
+	}
+	
+	//! backward compatibility [<1.27]
+	protected void ProcessHeatBuffer(float heatComfortCloths)
+	{
+		m_EnvironmentSnapshot.m_ClothingHeatComfort 	= heatComfortCloths;
+		m_EnvironmentSnapshot.m_TargetHeatComfort 	= m_TargetHeatComfort;
+		
+		ProcessHeatBuffer(m_EnvironmentSnapshot);
+	}
+	
+	//! backward compatibility [<1.28]
+	protected float WindEffectTemperatureValue(float temperatureInput)
+	{
+		float output = 0.0;
+
+		output = (temperatureInput - GameConstants.ENVIRO_WIND_CHILL_LIMIT) / (GameConstants.ENVIRO_WIND_EFFECT_SLOPE - GameConstants.ENVIRO_WIND_CHILL_LIMIT);
+		output = output * m_Wind * m_WorldData.GetWindCoef();
+		
+		return -output;
+	}
+	
 	float GetDayOrNight()
 	{
 		return m_DayOrNight;

+ 1 - 1
Scripts/4_world/classes/explosion.c

@@ -20,6 +20,6 @@ class ExplosionTest : House
 	
 	void ExplodeNow()
 	{
-		Explode(DT_EXPLOSION, "Explosion_NonLethal");
+		Explode(DamageType.EXPLOSION, "Explosion_NonLethal");
 	}
 }

+ 5 - 0
Scripts/4_world/classes/foodstage/foodstage.c

@@ -346,6 +346,11 @@ class FoodStage
 		return GetNutritionPropertyFromIndex( 6 , stage_type, stage, classname );
 	}
 	
+	static float GetAgentsPerDigest(FoodStage stage, int stageType = -1, string className = "")
+	{
+		return GetNutritionPropertyFromIndex(7, stageType, stage, className);
+	}
+	
 	//Food item
 	protected Edible_Base GetFoodItem()
 	{

+ 17 - 0
Scripts/4_world/classes/heatcomfortanimhandler.c

@@ -3,10 +3,12 @@ class HeatComfortAnimHandler
 	const float TICK_INTERVAL  = 2;
 	float m_TimeSinceLastTick;
 	float m_ProcessTimeAccuFreeze;
+	float m_ProcessTimeAccuFreezeRattle;
 	float m_ProcessTimeAccuHot;
 	
 	PlayerBase m_Player;
 	float m_EventTimeFreeze = -1;
+	float m_EventTimeFreezeRattle = -1;
 	float m_EventTimeHot = -1;
 	protected ref HumanMovementState m_MovementState = new HumanMovementState();
 	
@@ -53,6 +55,21 @@ class HeatComfortAnimHandler
 			float value_max;
 			float offset_time;
 			
+			if ( hc <= PlayerConstants.THRESHOLD_HEAT_COMFORT_MINUS_CRITICAL )
+			{
+				m_ProcessTimeAccuFreezeRattle++;
+				
+				if (m_EventTimeFreezeRattle < 0)
+					m_EventTimeFreezeRattle = GetEventTime(hc, -1 ,PlayerConstants.THRESHOLD_HEAT_COMFORT_MINUS_CRITICAL, TIME_INTERVAL_HC_MINUS_LOW_MIN, TIME_INTERVAL_HC_MINUS_HIGH_MIN, TIME_INTERVAL_HC_MINUS_LOW_MAX, TIME_INTERVAL_HC_MINUS_HIGH_MAX);
+				
+				if( m_ProcessTimeAccuFreezeRattle > m_EventTimeFreezeRattle )
+				{
+					m_ProcessTimeAccuFreezeRattle = 0;
+					m_EventTimeFreezeRattle = -1;
+					m_Player.GetSymptomManager().QueueUpPrimarySymptom(SymptomIDs.SYMPTOM_FREEZE_RATTLE);
+				}
+			}
+			
 			if ( hc <= PlayerConstants.THRESHOLD_HEAT_COMFORT_MINUS_WARNING )
 			{
 				m_ProcessTimeAccuFreeze++;

+ 4 - 4
Scripts/4_world/classes/meleetargeting.c

@@ -195,7 +195,7 @@ class MeleeTargeting
 	}
 	
 
-	bool FindMostSuitableComponentEx(Object obj, BoxCollidingResult bResult, MeleeTargetSettings settings, out float sum, out ref ComponentResult result, array<string> blacklistedDamageZones)
+	bool FindMostSuitableComponentEx(Object obj, BoxCollidingResult bResult, MeleeTargetSettings settings, out float sum, out ComponentResult result, array<string> blacklistedDamageZones)
 	{
 		foreach (ComponentInfo cInfo : bResult.componentInfo)
 		{
@@ -218,12 +218,12 @@ class MeleeTargeting
 		return result != null;
 	}
 
-	bool FindMostSuitableComponent(Object obj, BoxCollidingResult bResult, MeleeTargetSettings settings, out float sum, out ref ComponentResult result)
+	bool FindMostSuitableComponent(Object obj, BoxCollidingResult bResult, MeleeTargetSettings settings, out float sum, out ComponentResult result)
 	{
 		return FindMostSuitableComponentEx(obj, bResult, settings, sum, result, null);
 	}
 	
-	bool EvaluateComponentEx(Object obj, ComponentInfo cInfo, MeleeTargetSettings settings, out ref ComponentResult result, array<string> blacklistedDamageZones)
+	bool EvaluateComponentEx(Object obj, ComponentInfo cInfo, MeleeTargetSettings settings, out ComponentResult result, array<string> blacklistedDamageZones)
 	{
 		//! check if the component is on blacklist, if so, continue in lookup
 		foreach (string zoneName: blacklistedDamageZones)
@@ -269,7 +269,7 @@ class MeleeTargeting
 		return true;
 	}
 	
-	bool EvaluateComponent(Object obj, ComponentInfo cInfo, MeleeTargetSettings settings, out ref ComponentResult result)
+	bool EvaluateComponent(Object obj, ComponentInfo cInfo, MeleeTargetSettings settings, out ComponentResult result)
 	{
 		return EvaluateComponentEx(obj, cInfo, settings, result, {});
 	}

+ 3 - 1
Scripts/4_world/classes/nutritionalprofile.c

@@ -9,8 +9,10 @@ class NutritionalProfile
 	int m_Agents;
 	string m_LiquidClassname;
 	float m_Digestibility;
+	float m_AgentsPerDigest;
 	
-	void NutritionalProfile(float energy, float water_content, float nutritional_index, float fullness_index, float toxicity, int agents, float digestibility)
+	//! DEPRECATED - used as the structure with direct access
+	void NutritionalProfile(float energy = 0.0, float water_content = 0.0, float nutritional_index = 0.0, float fullness_index = 0.0, float toxicity = 0.0, int agents = 0.0, float digestibility = 0.0)
 	{
 		m_Energy = energy;
 		m_WaterContent = water_content;

Some files were not shown because too many files changed in this diff