diff --git a/.travis.yml b/.travis.yml
index 51ef58eb..c124a9f0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,15 +1,44 @@
 language: cpp
-os: linux
 dist: focal
-matrix:
+os: linux
+jobs:
   include:
   - env: TARGET=release_linux-amd64-librw_gl3_glfw-oal
+    os: linux
   - env: TARGET=debug_linux-amd64-librw_gl3_glfw-oal
+    os: linux
+  - env: TARGET=release_macosx-amd64-librw_gl3_glfw-oal PREMAKE5=premake-5.0.0-alpha15
+    compiler: clang
+    os: osx
+    osx_image: xcode12u
+  - env: TARGET=debug_macosx-amd64-librw_gl3_glfw-oal PREMAKE5=premake-5.0.0-alpha15
+    compiler: clang
+    os: osx
+    osx_image: xcode12u
+addons:
+  apt:
+    update: true
+    packages:
+      - linux-libc-dev
+      - libopenal-dev
+      - libglew-dev
+      - libglfw3-dev
+      - libsndfile1-dev
+      - libmpg123-dev
+      - gcc-8-multilib
+      - g++-8-multilib
+  homebrew:
+    packages:
+      - libsndfile
+      - mpg123
+      - glew
+      - glfw
+      - openal-soft
 script:
-  - sudo apt-get update
-  - sudo apt-get -y install linux-libc-dev libopenal-dev libglew-dev libglfw3-dev libsndfile1-dev libmpg123-dev gcc-8-multilib g++-8-multilib
   - mkdir -p "$TRAVIS_BUILD_DIR/build"
   - cd "$TRAVIS_BUILD_DIR"
-  - ./premake5Linux --with-librw gmake2
-  - cd build 
-  - CC=gcc-8 CXX=g++-8 make config=$TARGET -j4 verbose=1
+  - if [ "$TRAVIS_OS_NAME" = linux ]; then ./premake5Linux --with-librw gmake2; fi
+  - if [ "$TRAVIS_OS_NAME" = osx ]; then curl -L -o "${PREMAKE5}.zip" "https://github.com/premake/premake-core/releases/download/v5.0.0-alpha15/${PREMAKE5}-src.zip" && unzip -q "${PREMAKE5}.zip" && cd "$PREMAKE5" && make -f Bootstrap.mak osx && cd .. && "./${PREMAKE5}/bin/release/premake5" --with-librw gmake2; fi
+  - cd build
+  - if [ "$TRAVIS_OS_NAME" = linux ]; then env CC=gcc-8 CXX=g++-8 make config=$TARGET -j4 verbose=1; fi
+  - if [ "$TRAVIS_OS_NAME" = osx ]; then make config=$TARGET -j4 verbose=1; fi
diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json
new file mode 100644
index 00000000..327d8cc7
--- /dev/null
+++ b/.vscode/c_cpp_properties.json
@@ -0,0 +1,32 @@
+{
+  "configurations": [
+    {
+      "name": "Mac",
+      "includePath": ["${default}"],
+      "defines": [],
+      "macFrameworkPath": [
+        "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks"
+      ],
+      "compilerPath": "/opt/local/bin/clang",
+      "compilerArgs": ["-g"],
+      "cStandard": "gnu11",
+      "cppStandard": "gnu++14",
+      "browse": {
+        "path": [
+          "/opt/local/include",
+          "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include"
+        ]
+      }
+    },
+    {
+      "name": "Linux",
+      "includePath": ["${default}"],
+      "defines": ["XDG_ROOT"],
+      "compilerPath": "/usr/bin/gcc",
+      "compilerArgs": ["-ggdb"],
+      "cStandard": "gnu11",
+      "cppStandard": "gnu++14"
+    }
+  ],
+  "version": 4
+}
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 00000000..82ce041f
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,89 @@
+{
+  "configurations": [
+    {
+      "MIMode": "gdb",
+      "args": [],
+      "cwd": "${workspaceFolder}",
+      "environment": [],
+      "externalConsole": false,
+      "name": "(gdb) Launch (Linux Debug)",
+      "preLaunchTask": "Compile (Debug Linux x64)",
+      "program": "${workspaceFolder}/bin/linux-amd64-librw_gl3_glfw-oal/Debug/re3",
+      "request": "launch",
+      "setupCommands": [
+        {
+          "description": "Enable pretty-printing for gdb",
+          "ignoreFailures": true,
+          "text": "-enable-pretty-printing"
+        }
+      ],
+      "stopAtEntry": false,
+      "targetArchitecture": "x64",
+      "type": "cppdbg"
+    },
+    {
+      "MIMode": "gdb",
+      "args": [],
+      "cwd": "${workspaceFolder}",
+      "environment": [],
+      "externalConsole": false,
+      "name": "(gdb) Launch (Linux Release)",
+      "preLaunchTask": "Compile (Release Linux x64)",
+      "program": "${workspaceFolder}/bin/linux-amd64-librw_gl3_glfw-oal/Release/re3",
+      "request": "launch",
+      "setupCommands": [
+        {
+          "description": "Enable pretty-printing for gdb",
+          "ignoreFailures": true,
+          "text": "-enable-pretty-printing"
+        }
+      ],
+      "stopAtEntry": false,
+      "targetArchitecture": "x64",
+      "type": "cppdbg"
+    },
+    {
+      "MIMode": "lldb",
+      "args": [],
+      "cwd": "${workspaceFolder}",
+      "environment": [],
+      "externalConsole": false,
+      "name": "(lldb) Launch (macOS Debug)",
+      "preLaunchTask": "Compile (Debug macOS x64)",
+      "program": "${workspaceFolder}/bin/macosx-amd64-librw_gl3_glfw-oal/Debug/re3.app",
+      "request": "launch",
+      "setupCommands": [
+        {
+          "description": "Enable pretty-printing for lldb",
+          "ignoreFailures": true,
+          "text": "-enable-pretty-printing"
+        }
+      ],
+      "stopAtEntry": false,
+      "targetArchitecture": "x64",
+      "type": "cppdbg"
+    },
+    {
+      "MIMode": "lldb",
+      "args": [],
+      "cwd": "${workspaceFolder}",
+      "environment": [],
+      "externalConsole": false,
+      "name": "(lldb) Launch (macOS Release)",
+      "preLaunchTask": "Compile (Release macOS x64)",
+      "program": "${workspaceFolder}/bin/macosx-amd64-librw_gl3_glfw-oal/Release/re3.app",
+      "request": "launch",
+      "setupCommands": [
+        {
+          "description": "Enable pretty-printing for lldb",
+          "ignoreFailures": true,
+          "text": "-enable-pretty-printing"
+        }
+      ],
+      "stopAtEntry": false,
+      "targetArchitecture": "x64",
+      "type": "cppdbg"
+    }
+  ],
+  "version": "0.2.0"
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000..10cb5627
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,30 @@
+{
+  "C_Cpp.default.cStandard": "gnu11",
+  "C_Cpp.default.cppStandard": "gnu++14",
+  "C_Cpp.default.includePath": [
+    "src/animation",
+    "src/audio",
+    "src/control",
+    "src/core",
+    "src/entities",
+    "src/extras",
+    "src/fakerw",
+    "src/math",
+    "src/modelinfo",
+    "src/objects",
+    "src/peds",
+    "src/render",
+    "src/rw",
+    "src/save",
+    "src/skel",
+    "src/text",
+    "src/vehicles",
+    "src/weapons",
+    "vendor/librw"
+  ],
+  "C_Cpp.vcFormat.indent.gotoLabels": "leftmostColumn",
+  "C_Cpp.vcFormat.space.pointerReferenceAlignment": "right",
+  "cSpell.enabled": false,
+  "files.trimFinalNewlines": false,
+  "files.trimTrailingWhitespace": false
+}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 00000000..0f610d5f
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,95 @@
+{
+  "tasks": [
+    {
+      "args": ["--with-librw", "gmake2"],
+      "command": "./premake5Linux",
+      "label": "Premake (Linux)",
+      "problemMatcher": "$gcc",
+      "type": "shell"
+    },
+    {
+      "args": ["--with-librw", "gmake2"],
+      "command": "premake5",
+      "label": "Premake (macOS)",
+      "problemMatcher": "$gcc",
+      "type": "shell"
+    },
+    {
+      "args": [
+        "-j5",
+        "config=debug_linux-amd64-librw_gl3_glfw-oal",
+        "verbose=1"
+      ],
+      "command": "make",
+      "dependsOn": "Premake (Linux)",
+      "group": {
+        "isDefault": true,
+        "kind": "build"
+      },
+      "label": "Compile (Debug Linux x64)",
+      "options": {
+        "cwd": "${workspaceFolder}/build"
+      },
+      "problemMatcher": "$gcc",
+      "type": "shell"
+    },
+    {
+      "args": [
+        "-j5",
+        "config=release_linux-amd64-librw_gl3_glfw-oal",
+        "verbose=1"
+      ],
+      "command": "make",
+      "dependsOn": "Premake (Linux)",
+      "group": {
+        "isDefault": true,
+        "kind": "build"
+      },
+      "label": "Compile (Release Linux x64)",
+      "options": {
+        "cwd": "${workspaceFolder}/build"
+      },
+      "problemMatcher": "$gcc",
+      "type": "shell"
+    },
+    {
+      "args": [
+        "-j5",
+        "config=debug_macosx-amd64-librw_gl3_glfw-oal",
+        "verbose=1"
+      ],
+      "command": "make",
+      "dependsOn": "Premake (macOS)",
+      "group": {
+        "isDefault": true,
+        "kind": "build"
+      },
+      "label": "Compile (Debug macOS x64)",
+      "options": {
+        "cwd": "${workspaceFolder}/build"
+      },
+      "problemMatcher": "$gcc",
+      "type": "shell"
+    },
+    {
+      "args": [
+        "-j5",
+        "config=release_macosx-amd64-librw_gl3_glfw-oal",
+        "verbose=1"
+      ],
+      "command": "make",
+      "dependsOn": "Premake (macOS)",
+      "group": {
+        "isDefault": true,
+        "kind": "build"
+      },
+      "label": "Compile (Release macOS x64)",
+      "options": {
+        "cwd": "${workspaceFolder}/build"
+      },
+      "problemMatcher": "$gcc",
+      "type": "shell"
+    }
+  ],
+  "version": "2.0.0"
+}
diff --git a/CODING_STYLE.md b/CODING_STYLE.md
new file mode 100644
index 00000000..b8be02bc
--- /dev/null
+++ b/CODING_STYLE.md
@@ -0,0 +1,107 @@
+# Coding style
+
+I started writing in [Plan 9 style](http://man.cat-v.org/plan_9/6/style),
+but realize that this is not the most popular style, so I'm willing to compromise.
+Try not to deviate too much so the code will look similar across the whole project.
+
+To give examples, these two styles (or anything in between) are fine:
+
+```
+type
+functionname(args)
+{
+	if(a == b){
+		s1;
+		s2;
+	}else{
+		s3;
+		s4;
+	}
+	if(x != y)
+		s5;
+}
+
+type functionname(args)
+{
+	if (a == b) {
+		s1;
+		s2;
+	} else {
+		s3;
+		s4;
+	}
+	if (x != y)
+		s5;
+}
+```
+
+This one (or anything more extreme) is heavily discouraged:
+
+```
+type functionname ( args )
+{
+  if ( a == b )
+  {
+    s1;
+    s2;
+  }
+  else
+  {
+    s3;
+    s4;
+  }
+  if ( x != y )
+  {
+    s5;
+  }
+}
+```
+
+i.e. 
+
+* Put the brace on the same line as control statements
+
+* Put the brace on the next line after function definitions and structs/classes
+
+* Put an `else` on the same line with the braces
+
+* Don't put braces around single statements
+
+* Put the function return type on a separate line
+
+* Indent with TABS
+
+As for the less cosmetic choices, here are some guidelines how the code should look:
+
+* Don't use magic numbers where the original source code would have had an enum or similar.
+Even if you don't know the exact meaning it's better to call something `FOOBAR_TYPE_4` than just `4`,
+since `4` will be used in other places and you can't easily see where else the enum value is used.
+
+* Don't just copy paste code from IDA, make it look nice
+
+* Use the right types. In particular:
+
+    * don't use types like `__int16`, we have `int16` for that
+
+    * don't use `unsigned`, we have typedefs for that
+
+    * don't use `char` for anything but actual characters, use `int8`, `uint8` or `bool`
+
+    * don't even think about using win32 types (`BYTE`, `WORD`, &c.) unless you're writing win32 specific code
+
+    * declare pointers like `int *ptr;`, not `int* ptr;`
+
+* As for variable names, the original gta source code was not written in a uniform style,
+but here are some observations:
+
+    * many variables employ a form of hungarian notation, i.e.:
+
+    * `m_` may be used for class member variables (mostly those that are considered private)
+
+    * `ms_` for (mostly private) static members
+
+    * `f` is a float, `i` or `n` is an integer, `b` is a boolean, `a` is an array
+
+    * do *not* use `dw` for `DWORD` or so, we're not programming win32
+
+* Generally, try to make the code look as if R* could have written it
diff --git a/README.md b/README.md
index 9cb48c25..1bfe74d2 100644
--- a/README.md
+++ b/README.md
@@ -21,8 +21,7 @@ such that we have a working game at all times.
 
 ## Preparing the environment for building
 
-- Clone the repo.
-- Run `git submodule init` and `git submodule update`.
+- Clone the repo using the argument `--recursive`.
 - Point GTA_III_RE_DIR environment variable to GTA3 root folder.
 - Run premake
 	- On Windows: one of the `premake-vsXXXX.cmd` variants on root folder
@@ -36,6 +35,7 @@ such that we have a working game at all times.
 > :information_source: **Did you notice librw?** re3 uses completely homebrew RenderWare-replacement rendering engine; [librw](https://github.com/aap/librw/). librw comes as submodule of re3, but you also can use LIBRW enviorenment variable to specify path to your own librw.
 
 ## Contributing
+Please read the [Coding Style](https://github.com/GTAmodding/re3/blob/master/CODING_STYLE.md) Document
 
 ### Unreversed / incomplete classes (at least the ones we know)
 The following classes have only unused or practically unused code left:
@@ -44,110 +44,3 @@ CCullZone - only mobile stuff
 CCullZones - only mobile stuff
 ```
 
-### Coding style
-
-I started writing in [Plan 9 style](http://man.cat-v.org/plan_9/6/style),
-but realize that this is not the most popular style, so I'm willing to compromise.
-Try not to deviate too much so the code will look similar across the whole project.
-
-To give examples, these two styles (or anything in between) are fine:
-
-```
-type
-functionname(args)
-{
-	if(a == b){
-		s1;
-		s2;
-	}else{
-		s3;
-		s4;
-	}
-	if(x != y)
-		s5;
-}
-
-type functionname(args)
-{
-	if (a == b) {
-		s1;
-		s2;
-	} else {
-		s3;
-		s4;
-	}
-	if (x != y)
-		s5;
-}
-```
-
-This one (or anything more extreme) is heavily discouraged:
-
-```
-type functionname ( args )
-{
-  if ( a == b )
-  {
-    s1;
-    s2;
-  }
-  else
-  {
-    s3;
-    s4;
-  }
-  if ( x != y )
-  {
-    s5;
-  }
-}
-```
-
-i.e. 
-
-* Put the brace on the same line as control statements
-
-* Put the brace on the next line after function definitions and structs/classes
-
-* Put an `else` on the same line with the braces
-
-* Don't put braces around single statements
-
-* Put the function return type on a separate line
-
-* Indent with TABS
-
-As for the less cosmetic choices, here are some guidelines how the code should look:
-
-* Don't use magic numbers where the original source code would have had an enum or similar.
-Even if you don't know the exact meaning it's better to call something `FOOBAR_TYPE_4` than just `4`,
-since `4` will be used in other places and you can't easily see where else the enum value is used.
-
-* Don't just copy paste code from IDA, make it look nice
-
-* Use the right types. In particular:
-
-    * don't use types like `__int16`, we have `int16` for that
-
-    * don't use `unsigned`, we have typedefs for that
-
-    * don't use `char` for anything but actual characters, use `int8`, `uint8` or `bool`
-
-    * don't even think about using win32 types (`BYTE`, `WORD`, &c.) unless you're writing win32 specific code
-
-    * declare pointers like `int *ptr;`, not `int* ptr;`
-
-* As for variable names, the original gta source code was not written in a uniform style,
-but here are some observations:
-
-    * many variables employ a form of hungarian notation, i.e.:
-
-    * `m_` may be used for class member variables (mostly those that are considered private)
-
-    * `ms_` for (mostly private) static members
-
-    * `f` is a float, `i` or `n` is an integer, `b` is a boolean, `a` is an array
-
-    * do *not* use `dw` for `DWORD` or so, we're not programming win32
-
-* Generally, try to make the code look as if R* could have written it
diff --git a/premake5.lua b/premake5.lua
index b61a9c4c..334c9c53 100644
--- a/premake5.lua
+++ b/premake5.lua
@@ -84,6 +84,12 @@ workspace "reVC"
 		platforms {
 			"bsd-amd64-librw_gl3_glfw-oal"
 		}
+		
+	filter { "system:macosx" }
+		platforms {
+			"macosx-arm64-librw_gl3_glfw-oal",
+			"macosx-amd64-librw_gl3_glfw-oal",
+		}
 
 	filter "configurations:Debug"
 		defines { "DEBUG" }
@@ -100,6 +106,9 @@ workspace "reVC"
 		
 	filter { "platforms:bsd*" }
 		system "bsd"
+
+	filter { "platforms:macosx*" }
+		system "macosx"
 	
 	filter { "platforms:*x86*" }
 		architecture "x86"
@@ -109,6 +118,13 @@ workspace "reVC"
 
 	filter { "platforms:*arm*" }
 		architecture "ARM"
+		
+	filter { "platforms:macosx-arm64-*" }
+		buildoptions { "-target", "arm64-apple-macos11", "-std=gnu++14" }
+
+	filter { "platforms:macosx-amd64-*" }
+		buildoptions { "-target", "x86_64-apple-macos10.12", "-std=gnu++14" }
+
 
 	filter { "platforms:*librw_d3d9*" }
 		defines { "RW_D3D9" }
@@ -162,6 +178,13 @@ project "librw"
 	filter "platforms:bsd*"
 		includedirs { "/usr/local/include" }
 		libdirs { "/usr/local/lib" }
+
+	filter "platforms:macosx*"
+		-- Support MacPorts and Homebrew
+		includedirs { "/opt/local/include" }
+		includedirs {"/usr/local/include" }
+		libdirs { "/opt/local/lib" }
+		libdirs { "/usr/local/lib" }
 	
 	filter "platforms:*RW34*"
 		flags { "ExcludeFromBuild" }
@@ -277,6 +300,11 @@ project "reVC"
 		
 	filter "platforms:bsd*oal"
 		links { "openal", "mpg123", "sndfile", "pthread" }
+
+	filter "platforms:macosx*oal"
+		links { "openal", "mpg123", "sndfile", "pthread" }
+		includedirs { "/usr/local/opt/openal-soft/include" }
+		libdirs { "/usr/local/opt/openal-soft/lib" }
 	
 	if _OPTIONS["with-opus"] then
 		filter {}
@@ -330,3 +358,11 @@ project "reVC"
 		links { "GL", "GLEW", "glfw", "sysinfo" }
 		includedirs { "/usr/local/include" }
 		libdirs { "/usr/local/lib" }
+
+	filter "platforms:macosx*gl3_glfw*"
+		links { "GLEW", "glfw" }
+		linkoptions { "-framework OpenGL" }
+		includedirs { "/opt/local/include" }
+		includedirs { "/usr/local/include" }
+		libdirs { "/opt/local/lib" }
+		libdirs { "/usr/local/lib" }
diff --git a/src/audio/DMAudio.cpp b/src/audio/DMAudio.cpp
index 486daebf..a4db2862 100644
--- a/src/audio/DMAudio.cpp
+++ b/src/audio/DMAudio.cpp
@@ -5,6 +5,7 @@
 #include "AudioManager.h"
 #include "AudioScriptObject.h"
 #include "sampman.h"
+#include "Font.h"
 #include "Text.h"
 #include "crossplatform.h"
 
diff --git a/src/control/CarCtrl.cpp b/src/control/CarCtrl.cpp
index 74c4f006..93098d6d 100644
--- a/src/control/CarCtrl.cpp
+++ b/src/control/CarCtrl.cpp
@@ -3075,7 +3075,7 @@ void CCarCtrl::SwitchVehicleToRealPhysics(CVehicle* pVehicle)
 void CCarCtrl::JoinCarWithRoadSystem(CVehicle* pVehicle)
 {
 	pVehicle->AutoPilot.m_nPrevRouteNode = pVehicle->AutoPilot.m_nCurrentRouteNode = pVehicle->AutoPilot.m_nNextRouteNode = 0;
-	pVehicle->AutoPilot.m_nCurrentRouteNode = pVehicle->AutoPilot.m_nPreviousPathNodeInfo = pVehicle->AutoPilot.m_nNextPathNodeInfo = 0;
+	pVehicle->AutoPilot.m_nCurrentPathNodeInfo = pVehicle->AutoPilot.m_nPreviousPathNodeInfo = pVehicle->AutoPilot.m_nNextPathNodeInfo = 0;
 	int nodeId = ThePaths.FindNodeClosestToCoorsFavourDirection(pVehicle->GetPosition(), 0, pVehicle->GetForward().x, pVehicle->GetForward().y);
 	CPathNode* pNode = &ThePaths.m_pathNodes[nodeId];
 	int prevNodeId = -1;
diff --git a/src/control/Darkel.cpp b/src/control/Darkel.cpp
index 75d27e26..1191e8ad 100644
--- a/src/control/Darkel.cpp
+++ b/src/control/Darkel.cpp
@@ -14,6 +14,9 @@
 #include "Text.h"
 #include "Vehicle.h"
 #include "GameLogic.h"
+#ifdef FIX_BUGS
+#include "Replay.h"
+#endif
 
 #define FRENZY_ANY_PED -1
 #define FRENZY_ANY_CAR -2
@@ -62,6 +65,10 @@ CDarkel::CalcFade(uint32 time, uint32 start, uint32 end)
 void
 CDarkel::DrawMessages()
 {
+#ifdef FIX_BUGS
+	if (CReplay::IsPlayingBack())
+		return;
+#endif
 	switch (Status) {
 		case KILLFRENZY_ONGOING:
 		{
@@ -167,6 +174,10 @@ CDarkel::ReadStatus()
 void
 CDarkel::RegisterCarBlownUpByPlayer(CVehicle *vehicle)
 {
+#ifdef FIX_BUGS
+	if (CReplay::IsPlayingBack())
+		return;
+#endif
 	if (FrenzyOnGoing()) {
 		int32 model = vehicle->GetModelIndex();
 		if (ModelToKill == FRENZY_ANY_CAR || ModelToKill == model || ModelToKill2 == model || ModelToKill3 == model || ModelToKill4 == model) {
@@ -181,6 +192,10 @@ CDarkel::RegisterCarBlownUpByPlayer(CVehicle *vehicle)
 void
 CDarkel::RegisterKillByPlayer(CPed *victim, eWeaponType weapon, bool headshot)
 {
+#ifdef FIX_BUGS
+	if (CReplay::IsPlayingBack())
+		return;
+#endif
 	if (FrenzyOnGoing() && (weapon == WeaponType
 			|| weapon == WEAPONTYPE_EXPLOSION
 			|| weapon == WEAPONTYPE_UZI_DRIVEBY && WeaponType == WEAPONTYPE_UZI
@@ -206,6 +221,10 @@ CDarkel::RegisterKillByPlayer(CPed *victim, eWeaponType weapon, bool headshot)
 void
 CDarkel::RegisterKillNotByPlayer(CPed* victim, eWeaponType weapontype)
 {
+#ifdef FIX_BUGS
+	if (CReplay::IsPlayingBack())
+		return;
+#endif
 	CStats::PeopleKilledByOthers++;
 }
 
@@ -299,6 +318,11 @@ CDarkel::StartFrenzy(eWeaponType weaponType, int32 time, uint16 kill, int32 mode
 void
 CDarkel::Update()
 {
+#ifdef FIX_BUGS
+	if (CReplay::IsPlayingBack())
+		return;
+#endif
+
 	if (Status != KILLFRENZY_ONGOING)
 		return;
 
diff --git a/src/control/Garages.cpp b/src/control/Garages.cpp
index 290bacad..dc07d142 100644
--- a/src/control/Garages.cpp
+++ b/src/control/Garages.cpp
@@ -1462,7 +1462,7 @@ void CGarage::UpdateCrusherShake(float X, float Y)
 }
 
 // This is dumb but there is no way to avoid goto. What was there originally even?
-static bool DoINeedToRefreshPointer(CEntity * pDoor, bool bIsDummy, int8 nIndex)
+static bool DoINeedToRefreshPointer(CEntity * pDoor, bool bIsDummy, uint8 nIndex)
 {
 	bool bNeedToFindDoorEntities = false;
 	if (pDoor) {
diff --git a/src/control/OnscreenTimer.cpp b/src/control/OnscreenTimer.cpp
index d94e993e..52d00f43 100644
--- a/src/control/OnscreenTimer.cpp
+++ b/src/control/OnscreenTimer.cpp
@@ -7,29 +7,38 @@
 #include "Timer.h"
 #include "Script.h"
 #include "OnscreenTimer.h"
+#include "Camera.h"
+
+// --MIAMI: file done
 
 void COnscreenTimer::Init() {
 	m_bDisabled = false;
-	for(uint32 i = 0; i < NUMONSCREENTIMERENTRIES; i++) {
-		m_sEntries[i].m_nTimerOffset = 0;
-		m_sEntries[i].m_nCounterOffset = 0;
+	for(uint32 i = 0; i < NUMONSCREENCOUNTERS; i++) {
+		m_sCounters[i].m_nCounterOffset = 0;
 
-		for(uint32 j = 0; j < 10; j++) {
-			m_sEntries[i].m_aTimerText[j] = 0;
-			m_sEntries[i].m_aCounterText[j] = 0;
+		for(uint32 j = 0; j < ARRAY_SIZE(COnscreenCounterEntry::m_aCounterText); j++) {
+			m_sCounters[i].m_aCounterText[j] = 0;
 		}
 
-		m_sEntries[i].m_nType = COUNTER_DISPLAY_NUMBER;
-		m_sEntries[i].m_bTimerProcessed = false;
-		m_sEntries[i].m_bCounterProcessed = false;
-		m_sEntries[i].m_bTimerGoingDown = true;
+		m_sCounters[i].m_nType = COUNTER_DISPLAY_NUMBER;
+		m_sCounters[i].m_bCounterProcessed = false;
+	}
+	for(uint32 i = 0; i < NUMONSCREENCLOCKS; i++) {
+		m_sClocks[i].m_nClockOffset = 0;
+
+		for(uint32 j = 0; j < ARRAY_SIZE(COnscreenTimerEntry::m_aClockText); j++) {
+			m_sClocks[i].m_aClockText[j] = 0;
+		}
+
+		m_sClocks[i].m_bClockProcessed = false;
+		m_sClocks[i].m_bClockGoingDown = true;
 	}
 }
 
 void COnscreenTimer::Process() {
 	if(!CReplay::IsPlayingBack() && !m_bDisabled) {
-		for(uint32 i = 0; i < NUMONSCREENTIMERENTRIES; i++) {
-			m_sEntries[i].Process();
+		for(uint32 i = 0; i < NUMONSCREENCLOCKS; i++) {
+			m_sClocks[i].Process();
 		}
 	}
 }
@@ -37,8 +46,19 @@ void COnscreenTimer::Process() {
 void COnscreenTimer::ProcessForDisplay() {
 	if(CHud::m_Wants_To_Draw_Hud) {
 		m_bProcessed = false;
-		for(uint32 i = 0; i < NUMONSCREENTIMERENTRIES; i++) {
-			if(m_sEntries[i].ProcessForDisplay()) {
+		for(uint32 i = 0; i < NUMONSCREENCLOCKS; i++) {
+			m_sClocks[i].m_bClockProcessed = false;
+			if (m_sClocks[i].m_nClockOffset != 0) {
+				m_sClocks[i].ProcessForDisplayClock();
+				m_sClocks[i].m_bClockProcessed = true;
+				m_bProcessed = true;
+			}
+		}
+		for(uint32 i = 0; i < NUMONSCREENCOUNTERS; i++) {
+			m_sCounters[i].m_bCounterProcessed = false;
+			if (m_sCounters[i].m_nCounterOffset != 0) {
+				m_sCounters[i].ProcessForDisplayCounter();
+				m_sCounters[i].m_bCounterProcessed = true;
 				m_bProcessed = true;
 			}
 		}
@@ -46,77 +66,81 @@ void COnscreenTimer::ProcessForDisplay() {
 }
 
 void COnscreenTimer::ClearCounter(uint32 offset) {
-	for(uint32 i = 0; i < NUMONSCREENTIMERENTRIES; i++) {
-		if(offset == m_sEntries[i].m_nCounterOffset) {
-			m_sEntries[i].m_nCounterOffset = 0;
-			m_sEntries[i].m_aCounterText[0] = 0;
-			m_sEntries[i].m_nType = COUNTER_DISPLAY_NUMBER;
-			m_sEntries[i].m_bCounterProcessed = 0;
+	for(uint32 i = 0; i < NUMONSCREENCOUNTERS; i++) {
+		if(offset == m_sCounters[i].m_nCounterOffset) {
+			m_sCounters[i].m_nCounterOffset = 0;
+			m_sCounters[i].m_aCounterText[0] = 0;
+			m_sCounters[i].m_nType = COUNTER_DISPLAY_NUMBER;
+			m_sCounters[i].m_bCounterProcessed = 0;
 		}
 	}
 }
 
 void COnscreenTimer::ClearClock(uint32 offset) {
-	for(uint32 i = 0; i < NUMONSCREENTIMERENTRIES; i++) {
-		if(offset == m_sEntries[i].m_nTimerOffset) {
-			m_sEntries[i].m_nTimerOffset = 0;
-			m_sEntries[i].m_aTimerText[0] = 0;
-			m_sEntries[i].m_bTimerProcessed = 0;
+	for(uint32 i = 0; i < NUMONSCREENCLOCKS; i++) {
+		if(offset == m_sClocks[i].m_nClockOffset) {
+			m_sClocks[i].m_nClockOffset = 0;
+			m_sClocks[i].m_aClockText[0] = 0;
+			m_sClocks[i].m_bClockProcessed = 0;
+			m_sClocks[i].m_bClockGoingDown = true;
 		}
 	}
 }
 
 void COnscreenTimer::AddCounter(uint32 offset, uint16 type, char* text, uint16 pos) {
 
-	m_sEntries[pos].m_nCounterOffset = offset;
-	if (m_sEntries[pos].m_aCounterText[0] != '\0')
+	if (m_sCounters[pos].m_aCounterText[0] != '\0')
 		return;
+
+	m_sCounters[pos].m_nCounterOffset = offset;
 	if(text) {
-		strncpy(m_sEntries[pos].m_aCounterText, text, 10);
+		strncpy(m_sCounters[pos].m_aCounterText, text, ARRAY_SIZE(COnscreenCounterEntry::m_aCounterText));
 	} else {
-		m_sEntries[pos].m_aCounterText[0] = 0;
+		m_sCounters[pos].m_aCounterText[0] = 0;
 	}
 
-	m_sEntries[pos].m_nType = type;
+	m_sCounters[pos].m_nType = type;
 }
 
 void COnscreenTimer::AddClock(uint32 offset, char* text, bool bGoingDown) {
-	uint32 i = 0;
-	for(uint32 i = 0; i < NUMONSCREENTIMERENTRIES; i++) {
-		if(m_sEntries[i].m_nTimerOffset == 0) {
+
+	// dead code in here
+	uint32 i;
+	for(i = 0; i < NUMONSCREENCLOCKS; i++) {
+		if(m_sClocks[i].m_nClockOffset == 0) {
 			break;
 		}
 		return;
 	}
 
-	m_sEntries[i].m_nTimerOffset = offset;
-	m_sEntries[i].m_bTimerGoingDown = bGoingDown;
+	m_sClocks[i].m_nClockOffset = offset;
+	m_sClocks[i].m_bClockGoingDown = bGoingDown;
 	if(text) {
-		strncpy(m_sEntries[i].m_aTimerText, text, 10);
+		strncpy(m_sClocks[i].m_aClockText, text, ARRAY_SIZE(COnscreenTimerEntry::m_aClockText));
 	} else {
-		m_sEntries[i].m_aTimerText[0] = 0;
+		m_sClocks[i].m_aClockText[0] = 0;
 	}
 }
 
 void COnscreenTimerEntry::Process() {
-	if(m_nTimerOffset == 0) {
+	if(m_nClockOffset == 0) {
 		return;
 	}
 
-	int32* timerPtr = CTheScripts::GetPointerToScriptVariable(m_nTimerOffset);
+	int32* timerPtr = CTheScripts::GetPointerToScriptVariable(m_nClockOffset);
 	int32 oldTime = *timerPtr;
-	if (m_bTimerGoingDown) {
+	if (m_bClockGoingDown) {
 		int32 newTime = oldTime - int32(CTimer::GetTimeStepInMilliseconds());
+		*timerPtr = newTime;
 		if (newTime < 0) {
 			*timerPtr = 0;
-			m_bTimerProcessed = 0;
-			m_nTimerOffset = 0;
-			m_aTimerText[0] = 0;
+			m_bClockProcessed = 0;
+			m_nClockOffset = 0;
+			m_aClockText[0] = 0;
 		}
 		else {
-			*timerPtr = newTime;
 			int32 oldTimeSeconds = oldTime / 1000;
-			if (oldTimeSeconds < 12 && newTime / 1000 != oldTimeSeconds) {
+			if (oldTimeSeconds < 12 && newTime / 1000 != oldTimeSeconds && !TheCamera.m_WideScreenOn) {
 				DMAudio.PlayFrontEndSound(SOUND_CLOCK_TICK, newTime / 1000);
 			}
 		}
@@ -125,33 +149,13 @@ void COnscreenTimerEntry::Process() {
 		*timerPtr = oldTime + int32(CTimer::GetTimeStepInMilliseconds());
 }
 
-bool COnscreenTimerEntry::ProcessForDisplay() {
-	m_bTimerProcessed = false;
-	m_bCounterProcessed = false;
-
-	if(m_nTimerOffset == 0 && m_nCounterOffset == 0) {
-		return false;
-	}
-
-	if(m_nTimerOffset != 0) {
-		m_bTimerProcessed = true;
-		ProcessForDisplayClock();
-	}
-
-	if(m_nCounterOffset != 0) {
-		m_bCounterProcessed = true;
-		ProcessForDisplayCounter();
-	}
-	return true;
-}
-
 void COnscreenTimerEntry::ProcessForDisplayClock() {
-	uint32 time = *CTheScripts::GetPointerToScriptVariable(m_nTimerOffset);
-	sprintf(m_bTimerBuffer, "%02d:%02d", time / 1000 / 60,
+	uint32 time = *CTheScripts::GetPointerToScriptVariable(m_nClockOffset);
+	sprintf(m_aClockBuffer, "%02d:%02d", time / 1000 / 60 % 100,
 				   time / 1000 % 60);
 }
 
-void COnscreenTimerEntry::ProcessForDisplayCounter() {
+void COnscreenCounterEntry::ProcessForDisplayCounter() {
 	uint32 counter = *CTheScripts::GetPointerToScriptVariable(m_nCounterOffset);
-	sprintf(m_bCounterBuffer, "%d", counter);
+	sprintf(m_aCounterBuffer, "%d", counter);
 }
diff --git a/src/control/OnscreenTimer.h b/src/control/OnscreenTimer.h
index 09473397..8c049d7d 100644
--- a/src/control/OnscreenTimer.h
+++ b/src/control/OnscreenTimer.h
@@ -9,30 +9,37 @@ enum
 class COnscreenTimerEntry
 {
 public:
-	uint32 m_nTimerOffset;
-	uint32 m_nCounterOffset;
-	char m_aTimerText[10];
-	char m_aCounterText[10];
-	uint16 m_nType;
-	char m_bCounterBuffer[42];
-	char m_bTimerBuffer[42];
-	bool m_bTimerProcessed;
-	bool m_bTimerGoingDown;
-	bool m_bCounterProcessed;
+	uint32 m_nClockOffset;
+	char m_aClockText[10];
+	char m_aClockBuffer[40];
+	bool m_bClockProcessed;
+	bool m_bClockGoingDown;
 
 	void Process();
-	bool ProcessForDisplay();
-
 	void ProcessForDisplayClock();
+};
+
+VALIDATE_SIZE(COnscreenTimerEntry, 0x3C);
+
+class COnscreenCounterEntry
+{
+public:
+	uint32 m_nCounterOffset;
+	char m_aCounterText[10];
+	uint16 m_nType;
+	char m_aCounterBuffer[40];
+	bool m_bCounterProcessed;
+
 	void ProcessForDisplayCounter();
 };
 
-VALIDATE_SIZE(COnscreenTimerEntry, 0x74);
+VALIDATE_SIZE(COnscreenCounterEntry, 0x3C);
 
 class COnscreenTimer
 {
 public:
-	COnscreenTimerEntry m_sEntries[NUMONSCREENTIMERENTRIES];
+	COnscreenTimerEntry m_sClocks[NUMONSCREENCLOCKS];
+	COnscreenCounterEntry m_sCounters[NUMONSCREENCOUNTERS];
 	bool m_bProcessed;
 	bool m_bDisabled;
 
@@ -47,4 +54,4 @@ public:
 	void AddClock(uint32 offset, char* text, bool bGoingDown);
 };
 
-VALIDATE_SIZE(COnscreenTimer, 0x78);
+VALIDATE_SIZE(COnscreenTimer, 0xF4);
diff --git a/src/control/Replay.cpp b/src/control/Replay.cpp
index 796e4cab..034cfe4d 100644
--- a/src/control/Replay.cpp
+++ b/src/control/Replay.cpp
@@ -51,6 +51,7 @@
 #include "Camera.h"
 #include "Radar.h"
 #include "Fluff.h"
+#include "WaterCreatures.h"
 
 uint8 CReplay::Mode;
 CAddressInReplayBuffer CReplay::Record;
@@ -1300,7 +1301,7 @@ void CReplay::TriggerPlayback(uint8 cam_mode, float cam_x, float cam_y, float ca
 	DMAudio.SetEffectsFadeVol(0);
 	DMAudio.SetMusicFadeVol(0);
 	CEscalators::Shutdown();
-	// TODO(MIAMI): CWaterCreatures::RemoveAll();
+	CWaterCreatures::RemoveAll();
 	int current;
 	for (current = 0; current < NUM_REPLAYBUFFERS; current++)
 		if (BufferStatus[current] == REPLAYBUFFER_RECORD)
diff --git a/src/core/CdStreamPosix.cpp b/src/core/CdStreamPosix.cpp
index e114a29a..a6ab62bc 100644
--- a/src/core/CdStreamPosix.cpp
+++ b/src/core/CdStreamPosix.cpp
@@ -34,11 +34,11 @@ struct CdReadInfo
 	bool bReading;
 	int32 nStatus;
 #ifdef ONE_THREAD_PER_CHANNEL
-	int8 nThreadStatus; // 0: created 1:initalized 2:abort now
-    pthread_t pChannelThread;
-    sem_t pStartSemaphore;
+	int8 nThreadStatus; // 0: created 1:priority set up 2:abort now
+	pthread_t pChannelThread;
+	sem_t *pStartSemaphore;
 #endif
-	sem_t pDoneSemaphore; // used for CdStreamSync
+	sem_t *pDoneSemaphore; // used for CdStreamSync
 	int32 hFile;
 };
 
@@ -51,8 +51,8 @@ char *gImgNames[MAX_CDIMAGES];
 
 #ifndef ONE_THREAD_PER_CHANNEL
 pthread_t _gCdStreamThread;
-sem_t gCdStreamSema; // released when we have new thing to read(so channel is set)
-int8 gCdStreamThreadStatus; // 0: created 1:initalized 2:abort now
+sem_t *gCdStreamSema; // released when we have new thing to read(so channel is set)
+int8 gCdStreamThreadStatus; // 0: created 1:priority set up 2:abort now
 Queue gChannelRequestQ;
 bool _gbCdStreamOverlapped;
 #endif
@@ -69,49 +69,50 @@ void
 CdStreamInitThread(void)
 {
 	int status;
-
+	char semName[20];
 #ifndef ONE_THREAD_PER_CHANNEL
 	gChannelRequestQ.items = (int32 *)calloc(gNumChannels + 1, sizeof(int32));
 	gChannelRequestQ.head = 0;
 	gChannelRequestQ.tail = 0;
 	gChannelRequestQ.size = gNumChannels + 1;
 	ASSERT(gChannelRequestQ.items != nil );
-    status = sem_init(&gCdStreamSema, 0, 0);
-#endif
+	gCdStreamSema = sem_open("/semaphore_cd_stream", O_CREAT, 0644, 1);
 
 
-	if (status == -1) {
+	if (gCdStreamSema == SEM_FAILED) {
 		CDTRACE("failed to create stream semaphore");
 		ASSERT(0);
 		return;
 	}
-
+#endif
 
 	if ( gNumChannels > 0 )
 	{
 		for ( int32 i = 0; i < gNumChannels; i++ )
 		{
-		    status = sem_init(&gpReadInfo[i].pDoneSemaphore, 0, 0);
+			sprintf(semName,"/semaphore_done%d",i);
+			gpReadInfo[i].pDoneSemaphore = sem_open(semName, O_CREAT, 0644, 1);
 
-			if (status == -1)
+			if (gpReadInfo[i].pDoneSemaphore == SEM_FAILED)
 			{
 				CDTRACE("failed to create sync semaphore");
 				ASSERT(0);
 				return;
 			}
 #ifdef ONE_THREAD_PER_CHANNEL
-		    status = sem_init(&gpReadInfo[i].pStartSemaphore, 0, 0);
+			sprintf(semName,"/semaphore_start%d",i);
+			gpReadInfo[i].pStartSemaphore = sem_open(semName, O_CREAT, 0644, 1);
 
-			if (status == -1)
+			if (gpReadInfo[i].pStartSemaphore == SEM_FAILED)
 			{
 				CDTRACE("failed to create start semaphore");
 				ASSERT(0);
 				return;
 			}
-            gpReadInfo[i].nThreadStatus = 0;
+			gpReadInfo[i].nThreadStatus = 0;
 			int *channelI = (int*)malloc(sizeof(int));
 			*channelI = i;
-            status = pthread_create(&gpReadInfo[i].pChannelThread, NULL, CdStreamThread, (void*)channelI);
+			status = pthread_create(&gpReadInfo[i].pChannelThread, NULL, CdStreamThread, (void*)channelI);
 
 			if (status == -1)
 			{
@@ -124,32 +125,32 @@ CdStreamInitThread(void)
 	}
 
 #ifndef ONE_THREAD_PER_CHANNEL
-    debug("Using one streaming thread for all channels\n");
-    status = pthread_create(&_gCdStreamThread, NULL, CdStreamThread, nil);
-    gCdStreamThreadStatus = 0;
+	debug("Using one streaming thread for all channels\n");
+	gCdStreamThreadStatus = 0;
+	status = pthread_create(&_gCdStreamThread, NULL, CdStreamThread, nil);
 
-    if (status == -1)
-    {
-        CDTRACE("failed to create sync thread");
-        ASSERT(0);
-        return;
-    }
+	if (status == -1)
+	{
+		CDTRACE("failed to create sync thread");
+		ASSERT(0);
+		return;
+	}
 #else
-    debug("Using seperate streaming threads for each channel\n");
+	debug("Using separate streaming threads for each channel\n");
 #endif
 }
 
 void
 CdStreamInit(int32 numChannels)
 {
-    struct statvfs fsInfo;
+	struct statvfs fsInfo;
 
-    if((statvfs("models/gta3.img", &fsInfo)) < 0)
-    {
-        CDTRACE("can't get filesystem info");
-        ASSERT(0);
-        return;
-    }
+	if((statvfs("models/gta3.img", &fsInfo)) < 0)
+	{
+		CDTRACE("can't get filesystem info");
+		ASSERT(0);
+		return;
+	}
 #ifdef __linux__
 	_gdwCdStreamFlags = O_RDONLY | O_NOATIME;
 #else
@@ -163,7 +164,7 @@ CdStreamInit(int32 numChannels)
 		debug("Using no buffered loading for streaming\n");
 	}
 */
-	void *pBuffer = (void *)RwMallocAlign(CDSTREAM_SECTOR_SIZE, fsInfo.f_bsize);
+	void *pBuffer = (void *)RwMallocAlign(CDSTREAM_SECTOR_SIZE, (RwUInt32)fsInfo.f_bsize);
 	ASSERT( pBuffer != nil );
 
 	gNumImages = 0;
@@ -185,27 +186,27 @@ uint32
 GetGTA3ImgSize(void)
 {
 	ASSERT( gImgFiles[0] > 0 );
-    struct stat statbuf;
+	struct stat statbuf;
 
-    char path[PATH_MAX];
-    realpath(gImgNames[0], path);
-    if (stat(path, &statbuf) == -1) {
+	char path[PATH_MAX];
+	realpath(gImgNames[0], path);
+	if (stat(path, &statbuf) == -1) {
 		// Try case-insensitivity
 		char* real = casepath(gImgNames[0], false);
 		if (real)
 		{
 			realpath(real, path);
 			free(real);
-    		if (stat(path, &statbuf) != -1)
+			if (stat(path, &statbuf) != -1)
 				goto ok;
 		}
 
-        CDTRACE("can't get size of gta3.img");
-        ASSERT(0);
-        return 0;
-    }
-ok:
-	return statbuf.st_size;
+		CDTRACE("can't get size of gta3.img");
+		ASSERT(0);
+		return 0;
+	}
+	ok:
+	return (uint32)statbuf.st_size;
 }
 
 void
@@ -213,15 +214,13 @@ CdStreamShutdown(void)
 {
     // Destroying semaphores and free(gpReadInfo) will be done at threads
 #ifndef ONE_THREAD_PER_CHANNEL
-    gCdStreamThreadStatus = 2;
-    sem_post(&gCdStreamSema);
-#endif
-
-#ifdef ONE_THREAD_PER_CHANNEL
-    for ( int32 i = 0; i < gNumChannels; i++ ) {
-        gpReadInfo[i].nThreadStatus = 2;
-        sem_post(&gpReadInfo[i].pStartSemaphore);
-    }
+	gCdStreamThreadStatus = 2;
+	sem_post(gCdStreamSema);
+#else
+	for ( int32 i = 0; i < gNumChannels; i++ ) {
+		gpReadInfo[i].nThreadStatus = 2;
+		sem_post(gpReadInfo[i].pStartSemaphore);
+	}
 #endif
 }
 
@@ -241,27 +240,33 @@ CdStreamRead(int32 channel, void *buffer, uint32 offset, uint32 size)
 	CdReadInfo *pChannel = &gpReadInfo[channel];
 	ASSERT( pChannel != nil );
 
+
+	if ( pChannel->nSectorsToRead != 0 || pChannel->bReading ) {
+		if (pChannel->nSectorOffset == _GET_OFFSET(offset) && pChannel->nSectorsToRead >= size)
+			return STREAM_SUCCESS;
+			
+		flushStream[channel] = 1;
+		CdStreamSync(channel);
+		//return STREAM_NONE;
+	}
+
 	pChannel->hFile = hImage - 1;
-
-    if ( pChannel->nSectorsToRead != 0 || pChannel->bReading )
-        return STREAM_NONE;
-
-    pChannel->nStatus = STREAM_NONE;
-    pChannel->nSectorOffset = _GET_OFFSET(offset);
-    pChannel->nSectorsToRead = size;
-    pChannel->pBuffer = buffer;
-    pChannel->bLocked = 0;
+	pChannel->nStatus = STREAM_NONE;
+	pChannel->nSectorOffset = _GET_OFFSET(offset);
+	pChannel->nSectorsToRead = size;
+	pChannel->pBuffer = buffer;
+	pChannel->bLocked = 0;
 
 #ifndef ONE_THREAD_PER_CHANNEL
-    AddToQueue(&gChannelRequestQ, channel);
-    if ( sem_post(&gCdStreamSema) != 0 )
-        printf("Signal Sema Error\n");
+	AddToQueue(&gChannelRequestQ, channel);
+	if ( sem_post(gCdStreamSema) != 0 )
+		printf("Signal Sema Error\n");
 #else
-    if ( sem_post(&gpReadInfo[channel].pStartSemaphore) != 0 )
-        printf("Signal Sema Error\n");
+	if ( sem_post(pChannel->pStartSemaphore) != 0 )
+		printf("Signal Sema Error\n");
 #endif
 
-    return STREAM_SUCCESS;
+	return STREAM_SUCCESS;
 }
 
 int32
@@ -272,29 +277,29 @@ CdStreamGetStatus(int32 channel)
 	ASSERT( pChannel != nil );
 
 #ifdef ONE_THREAD_PER_CHANNEL
-    if (pChannel->nThreadStatus == 2)
-        return STREAM_NONE;
+	if (pChannel->nThreadStatus == 2)
+		return STREAM_NONE;
 #else
-    if (gCdStreamThreadStatus == 2)
-        return STREAM_NONE;
+	if (gCdStreamThreadStatus == 2)
+		return STREAM_NONE;
 #endif
 
-    if ( pChannel->bReading )
-        return STREAM_READING;
+	if ( pChannel->bReading )
+		return STREAM_READING;
 
-    if ( pChannel->nSectorsToRead != 0 )
-        return STREAM_WAITING;
+	if ( pChannel->nSectorsToRead != 0 )
+		return STREAM_WAITING;
 
-    if ( pChannel->nStatus != STREAM_NONE )
-    {
-        int32 status = pChannel->nStatus;
+	if ( pChannel->nStatus != STREAM_NONE )
+	{
+		int32 status = pChannel->nStatus;
 
-        pChannel->nStatus = STREAM_NONE;
+		pChannel->nStatus = STREAM_NONE;
 
-        return status;
-    }
+		return status;
+	}
 
-    return STREAM_NONE;
+	return STREAM_NONE;
 }
 
 int32
@@ -314,30 +319,35 @@ CdStreamSync(int32 channel)
 	if (flushStream[channel]) {
 #ifdef ONE_THREAD_PER_CHANNEL
 		pChannel->nSectorsToRead = 0;
-		pthread_kill(gpReadInfo[channel].pChannelThread, SIGINT);
-#else
+		pthread_kill(pChannel->pChannelThread, SIGUSR1);
 		if (pChannel->bReading) {
-			pChannel->nSectorsToRead = 0;
-			pthread_kill(_gCdStreamThread, SIGINT);
-		} else {
-			pChannel->nSectorsToRead = 0;
+			pChannel->bLocked = true;
+			sem_wait(pChannel->pDoneSemaphore);
+		}
+#else
+		pChannel->nSectorsToRead = 0;
+		if (pChannel->bReading) {
+			pChannel->bLocked = true;
+			pthread_kill(_gCdStreamThread, SIGUSR1);
+			sem_wait(pChannel->pDoneSemaphore);
+			
 		}
 #endif
 		pChannel->bReading = false;
 		flushStream[channel] = false;
-	    return STREAM_NONE;
+		return STREAM_NONE;
 	}
 
-    if ( pChannel->nSectorsToRead != 0 )
-    {
-        pChannel->bLocked = true;
+	if ( pChannel->nSectorsToRead != 0 )
+	{
+		pChannel->bLocked = true;
 
-        sem_wait(&pChannel->pDoneSemaphore);
-    }
+		sem_wait(pChannel->pDoneSemaphore);
+	}
 
-    pChannel->bReading = false;
+	pChannel->bReading = false;
 
-    return pChannel->nStatus;
+	return pChannel->nStatus;
 }
 
 void
@@ -382,53 +392,52 @@ void *CdStreamThread(void *param)
 	debug("Created cdstream thread\n");
 
 #ifndef ONE_THREAD_PER_CHANNEL
-    while (gCdStreamThreadStatus != 2) {
-		sem_wait(&gCdStreamSema);
-        int32 channel = GetFirstInQueue(&gChannelRequestQ);
+	while (gCdStreamThreadStatus != 2) {
+		sem_wait(gCdStreamSema);
+		int32 channel = GetFirstInQueue(&gChannelRequestQ);
 #else
-    int channel = *((int*)param);
-    while (gpReadInfo[channel].nThreadStatus != 2){
-		sem_wait(&gpReadInfo[channel].pStartSemaphore);
+	int channel = *((int*)param);
+	while (gpReadInfo[channel].nThreadStatus != 2){
+		sem_wait(gpReadInfo[channel].pStartSemaphore);
 #endif
-		ASSERT( channel < gNumChannels );
 
 		CdReadInfo *pChannel = &gpReadInfo[channel];
 		ASSERT( pChannel != nil );
 
-#ifdef ONE_THREAD_PER_CHANNEL
-        if (gpReadInfo[channel].nThreadStatus == 0){
-            gpReadInfo[channel].nThreadStatus = 1;
-#else
-        if (gCdStreamThreadStatus == 0){
-            gCdStreamThreadStatus = 1;
-#endif
-
-#ifdef __linux__
-            pid_t tid = syscall(SYS_gettid);
-            int ret = setpriority(PRIO_PROCESS, tid, getpriority(PRIO_PROCESS, getpid()) + 1);
-#endif
-	}
-
 		// spurious wakeup or we sent interrupt signal for flushing
 		if(pChannel->nSectorsToRead == 0)
-            continue;
+			continue;
 
 		pChannel->bReading = true;
 
+		// Not standard POSIX :shrug:
+#ifdef __linux__
+#ifdef ONE_THREAD_PER_CHANNEL
+		if (gpReadInfo[channel].nThreadStatus == 0){
+			gpReadInfo[channel].nThreadStatus = 1;
+#else
+		if (gCdStreamThreadStatus == 0){
+			gCdStreamThreadStatus = 1;
+#endif
+			pid_t tid = syscall(SYS_gettid);
+			int ret = setpriority(PRIO_PROCESS, tid, getpriority(PRIO_PROCESS, getpid()) + 1);
+		}
+#endif
 		if ( pChannel->nStatus == STREAM_NONE )
 		{
-            ASSERT(pChannel->hFile >= 0);
-            ASSERT(pChannel->pBuffer != nil );
+			ASSERT(pChannel->hFile >= 0);
+			ASSERT(pChannel->pBuffer != nil );
 
 			lseek(pChannel->hFile, pChannel->nSectorOffset * CDSTREAM_SECTOR_SIZE, SEEK_SET);
-		    if (read(pChannel->hFile, pChannel->pBuffer, pChannel->nSectorsToRead * CDSTREAM_SECTOR_SIZE) == -1) {
+			if (read(pChannel->hFile, pChannel->pBuffer, pChannel->nSectorsToRead * CDSTREAM_SECTOR_SIZE) == -1) {
 				// pChannel->nSectorsToRead == 0 at this point means we wanted to flush channel
-                pChannel->nStatus = pChannel->nSectorsToRead == 0 ? STREAM_NONE : STREAM_ERROR;
-            } else {
-                pChannel->nStatus = STREAM_NONE;
-            }
+				// STREAM_WAITING is a little hack to make CStreaming not process this data
+				pChannel->nStatus = pChannel->nSectorsToRead == 0 ? STREAM_WAITING : STREAM_ERROR;
+			} else {
+				pChannel->nStatus = STREAM_NONE;
+			}
 		}
-	
+
 #ifndef ONE_THREAD_PER_CHANNEL
 		RemoveFirstInQueue(&gChannelRequestQ);
 #endif
@@ -437,22 +446,33 @@ void *CdStreamThread(void *param)
 
 		if ( pChannel->bLocked )
 		{
-			sem_post(&pChannel->pDoneSemaphore);
+			sem_post(pChannel->pDoneSemaphore);	
 		}
 		pChannel->bReading = false;
 	}
+	char semName[20];
 #ifndef ONE_THREAD_PER_CHANNEL
-    for ( int32 i = 0; i < gNumChannels; i++ )
-    {
-        sem_destroy(&gpReadInfo[i].pDoneSemaphore);
-    }
-    sem_destroy(&gCdStreamSema);
+	for ( int32 i = 0; i < gNumChannels; i++ )
+	{
+		sem_close(gpReadInfo[i].pDoneSemaphore);
+		sprintf(semName,"/semaphore_done%d",i);
+		sem_unlink(semName);
+	}
+	sem_close(gCdStreamSema);
+	sem_unlink("/semaphore_cd_stream");
 	free(gChannelRequestQ.items);
 #else
-    sem_destroy(&gpReadInfo[channel].pStartSemaphore);
-    sem_destroy(&gpReadInfo[channel].pDoneSemaphore);
+	sem_close(gpReadInfo[channel].pStartSemaphore);
+	sprintf(semName,"/semaphore_start%d",channel);
+	sem_unlink(semName);
+
+	sem_close(gpReadInfo[channel].pDoneSemaphore);
+	sprintf(semName,"/semaphore_done%d",channel);
+	sem_unlink(semName);
 #endif
-    free(gpReadInfo);
+	if (gpReadInfo)
+		free(gpReadInfo);
+	gpReadInfo = nil;
 	pthread_exit(nil);
 }
 
@@ -469,7 +489,7 @@ CdStreamAddImage(char const *path)
 		char* real = casepath(path, false);
 		if (real)
 		{
-		    gImgFiles[gNumImages] = open(real, _gdwCdStreamFlags);
+			gImgFiles[gNumImages] = open(real, _gdwCdStreamFlags);
 			free(real);
 		}
 	}
@@ -502,8 +522,10 @@ CdStreamGetImageName(int32 cd)
 void
 CdStreamRemoveImages(void)
 {
-	for ( int32 i = 0; i < gNumChannels; i++ )
+	for ( int32 i = 0; i < gNumChannels; i++ ) {
+		flushStream[i] = 1;
 		CdStreamSync(i);
+	}
 
 	for ( int32 i = 0; i < gNumImages; i++ )
 	{
diff --git a/src/core/FileLoader.cpp b/src/core/FileLoader.cpp
index 635192ea..da678fd3 100644
--- a/src/core/FileLoader.cpp
+++ b/src/core/FileLoader.cpp
@@ -1052,7 +1052,11 @@ CFileLoader::Load2dEffect(const char *line)
 			&effect->attractor.dir.z,
 			&probability);
 		effect->attractor.type = flags;
+#ifdef FIX_BUGS
+		effect->attractor.probability = clamp(probability, 0, 255);
+#else
 		effect->attractor.probability = probability;
+#endif
 		break;
 	case EFFECT_PED_ATTRACTOR:
 		sscanf(line, "%d %f %f %f %d %d %d %d %d %d %f %f %f %f %f %f",
diff --git a/src/core/FileMgr.cpp b/src/core/FileMgr.cpp
index 4477a190..d3695cb4 100644
--- a/src/core/FileMgr.cpp
+++ b/src/core/FileMgr.cpp
@@ -142,17 +142,17 @@ static size_t
 myfread(void *buf, size_t elt, size_t n, int fd)
 {
 	if(myfiles[fd].isText){
-		char *p;
+		unsigned char *p;
 		size_t i;
 		int c;
 
 		n *= elt;
-		p = (char*)buf;
+		p = (unsigned char*)buf;
 		for(i = 0; i < n; i++){
 			c = myfgetc(fd);
 			if(c == EOF)
 				break;
-			*p++ = c;
+			*p++ = (unsigned char)c;
 		}
 		return i / elt;
 	}
@@ -163,12 +163,12 @@ static size_t
 myfwrite(void *buf, size_t elt, size_t n, int fd)
 {
 	if(myfiles[fd].isText){
-		char *p;
+		unsigned char *p;
 		size_t i;
 		int c;
 
 		n *= elt;
-		p = (char*)buf;
+		p = (unsigned char*)buf;
 		for(i = 0; i < n; i++){
 			c = *p++;
 			myfputc(c, fd);
diff --git a/src/core/Fire.cpp b/src/core/Fire.cpp
index a6928def..5b57cb73 100644
--- a/src/core/Fire.cpp
+++ b/src/core/Fire.cpp
@@ -16,6 +16,9 @@
 #include "Ped.h"
 #include "Fire.h"
 #include "GameLogic.h"
+#include "CarAI.h"
+
+// --MIAMI: file done
 
 CFireManager gFireManager;
 
@@ -26,14 +29,13 @@ CFire::CFire()
 	m_bPropagationFlag = true;
 	m_bAudioSet = true;
 	m_vecPos = CVector(0.0f, 0.0f, 0.0f);
-	m_pEntity = nil;
-	m_pSource = nil;
-	m_nFiremenPuttingOut = 0;
 	m_nExtinguishTime = 0;
 	m_nStartTime = 0;
-	field_20 = 1;
-	m_nNextTimeToAddFlames = 0;
+	m_pEntity = nil;
+	m_pSource = nil;
 	m_fStrength = 0.8f;
+	m_fWaterExtinguishCountdown = 1.0f;
+  	m_bExtinguishedWithWater = false;
 }
 
 CFire::~CFire() {}
@@ -52,6 +54,8 @@ CFire::ProcessFire(void)
 	CPed *ped = (CPed *)m_pEntity;
 	CVehicle *veh = (CVehicle*)m_pEntity;
 
+	m_fWaterExtinguishCountdown = Min(1.0f, 0.002f * CTimer::GetTimeStep() + m_fWaterExtinguishCountdown);
+
 	if (m_pEntity) {
 		m_vecPos = m_pEntity->GetPosition();
 
@@ -109,7 +113,7 @@ CFire::ProcessFire(void)
 		gFireManager.StartFire(FindPlayerPed(), m_pSource, 0.8f, 1);
 	}
 	if (CTimer::GetTimeInMilliseconds() > m_nNextTimeToAddFlames) {
-		m_nNextTimeToAddFlames = CTimer::GetTimeInMilliseconds() + 80;
+		m_nNextTimeToAddFlames = CTimer::GetTimeInMilliseconds() + (m_fWaterExtinguishCountdown < 0.3f ? 400 : (m_fWaterExtinguishCountdown < 0.7f ? 200 : 80));
 		firePos = m_vecPos;
 
 		if (veh && veh->IsVehicle() && veh->IsCar()) {
@@ -170,11 +174,23 @@ CFire::Extinguish(void)
 
 		m_nExtinguishTime = 0;
 		m_bIsOngoing = false;
+		m_bExtinguishedWithWater = false;
 
 		if (m_pEntity) {
 			if (m_pEntity->IsPed()) {
-				((CPed *)m_pEntity)->RestorePreviousState();
-				((CPed *)m_pEntity)->m_pFire = nil;
+				CPed *ped = (CPed*)m_pEntity;
+				if (ped->CanSetPedState()) {
+					if (ped->m_nPedState != PED_DRIVING && ped->m_nPedState != PED_FALL) {
+						if (ped->IsPlayer()) {
+							ped->SetIdle();
+						} else {
+							ped->m_nLastPedState = PED_NONE;
+							ped->SetWanderPath(0);
+							ped->SetWaitState(WAITSTATE_FINISH_FLEE, 0);
+						}
+					}
+				}
+				ped->m_pFire = nil;
 			} else if (m_pEntity->IsVehicle()) {
 				((CVehicle *)m_pEntity)->m_pCarFire = nil;
 			}
@@ -184,7 +200,7 @@ CFire::Extinguish(void)
 }
 
 void
-CFireManager::StartFire(CVector pos, float size, bool propagation)
+CFireManager::StartFire(CVector pos, float size, uint8 propagation)
 {
 	CFire *fire = GetNextFreeFire();
 
@@ -201,11 +217,12 @@ CFireManager::StartFire(CVector pos, float size, bool propagation)
 		fire->m_nNextTimeToAddFlames = 0;
 		fire->ReportThisFire();
 		fire->m_fStrength = size;
+		fire->m_bExtinguishedWithWater = false;
 	}
 }
 
 CFire *
-CFireManager::StartFire(CEntity *entityOnFire, CEntity *fleeFrom, float strength, bool propagation)
+CFireManager::StartFire(CEntity *entityOnFire, CEntity *fleeFrom, float strength, uint8 propagation)
 {
 	CPed *ped = (CPed *)entityOnFire;
 	CVehicle *veh = (CVehicle *)entityOnFire;
@@ -234,10 +251,11 @@ CFireManager::StartFire(CEntity *entityOnFire, CEntity *fleeFrom, float strength
 					ped->SetFlee(pos, 10000);
 					ped->m_fleeFrom = nil;
 				}
+				ped->m_fleeTimer = CTimer::GetTimeInMilliseconds() + 10000;
 				ped->bDrawLast = false;
 				ped->SetMoveState(PEDMOVE_SPRINT);
 				ped->SetMoveAnim();
-				ped->m_nPedState = PED_ON_FIRE;
+				ped->SetPedState(PED_ON_FIRE);
 			}
 			if (fleeFrom) {
 				if (ped->m_nPedType == PEDTYPE_COP) {
@@ -251,6 +269,9 @@ CFireManager::StartFire(CEntity *entityOnFire, CEntity *fleeFrom, float strength
 		} else {
 			if (entityOnFire->IsVehicle()) {
 				veh->m_pCarFire = fire;
+				if (CModelInfo::IsBikeModel(veh->GetModelIndex()) || CModelInfo::IsCarModel(veh->GetModelIndex()))
+        				CCarAI::TellOccupantsToFleeCar(veh);
+
 				if (fleeFrom) {
 					CEventList::RegisterEvent(EVENT_CAR_SET_ON_FIRE, EVENT_ENTITY_VEHICLE,
 						entityOnFire, (CPed *)fleeFrom, 10000);
@@ -259,6 +280,7 @@ CFireManager::StartFire(CEntity *entityOnFire, CEntity *fleeFrom, float strength
 		}
 
 		fire->m_bIsOngoing = true;
+		fire->m_bExtinguishedWithWater = false;
 		fire->m_bIsScriptFire = false;
 		fire->m_vecPos = entityOnFire->GetPosition();
 
@@ -297,26 +319,23 @@ CFireManager::Update(void)
 
 CFire* CFireManager::FindNearestFire(CVector vecPos, float *pDistance)
 {
-	for (int i = 0; i < MAX_FIREMEN_ATTENDING; i++) {
-		int fireId = -1;
-		float minDistance = 999999;
-		for (int j = 0; j < NUM_FIRES; j++) {
-			if (!m_aFires[j].m_bIsOngoing)
-				continue;
-			if (m_aFires[j].m_bIsScriptFire)
-				continue;
-			if (m_aFires[j].m_nFiremenPuttingOut != i)
-				continue;
-			float distance = (m_aFires[j].m_vecPos - vecPos).Magnitude2D();
-			if (distance < minDistance) {
-				minDistance = distance;
-				fireId = j;
-			}
+	int fireId = -1;
+	float minDistance = 999999;
+	for (int j = 0; j < NUM_FIRES; j++) {
+		if (!m_aFires[j].m_bIsOngoing)
+			continue;
+		if (m_aFires[j].m_bIsScriptFire)
+			continue;
+		float distance = (m_aFires[j].m_vecPos - vecPos).Magnitude2D();
+		if (distance < minDistance) {
+			minDistance = distance;
+			fireId = j;
 		}
-		*pDistance = minDistance;
-		if (fireId != -1)
-			return &m_aFires[fireId];
 	}
+	*pDistance = minDistance;
+	if (fireId != -1)
+		return &m_aFires[fireId];
+
 	return nil;
 }
 
@@ -369,8 +388,39 @@ CFireManager::ExtinguishPoint(CVector point, float range)
 	}
 }
 
+bool
+CFireManager::ExtinguishPointWithWater(CVector point, float range)
+{
+	int fireI = 0;
+	for (int i = 0; i < NUM_FIRES; i++) {
+		if (m_aFires[i].m_bIsOngoing) {
+			if ((point - m_aFires[i].m_vecPos).MagnitudeSqr() < sq(range)) {
+				fireI = i;
+				break;
+			}
+		}
+	}
+	if (fireI == NUM_FIRES)
+		return false;
+
+	CFire *fireToExtinguish = &m_aFires[fireI];
+	fireToExtinguish->m_fWaterExtinguishCountdown -= 0.012f * CTimer::GetTimeStep();
+	CVector steamPos = fireToExtinguish->m_vecPos +
+		CVector((CGeneral::GetRandomNumber() - 128) * 31.f / 200.f,
+			(CGeneral::GetRandomNumber() - 128) * 31.f / 200.f,
+			CGeneral::GetRandomNumber() / 200.f);
+
+	CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, steamPos, CVector(0.f, 0.f, 0.2f), nil, 0.5f);
+	CParticle::AddParticle(PARTICLE_STEAM_NY_SLOWMOTION, steamPos, CVector(0.f, 0.f, 0.1f), nil, 0.8f);
+	fireToExtinguish->m_bExtinguishedWithWater = true;
+	if (fireToExtinguish->m_fWaterExtinguishCountdown < 0.0f )
+	  fireToExtinguish->Extinguish();
+
+	return true;
+}
+
 int32
-CFireManager::StartScriptFire(const CVector &pos, CEntity *target, float strength, bool propagation)
+CFireManager::StartScriptFire(const CVector &pos, CEntity *target, float strength, uint8 propagation)
 {
 	CFire *fire;
 	CPed *ped = (CPed *)target;
@@ -397,12 +447,15 @@ CFireManager::StartScriptFire(const CVector &pos, CEntity *target, float strengt
 	fire->m_vecPos = pos;
 	fire->m_nStartTime = CTimer::GetTimeInMilliseconds() + 400;
 	fire->m_pEntity = target;
+	fire->m_bExtinguishedWithWater = false;
 
 	if (target)
 		target->RegisterReference(&fire->m_pEntity);
 	fire->m_pSource = nil;
 	fire->m_nNextTimeToAddFlames = 0;
 	fire->m_fStrength = strength;
+	fire->m_fWaterExtinguishCountdown = 1.0f;
+
 	if (target) {
 		if (target->IsPed()) {
 			ped->m_pFire = fire;
@@ -410,7 +463,7 @@ CFireManager::StartScriptFire(const CVector &pos, CEntity *target, float strengt
 				CVector2D pos = target->GetPosition();
 				ped->SetFlee(pos, 10000);
 				ped->SetMoveAnim();
-				ped->m_nPedState = PED_ON_FIRE;
+				ped->SetPedState(PED_ON_FIRE);
 			}
 		} else if (target->IsVehicle()) {
 			veh->m_pCarFire = fire;
@@ -430,8 +483,7 @@ CFireManager::RemoveAllScriptFires(void)
 {
 	for (int i = 0; i < NUM_FIRES; i++) {
 		if (m_aFires[i].m_bIsScriptFire) {
-			m_aFires[i].Extinguish();
-			m_aFires[i].m_bIsScriptFire = false;
+			RemoveScriptFire(i);
 		}
 	}
 }
diff --git a/src/core/Fire.h b/src/core/Fire.h
index 85e53f61..8126f830 100644
--- a/src/core/Fire.h
+++ b/src/core/Fire.h
@@ -14,10 +14,10 @@ public:
 	CEntity *m_pSource;
 	uint32 m_nExtinguishTime;
 	uint32 m_nStartTime;
-	int32 field_20;
 	uint32 m_nNextTimeToAddFlames;
-	uint32 m_nFiremenPuttingOut;
 	float m_fStrength;
+	float m_fWaterExtinguishCountdown;
+	bool m_bExtinguishedWithWater;
 
 	CFire();
 	~CFire();
@@ -34,15 +34,17 @@ class CFireManager
 public:
 	uint32 m_nTotalFires;
 	CFire m_aFires[NUM_FIRES];
-	void StartFire(CVector pos, float size, bool propagation);
-	CFire *StartFire(CEntity *entityOnFire, CEntity *fleeFrom, float strength, bool propagation);
+
+	void StartFire(CVector pos, float size, uint8 propagation);
+	CFire *StartFire(CEntity *entityOnFire, CEntity *fleeFrom, float strength, uint8 propagation);
 	void Update(void);
 	CFire *FindFurthestFire_NeverMindFireMen(CVector coords, float minRange, float maxRange);
 	CFire *FindNearestFire(CVector vecPos, float *pDistance);
 	CFire *GetNextFreeFire(void);
 	uint32 GetTotalActiveFires() const;
 	void ExtinguishPoint(CVector point, float range);
-	int32 StartScriptFire(const CVector &pos, CEntity *target, float strength, bool propagation);
+	bool ExtinguishPointWithWater(CVector point, float range);
+	int32 StartScriptFire(const CVector &pos, CEntity *target, float strength, uint8 propagation);
 	bool IsScriptFireExtinguish(int16 index);
 	void RemoveAllScriptFires(void);
 	void RemoveScriptFire(int16 index);
diff --git a/src/core/Game.cpp b/src/core/Game.cpp
index 6ba6b191..a8637980 100644
--- a/src/core/Game.cpp
+++ b/src/core/Game.cpp
@@ -90,6 +90,7 @@
 #include "debugmenu.h"
 #include "Ropes.h"
 #include "WindModifiers.h"
+#include "WaterCreatures.h"
 #include "postfx.h"
 #include "custompipes.h"
 
@@ -478,8 +479,7 @@ bool CGame::ShutDown(void)
 	CPlane::Shutdown();
 	CTrain::Shutdown();
 	CScriptPaths::Shutdown();
-	// TODO(Miami)
-	// CWaterCreatures::RemoveAll();
+	CWaterCreatures::RemoveAll();
 	CSpecialFX::Shutdown();
 #ifndef PS2
 	CGarages::Shutdown();
@@ -650,7 +650,7 @@ void CGame::ShutDownForRestart(void)
 	CRadar::RemoveRadarSections();
 	FrontEndMenuManager.UnloadTextures();
 	CParticleObject::RemoveAllExpireableParticleObjects();
-	//CWaterCreatures::RemoveAll(); //TODO(Miami)
+	CWaterCreatures::RemoveAll(); 
 	CSetPieces::Init();
 	CPedType::Shutdown();
 	CSpecialFX::Shutdown();
diff --git a/src/core/General.h b/src/core/General.h
index 7ab444a4..7e06b96e 100644
--- a/src/core/General.h
+++ b/src/core/General.h
@@ -2,6 +2,8 @@
 
 #include <ctype.h>
 
+// --MIAMI: file done
+
 class CGeneral
 {
 public:
diff --git a/src/core/Pad.cpp b/src/core/Pad.cpp
index aa6e5cc8..b723eb1d 100644
--- a/src/core/Pad.cpp
+++ b/src/core/Pad.cpp
@@ -3209,7 +3209,7 @@ int16 CPad::SniperModeLookLeftRight(void)
 
 	if ( Abs(axis) > Abs(dpad) ) {
 		if ( Abs(axis) > 35.0f ) {
-		  return (axis > 0.f ? axis - 35.f : axis + 35.f) * 1.3763441f;
+		  return (axis > 0.f ? axis - 35.f : axis + 35.f) * (128.f / (128 - 35));
 		} else {
 		  return 0;
 		}
@@ -3231,7 +3231,7 @@ int16 CPad::SniperModeLookUpDown(void)
 
 	if ( Abs(axis) > Abs(dpad) ) {
 	    if ( Abs(axis) > 35.0f ) {
-	      return (axis > 0.f ? axis - 35.f : axis + 35.f) * 1.3763441f;
+	      return (axis > 0.f ? axis - 35.f : axis + 35.f) * (128.f / (128 - 35));
 	    } else {
 	      return 0;
 	    }
@@ -3573,4 +3573,4 @@ void CPad::FixPadsAfterSave(void)
 		bObsoleteControllerMessage = false;
 		GetPad(0)->Phase = 0;
 	}
-}
\ No newline at end of file
+}
diff --git a/src/core/Streaming.cpp b/src/core/Streaming.cpp
index d296496e..ddde4664 100644
--- a/src/core/Streaming.cpp
+++ b/src/core/Streaming.cpp
@@ -46,7 +46,11 @@ CStreamingInfo CStreaming::ms_endRequestedList;
 int32 CStreaming::ms_oldSectorX;
 int32 CStreaming::ms_oldSectorY;
 int32 CStreaming::ms_streamingBufferSize;
+#ifndef ONE_THREAD_PER_CHANNEL
 int8 *CStreaming::ms_pStreamingBuffer[2];
+#else
+int8 *CStreaming::ms_pStreamingBuffer[4];
+#endif
 size_t CStreaming::ms_memoryUsed;
 CStreamingChannel CStreaming::ms_channel[2];
 int32 CStreaming::ms_channelError;
@@ -197,6 +201,10 @@ CStreaming::Init2(void)
 	ms_pStreamingBuffer[0] = (int8*)RwMallocAlign(ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE, CDSTREAM_SECTOR_SIZE);
 	ms_streamingBufferSize /= 2;
 	ms_pStreamingBuffer[1] = ms_pStreamingBuffer[0] + ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE;
+#ifdef ONE_THREAD_PER_CHANNEL
+	ms_pStreamingBuffer[2] = (int8*)RwMallocAlign(ms_streamingBufferSize*2*CDSTREAM_SECTOR_SIZE, CDSTREAM_SECTOR_SIZE);
+	ms_pStreamingBuffer[3] = ms_pStreamingBuffer[2] + ms_streamingBufferSize*CDSTREAM_SECTOR_SIZE;
+#endif
 	debug("Streaming buffer size is %d sectors", ms_streamingBufferSize);
 
 	// PC only, figure out how much memory we got
@@ -2196,6 +2204,110 @@ CStreaming::LoadRequestedModels(void)
 	}
 }
 
+
+// Let's load models first, then process it. Unfortunately processing models are still single-threaded.
+// Currently only supported on POSIX streamer.
+#ifdef ONE_THREAD_PER_CHANNEL
+void
+CStreaming::LoadAllRequestedModels(bool priority)
+{
+	static bool bInsideLoadAll = false;
+	int imgOffset, streamId, status;
+	int i;
+	uint32 posn, size;
+
+	if(bInsideLoadAll)
+		return;
+	bInsideLoadAll = true;
+
+	FlushChannels();
+	imgOffset = GetCdImageOffset(CdStreamGetLastPosn());
+
+	int streamIds[ARRAY_SIZE(ms_pStreamingBuffer)];
+	int streamSizes[ARRAY_SIZE(ms_pStreamingBuffer)];
+	int streamPoses[ARRAY_SIZE(ms_pStreamingBuffer)];
+	bool first = true;
+	int processI = 0;
+
+	while (true) {
+		// Enumerate files and start reading
+		for (int i=0; i<ARRAY_SIZE(ms_pStreamingBuffer); i++) {
+			if (!first && streamIds[i] != -1) {
+				processI = i;
+				continue;
+			}
+
+			if(ms_endRequestedList.m_prev != &ms_startRequestedList){
+				streamId = GetNextFileOnCd(0, priority);
+				if(streamId == -1){
+					streamIds[i] = -1;
+					break;
+				}
+
+				if (ms_aInfoForModel[streamId].GetCdPosnAndSize(posn, size)) {
+					streamIds[i] = -1;
+					if (size > (uint32)ms_streamingBufferSize) {
+						if (i + 1 == ARRAY_SIZE(ms_pStreamingBuffer))
+							continue;
+						else if (!first && streamIds[i+1] != -1)
+							continue;
+					} else {
+						if (i != 0 && streamIds[i-1] != -1 && streamSizes[i-1] > (uint32)ms_streamingBufferSize)
+							continue;
+					}
+					ms_aInfoForModel[streamId].RemoveFromList();
+					DecrementRef(streamId);
+
+					streamIds[i] = streamId;
+					streamSizes[i] = size;
+					streamPoses[i] = posn;
+					CdStreamRead(i, ms_pStreamingBuffer[i], imgOffset+posn, size);
+					processI = i;
+				} else {
+					ms_aInfoForModel[streamId].RemoveFromList();
+					DecrementRef(streamId);
+
+					ms_aInfoForModel[streamId].m_loadState = STREAMSTATE_LOADED;
+					streamIds[i] = -1;
+				}
+			} else
+				streamIds[i] = -1;
+		}
+
+		first = false;
+
+		// Now process
+		if (streamIds[processI] == -1) 
+			break;
+
+		// Try again on error
+		while (CdStreamSync(processI) != STREAM_NONE) {
+			CdStreamRead(processI, ms_pStreamingBuffer[processI], imgOffset+streamPoses[processI], streamSizes[processI]);
+		}
+		ms_aInfoForModel[streamIds[processI]].m_loadState = STREAMSTATE_READING;
+		
+		MakeSpaceFor(streamSizes[processI] * CDSTREAM_SECTOR_SIZE);
+		ConvertBufferToObject(ms_pStreamingBuffer[processI], streamIds[processI]);
+		if(ms_aInfoForModel[streamIds[processI]].m_loadState == STREAMSTATE_STARTED)
+			FinishLoadingLargeFile(ms_pStreamingBuffer[processI], streamIds[processI]);
+
+		if(streamIds[processI] < STREAM_OFFSET_TXD){
+			CSimpleModelInfo *mi = (CSimpleModelInfo*)CModelInfo::GetModelInfo(streamIds[processI]);
+			if(mi->IsSimple())
+				mi->m_alpha = 255;
+		}
+		streamIds[processI] = -1;
+	}
+
+	ms_bLoadingBigModel = false;
+	for(i = 0; i < 4; i++){
+		ms_channel[1].streamIds[i] = -1;
+		ms_channel[1].offsets[i] = -1;
+	}
+	ms_channel[1].state = CHANNELSTATE_IDLE;
+	bInsideLoadAll = false;
+}
+#else
 void
 CStreaming::LoadAllRequestedModels(bool priority)
 {
@@ -2256,6 +2368,7 @@ CStreaming::LoadAllRequestedModels(bool priority)
 	ms_channel[1].state = CHANNELSTATE_IDLE;
 	bInsideLoadAll = false;
 }
+#endif
 
 void
 CStreaming::FlushChannels(void)
@@ -2287,6 +2400,14 @@ CStreaming::FlushRequestList(void)
 		next = si->m_next;
 		RemoveModel(si - ms_aInfoForModel);
 	}
+#ifndef _WIN32
+	if(ms_channel[0].state == CHANNELSTATE_READING) {
+		flushStream[0] = 1;
+	}
+	if(ms_channel[1].state == CHANNELSTATE_READING) {
+		flushStream[1] = 1;
+	}
+#endif
 	FlushChannels();
 }
 
@@ -2815,10 +2936,15 @@ CStreaming::DeleteRwObjectsNotInFrustumInSectorList(CPtrList &list, size_t mem)
 void
 CStreaming::MakeSpaceFor(int32 size)
 {
-	// BUG: ms_memoryAvailable can be uninitialized
-	// the code still happens to work in that case because ms_memoryAvailable is unsigned
-	// but it's not nice....
-
+#ifdef FIX_BUGS
+#define MB (1024 * 1024)
+	if(ms_memoryAvailable == 0) {
+		extern size_t _dwMemAvailPhys;
+		ms_memoryAvailable = (_dwMemAvailPhys - 10 * MB) / 2;
+		if(ms_memoryAvailable < 65 * MB) ms_memoryAvailable = 65 * MB;
+	}
+#undef MB
+#endif
 	while(ms_memoryUsed >= ms_memoryAvailable - size)
 		if(!RemoveLeastUsedModel(STREAMFLAGS_20)){
 			DeleteRwObjectsBehindCamera(ms_memoryAvailable - size);
diff --git a/src/core/User.cpp b/src/core/User.cpp
index 53196d03..8d584b74 100644
--- a/src/core/User.cpp
+++ b/src/core/User.cpp
@@ -10,6 +10,8 @@
 #include "World.h"
 #include "Zones.h"
 
+// --MIAMI: file done
+
 CPlaceName CUserDisplay::PlaceName;
 COnscreenTimer CUserDisplay::OnscnTimer;
 CPager CUserDisplay::Pager;
diff --git a/src/core/Zones.cpp b/src/core/Zones.cpp
index 1eb1513e..288b975e 100644
--- a/src/core/Zones.cpp
+++ b/src/core/Zones.cpp
@@ -9,7 +9,7 @@
 #include "World.h"
 #include "Timer.h"
 
-//--MIAMI: file almost done (loading/saving will perhaps stay different)
+//--MIAMI: file done
 
 eLevelName CTheZones::m_CurrLevel;
 int16 CTheZones::FindIndex;
@@ -638,127 +638,132 @@ CTheZones::SaveAllZones(uint8 *buffer, uint32 *size)
 		+ sizeof(int16) // padding
 		+ sizeof(NavigationZoneArray) + sizeof(InfoZoneArray) + sizeof(ZoneInfoArray)
 		+ sizeof(TotalNumberOfNavigationZones) + sizeof(TotalNumberOfInfoZones) + sizeof(TotalNumberOfZoneInfos)
+		+ sizeof(int16) // padding
 		+ sizeof(MapZoneArray) + sizeof(AudioZoneArray)
 		+ sizeof(TotalNumberOfMapZones) + sizeof(NumberOfAudioZones);
 
-	WriteSaveHeader(buffer, 'Z', 'N', 'S', '\0', *size - SAVE_HEADER_SIZE);
+	uint32 length = 0;
+	WriteSaveHeaderWithLength(buffer, length, 'Z', 'N', 'S', '\0', *size - SAVE_HEADER_SIZE);
 
-	WriteSaveBuf(buffer, m_CurrLevel);
-	WriteSaveBuf(buffer, FindIndex);
-	WriteSaveBuf(buffer, (int16)0); // padding
+	WriteSaveBuf(buffer, length, m_CurrLevel);
+	WriteSaveBuf(buffer, length, FindIndex);
+	WriteSaveBuf(buffer, length, (int16)0); // padding
 
-// TODO(MIAMI) ? implement SaveOneZone
-	for(i = 0; i < ARRAY_SIZE(NavigationZoneArray); i++){
-		CZone *zone = WriteSaveBuf(buffer, NavigationZoneArray[i]);
-		zone->child = (CZone*)GetIndexForZonePointer(NavigationZoneArray[i].child);
-		zone->parent = (CZone*)GetIndexForZonePointer(NavigationZoneArray[i].parent);
-		zone->next = (CZone*)GetIndexForZonePointer(NavigationZoneArray[i].next);
-	}
+	for(i = 0; i < ARRAY_SIZE(NavigationZoneArray); i++)
+		SaveOneZone(&NavigationZoneArray[i], &buffer, &length, ZONE_NAVIG);
 
-	for(i = 0; i < ARRAY_SIZE(InfoZoneArray); i++){
-		CZone *zone = WriteSaveBuf(buffer, InfoZoneArray[i]);
-		/*
-		The call of GetIndexForZonePointer is wrong, as it is
-		meant for a different array, but the game doesn't brake
-		if those fields are nil. Let's make sure they are.
-		*/
-		assert(InfoZoneArray[i].child == nil);
-		assert(InfoZoneArray[i].parent == nil);
-		assert(InfoZoneArray[i].next == nil);
-		zone->child = (CZone*)GetIndexForZonePointer(InfoZoneArray[i].child);
-		zone->parent = (CZone*)GetIndexForZonePointer(InfoZoneArray[i].parent);
-		zone->next = (CZone*)GetIndexForZonePointer(InfoZoneArray[i].next);
-	}
+	for(i = 0; i < ARRAY_SIZE(InfoZoneArray); i++)
+		SaveOneZone(&InfoZoneArray[i], &buffer, &length, ZONE_INFO);
 
 	for(i = 0; i < ARRAY_SIZE(ZoneInfoArray); i++)
-		WriteSaveBuf(buffer, ZoneInfoArray[i]);
+		WriteSaveBuf(buffer, length, ZoneInfoArray[i]);
 
-	WriteSaveBuf(buffer, TotalNumberOfNavigationZones);
-	WriteSaveBuf(buffer, TotalNumberOfInfoZones);
-	WriteSaveBuf(buffer, TotalNumberOfZoneInfos);
+	WriteSaveBuf(buffer, length, TotalNumberOfNavigationZones);
+	WriteSaveBuf(buffer, length, TotalNumberOfInfoZones);
+	WriteSaveBuf(buffer, length, TotalNumberOfZoneInfos);
+	WriteSaveBuf(buffer, length, (int16)0); // padding
 
-	for(i = 0; i < ARRAY_SIZE(MapZoneArray); i++) {
-		CZone* zone = WriteSaveBuf(buffer, MapZoneArray[i]);
-
-		// see above
-		assert(MapZoneArray[i].child == nil);
-		assert(MapZoneArray[i].parent == nil);
-		assert(MapZoneArray[i].next == nil);
-		zone->child = (CZone*)GetIndexForZonePointer(MapZoneArray[i].child);
-		zone->parent = (CZone*)GetIndexForZonePointer(MapZoneArray[i].parent);
-		zone->next = (CZone*)GetIndexForZonePointer(MapZoneArray[i].next);
-	}
+	for(i = 0; i < ARRAY_SIZE(MapZoneArray); i++)
+		SaveOneZone(&MapZoneArray[i], &buffer, &length, ZONE_MAPZONE);
 
 	for(i = 0; i < ARRAY_SIZE(AudioZoneArray); i++)
-		WriteSaveBuf(buffer, AudioZoneArray[i]);
+		WriteSaveBuf(buffer, length, AudioZoneArray[i]);
 
-	WriteSaveBuf(buffer, TotalNumberOfMapZones);
-	WriteSaveBuf(buffer, NumberOfAudioZones);
+	WriteSaveBuf(buffer, length, TotalNumberOfMapZones);
+	WriteSaveBuf(buffer, length, NumberOfAudioZones);
 
 	VALIDATESAVEBUF(*size)
 }
 
+void
+CTheZones::SaveOneZone(CZone *zone, uint8 **buffer, uint32 *length, eZoneType zoneType)
+{
+	WriteSaveBuf(*buffer, *length, *(uint32*)&zone->name[0]);
+	WriteSaveBuf(*buffer, *length, *(uint32*)&zone->name[4]);
+
+	WriteSaveBuf(*buffer, *length, zone->minx);
+	WriteSaveBuf(*buffer, *length, zone->miny);
+	WriteSaveBuf(*buffer, *length, zone->minz);
+	WriteSaveBuf(*buffer, *length, zone->maxx);
+	WriteSaveBuf(*buffer, *length, zone->maxy);
+	WriteSaveBuf(*buffer, *length, zone->maxz);
+
+	WriteSaveBuf(*buffer, *length, zone->type);
+	WriteSaveBuf(*buffer, *length, zone->level);
+	WriteSaveBuf(*buffer, *length, zone->zoneinfoDay);
+	WriteSaveBuf(*buffer, *length, zone->zoneinfoNight);
+
+	int32 zoneId;
+	zoneId = GetIndexForZonePointer(zone->child);
+	WriteSaveBuf(*buffer, *length, zoneId);
+	zoneId = GetIndexForZonePointer(zone->parent);
+	WriteSaveBuf(*buffer, *length, zoneId);
+	zoneId = GetIndexForZonePointer(zone->next);
+	WriteSaveBuf(*buffer, *length, zoneId);
+}
+
 void
 CTheZones::LoadAllZones(uint8 *buffer, uint32 size)
 {
 	INITSAVEBUF
 	int i;
 
-	CheckSaveHeader(buffer, 'Z', 'N', 'S', '\0', size - SAVE_HEADER_SIZE);
+	uint32 length = 0;
+	CheckSaveHeaderWithLength(buffer, length, 'Z', 'N', 'S', '\0', size - SAVE_HEADER_SIZE);
 
-	m_CurrLevel = ReadSaveBuf<eLevelName>(buffer);
-	FindIndex = ReadSaveBuf<int16>(buffer);
-	ReadSaveBuf<int16>(buffer);
+	m_CurrLevel = ReadSaveBuf<eLevelName>(buffer, length);
+	FindIndex = ReadSaveBuf<int16>(buffer, length);
+	ReadSaveBuf<int16>(buffer, length);
 
-// TODO(MIAMI) ? implement LoadOneZone
-	for(i = 0; i < ARRAY_SIZE(NavigationZoneArray); i++){
-		NavigationZoneArray[i] = ReadSaveBuf<CZone>(buffer);
+	for(i = 0; i < ARRAY_SIZE(NavigationZoneArray); i++)
+		LoadOneZone(&NavigationZoneArray[i], &buffer, &length, ZONE_NAVIG);
 
-		NavigationZoneArray[i].child = GetPointerForZoneIndex((uintptr)NavigationZoneArray[i].child);
-		NavigationZoneArray[i].parent = GetPointerForZoneIndex((uintptr)NavigationZoneArray[i].parent);
-		NavigationZoneArray[i].next = GetPointerForZoneIndex((uintptr)NavigationZoneArray[i].next);
-	}
-
-	for(i = 0; i < ARRAY_SIZE(InfoZoneArray); i++){
-		InfoZoneArray[i] = ReadSaveBuf<CZone>(buffer);
-
-		/*
-		The call of GetPointerForZoneIndex is wrong, as it is
-		meant for a different array, but the game doesn't brake
-		if save data stored is -1.
-		*/
-		InfoZoneArray[i].child = GetPointerForZoneIndex((uintptr)InfoZoneArray[i].child);
-		InfoZoneArray[i].parent = GetPointerForZoneIndex((uintptr)InfoZoneArray[i].parent);
-		InfoZoneArray[i].next = GetPointerForZoneIndex((uintptr)InfoZoneArray[i].next);
-		assert(InfoZoneArray[i].child == nil);
-		assert(InfoZoneArray[i].parent == nil);
-		assert(InfoZoneArray[i].next == nil);
-	}
+	for (i = 0; i < ARRAY_SIZE(InfoZoneArray); i++)
+		LoadOneZone(&InfoZoneArray[i], &buffer, &length, ZONE_INFO);
 
 	for(i = 0; i < ARRAY_SIZE(ZoneInfoArray); i++)
-		ZoneInfoArray[i] = ReadSaveBuf<CZoneInfo>(buffer);
+		ZoneInfoArray[i] = ReadSaveBuf<CZoneInfo>(buffer, length);
 
-	TotalNumberOfNavigationZones = ReadSaveBuf<int16>(buffer);
-	TotalNumberOfInfoZones = ReadSaveBuf<int16>(buffer);
-	TotalNumberOfZoneInfos = ReadSaveBuf<int16>(buffer);
+	TotalNumberOfNavigationZones = ReadSaveBuf<int16>(buffer, length);
+	TotalNumberOfInfoZones = ReadSaveBuf<int16>(buffer, length);
+	TotalNumberOfZoneInfos = ReadSaveBuf<int16>(buffer, length);
+	ReadSaveBuf<int16>(buffer, length);
 
-	for(i = 0; i < ARRAY_SIZE(MapZoneArray); i++){
-		MapZoneArray[i] = ReadSaveBuf<CZone>(buffer);
-
-		// see above
-		MapZoneArray[i].child = GetPointerForZoneIndex((uintptr)MapZoneArray[i].child);
-		MapZoneArray[i].parent = GetPointerForZoneIndex((uintptr)MapZoneArray[i].parent);
-		MapZoneArray[i].next = GetPointerForZoneIndex((uintptr)MapZoneArray[i].next);
-		assert(MapZoneArray[i].child == nil);
-		assert(MapZoneArray[i].parent == nil);
-		assert(MapZoneArray[i].next == nil);
-	}
+	for(i = 0; i < ARRAY_SIZE(MapZoneArray); i++)
+		LoadOneZone(&MapZoneArray[i], &buffer, &length, ZONE_MAPZONE);
 
 	for(i = 0; i < ARRAY_SIZE(AudioZoneArray); i++)
-		AudioZoneArray[i] = ReadSaveBuf<int16>(buffer);
+		AudioZoneArray[i] = ReadSaveBuf<int16>(buffer, length);
 
-	TotalNumberOfMapZones = ReadSaveBuf<uint16>(buffer);
-	NumberOfAudioZones = ReadSaveBuf<uint16>(buffer);
+	TotalNumberOfMapZones = ReadSaveBuf<uint16>(buffer, length);
+	NumberOfAudioZones = ReadSaveBuf<uint16>(buffer, length);
 
 	VALIDATESAVEBUF(size)
 }
+
+void
+CTheZones::LoadOneZone(CZone *zone, uint8 **buffer, uint32 *length, eZoneType zoneType)
+{
+	*(uint32*)&zone->name[0] = ReadSaveBuf<uint32>(*buffer, *length);
+	*(uint32*)&zone->name[4] = ReadSaveBuf<uint32>(*buffer, *length);
+
+	zone->minx = ReadSaveBuf<float>(*buffer, *length);
+	zone->miny = ReadSaveBuf<float>(*buffer, *length);
+	zone->minz = ReadSaveBuf<float>(*buffer, *length);
+	zone->maxx = ReadSaveBuf<float>(*buffer, *length);
+	zone->maxy = ReadSaveBuf<float>(*buffer, *length);
+	zone->maxz = ReadSaveBuf<float>(*buffer, *length);
+
+	zone->type = ReadSaveBuf<eZoneType>(*buffer, *length);
+	zone->level = ReadSaveBuf<eLevelName>(*buffer, *length);
+	zone->zoneinfoDay = ReadSaveBuf<int16>(*buffer, *length);
+	zone->zoneinfoNight = ReadSaveBuf<int16>(*buffer, *length);
+
+	int32 zoneId;
+	zoneId = ReadSaveBuf<int32>(*buffer, *length);
+	zone->child = GetPointerForZoneIndex(zoneId);
+	zoneId = ReadSaveBuf<int32>(*buffer, *length);
+	zone->parent = GetPointerForZoneIndex(zoneId);
+	zoneId = ReadSaveBuf<int32>(*buffer, *length);
+	zone->next = GetPointerForZoneIndex(zoneId);
+}
\ No newline at end of file
diff --git a/src/core/Zones.h b/src/core/Zones.h
index 92e292b8..3a74427d 100644
--- a/src/core/Zones.h
+++ b/src/core/Zones.h
@@ -108,5 +108,7 @@ public:
 	static void AddZoneToAudioZoneArray(CZone *zone);
 	static void InitialiseAudioZoneArray(void);
 	static void SaveAllZones(uint8 *buffer, uint32 *length);
+	static void SaveOneZone(CZone *zone, uint8 **buffer, uint32 *length, eZoneType zoneType);
 	static void LoadAllZones(uint8 *buffer, uint32 length);
+	static void LoadOneZone(CZone *zone, uint8 **buffer, uint32 *length, eZoneType zoneType);
 };
diff --git a/src/core/common.h b/src/core/common.h
index 16d32b32..0e6bd60f 100644
--- a/src/core/common.h
+++ b/src/core/common.h
@@ -106,7 +106,7 @@ typedef uint16_t wchar;
 inline uint32 dpb(uint32 b, uint32 p, uint32 s, uint32 w)
 {
 	uint32 m = MASK(p,s);
-	return w & ~m | b<<p & m;
+	return (w & ~m) | ((b<<p) & m);
 }
 inline uint32 ldb(uint32 p, uint32 s, uint32 w)
 {
@@ -412,6 +412,15 @@ inline void SkipSaveBuf(uint8 *&buf, int32 skip)
 #endif
 }
 
+inline void SkipSaveBuf(uint8*& buf, uint32 &length, int32 skip)
+{
+	buf += skip;
+	length += skip;
+#ifdef VALIDATE_SAVE_SIZE
+	_saveBufCount += skip;
+#endif
+}
+
 template<typename T>
 inline const T ReadSaveBuf(uint8 *&buf)
 {
@@ -420,6 +429,14 @@ inline const T ReadSaveBuf(uint8 *&buf)
 	return value;
 }
 
+template<typename T>
+inline const T ReadSaveBuf(uint8 *&buf, uint32 &length)
+{
+	T &value = *(T*)buf;
+	SkipSaveBuf(buf, length, sizeof(T));
+	return value;
+}
+
 template<typename T>
 inline T *WriteSaveBuf(uint8 *&buf, const T &value)
 {
@@ -429,6 +446,15 @@ inline T *WriteSaveBuf(uint8 *&buf, const T &value)
 	return p;
 }
 
+template<typename T>
+inline T *WriteSaveBuf(uint8 *&buf, uint32 &length, const T &value)
+{
+	T *p = (T*)buf;
+	*p = value;
+	SkipSaveBuf(buf, length, sizeof(T));
+	return p;
+}
+
 
 #define SAVE_HEADER_SIZE (4*sizeof(char)+sizeof(uint32))
 
@@ -439,6 +465,13 @@ inline T *WriteSaveBuf(uint8 *&buf, const T &value)
 	WriteSaveBuf(buf, d);\
 	WriteSaveBuf<uint32>(buf, size);
 
+#define WriteSaveHeaderWithLength(buf,len,a,b,c,d,size) \
+	WriteSaveBuf(buf, len, a);\
+	WriteSaveBuf(buf, len, b);\
+	WriteSaveBuf(buf, len, c);\
+	WriteSaveBuf(buf, len, d);\
+	WriteSaveBuf<uint32>(buf, len, size);
+
 #define CheckSaveHeader(buf,a,b,c,d,size)\
 	assert(ReadSaveBuf<char>(buf) == a);\
 	assert(ReadSaveBuf<char>(buf) == b);\
@@ -446,5 +479,12 @@ inline T *WriteSaveBuf(uint8 *&buf, const T &value)
 	assert(ReadSaveBuf<char>(buf) == d);\
 	assert(ReadSaveBuf<uint32>(buf) == size);
 
+#define CheckSaveHeaderWithLength(buf,len,a,b,c,d,size)\
+	assert(ReadSaveBuf<char>(buf,len) == a);\
+	assert(ReadSaveBuf<char>(buf,len) == b);\
+	assert(ReadSaveBuf<char>(buf,len) == c);\
+	assert(ReadSaveBuf<char>(buf,len) == d);\
+	assert(ReadSaveBuf<uint32>(buf,len) == size);
+
 
 void cprintf(char*, ...);
\ No newline at end of file
diff --git a/src/core/config.h b/src/core/config.h
index 475a63f8..3f077f4c 100644
--- a/src/core/config.h
+++ b/src/core/config.h
@@ -87,7 +87,8 @@ enum Config {
 	NUMMBLURSTREAKS = 4,
 	NUMSKIDMARKS = 32,
 
-	NUMONSCREENTIMERENTRIES = 1,
+	NUMONSCREENCLOCKS = 1,
+	NUMONSCREENCOUNTERS = 3,
 	NUMRADARBLIPS = 75,
 	NUMGENERALPICKUPS = 320,
 	NUMSCRIPTEDPICKUPS = 16,
@@ -137,6 +138,7 @@ enum Config {
 
 	NUM_CRANES = 8,
 	NUM_ESCALATORS = 22,
+	NUM_WATER_CREATURES = 8,
 
 	NUM_EXPLOSIONS = 48,
 
@@ -248,6 +250,9 @@ enum Config {
 #if !defined(RW_GL3) && defined(_WIN32)
 #define XINPUT
 #endif
+#if !defined(_WIN32) && !defined(__SWITCH__)
+#define DONT_TRUST_RECOGNIZED_JOYSTICKS // Then we'll only rely on GLFW gamepad DB, and expect user to enter Controller->Detect joysticks if his joystick isn't on that list.
+#endif
 #define DETECT_PAD_INPUT_SWITCH // Adds automatic switch of pad related stuff between controller and kb/m
 #define KANGAROO_CHEAT
 #define RESTORE_ALLCARSHELI_CHEAT
@@ -257,7 +262,6 @@ enum Config {
 //#define BIND_VEHICLE_FIREWEAPON // Adds ability to rebind fire key for 'in vehicle' controls
 
 // Hud, frontend and radar
-#define HUD_ENHANCEMENTS	// Adjusts some aspects to make the HUD look/behave a little bit better.
 //#define BETA_SLIDING_TEXT
 #define TRIANGULAR_BLIPS	// height indicating triangular radar blips, as in VC
 #define PC_MENU
@@ -323,4 +327,4 @@ enum Config {
 	#undef NO_ISLAND_LOADING
 	#define PC_PARTICLE
 	#define VC_PED_PORTS // To not process collisions always. But should be tested if that's really beneficial
-#endif
\ No newline at end of file
+#endif
diff --git a/src/entities/Dummy.cpp b/src/entities/Dummy.cpp
index 92b69761..544e24a6 100644
--- a/src/entities/Dummy.cpp
+++ b/src/entities/Dummy.cpp
@@ -4,6 +4,8 @@
 #include "World.h"
 #include "Dummy.h"
 
+// --MIAMI: file done
+
 void *CDummy::operator new(size_t sz) { return CPools::GetDummyPool()->New();  }
 void CDummy::operator delete(void *p, size_t sz) { CPools::GetDummyPool()->Delete((CDummy*)p); }
 
diff --git a/src/entities/Entity.cpp b/src/entities/Entity.cpp
index cf1dd9e7..2285c93f 100644
--- a/src/entities/Entity.cpp
+++ b/src/entities/Entity.cpp
@@ -1207,4 +1207,37 @@ bool CEntity::IsEntityOccluded(void) {
 	}
 
 	return false;
+}
+
+/*
+0x487A10 - SetAtomicAlphaCB
+0x4879E0 - SetClumpAlphaCB
+*/
+
+RpMaterial* SetAtomicAlphaCB(RpMaterial *material, void *data) {
+	((RwRGBA*)RpMaterialGetColor(material))->alpha = (uint8)(uintptr)data;
+	return material;
+}
+
+RpAtomic* SetClumpAlphaCB(RpAtomic *atomic, void *data) {
+	RpGeometry *geometry = RpAtomicGetGeometry(atomic);
+	RpGeometrySetFlags(geometry, RpGeometryGetFlags(geometry) | rpGEOMETRYMODULATEMATERIALCOLOR);
+	RpGeometryForAllMaterials(geometry, SetAtomicAlphaCB, (void*)data);
+	return atomic;
+}
+
+void CEntity::SetRwObjectAlpha(int32 alpha) {
+	if (m_rwObject != nil) {
+		switch (RwObjectGetType(m_rwObject)) {
+		case rpATOMIC: {
+			RpGeometry *geometry = RpAtomicGetGeometry((RpAtomic*)m_rwObject);
+			RpGeometrySetFlags(geometry, RpGeometryGetFlags(geometry) | rpGEOMETRYMODULATEMATERIALCOLOR);
+			RpGeometryForAllMaterials(geometry, SetAtomicAlphaCB, (void*)alpha);
+			break;
+		}
+		case rpCLUMP:
+			RpClumpForAllAtomics((RpClump*)m_rwObject, SetClumpAlphaCB, (void*)alpha);
+			break;
+		}
+	}
 }
\ No newline at end of file
diff --git a/src/entities/Entity.h b/src/entities/Entity.h
index fd01b6f8..fbf5cb3b 100644
--- a/src/entities/Entity.h
+++ b/src/entities/Entity.h
@@ -179,6 +179,7 @@ public:
 	void ModifyMatrixForTreeInWind(void);
 	void ModifyMatrixForBannerInWind(void);
 	void ProcessLightsForEntity(void);
+	void SetRwObjectAlpha(int32 alpha);
 
 	static void AddSteamsFromGround(CPtrList& list);
 };
diff --git a/src/extras/ini_parser.hpp b/src/extras/ini_parser.hpp
new file mode 100644
index 00000000..99acf1ee
--- /dev/null
+++ b/src/extras/ini_parser.hpp
@@ -0,0 +1,314 @@
+/* 
+ *  Copyright (c) 2013-2015 Denilson das Merc�s Amorim <dma_2012@hotmail.com>
+ *  
+ *  This software is provided 'as-is', without any express or implied
+ *  warranty. In no event will the authors be held liable for any damages
+ *  arising from the use of this software.
+ * 
+ *  Permission is granted to anyone to use this software for any purpose,
+ *  including commercial applications, and to alter it and redistribute it
+ *  freely, subject to the following restrictions:
+ * 
+ *     1. The origin of this software must not be misrepresented; you must not
+ *     claim that you wrote the original software. If you use this software
+ *     in a product, an acknowledgment in the product documentation would be
+ *     appreciated but is not required.
+ * 
+ *     2. Altered source versions must be plainly marked as such, and must not be
+ *     misrepresented as being the original software.
+ * 
+ *     3. This notice may not be removed or altered from any source
+ *     distribution.
+ * 
+ */
+#ifndef LINB_INI_PARSER_HPP
+#define LINB_INI_PARSER_HPP
+
+/*
+ *  STL-like INI Container
+ */
+
+#include <string>       // for std::string
+#include <map>          // for std::map
+#include <cstdio>       // for std::FILE
+#include <algorithm>    // for std::find_if
+#include <functional>   // for std::function
+
+namespace linb
+{
+    template<
+        class CharT             = char,     /* Not compatible with other type here, since we're using C streams */
+        class StringType        = std::basic_string<CharT>,
+        class KeyContainer      = std::map<StringType, StringType>,
+        class SectionContainer  = std::map<StringType, KeyContainer>
+    > class basic_ini
+    {
+        public:
+            typedef CharT               char_type;
+            typedef StringType          string_type;
+            typedef KeyContainer        key_container;
+            typedef SectionContainer    section_container;
+            
+            // Typedef container values types
+            typedef typename section_container::value_type              value_type;
+            typedef typename section_container::key_type                key_type;
+            typedef typename section_container::mapped_type             mapped_type;
+            
+            // Typedef common types
+            typedef typename section_container::size_type               size_type;
+            typedef typename section_container::difference_type         difference_type;
+            
+            // Typedef iterators
+            typedef typename section_container::iterator                iterator;
+            typedef typename section_container::const_iterator          const_iterator;
+            typedef typename section_container::reverse_iterator        reverse_iterator;
+            typedef typename section_container::const_reverse_iterator  const_reverse_iterator;
+            
+            // typedef References and pointers
+            typedef typename section_container::reference               reference;
+            typedef typename section_container::const_reference         const_reference;
+            typedef typename section_container::pointer                 pointer;
+            typedef typename section_container::const_pointer           const_pointer;
+            
+        private:
+            section_container data;
+            
+        public:
+            
+            basic_ini()
+            { }
+
+            basic_ini(const char_type* filename)
+            { this->read_file(filename); }
+            
+            /* Iterator methods */
+            iterator begin()
+            { return data.begin(); }
+            const_iterator begin() const
+            { return data.begin(); }
+            iterator end()
+            { return data.end(); }
+            const_iterator end() const
+            { return data.end(); }
+            const_iterator cbegin() const
+            { return data.cbegin(); }
+            const_iterator cend() const
+            { return data.cend(); }
+            
+            /* Reverse iterator methods */
+            reverse_iterator rbegin()
+            { return data.rbegin(); }
+            const_reverse_iterator rbegin() const
+            { return data.rbegin(); }
+            reverse_iterator rend()
+            { return data.rend(); }
+            const_reverse_iterator rend() const
+            { return data.rend(); }
+            const_reverse_iterator crbegin() const
+            { return data.crbegin(); }
+            const_reverse_iterator crend() const
+            { return data.crend(); }
+            
+            /* Acessing index methods */
+            mapped_type& operator[](const string_type& sect)
+            { return data[sect]; }
+            mapped_type& operator[](string_type&& sect)
+            { return data[std::forward<string_type>(sect)]; }
+            mapped_type& at( const string_type& sect)
+            { return data.at(sect); }
+            const mapped_type& at(const string_type& sect) const
+            { return data.at(sect); }
+            
+            /* Capacity information */
+            bool empty() const
+            { return data.empty(); }
+            size_type size() const
+            { return data.size(); }
+            size_type max_size() const
+            { return data.max_size(); }
+            
+            /* Modifiers */
+            void clear()
+            { return data.clear(); }
+            
+            /* Lookup */
+            size_type count(const string_type& sect)
+            { return data.count(sect); }
+            iterator find(const string_type& sect)
+            { return data.find(sect); }
+
+            /* Gets a value from the specified section & key, default_value is returned if the sect & key doesn't exist */
+            string_type get(const string_type& sect, const key_type& key, const string_type& default_value)
+            {
+                auto it = this->find(sect);
+                if(it != this->end())
+                {
+                    auto itv = it->second.find(key);
+                    if(itv != it->second.end())
+                        return itv->second;
+                }
+                return default_value;
+            }
+
+            /* Sets the value of a value in the ini */
+            void set(const string_type& sect, const key_type& key, const string_type& value)
+            {
+                (*this)[sect][key] = value; // no emplace since overwrite!
+            }
+
+            /* Too lazy to continue this container... If you need more methods, just add it */
+            
+
+#if 1
+            bool read_file(const char_type* filename)
+            {
+                /* Using C stream in a STL-like container, funny?
+                 */
+                if(FILE* f = fopen(filename, "r"))
+                {
+                    key_container* keys = nullptr;
+                    char_type buf[2048];
+                    string_type line;
+                    string_type key;
+                    string_type value;
+                    string_type null_string;
+                    size_type pos;
+                    
+                    // Trims an string
+                    auto trim = [](string_type& s, bool trimLeft, bool trimRight) -> string_type&
+                    {
+                        if(s.size())
+                        {
+                            // Ignore UTF-8 BOM
+                            while(s.size() >= 3 && s[0] == (char)(0xEF) && s[1] == (char)(0xBB) && s[2] == (char)(0xBF))
+                                s.erase(s.begin(), s.begin() + 3);
+
+                            if(trimLeft)
+                                s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::function<int(int)>(::isspace))));
+                            if(trimRight)
+                                s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::function<int(int)>(::isspace))).base(), s.end());
+                        }
+                        return s;
+                    };
+                    
+                    // Start parsing
+                    while(fgets(buf, sizeof(buf), f))
+                    {
+                        // What a thing, reading into a char buffer and then putting in the string...
+                        line = buf;
+                        
+                        // Find comment and remove anything after it from the line
+                        if((pos = line.find_first_of(';')) != line.npos)
+                            line.erase(pos);
+                        
+                        // Trim the string, and if it gets empty, skip this line
+                        if(trim(line, true, true).empty())
+                            continue;
+                        
+                        // Find section name
+                        if(line.front() == '[' && line.back() == ']')
+                        {
+                            pos = line.length() - 1; //line.find_first_of(']');
+                            if(pos != line.npos)
+                            {
+                                trim(key.assign(line, 1, pos-1), true, true);
+                                keys = &data[std::move(key)];  // Create section
+                            }
+                            else
+                                keys = nullptr;
+                        }
+                        else
+                        {
+                            // Find key and value positions
+                            pos = line.find_first_of('=');
+                            if(pos == line.npos)
+                            {
+                                // There's only the key
+                                key = line;         // No need for trim, line is already trimmed
+                                value.clear();
+                            }
+                            else
+                            {
+                                // There's the key and the value
+                                trim(key.assign(line, 0, pos), false, true);                  // trim the right
+                                trim(value.assign(line, pos + 1, line.npos), true, false);    // trim the left
+                            }
+
+                            // Put the key/value into the current keys object, or into the section "" if no section has been found
+                            #if __cplusplus >= 201103L || _MSC_VER >= 1800
+                            (keys ? *keys : data[null_string]).emplace(std::move(key), std::move(value));
+                            #else
+                            (keys ? *keys : data[null_string])[key] = value;
+                            key.clear(); value.clear();
+                            #endif
+                        }
+                    }
+                    
+                    fclose(f);
+                    return true;
+                }
+                return false;
+            }
+
+            /*
+             *  Dumps the content of this container into an ini file
+             */
+            bool write_file(const char_type* filename)
+            {
+                if(FILE* f = fopen(filename, "w"))
+                {
+                    bool first = true;
+                    for(auto& sec : this->data)
+                    {
+                        fprintf(f, first? "[%s]\n" : "\n[%s]\n", sec.first.c_str());
+                        first = false;
+                        for(auto& kv : sec.second)
+                        {
+                            if(kv.second.empty())
+                                fprintf(f, "%s\n", kv.first.c_str());
+                            else
+                                fprintf(f, "%s = %s\n", kv.first.c_str(), kv.second.c_str());
+                        }
+                    }
+                    fclose(f);
+                    return true;
+                }
+                return false;
+            }
+
+
+            /*
+            */
+            bool load_file(const char_type* filename)
+            {
+                return read_file(filename);
+            }
+
+            bool load_file(const StringType& filename)
+            {
+                return load_file(filename.c_str());
+            }
+
+            bool write_file(const StringType& filename)
+            {
+                return write_file(filename.c_str());
+            }
+#endif       
+            
+
+        
+    };
+    
+    
+    /* Use default basic_ini
+     * 
+     *  Limitations:
+     *      * Not unicode aware
+     *      * Case sensitive
+     *      * Sections must have unique keys
+     */
+    typedef basic_ini<>     ini;
+}
+    
+#endif
+
diff --git a/src/objects/DummyObject.cpp b/src/objects/DummyObject.cpp
index 8dd1643d..8656abbb 100644
--- a/src/objects/DummyObject.cpp
+++ b/src/objects/DummyObject.cpp
@@ -3,6 +3,8 @@
 #include "DummyObject.h"
 #include "Pools.h"
 
+// --MIAMI: file done
+
 CDummyObject::CDummyObject(CObject *obj)
 {
 	SetModelIndexNoCreate(obj->GetModelIndex());
diff --git a/src/objects/Object.cpp b/src/objects/Object.cpp
index daa48d98..9a9eaa73 100644
--- a/src/objects/Object.cpp
+++ b/src/objects/Object.cpp
@@ -36,6 +36,7 @@ CObject::CObject(void)
 	m_colour2 = 0;
 	m_colour1 = m_colour2;
 	m_nBonusValue = 0;
+	// m_nCostValue = 0; // TODO(Miami)
 	bIsPickup = false;
 	bPickupObjWithMessage = false;
 	bOutOfStock = false;
@@ -44,8 +45,12 @@ CObject::CObject(void)
 	bHasBeenDamaged = false;
 	m_nRefModelIndex = -1;
 	bUseVehicleColours = false;
+//	bIsStreetLight = false;		// duplicate
 	m_pCurSurface = nil;
 	m_pCollidingEntity = nil;
+	m_nBeachballBounces = 0;
+	bIsStreetLight = false;
+	m_area = AREA_EVERYWHERE;
 }
 
 CObject::CObject(int32 mi, bool createRW)
@@ -138,12 +143,16 @@ CObject::Render(void)
 bool
 CObject::SetupLighting(void)
 {
-	DeActivateDirectional();
-	SetAmbientColours();
-
 	if(bRenderScorched){
 		WorldReplaceNormalLightsWithScorched(Scene.world, 0.1f);
 		return true;
+	} else if (bIsPickup) {
+		SetFullAmbient();
+		return true;
+	} else if (bIsWeapon) {
+		ActivateDirectional();
+		SetAmbientColoursForPedsCarsAndObjects();
+		return true;
 	}
 	return false;
 }
@@ -151,8 +160,10 @@ CObject::SetupLighting(void)
 void
 CObject::RemoveLighting(bool reset)
 {
-	if(reset)
-		WorldReplaceScorchedLightsWithNormal(Scene.world);
+	if(reset) {
+		SetAmbientColours();
+		DeActivateDirectional();
+	}
 }
 
 void 
@@ -363,6 +374,8 @@ CObject::CanBeDeleted(void)
 			return true;
 		case CUTSCENE_OBJECT:
 			return false;
+		case CONTROLLED_SUB_OBJECT:
+			return false;
 		default:
 			return true;
 	}
diff --git a/src/objects/Object.h b/src/objects/Object.h
index 5f0ec0ab..b81e84b6 100644
--- a/src/objects/Object.h
+++ b/src/objects/Object.h
@@ -8,7 +8,7 @@ enum {
 	MISSION_OBJECT = 2,
 	TEMP_OBJECT = 3,
 	CUTSCENE_OBJECT = 4,
-	ESCALATOR_OBJECT = 5,
+	CONTROLLED_SUB_OBJECT = 5,
 };
 
 enum CollisionSpecialResponseCase
diff --git a/src/objects/Projectile.cpp b/src/objects/Projectile.cpp
index fe8b0c68..fc4b25cf 100644
--- a/src/objects/Projectile.cpp
+++ b/src/objects/Projectile.cpp
@@ -2,6 +2,8 @@
 
 #include "Projectile.h"
 
+// --MIAMI: file done
+
 CProjectile::CProjectile(int32 model) : CObject()
 {
 	m_fMass = 1.0f;
diff --git a/src/objects/Stinger.cpp b/src/objects/Stinger.cpp
index f33125ee..b3660881 100644
--- a/src/objects/Stinger.cpp
+++ b/src/objects/Stinger.cpp
@@ -23,7 +23,7 @@ CStingerSegment::CStingerSegment()
 	m_fBuoyancy = GRAVITY * m_fMass * 0.1f;
 	bExplosionProof = true;
 	SetModelIndex(MI_PLC_STINGER);
-	ObjectCreatedBy = ESCALATOR_OBJECT;
+	ObjectCreatedBy = CONTROLLED_SUB_OBJECT;
 	NumOfStingerSegments++;
 }
 
diff --git a/src/peds/EmergencyPed.cpp b/src/peds/EmergencyPed.cpp
index a33e31f9..5b91daad 100644
--- a/src/peds/EmergencyPed.cpp
+++ b/src/peds/EmergencyPed.cpp
@@ -104,7 +104,6 @@ CEmergencyPed::FiremanAI(void)
 				m_pAttendedFire = nearestFire;
 #ifdef FIX_BUGS
 				bIsRunning = true;
-				++nearestFire->m_nFiremenPuttingOut;
 #endif
 			}
 			break;
@@ -116,10 +115,6 @@ CEmergencyPed::FiremanAI(void)
 				SetMoveState(PEDMOVE_RUN);
 #ifdef FIX_BUGS
 				bIsRunning = true;
-				if (m_pAttendedFire) {
-					--m_pAttendedFire->m_nFiremenPuttingOut;
-				}
-				++nearestFire->m_nFiremenPuttingOut;
 				m_pAttendedFire = nearestFire;
 			} else if (!nearestFire) {
 #else
@@ -153,10 +148,7 @@ CEmergencyPed::FiremanAI(void)
 		case EMERGENCY_PED_STOP:
 #ifdef FIX_BUGS
 			bIsRunning = false;
-			if (m_pAttendedFire)
 #endif
-				--m_pAttendedFire->m_nFiremenPuttingOut;
-
 			m_nPedState = PED_NONE;
 			SetWanderPath(CGeneral::GetRandomNumber() & 7);
 			m_pAttendedFire = nil;
diff --git a/src/peds/PedRoutes.cpp b/src/peds/PedRoutes.cpp
index 3ff080e6..2de90eae 100644
--- a/src/peds/PedRoutes.cpp
+++ b/src/peds/PedRoutes.cpp
@@ -3,6 +3,8 @@
 #include "main.h"
 #include "PedRoutes.h"
 
+// --MIAMI: file done
+
 CRouteNode gaRoutes[NUMPEDROUTES];
 
 void
diff --git a/src/peds/PedStats.cpp b/src/peds/PedStats.cpp
index 1f7a95b4..fe594bdf 100644
--- a/src/peds/PedStats.cpp
+++ b/src/peds/PedStats.cpp
@@ -4,6 +4,8 @@
 #include "FileMgr.h"
 #include "PedStats.h"
 
+// --MIAMI: file done
+
 CPedStats *CPedStats::ms_apPedStats[NUM_PEDSTATS];
 
 void
diff --git a/src/render/Draw.cpp b/src/render/Draw.cpp
index 131534a3..912399c9 100644
--- a/src/render/Draw.cpp
+++ b/src/render/Draw.cpp
@@ -24,12 +24,17 @@ float
 CDraw::CalculateAspectRatio(void)
 {
 	if (FrontEndMenuManager.m_PrefsUseWideScreen) {
+#ifdef ASPECT_RATIO_SCALE
+		if (TheCamera.m_WideScreenOn)
+			CDraw::ms_fAspectRatio = FrontEndMenuManager.m_PrefsUseWideScreen == AR_AUTO ?
+				(5.f / 3.f) * (SCREEN_WIDTH / SCREEN_HEIGHT) / (16.f / 9.f) :
+				5.f / 3.f; // It's used on theatrical showings according to Wiki
+		else
+			CDraw::ms_fAspectRatio = FrontEndMenuManager.m_PrefsUseWideScreen == AR_AUTO ? SCREEN_WIDTH / SCREEN_HEIGHT : 16.f / 9.f;
+#else
 		if (TheCamera.m_WideScreenOn)
 			CDraw::ms_fAspectRatio = 5.f / 3.f; // It's used on theatrical showings according to Wiki
 		else
-#ifdef ASPECT_RATIO_SCALE
-			CDraw::ms_fAspectRatio = FrontEndMenuManager.m_PrefsUseWideScreen == AR_AUTO ? SCREEN_WIDTH / SCREEN_HEIGHT : 16.f / 9.f;
-#else
 			CDraw::ms_fAspectRatio = 16.f / 9.f;
 #endif
 	} else if (TheCamera.m_WideScreenOn) {
diff --git a/src/render/Fluff.cpp b/src/render/Fluff.cpp
index 31bf92a8..773561f3 100644
--- a/src/render/Fluff.cpp
+++ b/src/render/Fluff.cpp
@@ -1358,7 +1358,7 @@ CEscalator::Update(void) {
 					if (m_pSteps[i]) {
 						m_pSteps[i]->SetPosition(m_pos1);
 						CWorld::Add(m_pSteps[i]);
-						m_pSteps[i]->ObjectCreatedBy = ESCALATOR_OBJECT;
+						m_pSteps[i]->ObjectCreatedBy = CONTROLLED_SUB_OBJECT;
 					}
 				}
 			}
@@ -1713,4 +1713,4 @@ void CScriptPaths::Save_ForReplay(void) {
 			g_pScriptPathObjects[6 * i + j] = aArray[i].m_pObjects[j];
 		}
 	}
-}
\ No newline at end of file
+}
diff --git a/src/render/Font.cpp b/src/render/Font.cpp
index c0cc333a..f14e45cd 100644
--- a/src/render/Font.cpp
+++ b/src/render/Font.cpp
@@ -5,6 +5,46 @@
 #include "Font.h"
 #include "Timer.h"
 
+void
+AsciiToUnicode(const char *src, wchar *dst)
+{
+	while((*dst++ = (unsigned char)*src++) != '\0');
+}
+
+void
+UnicodeStrcat(wchar *dst, wchar *append)
+{
+	UnicodeStrcpy(&dst[UnicodeStrlen(dst)], append);
+}
+
+void
+UnicodeStrcpy(wchar *dst, const wchar *src)
+{
+	while((*dst++ = *src++) != '\0');
+}
+
+int
+UnicodeStrlen(const wchar *str)
+{
+	int len;
+	for(len = 0; *str != '\0'; len++, str++);
+	return len;
+}
+
+void
+UnicodeMakeUpperCase(wchar *dst, const wchar *src) //idk what to do with it, seems to be incorrect implementation by R*
+{
+	while (*src != '\0') {
+		if (*src < 'a' || *src > 'z')
+			*dst = *src;
+		else
+			*dst = *src - 32;
+		dst++;
+		src++;
+	}
+	*dst = '\0';
+}
+
 CFontDetails CFont::Details;
 int16 CFont::NewLine;
 CSprite2d CFont::Sprite[MAX_FONTS];
@@ -1102,6 +1142,7 @@ CFont::ParseToken(wchar *s)
 		switch(*s){
 		case 'B':
 			Details.bBold = !Details.bBold;
+			break;
 		case 'N':
 		case 'n':
 			NewLine = 1;
@@ -1109,7 +1150,7 @@ CFont::ParseToken(wchar *s)
 		case 'b': SetColor(CRGBA(27, 89, 130, 255)); Details.anonymous_23 = true; break;
 		case 'f':
 			Details.bFlash = !Details.bFlash;
-			if (Details.bFlash)
+			if (!Details.bFlash)
 				Details.color.a = 255;
 			break;
 		case 'g': SetColor(CRGBA(255, 150, 225, 255)); Details.anonymous_23 = true; break;
diff --git a/src/render/Font.h b/src/render/Font.h
index ca0ed7d0..47a39f73 100644
--- a/src/render/Font.h
+++ b/src/render/Font.h
@@ -1,5 +1,11 @@
 #pragma once
 
+void AsciiToUnicode(const char *src, wchar *dst);
+void UnicodeStrcpy(wchar *dst, const wchar *src);
+void UnicodeStrcat(wchar *dst, wchar *append);
+int UnicodeStrlen(const wchar *str);
+void UnicodeMakeUpperCase(wchar *dst, const wchar *src);
+
 struct CFontDetails
 {
 	CRGBA color;
diff --git a/src/render/Hud.cpp b/src/render/Hud.cpp
index 36815ab5..3ea756fa 100644
--- a/src/render/Hud.cpp
+++ b/src/render/Hud.cpp
@@ -23,6 +23,9 @@
 #include "CutsceneMgr.h"
 #include "Stats.h"
 #include "main.h"
+#include "General.h"
+
+// --MIAMI: file done
 
 // Game has colors inlined in code.
 // For easier modification we collect them here:
@@ -46,13 +49,14 @@ CRGBA ODDJOB_COLOR(0, 207, 133, 255);
 CRGBA ODDJOB2_COLOR(97, 194, 247, 255);
 CRGBA MISSIONTITLE_COLOR(220, 172, 2, 255);
 
-wchar CHud::m_HelpMessage[256];
-wchar CHud::m_LastHelpMessage[256];
+wchar CHud::m_HelpMessage[HELP_MSG_LENGTH];
+wchar CHud::m_LastHelpMessage[HELP_MSG_LENGTH];
 uint32 CHud::m_HelpMessageState;
 uint32 CHud::m_HelpMessageTimer;
 int32 CHud::m_HelpMessageFadeTimer;
-wchar CHud::m_HelpMessageToPrint[256];
-float CHud::m_fHelpMessageTime;
+wchar CHud::m_HelpMessageToPrint[HELP_MSG_LENGTH];
+float CHud::m_HelpMessageDisplayTime;
+bool CHud::m_HelpMessageDisplayForever;
 bool CHud::m_HelpMessageQuick;
 uint32 CHud::m_ZoneState;
 int32 CHud::m_ZoneFadeTimer;
@@ -73,16 +77,16 @@ bool CHud::m_Wants_To_Draw_3dMarkers;
 wchar CHud::m_BigMessage[6][128];
 int16 CHud::m_ItemToFlash;
 bool CHud::m_HideRadar;
-int32 CHud::m_DrawClock;
+int32 CHud::m_ClockState;
 
 // These aren't really in CHud
 float CHud::BigMessageInUse[6];
 float CHud::BigMessageAlpha[6];
 float CHud::BigMessageX[6];
 float CHud::OddJob2OffTimer;
-bool CHud::CounterOnLastFrame;
+bool CHud::CounterOnLastFrame[NUMONSCREENCOUNTERS];
 float CHud::OddJob2XOffset;
-uint16 CHud::CounterFlashTimer;
+uint16 CHud::CounterFlashTimer[NUMONSCREENCOUNTERS];
 uint16 CHud::OddJob2Timer;
 bool CHud::TimerOnLastFrame;
 int16 CHud::OddJob2On;
@@ -110,6 +114,8 @@ uint32 CHud::m_WeaponTimer;
 
 uint32 CHud::m_LastDisplayScore;
 uint32 CHud::m_LastWanted;
+uint32 CHud::m_LastWeapon;
+uint32 CHud::m_LastTimeEnergyLost;
 
 CSprite2d CHud::Sprites[NUM_HUD_SPRITES];
 
@@ -118,33 +124,82 @@ struct
 	const char *name;
 	const char *mask;
 } WeaponFilenames[] = {
-	{"fist", "fistm"},
-	{"bat", "batm"},
-	{"pistol", "pistolm" },
-	{"uzi", "uzim"},
-	{"shotgun", "shotgunm"},
-	{"ak47", "ak47m"},
-	{"m16", "m16m"},
-	{"sniper", "sniperm"},
-	{"rocket", "rocketm"},
-	{"flame", "flamem"},
-	{"molotov", "molotovm"},
-	{"grenade", "grenadem"},
-	{"detonator", "detonator_mask"},
-	{"", ""},
-	{"", ""},
-	{"radardisc", "radardisc"},
-	{"pager", "pagerm"},
-	{"", ""},
-	{"", ""},
-	{"bleeder", ""},
-	{"sitesniper", "sitesniperm"},
-	{"siteM16", "siteM16m"},
-	{"siterocket", "siterocket"}
+  { "fist", "fistm" },
+  { "brassk", "brasskA" },
+  { "screw", "screwA" },
+  { "golf", "golfA" },
+  { "nightstick", "nightstickA" },
+  { "knife", "knifeA" },
+  { "bat", "batm" },
+  { "hammer", "hammerA" },
+  { "cleaver", "cleaverA" },
+  { "machete", "macheteA" },
+  { "sword", "swordA" },
+  { "chainsaw", "chainsawA" },
+  { "grenade", "grenadeA" },
+  { "grenade", "grenadeA" },
+  { "teargas", "teargasA" },
+  { "molotov", "molotovA" },
+  { "rocket", "rocketA" },
+  { "handGun1", "handGun1A" },
+  { "", "" },
+  { "python", "pythonA" },
+  { "chromegun", "chromegunA" },
+  { "spasshotGun", "spasshotGunA" },
+  { "stubshotGun", "stubshotGunA" },
+  { "tec9", "tec9A" },
+  { "uzi1", "uzi1A" },
+  { "uzi2", "uzi2A" },
+  { "mp5", "mp5A" },
+  { "", "" },
+  { "m4", "m4A" },
+  { "ruger", "rugerA" },
+  { "sniper", "sniperA" },
+  { "laserscope", "laserscopeA" },
+  { "", "" },
+  { "rocket", "rocketA" },
+  { "flamer", "flamerA" },
+  { "m60", "m60A" },
+  { "minigun", "minigunA" },
+  { "bomb", "bombA" },
+  { "", "" },
+  { "camera", "cameraA" },
+  { "", "" },
+  { "siterocket", "siterocket" },
+  { "", "" },
+  { "", "" },
+  { "", "" },
+  { "", "" },
+  { "", "" },
+  { "", "" },
+  { "", "" },
+  { "", "" },
+  { "radardisc", "radardisc" },
+  { "", "" },
+  { "", "" },
+  { "", "" },
+  { "", "" },
+  { "", "" },
+  { "", "" },
+  { "", "" },
+  { "", "" },
+  { "", "" },
+  { "", "" },
+  { "", "" },
+  { "", "" },
+  { "sitesniper", "sitesniperm" },
+  { "siteM16", "siteM16m" },
+  { "sitelaser", "sitelaserm" },
+  { "laserdot", "laserdotm" },
+  { "viewfinder_128", "viewfinder_128m" },
+  { "bleeder", "" }
 };
 
 RwTexture *gpSniperSightTex;
 RwTexture *gpRocketSightTex;
+RwTexture *gpLaserSightTex;
+RwTexture *gpLaserDotTex;
+RwTexture *gpViewFinderTex;
 
 void CHud::Draw()
 {
@@ -160,7 +215,9 @@ void CHud::Draw()
 		return;
 
 	if (m_Wants_To_Draw_Hud && !TheCamera.m_WideScreenOn) {
+		// unused statics in here
 		bool DrawCrossHair = false;
+		bool CrossHairHidesHud = false;
 		bool DrawCrossHairPC = false;
 
 		CPlayerPed *playerPed = FindPlayerPed();
@@ -251,38 +308,70 @@ void CHud::Draw()
 
 					CSprite::RenderOneXLUSprite(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 1.0f, SCREEN_SCALE_X(40.0f), SCREEN_SCALE_Y(40.0f), (100.0f * fMultBright), (200.0f * fMultBright), (100.0f * fMultBright), 255, 1.0f, 255);
 					RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
+					RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
 				}
 				else {
+					int sprite = HUD_SITESNIPER;
+					float xOffset = SCREEN_SCALE_X(210.0f);
+					float yOffset = SCREEN_SCALE_Y(210.0f);
 
-					// TODO(Miami)
-					// Sniper
-					rect.left = SCREEN_WIDTH/2 - SCREEN_SCALE_X(210.0f);
-					rect.top = SCREEN_HEIGHT/2 - SCREEN_SCALE_Y(210.0f);
+					if (FindPlayerPed()->GetWeapon()->m_eWeaponType == WEAPONTYPE_LASERSCOPE)
+					  sprite = HUD_SITELASER;
+
+					if (FindPlayerPed()->GetWeapon()->m_eWeaponType == WEAPONTYPE_CAMERA) {
+						sprite = HUD_VIEWFINDER;
+						CrossHairHidesHud = true;
+						xOffset = SCREEN_SCALE_X(256.0f);
+						yOffset = SCREEN_SCALE_Y(192.0f);
+					}
+
+					rect.left = SCREEN_WIDTH/2 - xOffset;
+					rect.top = SCREEN_HEIGHT/2 - yOffset;
 					rect.right = SCREEN_WIDTH/2;
 					rect.bottom = SCREEN_HEIGHT/2;
-					Sprites[HUD_SITESNIPER].Draw(CRect(rect), CRGBA(255, 255, 255, 255),
+					Sprites[sprite].Draw(CRect(rect), CRGBA(255, 255, 255, 255),
 						0.01f, 0.01f,  1.0f, 0.0f,  0.01f, 1.0f,  1.0f, 1.0f);
 
 					rect.left = SCREEN_WIDTH/2;
-					rect.top = SCREEN_HEIGHT/2 - SCREEN_SCALE_Y(210.0f);
-					rect.right = SCREEN_WIDTH/2 + SCREEN_SCALE_X(210.0f);
+					rect.top = SCREEN_HEIGHT/2 - yOffset;
+					rect.right = SCREEN_WIDTH/2 + xOffset;
 					rect.bottom = SCREEN_HEIGHT/2;
-					Sprites[HUD_SITESNIPER].Draw(CRect(rect), CRGBA(255, 255, 255, 255),
+					Sprites[sprite].Draw(CRect(rect), CRGBA(255, 255, 255, 255),
 						0.99f, 0.0f,  0.01f, 0.01f,  0.99f, 1.0f,  0.01f, 1.0f);
 
-					rect.left = SCREEN_WIDTH/2 - SCREEN_SCALE_X(210.0f);
+					rect.left = SCREEN_WIDTH/2 - xOffset;
 					rect.top = SCREEN_HEIGHT/2;
 					rect.right = SCREEN_WIDTH/2;
-					rect.bottom = SCREEN_HEIGHT/2 + SCREEN_SCALE_Y(210.0f);
-					Sprites[HUD_SITESNIPER].Draw(CRect(rect), CRGBA(255, 255, 255, 255),
+					rect.bottom = SCREEN_HEIGHT/2 + yOffset;
+					Sprites[sprite].Draw(CRect(rect), CRGBA(255, 255, 255, 255),
 						0.01f, 0.99f,  1.0f, 0.99f,  0.01f, 0.01f,  1.0f, 0.01f);
 
 					rect.left = SCREEN_WIDTH/2;
 					rect.top = SCREEN_HEIGHT/2;
-					rect.right = SCREEN_WIDTH/2 + SCREEN_SCALE_X(210.0f);
-					rect.bottom = SCREEN_HEIGHT/2 + SCREEN_SCALE_Y(210.0f);
-					Sprites[HUD_SITESNIPER].Draw(CRect(rect), CRGBA(255, 255, 255, 255),
+					rect.right = SCREEN_WIDTH/2 + xOffset;
+					rect.bottom = SCREEN_HEIGHT/2 + yOffset;
+					Sprites[sprite].Draw(CRect(rect), CRGBA(255, 255, 255, 255),
 						0.99f, 0.99f,  0.01f, 0.99f,  0.99f, 0.01f,  0.1f, 0.01f);
+
+					CVector dotPos;
+					float size = 25.0f;
+					if (FindPlayerPed()->GetWeapon()->m_eWeaponType == WEAPONTYPE_LASERSCOPE && FindPlayerPed()->GetWeapon()->LaserScopeDot(&dotPos, &size)) {
+						RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
+						RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
+						RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)FALSE);
+						RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
+						RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVDESTALPHA);
+						RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(gpLaserDotTex));
+#ifdef FIX_BUGS
+						int intensity = CGeneral::GetRandomNumberInRange(0, 37);
+#else
+						int intensity = CGeneral::GetRandomNumberInRange(0, 35);
+#endif
+						CSprite::RenderOneXLUSprite(dotPos.x, dotPos.y, dotPos.z,
+							SCREEN_SCALE_X(size), SCREEN_SCALE_Y(size), intensity - 36, 0, 0, intensity - 36, 1.0f, 127);
+
+						RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
+					}
 				}
 			}
 			RwRenderStateSet(rwRENDERSTATESRCBLEND, (void *)rwBLENDSRCALPHA);
@@ -293,6 +382,9 @@ void CHud::Draw()
 			SpriteBrightness = 0;
 		}
 
+		if (CrossHairHidesHud)
+			return;
+
 		/*
 			DrawMoneyCounter
 		*/
@@ -303,9 +395,9 @@ void CHud::Draw()
 		float alpha;
 
 		if (m_LastDisplayScore == CWorld::Players[CWorld::PlayerInFocus].m_nVisibleMoney) {
-			alpha = CHud::DrawFadeState(HUD_SCORE_FADING, 0);
+			alpha = DrawFadeState(HUD_SCORE_FADING, 0);
 		} else {
-			alpha = CHud::DrawFadeState(HUD_SCORE_FADING, 1);
+			alpha = DrawFadeState(HUD_SCORE_FADING, 1);
 			m_LastDisplayScore = CWorld::Players[CWorld::PlayerInFocus].m_nVisibleMoney;
 		}
 		if (m_DisplayScoreState != FADED_OUT) {
@@ -334,151 +426,168 @@ void CHud::Draw()
 		/*
 			DrawAmmo
 		*/
-		CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo((eWeaponType)WeaponType);
-		CWeapon *weapon = playerPed->GetWeapon();
-		uint32 AmmoAmount = weaponInfo->m_nAmountofAmmunition;
-		uint32 AmmoInClip = weapon->m_nAmmoInClip;
-		uint32 TotalAmmo = weapon->m_nAmmoTotal;
-		uint32 Ammo, Clip;
-
-		if (AmmoAmount <= 1 || AmmoAmount >= 1000)
-			sprintf(sTemp, "%d", TotalAmmo);
-		else {
-			if (WeaponType == WEAPONTYPE_FLAMETHROWER) {
-				Clip = AmmoInClip / 10;
-
-				if ((TotalAmmo - AmmoInClip) / 10 <= 9999)
-					Ammo = (TotalAmmo - AmmoInClip) / 10;
-				else
-					Ammo = 9999;
-			}
-			else {
-				Clip = AmmoInClip;
-
-				if ((TotalAmmo - AmmoInClip) > 9999)
-					Ammo = 9999;
-				else
-					Ammo = TotalAmmo - AmmoInClip;
-			}
-
-			sprintf(sTemp, "%d-%d", Ammo, Clip);
-		}
-
-		AsciiToUnicode(sTemp, sPrint);
-
-		/*
-			DrawWeaponIcon
-		*/
-
-		if (weaponInfo->m_nModelId <= 0) {
-			RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR);
-			Sprites[WeaponType].Draw(
-				CRect(SCREEN_SCALE_FROM_RIGHT(99.0f), SCREEN_SCALE_Y(27.0f), SCREEN_SCALE_FROM_RIGHT(35.0f), SCREEN_SCALE_Y(91.0f)),
-				CRGBA(255, 255, 255, 255),
-				0.015f,
-				0.015f,
-				1.0f,
-				0.0f,
-				0.015f,
-				1.0f,
-				1.0f,
-				1.0f);
+		if (m_LastWeapon == playerPed->GetWeapon()->m_eWeaponType) {
+			alpha = CHud::DrawFadeState(HUD_WEAPON_FADING, 0);
 		} else {
-			CBaseModelInfo *weaponModel = CModelInfo::GetModelInfo(weaponInfo->m_nModelId);
-			RwTexDictionary *weaponTxd = CTxdStore::GetSlot(weaponModel->GetTxdSlot())->texDict;
-			if (weaponTxd) {
-				RwTexture *weaponIcon = RwTexDictionaryFindNamedTexture(weaponTxd, weaponModel->GetName());
-				if (weaponIcon) {
+			alpha = CHud::DrawFadeState(HUD_WEAPON_FADING, 1);
+			m_LastWeapon = playerPed->GetWeapon()->m_eWeaponType;
+		}
+		if (m_WeaponState != FADED_OUT) {
+			CWeapon *weapon = playerPed->GetWeapon();
+			uint32 AmmoAmount = CWeaponInfo::GetWeaponInfo((eWeaponType)WeaponType)->m_nAmountofAmmunition;
+			uint32 AmmoInClip = weapon->m_nAmmoInClip;
+			uint32 TotalAmmo = weapon->m_nAmmoTotal;
+			uint32 Ammo, Clip;
+
+			if (AmmoAmount <= 1 || AmmoAmount >= 1000)
+				sprintf(sTemp, "%d", TotalAmmo);
+			else {
+				if (WeaponType == WEAPONTYPE_FLAMETHROWER) {
+					Clip = AmmoInClip / 10;
+
+					Ammo = Min((TotalAmmo - AmmoInClip) / 10, 9999);
+				} else {
+					Clip = AmmoInClip;
+
+					Ammo = Min(TotalAmmo - AmmoInClip, 9999);
+				}
+
+				sprintf(sTemp, "%d-%d", Ammo, Clip);
+			}
+
+			AsciiToUnicode(sTemp, sPrint);
+			CWeaponInfo *weaponInfo = CWeaponInfo::GetWeaponInfo((eWeaponType)WeaponType);
+			/*
+				DrawWeaponIcon
+			*/
+
+			if (FrontEndMenuManager.m_PrefsShowHud) {
+				if (weaponInfo->m_nModelId <= 0) {
 					RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR);
-					RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
-					RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(weaponIcon));
-					const float xSize = SCREEN_SCALE_X(64.0f / 2.0f);
-					const float ySize = SCREEN_SCALE_X(64.0f / 2.0f);
-					CSprite::RenderOneXLUSprite(SCREEN_SCALE_FROM_RIGHT(99.0f) + xSize, SCREEN_SCALE_Y(25.0f) + ySize, 1.0f, xSize, ySize,
-						255, 255, 255, 255, 1.0f, 255);
-					RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
+					if (FrontEndMenuManager.m_PrefsShowHud)
+						Sprites[WeaponType].Draw(
+							CRect(SCREEN_SCALE_FROM_RIGHT(99.0f), SCREEN_SCALE_Y(27.0f), SCREEN_SCALE_FROM_RIGHT(35.0f), SCREEN_SCALE_Y(91.0f)),
+							CRGBA(255, 255, 255, alpha),
+							0.015f,
+							0.015f,
+							1.0f,
+							0.0f,
+							0.015f,
+							1.0f,
+							1.0f,
+							1.0f);
+				} else {
+					CBaseModelInfo *weaponModel = CModelInfo::GetModelInfo(weaponInfo->m_nModelId);
+					RwTexDictionary *weaponTxd = CTxdStore::GetSlot(weaponModel->GetTxdSlot())->texDict;
+					if (weaponTxd) {
+						RwTexture *weaponIcon = RwTexDictionaryFindNamedTexture(weaponTxd, weaponModel->GetName());
+						if (weaponIcon) {
+							RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR);
+							RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)FALSE);
+							RwRenderStateSet(rwRENDERSTATETEXTURERASTER, RwTextureGetRaster(weaponIcon));
+							const float xSize = SCREEN_SCALE_X(64.0f / 2.0f);
+							const float ySize = SCREEN_SCALE_X(64.0f / 2.0f);
+							CSprite::RenderOneXLUSprite(SCREEN_SCALE_FROM_RIGHT(99.0f) + xSize, SCREEN_SCALE_Y(25.0f) + ySize, 1.0f, xSize, ySize,
+								255, 255, 255, 255, 1.0f, 255);
+							RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
+						}
+					}
+				}
+
+				CFont::SetBackgroundOff();
+				CFont::SetScale(SCREEN_SCALE_X(0.5f), SCREEN_SCALE_Y(0.8f));
+				CFont::SetJustifyOff();
+				CFont::SetCentreOn();
+				CFont::SetCentreSize(SCREEN_STRETCH_X(DEFAULT_SCREEN_WIDTH));
+				CFont::SetPropOn();
+				CFont::SetDropShadowPosition(0);
+				CFont::SetFontStyle(FONT_STANDARD);
+
+				if (Min(9999, TotalAmmo - AmmoInClip) != 9999 && !CDarkel::FrenzyOnGoing() && weaponInfo->m_nWeaponSlot > 1 && weapon->m_eWeaponType != WEAPONTYPE_DETONATOR) {
+					CFont::SetDropShadowPosition(2);
+					CFont::SetDropColor(CRGBA(0, 0, 0, alpha));
+					AMMO_COLOR.a = alpha;
+					CFont::SetColor(AMMO_COLOR);
+					if (FrontEndMenuManager.m_PrefsShowHud)
+						CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(66.0f), SCREEN_SCALE_Y(90.0f), sPrint);
+					CFont::SetDropShadowPosition(0);
 				}
 			}
 		}
 
-		CFont::SetBackgroundOff();
-		CFont::SetScale(SCREEN_SCALE_X(0.5f), SCREEN_SCALE_Y(0.8f));
-		CFont::SetJustifyOff();
-		CFont::SetCentreOn();
-		CFont::SetCentreSize(SCREEN_STRETCH_X(DEFAULT_SCREEN_WIDTH));
-		CFont::SetPropOn();
-		CFont::SetDropShadowPosition(0);
-		CFont::SetFontStyle(FONT_STANDARD);
-
-		if (Min(9999, TotalAmmo - AmmoInClip) != 9999 && !CDarkel::FrenzyOnGoing() && weaponInfo->m_nWeaponSlot > 1 && weapon->m_eWeaponType != WEAPONTYPE_DETONATOR) {
-			CFont::SetDropShadowPosition(2);
-			CFont::SetDropColor(CRGBA(0, 0, 0, 255));
-			CFont::SetColor(AMMO_COLOR);
-			CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(66.0f), SCREEN_SCALE_Y(90.0f), sPrint);
-			CFont::SetDropShadowPosition(0);
-		}
-
 		/*
 			DrawHealth
 		*/
-		CFont::SetBackgroundOff();
-		CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y));
-		CFont::SetJustifyOff();
-		CFont::SetCentreOff();
-		CFont::SetRightJustifyWrap(0.0f);
-		CFont::SetRightJustifyOn();
-		CFont::SetPropOff();
-		CFont::SetFontStyle(FONT_HEADING);
-		CFont::SetDropShadowPosition(2);
-		CFont::SetDropColor(CRGBA(0, 0, 0, 255));
-
-		if (m_ItemToFlash == ITEM_HEALTH && CTimer::GetFrameCounter() & 8
-			|| m_ItemToFlash != ITEM_HEALTH
-			|| playerPed->m_fHealth < 10
-			&& CTimer::GetFrameCounter() & 8) {
-			if (playerPed->m_fHealth >= 10
-				|| playerPed->m_fHealth < 10 && CTimer::GetFrameCounter() & 8) {
-
-				AsciiToUnicode("{", sPrintIcon);
-#ifdef FIX_BUGS
-				sprintf(sTemp, "%03d", int32(playerPed->m_fHealth + 0.5f));
-#else
-				sprintf(sTemp, "%03d", (int32)playerPed->m_fHealth);
-#endif
-				AsciiToUnicode(sTemp, sPrint);
-
-				CFont::SetColor(HEALTH_COLOR);
-
-				CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(110.0f), SCREEN_SCALE_Y(65.0f), sPrint);
-
-				if (!CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss || CTimer::GetTimeInMilliseconds() > CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss + 2000 || CTimer::GetFrameCounter() & 4) {
-					// CFont::SetColor(HEALTH_COLOR);
-					CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(110.0f + 54.0f), SCREEN_SCALE_Y(65.0f), sPrintIcon);
-				}
-			}
+		if ( m_LastTimeEnergyLost == CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss ) {
+			CHud::DrawFadeState(HUD_ENERGY_FADING, 0);
+		} else {
+			CHud::DrawFadeState(HUD_ENERGY_FADING, 1);
+			m_LastTimeEnergyLost = CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss;
 		}
 
-		/*
-			DrawArmour
-		*/
-		if (m_ItemToFlash == ITEM_ARMOUR && CTimer::GetFrameCounter() & 8 || m_ItemToFlash != ITEM_ARMOUR) {
+		if (m_EnergyLostState != FADED_OUT) {
+			CFont::SetBackgroundOff();
 			CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y));
-			if (playerPed->m_fArmour > 1.0f) {
-				AsciiToUnicode("<", sPrintIcon);
+			CFont::SetJustifyOff();
+			CFont::SetCentreOff();
+			CFont::SetRightJustifyWrap(0.0f);
+			CFont::SetRightJustifyOn();
+			CFont::SetPropOff();
+			CFont::SetFontStyle(FONT_HEADING);
+			CFont::SetDropShadowPosition(2);
+			CFont::SetDropColor(CRGBA(0, 0, 0, 255));
+
+			if (m_ItemToFlash == ITEM_HEALTH && CTimer::GetFrameCounter() & 8
+				|| m_ItemToFlash != ITEM_HEALTH
+				|| playerPed->m_fHealth < 10
+				&& CTimer::GetFrameCounter() & 8) {
+				if (playerPed->m_fHealth >= 10
+					|| playerPed->m_fHealth < 10 && CTimer::GetFrameCounter() & 8) {
+
+					AsciiToUnicode("{", sPrintIcon);
 #ifdef FIX_BUGS
-				sprintf(sTemp, "%03d", int32(playerPed->m_fArmour + 0.5f));
+					sprintf(sTemp, "%03d", int32(playerPed->m_fHealth + 0.5f));
 #else
-				sprintf(sTemp, "%03d", (int32)playerPed->m_fArmour);
+					sprintf(sTemp, "%03d", (int32)playerPed->m_fHealth);
 #endif
-				AsciiToUnicode(sTemp, sPrint);
+					AsciiToUnicode(sTemp, sPrint);
 
-				CFont::SetColor(ARMOUR_COLOR);
+					CFont::SetColor(HEALTH_COLOR);
+					if (FrontEndMenuManager.m_PrefsShowHud) {
+						CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(110.0f), SCREEN_SCALE_Y(65.0f), sPrint);
 
-				CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(182.0f), SCREEN_SCALE_Y(65.0f), sPrint);
+						if (!CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss || CTimer::GetTimeInMilliseconds() > CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss + 2000 || CTimer::GetFrameCounter() & 4) {
+							// CFont::SetColor(HEALTH_COLOR);
+							CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(110.0f + 54.0f), SCREEN_SCALE_Y(65.0f), sPrintIcon);
+						}
+					}
+				}
+			}
 
-				if (!CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastArmourLoss || CTimer::GetTimeInMilliseconds() > CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastArmourLoss + 2000 || CTimer::GetFrameCounter() & 1) {
-					// CFont::SetColor(ARMOUR_COLOR);
-					CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(182.0f + 52.0f), SCREEN_SCALE_Y(65.0f), sPrintIcon);
+			/*
+				DrawArmour
+			*/
+			if (m_ItemToFlash == ITEM_ARMOUR && CTimer::GetFrameCounter() & 8 || m_ItemToFlash != ITEM_ARMOUR) {
+				CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y));
+				if (playerPed->m_fArmour > 1.0f) {
+					AsciiToUnicode("<", sPrintIcon);
+#ifdef FIX_BUGS
+					sprintf(sTemp, "%03d", int32(playerPed->m_fArmour + 0.5f));
+#else
+					sprintf(sTemp, "%03d", (int32)playerPed->m_fArmour);
+#endif
+					AsciiToUnicode(sTemp, sPrint);
+
+					CFont::SetColor(ARMOUR_COLOR);
+					if (FrontEndMenuManager.m_PrefsShowHud) {
+
+						CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(182.0f), SCREEN_SCALE_Y(65.0f), sPrint);
+
+						if (!CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastArmourLoss || CTimer::GetTimeInMilliseconds() > CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastArmourLoss + 2000 || CTimer::GetFrameCounter() & 1) {
+							// CFont::SetColor(ARMOUR_COLOR);
+							CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(182.0f + 52.0f), SCREEN_SCALE_Y(65.0f), sPrintIcon);
+						}
+					}
 				}
 			}
 		}
@@ -486,10 +595,10 @@ void CHud::Draw()
 		/*
 			DrawWantedLevel
 		*/
-		if (m_LastWanted == playerPed->m_pWanted->m_nWantedLevel)
-			alpha = CHud::DrawFadeState(HUD_WANTED_FADING, 0);
-		else {
-			alpha = CHud::DrawFadeState(HUD_WANTED_FADING, 1);
+		if (m_LastWanted == playerPed->m_pWanted->m_nWantedLevel) {
+			alpha = DrawFadeState(HUD_WANTED_FADING, 0);
+		} else {
+			alpha = DrawFadeState(HUD_WANTED_FADING, 1);
 			m_LastWanted = playerPed->m_pWanted->m_nWantedLevel;
 		}
 
@@ -745,11 +854,7 @@ void CHud::Draw()
 					break;
 				}
 
-#ifndef HUD_ENHANCEMENTS
 				if (!m_Message[0]) {
-#else
-				if (!m_Message[0] && !m_BigMessage[2][0]) { // Hide vehicle name if wasted/busted text is displaying
-#endif
 					m_VehicleNameTimer += CTimer::GetTimeStepInMilliseconds();
 					CFont::SetJustifyOff();
 					CFont::SetPropOn();
@@ -787,7 +892,7 @@ void CHud::Draw()
 		/*
 			DrawClock
 		*/
-		if (m_DrawClock) {
+		if (m_ClockState) {
 			CFont::SetJustifyOff();
 			CFont::SetCentreOff();
 			CFont::SetBackgroundOff();
@@ -814,30 +919,28 @@ void CHud::Draw()
 		
 		wchar sTimer[16];
 
-		if (!CUserDisplay::OnscnTimer.m_sEntries[0].m_bTimerProcessed)
+		if (!CUserDisplay::OnscnTimer.m_sClocks[0].m_bClockProcessed)
 			TimerOnLastFrame = false;
-		if (!CUserDisplay::OnscnTimer.m_sEntries[0].m_bCounterProcessed)
-			CounterOnLastFrame = false;
 
-#ifdef FIX_BUGS
-#define TIMER_RIGHT_OFFSET 34.0f // Taken from VC frenzy timer
-#else
-#define TIMER_RIGHT_OFFSET 27.0f
-#endif
+		for(uint32 i = 0; i < NUMONSCREENCOUNTERS; i++) {
+			if (!CUserDisplay::OnscnTimer.m_sCounters[0].m_bCounterProcessed)
+				CounterOnLastFrame[i] = false;
+		}
+
 		if (CUserDisplay::OnscnTimer.m_bProcessed) {
-			if (CUserDisplay::OnscnTimer.m_sEntries[0].m_bTimerProcessed) {
+			if (CUserDisplay::OnscnTimer.m_sClocks[0].m_bClockProcessed) {
 				if (!TimerOnLastFrame)
 					TimerFlashTimer = 1;
 
 				TimerOnLastFrame = true;
 
-				if (TimerFlashTimer) {
+				if (TimerFlashTimer != 0) {
 					if (++TimerFlashTimer > 50)
 						TimerFlashTimer = 0;
 				}
 
-				if (CTimer::GetFrameCounter() & 4 || !TimerFlashTimer) {
-					AsciiToUnicode(CUserDisplay::OnscnTimer.m_sEntries[0].m_bTimerBuffer, sTimer);
+				if (CTimer::GetFrameCounter() & 4 || TimerFlashTimer == 0) {
+					AsciiToUnicode(CUserDisplay::OnscnTimer.m_sClocks[0].m_aClockBuffer, sTimer);
 					CFont::SetPropOn();
 					CFont::SetBackgroundOff();
 					CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y));
@@ -846,126 +949,84 @@ void CHud::Draw()
 					CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING));
 					CFont::SetPropOff();
 					CFont::SetBackGroundOnlyTextOn();
-					CFont::SetColor(CRGBA(0, 0, 0, 255));
-					CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(TIMER_RIGHT_OFFSET) + SCREEN_SCALE_X(2.0f), SCREEN_SCALE_Y(110.0f) + SCREEN_SCALE_Y(2.0f), sTimer);
+					CFont::SetDropShadowPosition(2);
+					CFont::SetDropColor(CRGBA(0, 0, 0, 255));
 					CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y));
 					CFont::SetColor(TIMER_COLOR);
-					CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(TIMER_RIGHT_OFFSET), SCREEN_SCALE_Y(110.0f), sTimer);
-
-					if (CUserDisplay::OnscnTimer.m_sEntries[0].m_aTimerText[0]) {
-						CFont::SetPropOn();
-						CFont::SetColor(CRGBA(0, 0, 0, 255));
-						CFont::SetScale(SCREEN_SCALE_X(0.64f), SCREEN_SCALE_Y(1.35f));
-						CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(TIMER_RIGHT_OFFSET) - SCREEN_SCALE_X(80.0f) + SCREEN_SCALE_X(2.0f), SCREEN_SCALE_Y(110.0f) + SCREEN_SCALE_Y(2.0f), TheText.Get(CUserDisplay::OnscnTimer.m_sEntries[0].m_aTimerText));
+					CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(37.0f), SCREEN_SCALE_Y(110.0f), sTimer);
+					CFont::SetPropOn();
 
+					if (CUserDisplay::OnscnTimer.m_sClocks[0].m_aClockText[0]) {
+						CFont::SetDropShadowPosition(2);
+						CFont::SetDropColor(CRGBA(0, 0, 0, 255));
 						CFont::SetColor(TIMER_COLOR);
-						CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(TIMER_RIGHT_OFFSET) - SCREEN_SCALE_X(80.0f), SCREEN_SCALE_Y(110.0f), TheText.Get(CUserDisplay::OnscnTimer.m_sEntries[0].m_aTimerText));
+						CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(37.0f) - SCREEN_SCALE_X(80.0f), SCREEN_SCALE_Y(110.0f), TheText.Get(CUserDisplay::OnscnTimer.m_sClocks[0].m_aClockText));
 					}
 				}
 			}
-			if (CUserDisplay::OnscnTimer.m_sEntries[0].m_bCounterProcessed) {
-				if (!CounterOnLastFrame)
-					CounterFlashTimer = 1;
 
-				CounterOnLastFrame = true;
+			for(uint32 i = 0; i < NUMONSCREENCOUNTERS; i++) {
+				if (CUserDisplay::OnscnTimer.m_sCounters[i].m_bCounterProcessed) {
+					if (!CounterOnLastFrame[i])
+						CounterFlashTimer[i] = 1;
 
-				if (CounterFlashTimer) {
-					if (++CounterFlashTimer > 50)
-						CounterFlashTimer = 0;
-				}
+					CounterOnLastFrame[i] = true;
 
-				if (CTimer::GetFrameCounter() & 4 || !CounterFlashTimer) {
-					if (CUserDisplay::OnscnTimer.m_sEntries[0].m_nType == COUNTER_DISPLAY_NUMBER) {
-						AsciiToUnicode(CUserDisplay::OnscnTimer.m_sEntries[0].m_bCounterBuffer, sTimer);
-						CFont::SetPropOn();
-
-						CFont::SetBackgroundOff();
-						CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y));
-						CFont::SetCentreOff();
-						CFont::SetRightJustifyOn();
-						CFont::SetRightJustifyWrap(0.0f);
-						CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING));
-						CFont::SetColor(CRGBA(244, 20, 20, 255));
-						CFont::SetWrapx(SCREEN_STRETCH_X(DEFAULT_SCREEN_WIDTH));
-						CFont::SetPropOff();
-						CFont::SetBackGroundOnlyTextOn();
-
-						CFont::SetColor(CRGBA(0, 0, 0, 255));
-						CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(TIMER_RIGHT_OFFSET) + SCREEN_SCALE_X(2.0f), SCREEN_SCALE_Y(132.0f) + SCREEN_SCALE_Y(2.0f), sTimer);
-
-						CFont::SetColor(COUNTER_COLOR);
-						CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(TIMER_RIGHT_OFFSET), SCREEN_SCALE_Y(132.0f), sTimer);
-					} else {
-						int counter = atoi(CUserDisplay::OnscnTimer.m_sEntries[0].m_bCounterBuffer);
-#ifdef FIX_BUGS
-						counter = Min(counter, 100);
-#endif
-						CSprite2d::DrawRect(CRect(SCREEN_SCALE_FROM_RIGHT(TIMER_RIGHT_OFFSET) - SCREEN_SCALE_X(100.0f) / 2 + SCREEN_SCALE_X(4.0f), SCREEN_SCALE_Y(132.0f) + SCREEN_SCALE_Y(8.0f), SCREEN_SCALE_FROM_RIGHT(TIMER_RIGHT_OFFSET) + SCREEN_SCALE_X(4.0f), SCREEN_SCALE_Y(132.0f) + SCREEN_SCALE_Y(11.0f) + SCREEN_SCALE_Y(8.0f)), CRGBA(0, 106, 164, 80));
-						CSprite2d::DrawRect(CRect(SCREEN_SCALE_FROM_RIGHT(TIMER_RIGHT_OFFSET) - SCREEN_SCALE_X(100.0f) / 2 + SCREEN_SCALE_X(4.0f), SCREEN_SCALE_Y(132.0f) + SCREEN_SCALE_Y(8.0f), SCREEN_SCALE_X(counter) / 2.0f + SCREEN_SCALE_FROM_RIGHT(TIMER_RIGHT_OFFSET + 50.0f) + SCREEN_SCALE_X(4.0f), SCREEN_SCALE_Y(132.0f) + SCREEN_SCALE_Y(11.0f) + SCREEN_SCALE_Y(8.0f)), CRGBA(0, 106, 164, 255));
+					if (CounterFlashTimer[i] != 0) {
+						if (++CounterFlashTimer[i] > 50)
+							CounterFlashTimer[i] = 0;
 					}
 
-					if (CUserDisplay::OnscnTimer.m_sEntries[0].m_aCounterText[0]) {
-						CFont::SetPropOn();
-						CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y));
-						CFont::SetColor(CRGBA(0, 0, 0, 255));
-						CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(TIMER_RIGHT_OFFSET) - SCREEN_SCALE_X(61.0f) + SCREEN_SCALE_Y(2.0f), SCREEN_SCALE_Y(132.0f) + SCREEN_SCALE_Y(2.0f), TheText.Get(CUserDisplay::OnscnTimer.m_sEntries[0].m_aCounterText));
+					if (CTimer::GetFrameCounter() & 4 || CounterFlashTimer[i] == 0) {
+						if (CUserDisplay::OnscnTimer.m_sCounters[i].m_nType == COUNTER_DISPLAY_NUMBER) {
+							AsciiToUnicode(CUserDisplay::OnscnTimer.m_sCounters[i].m_aCounterBuffer, sTimer);
+							CFont::SetPropOn();
+							CFont::SetBackgroundOff();
+							CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y));
+							CFont::SetCentreOff();
+							CFont::SetRightJustifyOn();
+							CFont::SetRightJustifyWrap(0.0f);
+							CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING));
+							CFont::SetWrapx(SCREEN_STRETCH_X(DEFAULT_SCREEN_WIDTH));
+							CFont::SetPropOn();
+							CFont::SetBackGroundOnlyTextOn();
+							CFont::SetDropShadowPosition(2);
+							CFont::SetDropColor(CRGBA(0, 0, 0, 255));
+							CFont::SetColor(COUNTER_COLOR);
+							CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(37.0f), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y * 20.f * i) + SCREEN_SCALE_Y(132.0f), sTimer);
+						} else {
+							int counter = atoi(CUserDisplay::OnscnTimer.m_sCounters[i].m_aCounterBuffer);
 
-						CFont::SetColor(COUNTER_COLOR);
-						CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(TIMER_RIGHT_OFFSET) - SCREEN_SCALE_X(61.0f), SCREEN_SCALE_Y(132.0f), TheText.Get(CUserDisplay::OnscnTimer.m_sEntries[0].m_aCounterText));
+							const float barWidth = SCREEN_SCALE_X(100.f / 2.f);
+							const float right = SCREEN_SCALE_FROM_RIGHT(37.0f);
+							const float left = right - barWidth;
+
+							const float barHeight = SCREEN_SCALE_Y(11.0f);
+							const float top = SCREEN_SCALE_Y(132.0f) + SCREEN_SCALE_Y(8.0f) + SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y * 20.f * i);
+							const float bottom = top + barHeight;
+
+							// shadow
+							CSprite2d::DrawRect(CRect(left + SCREEN_SCALE_X(6.0f), top + SCREEN_SCALE_Y(2.0f), right + SCREEN_SCALE_X(6.0f), bottom + SCREEN_SCALE_Y(2.0f)), CRGBA(0, 0, 0, 255));
+							
+							CSprite2d::DrawRect(CRect(left + SCREEN_SCALE_X(4.0f), top, right + SCREEN_SCALE_X(4.0f), bottom), CRGBA(27, 89, 130, 255));
+							CSprite2d::DrawRect(CRect(left + SCREEN_SCALE_X(4.0f), top, left + SCREEN_SCALE_X(counter) / 2.0f + SCREEN_SCALE_X(4.0f), bottom), CRGBA(97, 194, 247, 255));
+						}
+
+						if (CUserDisplay::OnscnTimer.m_sCounters[i].m_aCounterText[0]) {
+							CFont::SetPropOn();
+							CFont::SetFontStyle(FONT_LOCALE(FONT_HEADING));
+							CFont::SetScale(SCREEN_SCALE_X(HUD_TEXT_SCALE_X), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y));
+							CFont::SetDropShadowPosition(2);
+							CFont::SetDropColor(CRGBA(0, 0, 0, 255));
+							CFont::SetColor(COUNTER_COLOR);
+							CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(37.0f) - SCREEN_SCALE_X(61.0f), SCREEN_SCALE_Y(HUD_TEXT_SCALE_Y * 20.f * i) + SCREEN_SCALE_Y(132.0f), TheText.Get(CUserDisplay::OnscnTimer.m_sCounters[i].m_aCounterText));
+						}
+						// unused/leftover color. I wonder what was it for
+						CFont::SetColor(CRGBA(244, 225, 91, 255));
 					}
 				}
 			}
 		}
-#undef TIMER_RIGHT_OFFSET
-
-		/*
-			DrawPager
-		*/
-		if (!m_PagerMessage[0] && PagerOn == 1) {
-			PagerSoundPlayed = false;
-			PagerOn = 2;
-		}
-		if (m_PagerMessage[0] || PagerOn == 2) {
-			if (!PagerOn) {
-				PagerOn = 1;
-				PagerXOffset = 150.0f;
-			}
-			if (PagerOn == 1) {
-				if (PagerXOffset > 0.0f) {
-					float fStep = PagerXOffset * 0.1f;
-					if (fStep > 10.0f)
-						fStep = 10.0f;
-					PagerXOffset -= fStep * CTimer::GetTimeStep();
-				}
-				if (!PagerSoundPlayed) {
-					DMAudio.PlayFrontEndSound(SOUND_PAGER, 0);
-					PagerSoundPlayed = 1;
-				}
-			}
-			else if (PagerOn == 2) {
-				float fStep = PagerXOffset * 0.1f;
-				if (fStep < 2.0f)
-					fStep = 2.0f;
-				PagerXOffset += fStep;
-				if (PagerXOffset > 150.0f) {
-					PagerXOffset = 150.0f;
-					PagerOn = 0;
-				}
-			}
-
-			Sprites[HUD_PAGER].Draw(CRect(SCREEN_SCALE_X(26.0f - PagerXOffset), SCREEN_SCALE_Y(27.0f), SCREEN_SCALE_X(160.0 + 26.0f - PagerXOffset), SCREEN_SCALE_Y(80.0f + 27.0f)), CRGBA(255, 255, 255, 255));
-
-			CFont::SetBackgroundOff();
-			CFont::SetScale(SCREEN_SCALE_X(0.84f), SCREEN_SCALE_Y(1.0f));
-			CFont::SetColor(PAGER_COLOR);
-			CFont::SetRightJustifyOff();
-			CFont::SetBackgroundOff();
-			CFont::SetCentreOff();
-			CFont::SetJustifyOff();
-			CFont::SetPropOff();
-			CFont::SetFontStyle(FONT_STANDARD);
-			CFont::PrintString(SCREEN_SCALE_X(52.0f - PagerXOffset), SCREEN_SCALE_Y(54.0f), m_PagerMessage);
-		}
 
 		/*
 			DrawRadar
@@ -1126,14 +1187,14 @@ void CHud::Draw()
 		*/
 
 		if (m_HelpMessage[0]) {
-			if (!CMessages::WideStringCompare(m_HelpMessage, m_LastHelpMessage, 256)) {
+			if (!CMessages::WideStringCompare(m_HelpMessage, m_LastHelpMessage, HELP_MSG_LENGTH)) {
 				switch (m_HelpMessageState) {
 				case 0:
 					m_HelpMessageFadeTimer = 0;
 					m_HelpMessageState = 2;
 					m_HelpMessageTimer = 0;
-					CMessages::WideStringCopy(m_HelpMessageToPrint, m_HelpMessage, 256);
-					m_fHelpMessageTime = CMessages::GetWideStringLength(m_HelpMessage) * 0.05f + 3.0f;
+					CMessages::WideStringCopy(m_HelpMessageToPrint, m_HelpMessage, HELP_MSG_LENGTH);
+					m_HelpMessageDisplayTime = CMessages::GetWideStringLength(m_HelpMessage) * 0.05f + 3.0f;
 
 					if (TheCamera.m_ScreenReductionPercentage == 0.0f)
 						DMAudio.PlayFrontEndSound(SOUND_HUD_SOUND, 0);
@@ -1148,7 +1209,7 @@ void CHud::Draw()
 				default:
 					break;
 				}
-				CMessages::WideStringCopy(m_LastHelpMessage, m_HelpMessage, 256);
+				CMessages::WideStringCopy(m_LastHelpMessage, m_HelpMessage, HELP_MSG_LENGTH);
 			}
 
 			float fAlpha = 225.0f;
@@ -1158,7 +1219,9 @@ void CHud::Draw()
 				case 1:
 					fAlpha = 225.0f;
 					m_HelpMessageFadeTimer = 600;
-					if (m_HelpMessageTimer > m_fHelpMessageTime * 1000.0f || m_HelpMessageQuick && m_HelpMessageTimer > 1500.0f) {
+					if (!m_HelpMessageDisplayForever && m_HelpMessageTimer > m_HelpMessageDisplayTime * 1000.0f ||
+						m_HelpMessageQuick && m_HelpMessageTimer > 1500.0f) {
+
 						m_HelpMessageFadeTimer = 600;
 						m_HelpMessageState = 3;
 					}
@@ -1187,7 +1250,7 @@ void CHud::Draw()
 					if (m_HelpMessageFadeTimer < 0) {
 						m_HelpMessageState = 2;
 						m_HelpMessageFadeTimer = 0;
-						CMessages::WideStringCopy(m_HelpMessageToPrint, m_LastHelpMessage, 256);
+						CMessages::WideStringCopy(m_HelpMessageToPrint, m_LastHelpMessage, HELP_MSG_LENGTH);
 					}
 					fAlpha = m_HelpMessageFadeTimer * 0.001f * 225.0f;
 					break;
@@ -1212,7 +1275,7 @@ void CHud::Draw()
 						CFont::SetScale(SCREEN_SCALE_X(0.52f), SCREEN_SCALE_Y(1.1f));
 
 					CFont::DrawFonts();
-					// CFont::SetColor(CRGBA(175, 175, 175, 255));
+					CFont::SetColor(CRGBA(175, 175, 175, 255));
 					CFont::SetJustifyOff();
 #ifdef MORE_LANGUAGES
 					if (CFont::IsJapanese())
@@ -1224,9 +1287,9 @@ void CHud::Draw()
 					CFont::SetBackgroundOn();
 					CFont::SetBackGroundOnlyTextOff();
 					CFont::SetDropShadowPosition(0);
-					CFont::SetColor(CRGBA(175, 175, 175, 255));
 					CFont::SetBackgroundColor(CRGBA(0, 0, 0, fAlpha * 0.9f));
-					CFont::PrintString(SCREEN_SCALE_X(34.0f), SCREEN_SCALE_Y(28.0f + (150.0f - PagerXOffset) * 0.6f), CHud::m_HelpMessageToPrint);
+					CFont::SetColor(CRGBA(175, 175, 175, 255));
+					CFont::PrintString(SCREEN_SCALE_X(34.0f), SCREEN_SCALE_Y(28.0f + (150.0f - PagerXOffset) * 0.6f), m_HelpMessageToPrint);
 					CFont::SetAlphaFade(255.0f);
 					CFont::SetWrapx(SCREEN_WIDTH);
 				}
@@ -1311,19 +1374,19 @@ void CHud::Draw()
 				CFont::SetRightJustifyOn();
 				CFont::SetFontStyle(FONT_HEADING);
 
-				CFont::SetColor(CRGBA(0, 0, 0, BigMessageAlpha[2]));
-				CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(20.0f - 4.0f), SCREEN_SCALE_FROM_BOTTOM(78.0f), m_BigMessage[2]);
+				CFont::SetDropShadowPosition(2);
+				CFont::SetDropColor(CRGBA(0, 0, 0, BigMessageAlpha[2]));
 
 				CFont::SetColor(CRGBA(WASTEDBUSTED_COLOR.r, WASTEDBUSTED_COLOR.g, WASTEDBUSTED_COLOR.b, BigMessageAlpha[2]));
-				CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(20.0f), SCREEN_SCALE_FROM_BOTTOM(82.0f), m_BigMessage[2]);
+				CFont::PrintString(SCREEN_SCALE_FROM_RIGHT(20.0f), SCREEN_SCALE_FROM_BOTTOM(90.0f), m_BigMessage[2]);
 			}
 			else {
 				BigMessageInUse[2] = 1.0f;
 				BigMessageAlpha[2] = 0.0f;
-				if (CHud::m_VehicleState != 0)
-					CHud::m_VehicleState = 0;
-				if (CHud::m_ZoneState != 0)
-					CHud::m_ZoneState = 0;
+				if (m_VehicleState != 0) // Hide vehicle name if wasted/busted text is displaying
+					m_VehicleState = 0;
+				if (m_ZoneState != 0)
+					m_ZoneState = 0;
 			}
 		}
 		else {
@@ -1332,7 +1395,6 @@ void CHud::Draw()
 	}
 }
 
-// --MIAMI: Done
 void CHud::DrawAfterFade()
 {
 	RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERNEAREST);
@@ -1522,7 +1584,7 @@ void CHud::DrawAfterFade()
 					BigMessageInUse[1] = 120.0f;
 					BigMessageAlpha[1] -= CTimer::GetTimeStepInMilliseconds();
 				}
-				if (BigMessageAlpha[1] <= 0) {
+				if (BigMessageAlpha[1] <= 0.0f) {
 					m_BigMessage[1][0] = 0;
 					BigMessageInUse[1] = 0.0f;
 					BigMessageAlpha[1] = 0.0f;
@@ -1558,12 +1620,11 @@ void CHud::DrawAfterFade()
 
 void CHud::GetRidOfAllHudMessages()
 {
-	m_ZoneState = 0;
-	m_pLastZoneName = nil;
 	m_ZoneNameTimer = 0;
 	m_pZoneName = nil;
+	m_ZoneState = 0;
 
-	for (int i = 0; i < 256; i++) {
+	for (int i = 0; i < HELP_MSG_LENGTH; i++) {
 		m_HelpMessage[i] = 0;
 		m_LastHelpMessage[i] = 0;
 		m_HelpMessageToPrint[i] = 0;
@@ -1573,15 +1634,15 @@ void CHud::GetRidOfAllHudMessages()
 	m_HelpMessageFadeTimer = 0;
 	m_HelpMessageState = 0;
 	m_HelpMessageQuick = 0;
-	m_fHelpMessageTime = 1.0f;
+	m_HelpMessageDisplayForever = false;
+	m_HelpMessageDisplayTime = 1.0f;
 	m_VehicleName = nil;
-	m_pLastVehicleName = nil;
 	m_pVehicleNameToPrint = nil;
 	m_VehicleNameTimer = 0;
 	m_VehicleFadeTimer = 0;
 	m_VehicleState = 0;
 
-	for (int i = 0; i < 256; i++)
+	for (int i = 0; i < ARRAY_SIZE(m_Message); i++)
 		m_Message[i] = 0;
 
 	for (int i = 0; i < 6; i++) {
@@ -1631,21 +1692,34 @@ void CHud::Initialise()
 		Sprites[i].SetTexture(WeaponFilenames[i].name, WeaponFilenames[i].mask);
 	}
 
+	m_pLastZoneName = nil;
 	GetRidOfAllHudMessages();
+	m_pLastVehicleName = nil;
 
 	if (gpSniperSightTex == nil)
-		gpSniperSightTex = RwTextureRead("sitesniper", nil);
+		gpSniperSightTex = RwTextureRead("sitesniper", nil); // unused
 	if (gpRocketSightTex == nil)
 		gpRocketSightTex = RwTextureRead("siterocket", nil);
+	if (gpLaserSightTex == nil)
+		gpLaserSightTex = RwTextureRead("sitelaser", nil); // unused
+	if (gpLaserDotTex == nil)
+		gpLaserDotTex = RwTextureRead("laserdot", "laserdotm");
+	if (gpViewFinderTex == nil)
+		gpViewFinderTex = RwTextureRead("viewfinder_128", "viewfinder_128m"); // unused
 
-	m_DrawClock = 1;
-	CounterOnLastFrame = false;
+	m_ClockState = 1;
+	CounterOnLastFrame[0] = false;
+	CounterOnLastFrame[1] = false;
+	CounterOnLastFrame[2] = false;
+	
 	m_ItemToFlash = ITEM_NONE;
 	OddJob2Timer = 0;
 	OddJob2OffTimer = 0.0f;
 	OddJob2On = 0;
 	OddJob2XOffset = 0.0f;
-	CounterFlashTimer = 0;
+	CounterFlashTimer[0] = 0;
+	CounterFlashTimer[1] = 0;
+	CounterFlashTimer[2] = 0;
 	TimerOnLastFrame = false;
 	TimerFlashTimer = 0;
 	SpriteBrightness = 0;
@@ -1676,7 +1750,9 @@ void CHud::Initialise()
 
 	m_HideRadar = false;
 	m_LastDisplayScore = CWorld::Players[CWorld::PlayerInFocus].m_nVisibleMoney;
+	m_LastTimeEnergyLost = CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss;
 	m_LastWanted = 0;
+	m_LastWeapon = 0;
 
 	CTxdStore::PopCurrentTxd();
 }
@@ -1685,16 +1761,22 @@ void CHud::ReInitialise() {
 	m_Wants_To_Draw_Hud = true;
 	m_Wants_To_Draw_3dMarkers = true;
 
+	m_pLastZoneName = nil;
 	GetRidOfAllHudMessages();
+	m_pLastVehicleName = nil;
 
-	CounterOnLastFrame = false;
+	CounterOnLastFrame[0] = false;
+	CounterOnLastFrame[1] = false;
+	CounterOnLastFrame[2] = false;
 	m_ItemToFlash = ITEM_NONE;
-	m_DrawClock = 1;
+	m_ClockState = 1;
 	OddJob2Timer = 0;
 	OddJob2OffTimer = 0.0f;
 	OddJob2On = 0;
 	OddJob2XOffset = 0.0f;
-	CounterFlashTimer = 0;
+	CounterFlashTimer[0] = 0;
+	CounterFlashTimer[1] = 0;
+	CounterFlashTimer[2] = 0;
 	TimerOnLastFrame = false;
 	TimerFlashTimer = 0;
 	SpriteBrightness = 0;
@@ -1725,15 +1807,20 @@ void CHud::ReInitialise() {
 
 	m_HideRadar = false;
 	m_LastDisplayScore = CWorld::Players[CWorld::PlayerInFocus].m_nVisibleMoney;
+	m_LastTimeEnergyLost = CWorld::Players[CWorld::PlayerInFocus].m_nTimeLastHealthLoss;
 	m_LastWanted = 0;
+	m_LastWeapon = 0;
 }
 
 wchar LastBigMessage[6][128];
 
-void CHud::SetBigMessage(wchar *message, int16 style)
+void CHud::SetBigMessage(wchar *message, uint16 style)
 {
 	int i = 0;
 
+	if (BigMessageInUse[style] != 0.0f)
+		return;
+
 	if (style == 5) {
 		for (i = 0; i < 128; i++) {
 			if (message[i] == 0)
@@ -1758,18 +1845,42 @@ void CHud::SetBigMessage(wchar *message, int16 style)
 	m_BigMessage[style][i] = 0;
 }
 
-void CHud::SetHelpMessage(wchar *message, bool quick)
+void CHud::SetHelpMessage(wchar *message, bool quick, bool displayForever)
 {
 	if (!CReplay::IsPlayingBack()) {
-		CMessages::WideStringCopy(m_HelpMessage, message, 256);
-		CMessages::InsertPlayerControlKeysInString(m_HelpMessage);
-
-		for (int i = 0; i < 256; i++) {
+		for (int i = 0; i < HELP_MSG_LENGTH; i++) {
+			m_HelpMessage[i] = 0;
+		}
+		for (int i = 0; i < HELP_MSG_LENGTH; i++) {
 			m_LastHelpMessage[i] = 0;
 		}
+		for (int i = 0; i < HELP_MSG_LENGTH; i++) {
+			m_HelpMessageToPrint[i] = 0;
+		}
+
+		CMessages::WideStringCopy(m_HelpMessage, message, HELP_MSG_LENGTH);
+		CMessages::InsertPlayerControlKeysInString(m_HelpMessage);
+		if (m_HelpMessageState == 0 || !CMessages::WideStringCompare(m_HelpMessage, m_HelpMessageToPrint, HELP_MSG_LENGTH)) {
+			for (int i = 0; i < HELP_MSG_LENGTH; i++) {
+				m_LastHelpMessage[i] = 0;
+			}
+
+			if (!message) {
+				m_HelpMessage[0] = 0;
+				m_HelpMessageToPrint[0] = 0;
+			}
+			if (!displayForever) {
+				m_HelpMessageState = displayForever;
+			} else {
+				m_HelpMessageState = 1;
+				CMessages::WideStringCopy(m_HelpMessageToPrint, m_HelpMessage, HELP_MSG_LENGTH);
+				CMessages::WideStringCopy(m_LastHelpMessage, m_HelpMessage, HELP_MSG_LENGTH);
+			}
+
+			m_HelpMessageQuick = quick;
+			m_HelpMessageDisplayForever = displayForever;
+		}
 
-		m_HelpMessageState = 0;
-		m_HelpMessageQuick = quick;
 	}
 }
 
@@ -1778,11 +1889,10 @@ bool CHud::IsHelpMessageBeingDisplayed(void)
 	return m_HelpMessageState != 0;
 }
 
-
 void CHud::SetMessage(wchar *message)
 {
 	int i = 0;
-	for (i = 0; i < 256; i++) {
+	for (i = 0; i < ARRAY_SIZE(m_Message); i++) {
 		if (message[i] == 0)
 			break;
 
@@ -1794,7 +1904,7 @@ void CHud::SetMessage(wchar *message)
 void CHud::SetPagerMessage(wchar *message)
 {
 	int i = 0;
-	for (i = 0; i < 256; i++) {
+	for (i = 0; i < ARRAY_SIZE(m_PagerMessage); i++) {
 		if (message[i] == 0)
 			break;
 
@@ -1825,6 +1935,15 @@ void CHud::Shutdown()
 	RwTextureDestroy(gpRocketSightTex);
 	gpRocketSightTex = nil;
 
+	RwTextureDestroy(gpLaserSightTex);
+	gpLaserSightTex = nil;
+
+	RwTextureDestroy(gpLaserDotTex);
+	gpLaserDotTex = nil;
+
+	RwTextureDestroy(gpViewFinderTex);
+	gpViewFinderTex = nil;
+
 	int HudTXD = CTxdStore::FindTxdSlot("hud");
 	CTxdStore::RemoveTxdSlot(HudTXD);
 }
diff --git a/src/render/Hud.h b/src/render/Hud.h
index f4c0d8c4..a4b9609a 100644
--- a/src/render/Hud.h
+++ b/src/render/Hud.h
@@ -1,6 +1,11 @@
 #pragma once
 #include "Sprite2d.h"
 
+#define HELP_MSG_LENGTH 256
+
+#define HUD_TEXT_SCALE_X 0.7f
+#define HUD_TEXT_SCALE_Y 1.25f
+
 enum eItems
 {
 	ITEM_NONE = -1,
@@ -31,41 +36,29 @@ enum eFadeOperation
 enum eSprites 
 {
 	HUD_FIST,
-	HUD_BAT,
-	HUD_PISTOL,
-	HUD_UZI,
-	HUD_SHOTGUN,
-	HUD_AK47,
-	HUD_M16,
-	HUD_SNIPER,
-	HUD_ROCKET,
-	HUD_FLAME,
-	HUD_MOLOTOV,
-	HUD_GRENADE,
-	HUD_DETONATOR,
-	HUD_RADARDISC = 15,
-	HUD_PAGER = 16,
-	HUD_SITESNIPER = 20,
+	HUD_SITEROCKET = 41,
+	HUD_RADARDISC = 50,
+	HUD_SITESNIPER = 63,
 	HUD_SITEM16,
-	HUD_SITEROCKET,
-	NUM_HUD_SPRITES,
+	HUD_SITELASER,
+	HUD_LASERDOT,
+	HUD_VIEWFINDER,
+	HUD_BLEEDER,
+	NUM_HUD_SPRITES = 69,
 };
 
-#define HUD_TEXT_SCALE_X 0.7f
-#define HUD_TEXT_SCALE_Y 1.25f
-
 class CHud
 {
 public:
 	static CSprite2d Sprites[NUM_HUD_SPRITES];
-	static wchar m_HelpMessage[256];
-	static wchar m_LastHelpMessage[256];
+	static wchar m_HelpMessage[HELP_MSG_LENGTH];
+	static wchar m_LastHelpMessage[HELP_MSG_LENGTH];
 	static uint32 m_HelpMessageState;
 	static uint32 m_HelpMessageTimer;
 	static int32 m_HelpMessageFadeTimer;
-	static wchar m_HelpMessageToPrint[256];
-	static float &m_HelpMessageDisplayTime;
-	static float m_fHelpMessageTime;
+	static wchar m_HelpMessageToPrint[HELP_MSG_LENGTH];
+	static float m_HelpMessageDisplayTime;
+	static bool m_HelpMessageDisplayForever;
 	static bool	m_HelpMessageQuick;
 	static uint32 m_ZoneState;
 	static int32 m_ZoneFadeTimer;
@@ -86,16 +79,16 @@ public:
 	static wchar m_BigMessage[6][128];
 	static int16 m_ItemToFlash;
 	static bool m_HideRadar;
-	static int32 m_DrawClock;
+	static int32 m_ClockState;
 
 	// These aren't really in CHud
 	static float BigMessageInUse[6];
 	static float BigMessageAlpha[6];
 	static float BigMessageX[6];
 	static float OddJob2OffTimer;
-	static bool CounterOnLastFrame;
+	static bool CounterOnLastFrame[NUMONSCREENCOUNTERS];
 	static float OddJob2XOffset;
-	static uint16 CounterFlashTimer;
+	static uint16 CounterFlashTimer[NUMONSCREENCOUNTERS];
 	static uint16 OddJob2Timer;
 	static bool TimerOnLastFrame;
 	static int16 OddJob2On;
@@ -121,6 +114,8 @@ public:
 
 	static uint32 m_LastDisplayScore;
 	static uint32 m_LastWanted;
+	static uint32 m_LastWeapon;
+	static uint32 m_LastTimeEnergyLost;
 
 public:
 	static void Draw();
@@ -131,8 +126,8 @@ public:
 #endif
 	static void Initialise();
 	static void ReInitialise();
-	static void SetBigMessage(wchar *message, int16 style);
-	static void SetHelpMessage(wchar *message, bool quick);
+	static void SetBigMessage(wchar *message, uint16 style);
+	static void SetHelpMessage(wchar *message, bool quick, bool displayForever = false);
 	static bool IsHelpMessageBeingDisplayed(void);
 	static void SetMessage(wchar *message);
 	static void SetPagerMessage(wchar *message);
diff --git a/src/render/Occlusion.cpp b/src/render/Occlusion.cpp
index 1ea9da50..79ce0461 100644
--- a/src/render/Occlusion.cpp
+++ b/src/render/Occlusion.cpp
@@ -366,6 +366,12 @@ COcclusion::ProcessBeforeRendering(void)
 					}
 					NumActiveOccluders--;
 					i--;
+					// Taken from Mobile!
+#ifdef FIX_BUGS
+					if (i == -1) {
+						i = 0;
+					}
+#endif
 				}
 			}
 		}
@@ -481,4 +487,4 @@ void COcclusion::Render() {
 
 	DefinedState();
 }
-#endif
\ No newline at end of file
+#endif
diff --git a/src/render/Particle.cpp b/src/render/Particle.cpp
index 2f0c3809..6a42bc4d 100644
--- a/src/render/Particle.cpp
+++ b/src/render/Particle.cpp
@@ -1220,8 +1220,11 @@ void CParticle::Update()
 			
 			if ( psystem->m_Type == PARTICLE_HEATHAZE || psystem->m_Type == PARTICLE_HEATHAZE_IN_DIST )
 			{
+#ifdef FIX_BUGS
+				int32 nSinCosIndex = (int32(DEGTORAD((float)particle->m_nRotation) * float(SIN_COS_TABLE_SIZE) / TWOPI) + SIN_COS_TABLE_SIZE) % SIN_COS_TABLE_SIZE;
+#else
 				int32 nSinCosIndex = int32(DEGTORAD((float)particle->m_nRotation) * float(SIN_COS_TABLE_SIZE) / TWOPI) % SIN_COS_TABLE_SIZE;
-				
+#endif
 				vecMoveStep.x = Sin(nSinCosIndex);
 				vecMoveStep.y = Sin(nSinCosIndex);
 				
@@ -1233,8 +1236,11 @@ void CParticle::Update()
 			
 			if ( psystem->m_Type == PARTICLE_BEASTIE )
 			{
+#ifdef FIX_BUGS
+				int32 nSinCosIndex = (int32(DEGTORAD((float)particle->m_nRotation) * float(SIN_COS_TABLE_SIZE) / TWOPI) + SIN_COS_TABLE_SIZE) % SIN_COS_TABLE_SIZE;
+#else
 				int32 nSinCosIndex = int32(DEGTORAD((float)particle->m_nRotation) * float(SIN_COS_TABLE_SIZE) / TWOPI) % SIN_COS_TABLE_SIZE;
-				
+#endif				
 				particle->m_vecVelocity.x = 0.50f * Cos(nSinCosIndex);
 				particle->m_vecVelocity.y = Cos(nSinCosIndex);
 				particle->m_vecVelocity.z = 0.25f * Sin(nSinCosIndex);
@@ -1750,7 +1756,11 @@ void CParticle::Update()
 			}
 			
 			if ( particle->m_nRotationStep != 0 )
+#ifdef FIX_BUGS
+				particle->m_nRotation = CGeneral::LimitAngle(particle->m_nRotation + particle->m_nRotationStep);
+#else
 				particle->m_nRotation += particle->m_nRotationStep;
+#endif
 			
 			if ( particle->m_fCurrentZRadius != 0.0f )
 			{
@@ -2403,7 +2413,7 @@ void CParticle::HandleShootableBirdsStuff(CEntity *entity, CVector const&camPos)
 {
 	float fHeadingRad = entity->GetForward().Heading();
 	float fHeading = RADTODEG(fHeadingRad);
-	float fBirdAngle = Cos(DEGTORAD(1.5f));
+	float fBirdAngle = ::Cos(DEGTORAD(1.5f));
 	
 	tParticleSystemData *psystem = &mod_ParticleSystemManager.m_aParticles[PARTICLE_BIRD_FRONT];
 	CParticle *particle = psystem->m_pParticles;
diff --git a/src/render/WaterCannon.cpp b/src/render/WaterCannon.cpp
index 2b34db37..bd2b9a68 100644
--- a/src/render/WaterCannon.cpp
+++ b/src/render/WaterCannon.cpp
@@ -77,9 +77,13 @@ void CWaterCannon::Update_OncePerFrame(int16 index)
 		}
 	}
 	
-	int32 extinguishingPoint = CGeneral::GetRandomNumber() & (NUM_SEGMENTPOINTS - 1);
-	if ( m_abUsed[extinguishingPoint] )
-		gFireManager.ExtinguishPoint(m_avecPos[extinguishingPoint], 3.0f);
+	for ( int32 i = 0; i < NUM_SEGMENTPOINTS; i++ )
+	{
+		if ( m_abUsed[i] && gFireManager.ExtinguishPointWithWater(m_avecPos[i], 4.0f) )
+		{
+			break;
+		}
+	}
 	
 	if ( ((index + CTimer::GetFrameCounter()) & 3) == 0 )
 		PushPeds();
diff --git a/src/render/WaterCreatures.cpp b/src/render/WaterCreatures.cpp
new file mode 100644
index 00000000..90f24183
--- /dev/null
+++ b/src/render/WaterCreatures.cpp
@@ -0,0 +1,267 @@
+#include "WaterCreatures.h"
+#include "ModelIndices.h"
+#include "World.h"
+#include "WaterLevel.h"
+#include "Camera.h"
+#include "PlayerPed.h"
+#include "config.h"
+#include "General.h"
+
+int CWaterCreatures::nNumActiveSeaLifeForms;
+CWaterCreature CWaterCreatures::aWaterCreatures[NUM_WATER_CREATURES];
+
+struct WaterCreatureProperties aProperties[65] = {
+	{ &MI_FISH1SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH2S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH2SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH2S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH3SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH3S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH1SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH2S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH2SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH2S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH3SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH3S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_JELLYFISH,	0.01f, 2.2f, 0.0005f, 3.5f },
+	{ &MI_JELLYFISH01,	0.01f, 2.2f, 0.0005f, 3.5f },
+	{ &MI_FISH1SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH2S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH2SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH2S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH3SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH3S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH1SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH2S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH2SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH2S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH3SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH3S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_TURTLE,		0.01f, 2.0f, 0.0005f, 4.0f },
+	{ &MI_FISH1SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH2S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH2SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH2S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH3SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH3S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH1SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH2S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH2SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH2S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH3SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH3S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_DOLPHIN,		0.03f, 1.5f, 0.0005f, 4.0f },
+	{ &MI_FISH1SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH2S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH2SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH2S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH3SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH3S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH1SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH2S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH2SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH2S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH3SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH3S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_SHARK,		0.03f, 0.4f, 0.0005f, 4.0f },
+	{ &MI_FISH1SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH2S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH2SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH2S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH3SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH3S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH1SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH2S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH2SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH2S,		0.04f, 1.5f, 0.0008f, 3.0f },
+	{ &MI_FISH3SINGLE,	0.04f, 3.0f, 0.0008f, 3.0f },
+	{ &MI_FISH3S,		0.04f, 1.5f, 0.0008f, 3.0f },
+};
+
+CWaterCreature::CWaterCreature() {
+	Free();
+}
+
+CWaterCreature::~CWaterCreature() {
+	//looks like unused
+}
+
+void CWaterCreature::Initialise(CObject *pObj, float fRightMult, float fZTurnSpeed, float fWaterDepth, uint32 alpha, eFishSlotState state) {
+	this->m_pObj = pObj;
+	this->m_fRightMult = fRightMult;
+	this->m_fZTurnSpeed = fZTurnSpeed;
+	this->m_fWaterDepth = fWaterDepth;
+	this->m_alpha = alpha;
+	this->m_state = state;
+}
+
+void CWaterCreature::Allocate(CObject *pObj, float fRightMult, float fZTurnSpeed, float fWaterDepth, uint32 alpha, eFishSlotState state) {
+	CWaterCreature::Initialise(pObj, fRightMult, fZTurnSpeed, fWaterDepth, alpha, state);
+}
+
+void CWaterCreature::Free() {
+	CWaterCreature::Initialise(nil, 0.0f, 0.0f, 0.0f, 0, WATER_CREATURE_DISABLED);
+}
+
+CWaterCreature *CWaterCreatures::GetFishStructSlot() {
+	for (int i = 0; i < NUM_WATER_CREATURES; i++)
+		if (aWaterCreatures[i].m_state == WATER_CREATURE_DISABLED)
+			return &aWaterCreatures[i];
+	
+	return nil;
+}
+
+CObject *CWaterCreatures::CreateSeaLifeForm(CVector const& pos, int16 modelID, int32 zRotAngle) {
+	if (CObject::nNoTempObjects >= 40)
+		return nil;
+
+	CObject *pObj = new CObject(modelID, true);
+
+	if (!pObj) return nil;
+	
+	pObj->GetMatrix().GetPosition() = pos;
+	pObj->GetMatrix().UpdateRW();
+	pObj->m_vecMoveSpeed = CVector(0.0f, 0.0f, 0.0f);
+	pObj->m_vecTurnSpeed = CVector(0.0f, 0.0f, 0.0f);
+	pObj->GetMatrix().SetRotateZOnly(DEGTORAD(zRotAngle));
+	pObj->GetMatrix().UpdateRW();
+	pObj->ObjectCreatedBy = CONTROLLED_SUB_OBJECT;
+	pObj->bIsStatic = false;
+
+	if (pObj->ObjectCreatedBy == TEMP_OBJECT) {
+		CObject::nNoTempObjects++;
+		pObj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 60000;
+	}
+
+	pObj->bTouchingWater = true;
+	pObj->bUnderwater = true;
+	CWorld::Add(pObj);
+
+	return pObj;
+}
+
+bool CWaterCreatures::IsSpaceForMoreWaterCreatures() {
+	return nNumActiveSeaLifeForms < NUM_WATER_CREATURES;
+}
+
+float CWaterCreatures::CalculateFishHeading(CVector const& pos1, CVector const& pos2) {
+	CVector delta = pos1 - pos2;
+	delta.Normalise();
+
+	return Atan2(-delta.x, delta.y);
+}
+
+void CWaterCreatures::CreateOne(CVector const& pos, int32 modelID) {
+	if (!IsSpaceForMoreWaterCreatures())
+		return;
+
+	CVector storedPos = pos;
+	float fDepth, fLevelNoWaves;
+	if (!TheCamera.IsSphereVisible(storedPos, 3.0f)
+		&& CWaterLevel::GetWaterDepth(storedPos, &fDepth, &fLevelNoWaves, nil) && fDepth > 4.5f) {
+		
+		if (modelID == -1 || modelID < 0 || modelID > 64)
+			modelID = CGeneral::GetRandomNumberInRange(0, 64);
+
+		WaterCreatureProperties *creature = &aProperties[modelID];
+		storedPos.z = fLevelNoWaves - creature->fLevel;
+		float fRightMult = CGeneral::GetRandomNumberInRange(0.0f, creature->fRightMult) + 0.01f;
+		float angle = CWaterCreatures::CalculateFishHeading(FindPlayerPed()->GetPosition(), storedPos);
+		
+		CObject *fish = CreateSeaLifeForm(storedPos, *(int16*)creature->modelID, angle);
+		if (!fish) return;
+
+		fish->SetRwObjectAlpha(255); 
+		CWaterCreature *wc = GetFishStructSlot();
+		wc->Allocate(fish, fRightMult, 0.0f, creature->fWaterDepth, 255, WATER_CREATURE_ALLOCATED);
+		nNumActiveSeaLifeForms++;
+	}
+}
+
+void CWaterCreatures::FreeFishStructSlot(CWaterCreature *wc) {
+	wc->Free();
+}
+
+void CWaterCreatures::UpdateAll() {
+	if (nNumActiveSeaLifeForms == 0)
+		return;
+
+	CVector playerPos = FindPlayerPed()->GetPosition();
+	for (int i = 0; i < NUM_WATER_CREATURES; i++) {
+		switch (aWaterCreatures[i].m_state) {
+		case WATER_CREATURE_ACTIVE: 
+			aWaterCreatures[i].m_pObj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 40000;
+			if (!aWaterCreatures[i].m_pObj->GetIsOnScreen()) {
+				aWaterCreatures[i].m_pObj->SetRwObjectAlpha(0);
+				aWaterCreatures[i].m_state = WATER_CREATURE_TO_REMOVE;
+				break;
+			}
+		case WATER_CREATURE_ALLOCATED: {
+			if ((playerPos - aWaterCreatures[i].m_pObj->GetPosition()).Magnitude() < SQR(75.0f)) {
+				if (aWaterCreatures[i].m_alpha < 255)
+					aWaterCreatures[i].m_alpha = Min(aWaterCreatures[i].m_alpha + 4, 255);
+				aWaterCreatures[i].m_pObj->SetRwObjectAlpha(aWaterCreatures[i].m_alpha);
+				CVector newRight = aWaterCreatures[i].m_pObj->GetRight();
+				newRight.Normalise();
+				aWaterCreatures[i].m_pObj->m_vecMoveSpeed = newRight * aWaterCreatures[i].m_fRightMult;
+				aWaterCreatures[i].m_pObj->m_vecTurnSpeed = CVector(0.0f, 0.0f, aWaterCreatures[i].m_fZTurnSpeed);
+				aWaterCreatures[i].m_pObj->bIsStatic = false;
+				float fDepth = 0.0;
+				CWaterLevel::GetWaterDepth(aWaterCreatures[i].m_pObj->GetPosition(), &fDepth, nil, nil);
+				if (aWaterCreatures[i].m_fWaterDepth < fDepth) {
+					if (aWaterCreatures[i].m_pObj->m_nEndOfLifeTime - 40000 <= CTimer::GetTimeInMilliseconds())
+						aWaterCreatures[i].m_state = WATER_CREATURE_ACTIVE;
+				}
+				else {
+					aWaterCreatures[i].m_state = WATER_CREATURE_UPDATE;
+				}
+
+			}
+			else {
+				aWaterCreatures[i].m_state = WATER_CREATURE_TO_REMOVE;
+			}
+			break;
+		}
+		case WATER_CREATURE_UPDATE: {
+			aWaterCreatures[i].m_pObj->m_nEndOfLifeTime = CTimer::GetTimeInMilliseconds() + 40000;
+			if (aWaterCreatures[i].m_alpha <= 0) {
+				aWaterCreatures[i].m_state = WATER_CREATURE_TO_REMOVE;
+			}
+			else {
+				aWaterCreatures[i].m_alpha = Max(aWaterCreatures[i].m_alpha - 6, 0);
+				aWaterCreatures[i].m_pObj->SetRwObjectAlpha(aWaterCreatures[i].m_alpha);
+				CVector newRight = aWaterCreatures[i].m_pObj->GetRight();
+				newRight.Normalise();
+				newRight.x *= aWaterCreatures[i].m_fRightMult;
+				newRight.y *= aWaterCreatures[i].m_fRightMult;
+				newRight.z -= 0.015f;
+				aWaterCreatures[i].m_pObj->m_vecMoveSpeed = newRight;
+
+				if (!aWaterCreatures[i].m_pObj->GetIsOnScreen())
+					aWaterCreatures[i].m_state = WATER_CREATURE_TO_REMOVE;
+			}
+			break;
+		}
+		case WATER_CREATURE_TO_REMOVE: 
+			if (aWaterCreatures[i].m_pObj)
+				CWorld::Remove(aWaterCreatures[i].m_pObj);
+			FreeFishStructSlot(&aWaterCreatures[i]);
+			nNumActiveSeaLifeForms--;
+			aWaterCreatures[i].m_state = WATER_CREATURE_DISABLED;
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+void CWaterCreatures::RemoveAll() {
+	for (int i = 0; i < NUM_WATER_CREATURES; i++) {
+		if (aWaterCreatures[i].m_state != WATER_CREATURE_DISABLED) {
+			CWorld::Remove(aWaterCreatures[i].m_pObj);
+			FreeFishStructSlot(&aWaterCreatures[i]);
+			aWaterCreatures[i].m_state = WATER_CREATURE_DISABLED;
+			nNumActiveSeaLifeForms--;
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/render/WaterCreatures.h b/src/render/WaterCreatures.h
new file mode 100644
index 00000000..7dcaa288
--- /dev/null
+++ b/src/render/WaterCreatures.h
@@ -0,0 +1,49 @@
+#pragma once
+#include "Object.h"
+
+enum eFishSlotState {
+	WATER_CREATURE_ALLOCATED = 0,
+	WATER_CREATURE_ACTIVE,
+	WATER_CREATURE_UPDATE,
+	WATER_CREATURE_TO_REMOVE,
+	WATER_CREATURE_DISABLED
+};
+
+class CWaterCreature {
+public:
+	CObject *m_pObj;
+	float m_fRightMult;
+	float m_fZTurnSpeed;
+	int32 m_alpha;
+	float m_fWaterDepth;
+	int32 m_state;
+
+	CWaterCreature();
+	~CWaterCreature();
+	void Allocate(CObject *pObj, float fRightMult, float fZTurnSpeed, float fWaterDepth, uint32 alpha, eFishSlotState state);
+	void Free();
+	void Initialise(CObject *pObj, float fRightMult, float fZTurnSpeed, float fWaterDepth, uint32 alpha, eFishSlotState state);
+};
+
+class CWaterCreatures {
+
+public:
+	static CWaterCreature aWaterCreatures[NUM_WATER_CREATURES];
+	static int32 nNumActiveSeaLifeForms;
+	static CObject *CreateSeaLifeForm(CVector const& pos, int16 modelID, int32 zRotAngle);
+	static void CreateOne(CVector const& pos, int32 modelID);
+	static void UpdateAll();
+	static void FreeFishStructSlot(CWaterCreature *wc);
+	static bool IsSpaceForMoreWaterCreatures();
+	static float CalculateFishHeading(CVector const& pos1, CVector const& pos2);
+	static void RemoveAll();
+	static CWaterCreature* GetFishStructSlot();
+};
+
+struct WaterCreatureProperties {
+	int16 *modelID;
+	float fRightMult;
+	float fLevel;
+	float fUnknown; //unused
+	float fWaterDepth;
+};
\ No newline at end of file
diff --git a/src/render/WaterLevel.cpp b/src/render/WaterLevel.cpp
index efcb6c14..ec9485d2 100644
--- a/src/render/WaterLevel.cpp
+++ b/src/render/WaterLevel.cpp
@@ -27,6 +27,7 @@
 #include "Replay.h"
 #include "WaterLevel.h"
 #include "SurfaceTable.h"
+#include "WaterCreatures.h"
 
 #define RwIm3DVertexSet_RGBA(vert, rgba) RwIm3DVertexSetRGBA(vert, rgba.red, rgba.green, rgba.blue, rgba.alpha) // (RwRGBAAssign(&(_dst)->color, &_src))
 
@@ -2821,8 +2822,7 @@ CWaterLevel::HandleSeaLifeForms()
 	}
 	else if ( (CTimer::GetTimeInMilliseconds() - timecounter) > 5000 )
 	{
-//TODO(MIAMI)
-//		if ( CWaterCreatures::IsSpaceForMoreWaterCreatures() )
+	if ( CWaterCreatures::IsSpaceForMoreWaterCreatures() )
 		{
 			for ( int32 i = 0; i < 3; i++ )
 			{
@@ -2838,14 +2838,12 @@ CWaterLevel::HandleSeaLifeForms()
 				vecPos.x += (fCos - fSin) * fAngle;
 				vecPos.y += (fSin + fCos) * fAngle;
 				
-				//TODO(MIAMI)
-				//CWaterCreatures::CreateOne(vecPos, 0xFFFFFFFF);
+				CWaterCreatures::CreateOne(vecPos, -1);
 			}
 		}
 	}
 	
-	//TODO(MIAMI)
-	//CWaterCreatures::UpdateAll();
+	CWaterCreatures::UpdateAll();
 }
 
 void
diff --git a/src/rw/Lights.h b/src/rw/Lights.h
index b296816b..c3543d77 100644
--- a/src/rw/Lights.h
+++ b/src/rw/Lights.h
@@ -22,3 +22,4 @@ void SetAmbientColours(void);
 void SetAmbientColoursForPedsCarsAndObjects(void);
 void SetAmbientColoursToIndicateRoadGroup(int i);
 void SetAmbientColours(RwRGBAReal *color);
+void SetFullAmbient(void);
diff --git a/src/save/GenericGameStorage.cpp b/src/save/GenericGameStorage.cpp
index b98b8243..18eecd95 100644
--- a/src/save/GenericGameStorage.cpp
+++ b/src/save/GenericGameStorage.cpp
@@ -11,6 +11,7 @@
 #include "Clock.h"
 #include "Date.h"
 #include "FileMgr.h"
+#include "Font.h"
 #include "Frontend.h"
 #include "GameLogic.h"
 #include "Gangs.h"
diff --git a/src/save/PCSave.cpp b/src/save/PCSave.cpp
index 6449d586..dd0926cf 100644
--- a/src/save/PCSave.cpp
+++ b/src/save/PCSave.cpp
@@ -3,6 +3,7 @@
 #include "crossplatform.h"
 
 #include "FileMgr.h"
+#include "Font.h"
 #ifdef MORE_LANGUAGES
 #include "Game.h"
 #endif
diff --git a/src/skel/crossplatform.cpp b/src/skel/crossplatform.cpp
index 6188992d..452ad9fa 100644
--- a/src/skel/crossplatform.cpp
+++ b/src/skel/crossplatform.cpp
@@ -84,6 +84,16 @@ void FileTimeToSystemTime(time_t* writeTime, SYSTEMTIME* out) {
 }
 #endif
 
+// Because wchar length differs between platforms.
+wchar*
+AllocUnicode(const char* src)
+{
+	wchar *dst = (wchar*)malloc(strlen(src)*2 + 2);
+	wchar *i = dst;
+	while((*i++ = (unsigned char)*src++) != '\0');
+	return dst;
+}
+
 // Funcs/features from Windows that we need on other platforms
 #ifndef _WIN32
 char *strupr(char *s) {
diff --git a/src/skel/crossplatform.h b/src/skel/crossplatform.h
index 69600385..1635781b 100644
--- a/src/skel/crossplatform.h
+++ b/src/skel/crossplatform.h
@@ -67,6 +67,10 @@ void CapturePad(RwInt32 padID);
 void joysChangeCB(int jid, int event);
 #endif
 
+#ifdef DONT_TRUST_RECOGNIZED_JOYSTICKS
+extern char gSelectedJoystickName[128];
+#endif
+
 enum eGameState
 {
     GS_START_UP = 0,
diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp
index b40130c4..08e5c021 100644
--- a/src/skel/glfw/glfw.cpp
+++ b/src/skel/glfw/glfw.cpp
@@ -41,7 +41,6 @@
 #include "AnimViewer.h"
 #include "Font.h"
 
-
 #define MAX_SUBSYSTEMS		(16)
 
 
@@ -81,12 +80,22 @@ DWORD _dwOperatingSystemVersion;
 #include "resource.h"
 #else
 long _dwOperatingSystemVersion;
+#ifndef __APPLE__
 #include <sys/sysinfo.h>
+#else
+#include <mach/mach_host.h>
+#include <sys/sysctl.h>
+#endif
 #include <stddef.h>
 #include <locale.h>
 #include <signal.h>
 #include <errno.h>
 #endif
+
+#ifdef DONT_TRUST_RECOGNIZED_JOYSTICKS
+char gSelectedJoystickName[128] = "";
+#endif
+
 /*
  *****************************************************************************
  */
@@ -424,6 +433,10 @@ psInitialize(void)
 			_dwOperatingSystemVersion = OS_WIN95;
 		}
 	}
+#else
+	_dwOperatingSystemVersion = OS_WINXP; // To fool other classes
+#endif
+
 	
 #ifndef PS2_MENU
 
@@ -433,6 +446,8 @@ psInitialize(void)
 
 #endif
 
+
+#ifdef _WIN32
 	MEMORYSTATUS memstats;
 	GlobalMemoryStatus(&memstats);
 
@@ -440,26 +455,44 @@ psInitialize(void)
 
 	debug("Physical memory size %u\n", memstats.dwTotalPhys);
 	debug("Available physical memory %u\n", memstats.dwAvailPhys);
+#elif defined (__APPLE__)
+	uint64_t size = 0;
+	uint64_t page_size = 0;
+	size_t uint64_len = sizeof(uint64_t);
+	size_t ull_len = sizeof(unsigned long long);
+	sysctl((int[]){CTL_HW, HW_PAGESIZE}, 2, &page_size, &ull_len, NULL, 0);
+	sysctl((int[]){CTL_HW, HW_MEMSIZE}, 2, &size, &uint64_len, NULL, 0);
+	vm_statistics_data_t vm_stat;
+	mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
+	host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vm_stat, &count);
+	_dwMemAvailPhys = (uint64_t)(vm_stat.free_count * page_size);
+	debug("Physical memory size %llu\n", _dwMemAvailPhys);
+	debug("Available physical memory %llu\n", size);
 #else
-	
-#ifndef PS2_MENU
-
-#ifdef GTA3_1_1_PATCH
-	FrontEndMenuManager.LoadSettings();
-#endif
-
-#endif
-	struct sysinfo systemInfo;
+#ifndef __APPLE__
+ 	struct sysinfo systemInfo;
 	sysinfo(&systemInfo);
-	
 	_dwMemAvailPhys = systemInfo.freeram;
-	_dwOperatingSystemVersion = OS_WINXP; // To fool other classes
-
 	debug("Physical memory size %u\n", systemInfo.totalram);
 	debug("Available physical memory %u\n", systemInfo.freeram);
-
+#else
+	uint64_t size = 0;
+	uint64_t page_size = 0;
+	size_t uint64_len = sizeof(uint64_t);
+	size_t ull_len = sizeof(unsigned long long);
+	sysctl((int[]){CTL_HW, HW_PAGESIZE}, 2, &page_size, &ull_len, NULL, 0);
+	sysctl((int[]){CTL_HW, HW_MEMSIZE}, 2, &size, &uint64_len, NULL, 0);
+	vm_statistics_data_t vm_stat;
+	mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
+	host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vm_stat, &count);
+	_dwMemAvailPhys = (uint64_t)(vm_stat.free_count * page_size);
+	debug("Physical memory size %llu\n", _dwMemAvailPhys);
+	debug("Available physical memory %llu\n", size);
 #endif
-	TheText.Unload();
+	_dwOperatingSystemVersion = OS_WINXP; // To fool other classes
+#endif
+  
+  TheText.Unload();
 
 	return TRUE;
 }
@@ -826,35 +859,26 @@ void joysChangeCB(int jid, int event);
 
 bool IsThisJoystickBlacklisted(int i)
 {
+#ifndef DONT_TRUST_RECOGNIZED_JOYSTICKS
+	return false;
+#else
 	if (glfwJoystickIsGamepad(i))
 		return false;
 
 	const char* joyname = glfwGetJoystickName(i);
 
-	// this is just a keyboard and mouse
-	// Microsoft Microsoft® 2.4GHz Transceiver v8.0 Consumer Control
-	// Microsoft Microsoft® 2.4GHz Transceiver v8.0 System Control
-	if (strstr(joyname, "2.4GHz Transceiver"))
-		return true;
-	// COMPANY USB Device System Control
-	// COMPANY USB Device Consumer Control
-	if (strstr(joyname, "COMPANY USB"))
-		return true;
-	// i.e. Synaptics TM2438-005
-	if (strstr(joyname, "Synaptics "))
-		return true;
-	// i.e. ELAN Touchscreen
-	if (strstr(joyname, "ELAN "))
-		return true;
-	// i.e. Primax Electronics, Ltd HP Wireless Keyboard Mouse Kit Consumer Control
-	if (strstr(joyname, "Keyboard"))
-		return true;
+	if (strncmp(joyname, gSelectedJoystickName, strlen(gSelectedJoystickName)) == 0)
+		return false;
 
-	return false;
+	return true;
+#endif
 }
 
 void _InputInitialiseJoys()
 {
+	PSGLOBAL(joy1id) = -1;
+	PSGLOBAL(joy2id) = -1;
+
 	for (int i = 0; i <= GLFW_JOYSTICK_LAST; i++) {
 		if (glfwJoystickPresent(i) && !IsThisJoystickBlacklisted(i)) {
 			if (PSGLOBAL(joy1id) == -1)
@@ -869,6 +893,9 @@ void _InputInitialiseJoys()
 	if (PSGLOBAL(joy1id) != -1) {
 		int count;
 		glfwGetJoystickButtons(PSGLOBAL(joy1id), &count);
+#ifdef DONT_TRUST_RECOGNIZED_JOYSTICKS
+		strcpy(gSelectedJoystickName, glfwGetJoystickName(PSGLOBAL(joy1id)));
+#endif
 		ControlsManager.InitDefaultControlConfigJoyPad(count);
 	}
 }
@@ -1211,7 +1238,9 @@ void terminateHandler(int sig, siginfo_t *info, void *ucontext) {
 }
 
 void dummyHandler(int sig){
+	// Don't kill the app pls
 }
+
 #endif
 
 void resizeCB(GLFWwindow* window, int width, int height) {
@@ -1458,9 +1487,10 @@ main(int argc, char *argv[])
 	act.sa_flags = SA_SIGINFO;
 	sigaction(SIGTERM, &act, NULL);
 	struct sigaction sa;
+	sigemptyset(&sa.sa_mask);
 	sa.sa_handler = dummyHandler;
 	sa.sa_flags = 0;
-	sigaction(SIGINT, &sa, NULL); // Needed for CdStreamPosix
+	sigaction(SIGUSR1, &sa, NULL); // Needed for CdStreamPosix
 #endif
 
 	/* 
@@ -2084,18 +2114,19 @@ void CapturePad(RwInt32 padID)
 
 void joysChangeCB(int jid, int event)
 {
-	if (event == GLFW_CONNECTED && !IsThisJoystickBlacklisted(jid))
-	{
-		if (PSGLOBAL(joy1id) == -1)
+	if (event == GLFW_CONNECTED && !IsThisJoystickBlacklisted(jid)) {
+		if (PSGLOBAL(joy1id) == -1) {
 			PSGLOBAL(joy1id) = jid;
-		else if (PSGLOBAL(joy2id) == -1)
+#ifdef DONT_TRUST_RECOGNIZED_JOYSTICKS
+			strcpy(gSelectedJoystickName, glfwGetJoystickName(jid));
+#endif
+		} else if (PSGLOBAL(joy2id) == -1)
 			PSGLOBAL(joy2id) = jid;
-	}
-	else if (event == GLFW_DISCONNECTED)
-	{
-		if (PSGLOBAL(joy1id) == jid)
+
+	} else if (event == GLFW_DISCONNECTED) {
+		if (PSGLOBAL(joy1id) == jid) {
 			PSGLOBAL(joy1id) = -1;
-		else if (PSGLOBAL(joy2id) == jid)
+		} else if (PSGLOBAL(joy2id) == jid)
 			PSGLOBAL(joy2id) = -1;
 	}
 }
diff --git a/src/skel/win/win.cpp b/src/skel/win/win.cpp
index b98d5710..1d1037af 100644
--- a/src/skel/win/win.cpp
+++ b/src/skel/win/win.cpp
@@ -3414,4 +3414,4 @@ int strncasecmp(const char *str1, const char *str2, size_t len)
 	return _strnicmp(str1, str2, len);
 }
 #endif
-#endif
\ No newline at end of file
+#endif
diff --git a/src/text/Text.cpp b/src/text/Text.cpp
index 64f303eb..5adc576a 100644
--- a/src/text/Text.cpp
+++ b/src/text/Text.cpp
@@ -9,11 +9,12 @@
 #include "Text.h"
 #include "Timer.h"
 
+//--MIAMI: file done
+
 static wchar WideErrorString[25];
 
 CText TheText;
 
-//--MIAMI: DONE
 CText::CText(void)
 {
 	encoding = 'e';
@@ -23,7 +24,6 @@ CText::CText(void)
 	memset(WideErrorString, 0, sizeof(WideErrorString));
 }
 
-//--MIAMI: DONE
 void
 CText::Load(void)
 {
@@ -95,7 +95,6 @@ CText::Load(void)
 	CFileMgr::SetDir("");
 }
 
-//--MIAMI: DONE
 void
 CText::Unload(void)
 {
@@ -108,7 +107,6 @@ CText::Unload(void)
 	memset(szMissionTableName, 0, sizeof(szMissionTableName));
 }
 
-//--MIAMI: DONE
 wchar*
 CText::Get(const char *key)
 {
@@ -158,7 +156,6 @@ wchar FrenchUpperCaseTable[128] = {
 	253, 254, 255
 };
 
-//--MIAMI: TODO (check tables)
 wchar
 CText::GetUpperCase(wchar c)
 {
@@ -190,7 +187,6 @@ CText::GetUpperCase(wchar c)
 	return c;
 }
 
-//--MIAMI: DONE
 void
 CText::UpperCase(wchar *s)
 {
@@ -200,27 +196,33 @@ CText::UpperCase(wchar *s)
 	}
 }
 
-//--MIAMI: DONE
 void
 CText::GetNameOfLoadedMissionText(char *outName)
 {
 	strcpy(outName, szMissionTableName);
 }
 
-//--MIAMI: DONE
 void
 CText::ReadChunkHeader(ChunkHeader *buf, int32 file, size_t *offset)
 {
+#if DUMB
+	char *_buf = (char*)buf;
+	for (int i = 0; i < sizeof(ChunkHeader); i++) {
+		CFileMgr::Read(file, &_buf[i], 1);
+		(*offset)++;
+	}
+#else
 	// original code loops 8 times to read 1 byte with CFileMgr::Read, that's retarded
 	CFileMgr::Read(file, (char*)buf, sizeof(ChunkHeader));
 	*offset += sizeof(ChunkHeader);
+#endif
 }
 
-//--MIAMI: DONE
 void
 CText::LoadMissionText(char *MissionTableName)
 {
 	char filename[32];
+	CMessages::ClearAllMessagesDisplayedByGame();
 
 	mission_keyArray.Unload();
 	mission_data.Unload();
@@ -306,7 +308,6 @@ CText::LoadMissionText(char *MissionTableName)
 }
 
 
-//--MIAMI: DONE
 void
 CKeyArray::Load(size_t length, int file, size_t* offset)
 {
@@ -318,14 +319,16 @@ CKeyArray::Load(size_t length, int file, size_t* offset)
 	rawbytes = (char*)entries;
 
 #if DUMB
-	for (uint32 i = 0; i < length; i++)
+	for (uint32 i = 0; i < length; i++) {
 		CFileMgr::Read(file, &rawbytes[i], 1);
+		(*offset)++;
+	}
 #else
 	CFileMgr::Read(file, rawbytes, length);
+	*offset += length;
 #endif
 }
 
-//--MIAMI: DONE
 void
 CKeyArray::Unload(void)
 {
@@ -334,7 +337,6 @@ CKeyArray::Unload(void)
 	numEntries = 0;
 }
 
-//--MIAMI: DONE
 void
 CKeyArray::Update(wchar *chars)
 {
@@ -345,7 +347,6 @@ CKeyArray::Update(wchar *chars)
 #endif
 }
 
-//--MIAMI: DONE
 CKeyEntry*
 CKeyArray::BinarySearch(const char *key, CKeyEntry *entries, int16 low, int16 high)
 {
@@ -366,7 +367,6 @@ CKeyArray::BinarySearch(const char *key, CKeyEntry *entries, int16 low, int16 hi
 	return nil;
 }
 
-//--MIAMI: DONE
 wchar*
 #ifdef FIX_BUGS
 CKeyArray::Search(const char *key, wchar *data, uint8 *result)
@@ -402,7 +402,6 @@ CKeyArray::Search(const char *key, uint8 *result)
 	return WideErrorString;
 }
 
-//--MIAMI: DONE
 void
 CData::Load(size_t length, int file, size_t * offset)
 {
@@ -414,14 +413,16 @@ CData::Load(size_t length, int file, size_t * offset)
 	rawbytes = (char*)chars;
 
 #if DUMB
-	for(uint32 i = 0; i < length; i++)
+	for(uint32 i = 0; i < length; i++){
 		CFileMgr::Read(file, &rawbytes[i], 1);
+		(*offset)++;
+	}
 #else
 	CFileMgr::Read(file, rawbytes, length);
+	*offset += length;
 #endif
 }
 
-//--MIAMI: DONE
 void
 CData::Unload(void)
 {
@@ -430,22 +431,31 @@ CData::Unload(void)
 	numChars = 0;
 }
 
-//--MIAMI: DONE
 void
 CMissionTextOffsets::Load(size_t table_size, int file, size_t *offset, int)
 {
+#if DUMB
+	size_t num_of_entries = table_size / sizeof(CMissionTextOffsets::Entry);
+	for (size_t mi = 0; mi < num_of_entries; mi++) {
+		for (uint32 i = 0; i < sizeof(data[mi].szMissionName); i++) {
+			CFileMgr::Read(file, &data[i].szMissionName[i], 1);
+			(*offset)++;
+		}
+		char* _buf = (char*)&data[mi].offset;
+		for (uint32 i = 0; i < sizeof(data[mi].offset); i++) {
+			CFileMgr::Read(file, &_buf[i], 1);
+			(*offset)++;
+		}
+	}
+	size = (uint16)num_of_entries;
+#else
 	// not exact VC code but smaller and better :P
 
 	// You can make this size_t if you want to exceed 32-bit boundaries, everything else should be ready.
 	size = (uint16) (table_size / sizeof(CMissionTextOffsets::Entry));
 	CFileMgr::Read(file, (char*)data, sizeof(CMissionTextOffsets::Entry) * size);
 	*offset += sizeof(CMissionTextOffsets::Entry) * size;
-}
-
-void
-AsciiToUnicode(const char *src, wchar *dst)
-{
-	while((*dst++ = (unsigned char)*src++) != '\0');
+#endif
 }
 
 char*
@@ -460,8 +470,29 @@ UnicodeToAscii(wchar *src)
 		if(*src < 128)
 #endif
 			aStr[len] = *src;
-		else
-			aStr[len] = '#';
+		// convert to CP1252
+		else if(*src <= 131)
+			aStr[len] = *src + 64;
+		else if (*src <= 141)
+			aStr[len] = *src + 66;
+		else if (*src <= 145)
+			aStr[len] = *src + 68;
+		else if (*src <= 149)
+			aStr[len] = *src + 71;
+		else if (*src <= 154)
+			aStr[len] = *src + 73;
+		else if (*src <= 164)
+			aStr[len] = *src + 75;
+		else if (*src <= 168)
+			aStr[len] = *src + 77;
+		else if (*src <= 204)
+			aStr[len] = *src + 80;
+		else switch (*src) {
+		case 205: aStr[len] = 209; break;
+		case 206: aStr[len] = 241; break;
+		case 207: aStr[len] = 191; break;
+		default: aStr[len] = '#'; break;
+		}
 	aStr[len] = '\0';
 	return aStr;
 }
@@ -471,7 +502,7 @@ UnicodeToAsciiForSaveLoad(wchar *src)
 {
 	static char aStr[256];
 	int len;
-	for(len = 0; *src != '\0' && len < 256-1; len++, src++)
+	for(len = 0; *src != '\0' && len < 256; len++, src++)
 		if(*src < 256)
 			aStr[len] = *src;
 		else
@@ -485,7 +516,7 @@ UnicodeToAsciiForMemoryCard(wchar *src)
 {
 	static char aStr[256];
 	int len;
-	for(len = 0; *src != '\0' && len < 256-1; len++, src++)
+	for(len = 0; *src != '\0' && len < 256; len++, src++)
 		if(*src < 256)
 			aStr[len] = *src;
 		else
@@ -494,42 +525,8 @@ UnicodeToAsciiForMemoryCard(wchar *src)
 	return aStr;
 }
 
-void
-UnicodeMakeUpperCase(wchar *dst, wchar *src) //idk what to do with it, seems to be incorrect implementation by R*
-{
-	while (*src != '\0') {
-		if (*src < 'a' || *src > 'z')
-			*dst = *src;
-		else
-			*dst = *src - 32;
-		dst++;
-		src++;
-	}
-	*dst = '\0';
-}
-
-void
-UnicodeStrcpy(wchar *dst, const wchar *src)
-{
-	while((*dst++ = *src++) != '\0');
-}
-
-void
-UnicodeStrcat(wchar *dst, wchar *append)
-{
-	UnicodeStrcpy(&dst[UnicodeStrlen(dst)], append);
-}
-
-int
-UnicodeStrlen(const wchar *str)
-{
-	int len;
-	for(len = 0; *str != '\0'; len++, str++);
-	return len;
-}
-
 void
 TextCopy(wchar *dst, const wchar *src)
 {
 	while((*dst++ = *src++) != '\0');
-}
\ No newline at end of file
+}
diff --git a/src/text/Text.h b/src/text/Text.h
index 0bad9a83..33dc313e 100644
--- a/src/text/Text.h
+++ b/src/text/Text.h
@@ -1,14 +1,9 @@
 #pragma once
 
-void AsciiToUnicode(const char *src, wchar *dst);
 char *UnicodeToAscii(wchar *src);
 char *UnicodeToAsciiForSaveLoad(wchar *src);
 char *UnicodeToAsciiForMemoryCard(wchar *src);
-void UnicodeStrcpy(wchar *dst, const wchar *src);
-void UnicodeStrcat(wchar *dst, wchar *append);
-int UnicodeStrlen(const wchar *str);
 void TextCopy(wchar *dst, const wchar *src);
-void UnicodeMakeUpperCase(wchar *dst, wchar *src);
 
 struct CKeyEntry
 {
diff --git a/src/vehicles/CarGen.cpp b/src/vehicles/CarGen.cpp
index b0fa91a4..598b8342 100644
--- a/src/vehicles/CarGen.cpp
+++ b/src/vehicles/CarGen.cpp
@@ -28,8 +28,13 @@ uint32 CTheCarGenerators::CurrentActiveCount;
 
 void CCarGenerator::SwitchOff()
 {
-	m_nUsesRemaining = 0;
-	--CTheCarGenerators::CurrentActiveCount;
+#ifdef FIX_BUGS
+	if (m_nUsesRemaining != 0)
+#endif
+	{
+		m_nUsesRemaining = 0;
+		--CTheCarGenerators::CurrentActiveCount;
+	}
 }
 
 void CCarGenerator::SwitchOn()
diff --git a/src/vehicles/Heli.cpp b/src/vehicles/Heli.cpp
index a770f83d..2c2fb33c 100644
--- a/src/vehicles/Heli.cpp
+++ b/src/vehicles/Heli.cpp
@@ -1011,14 +1011,16 @@ CHeli::TestSniperCollision(CVector *line0, CVector *line1)
 	bool hit = false;
 
 	for(i = 0; i < NUM_HELIS; i++){
-		CVector pilotPos = pHelis[i]->GetMatrix() * CVector(-0.43f, 1.49f, 1.5f);
-		if(pHelis[i] && !pHelis[i]->bBulletProof && CCollision::DistToLine(line0, line1, &pilotPos) < 0.8f){
-			pHelis[i]->m_fAngularSpeed = CGeneral::GetRandomTrueFalse() ? 0.05f : -0.05f;
-			pHelis[i]->m_heliStatus = HELI_STATUS_SHOT_DOWN;
-			pHelis[i]->m_nExplosionTimer = CTimer::GetTimeInMilliseconds() + 9999999;
-			pHelis[i]->m_numSwat = 0;
+		if(pHelis[i] && !pHelis[i]->bBulletProof) {
+			CVector pilotPos = pHelis[i]->GetMatrix() * CVector(-0.43f, 1.49f, 1.5f);
+			if(CCollision::DistToLine(line0, line1, &pilotPos) < 0.8f){
+				pHelis[i]->m_fAngularSpeed = CGeneral::GetRandomTrueFalse() ? 0.05f : -0.05f;
+				pHelis[i]->m_heliStatus = HELI_STATUS_SHOT_DOWN;
+				pHelis[i]->m_nExplosionTimer = CTimer::GetTimeInMilliseconds() + 9999999;
+				pHelis[i]->m_numSwat = 0;
 
-			hit = true;
+				hit = true;
+			}
 		}
 	}
 	return hit;
diff --git a/src/vehicles/Vehicle.cpp b/src/vehicles/Vehicle.cpp
index 6e86dbe4..994f3c99 100644
--- a/src/vehicles/Vehicle.cpp
+++ b/src/vehicles/Vehicle.cpp
@@ -1303,7 +1303,7 @@ CVehicle::InflictDamage(CEntity *damagedBy, eWeaponType weaponType, float damage
 		}
 	}
 #ifdef FIX_BUGS // removing dumb case when shooting police car in player's own garage gives wanted level
-	if (GetModelIndex() == MI_POLICE && damagedBy == FindPlayerPed() && !bHasBeenOwnedByPlayer)
+	if (GetModelIndex() == MI_POLICE && damagedBy == FindPlayerPed() && damagedBy != nil && !bHasBeenOwnedByPlayer)
 #else
 	if (GetModelIndex() == MI_POLICE && damagedBy == FindPlayerPed())
 #endif
diff --git a/src/vehicles/Vehicle.h b/src/vehicles/Vehicle.h
index 705fca3a..004f1fd9 100644
--- a/src/vehicles/Vehicle.h
+++ b/src/vehicles/Vehicle.h
@@ -187,7 +187,7 @@ public:
 	CAutoPilot AutoPilot;
 	uint8 m_currentColour1;
 	uint8 m_currentColour2;
-	uint8 m_aExtras[2];
+	int8 m_aExtras[2];
 	int16 m_nAlarmState;
 	int16 m_nRouteSeed;
 	CPed *pDriver;
diff --git a/src/weapons/WeaponInfo.cpp b/src/weapons/WeaponInfo.cpp
index 6debf725..b911805d 100644
--- a/src/weapons/WeaponInfo.cpp
+++ b/src/weapons/WeaponInfo.cpp
@@ -249,28 +249,28 @@ CWeaponInfo::LoadWeaponData(void)
 		ms_apWeaponInfos[weaponType].m_nModelId = modelId;
 		ms_apWeaponInfos[weaponType].m_nModel2Id = modelId2;
 
-		ms_apWeaponInfos[weaponType].m_bUseGravity = flags;
-		ms_apWeaponInfos[weaponType].m_bSlowsDown = flags >> 1;
-		ms_apWeaponInfos[weaponType].m_bDissipates = flags >> 2;
-		ms_apWeaponInfos[weaponType].m_bRandSpeed = flags >> 3;
-		ms_apWeaponInfos[weaponType].m_bExpands = flags >> 4;
-		ms_apWeaponInfos[weaponType].m_bExplodes = flags >> 5;
-		ms_apWeaponInfos[weaponType].m_bCanAim = flags >> 6;
-		ms_apWeaponInfos[weaponType].m_bCanAimWithArm = flags >> 7;
-		ms_apWeaponInfos[weaponType].m_b1stPerson = flags >> 8;
-		ms_apWeaponInfos[weaponType].m_bHeavy = flags >> 9;
-		ms_apWeaponInfos[weaponType].m_bThrow = flags >> 10;
-		ms_apWeaponInfos[weaponType].m_bReloadLoop2Start = flags >> 11;
-		ms_apWeaponInfos[weaponType].m_bUse2nd = flags >> 12;
-		ms_apWeaponInfos[weaponType].m_bGround2nd = flags >> 13;
-		ms_apWeaponInfos[weaponType].m_bFinish3rd = flags >> 14;
-		ms_apWeaponInfos[weaponType].m_bReload = flags >> 15;
-		ms_apWeaponInfos[weaponType].m_bFightMode = flags >> 16;
-		ms_apWeaponInfos[weaponType].m_bCrouchFire = flags >> 17;
-		ms_apWeaponInfos[weaponType].m_bCop3rd = flags >> 18;
-		ms_apWeaponInfos[weaponType].m_bGround3rd = flags >> 19;
-		ms_apWeaponInfos[weaponType].m_bPartialAttack = flags >> 20;
-		ms_apWeaponInfos[weaponType].m_bAnimDetonate = flags >> 21;
+		ms_apWeaponInfos[weaponType].m_bUseGravity = flags & 1;
+		ms_apWeaponInfos[weaponType].m_bSlowsDown = (flags >> 1) & 1;
+		ms_apWeaponInfos[weaponType].m_bDissipates = (flags >> 2) & 1;
+		ms_apWeaponInfos[weaponType].m_bRandSpeed = (flags >> 3) & 1;
+		ms_apWeaponInfos[weaponType].m_bExpands = (flags >> 4) & 1;
+		ms_apWeaponInfos[weaponType].m_bExplodes = (flags >> 5) & 1;
+		ms_apWeaponInfos[weaponType].m_bCanAim = (flags >> 6) & 1;
+		ms_apWeaponInfos[weaponType].m_bCanAimWithArm = (flags >> 7) & 1;
+		ms_apWeaponInfos[weaponType].m_b1stPerson = (flags >> 8) & 1;
+		ms_apWeaponInfos[weaponType].m_bHeavy = (flags >> 9) & 1;
+		ms_apWeaponInfos[weaponType].m_bThrow = (flags >> 10) & 1;
+		ms_apWeaponInfos[weaponType].m_bReloadLoop2Start = (flags >> 11) & 1;
+		ms_apWeaponInfos[weaponType].m_bUse2nd = (flags >> 12) & 1;
+		ms_apWeaponInfos[weaponType].m_bGround2nd = (flags >> 13) & 1;
+		ms_apWeaponInfos[weaponType].m_bFinish3rd = (flags >> 14) & 1;
+		ms_apWeaponInfos[weaponType].m_bReload = (flags >> 15) & 1;
+		ms_apWeaponInfos[weaponType].m_bFightMode = (flags >> 16) & 1;
+		ms_apWeaponInfos[weaponType].m_bCrouchFire = (flags >> 17) & 1;
+		ms_apWeaponInfos[weaponType].m_bCop3rd = (flags >> 18) & 1;
+		ms_apWeaponInfos[weaponType].m_bGround3rd = (flags >> 19) & 1;
+		ms_apWeaponInfos[weaponType].m_bPartialAttack = (flags >> 20) & 1;
+		ms_apWeaponInfos[weaponType].m_bAnimDetonate = (flags >> 21) & 1;
 
 		ms_apWeaponInfos[weaponType].m_nWeaponSlot = weaponSlot;