 git.diff
 git.diff (133,696 bytes) 
2011-04-01 02:20  
diff --git a/rts/Game/Camera.cpp b/rts/Game/Camera.cpp
index 3a13aae..aec1220 100644
--- a/rts/Game/Camera.cpp
+++ b/rts/Game/Camera.cpp
@@ -132,9 +132,8 @@ void CCamera::Update(bool freeze, bool resetUp)
 
 	const float gndHeight = ground->GetHeightAboveWater(pos.x, pos.z);
 	const float rangemod = 1.0f + std::max(0.0f, pos.y - gndHeight - 500.0f) * 0.0003f;
-	const float zNear = (CGlobalRendering::NEAR_PLANE * rangemod);
-
-	globalRendering->viewRange = CGlobalRendering::MAX_VIEW_RANGE * rangemod;
+	const float zNear = (NEAR_PLANE * rangemod);
+	globalRendering->viewRange = MAX_VIEW_RANGE * rangemod;
 
 	glMatrixMode(GL_PROJECTION);
 	glLoadIdentity();
diff --git a/rts/Game/Game.cpp b/rts/Game/Game.cpp
index 4e046dc..85ea86f 100644
--- a/rts/Game/Game.cpp
+++ b/rts/Game/Game.cpp
@@ -217,6 +217,7 @@ CR_REG_METADATA(CGame,(
 
 
 CGame::CGame(const std::string& mapname, const std::string& modName, ILoadSaveHandler* saveFile) :
+	finishedLoading(false),
 	gameDrawMode(gameNotDrawing),
 	defsParser(NULL),
 	fps(0),
@@ -230,7 +231,6 @@ CGame::CGame(const std::string& mapname, const std::string& modName, ILoadSaveHa
 	gameOver(false),
 
 	noSpectatorChat(false),
-	finishedLoading(false),
 
 	infoConsole(NULL),
 	consoleHistory(NULL),
@@ -708,23 +708,20 @@ void CGame::ResizeEvent()
 }
 
 
-int CGame::KeyPressed(unsigned short key, bool isRepeat)
+int CGame::KeyPressed(unsigned short k, bool isRepeat)
 {
 	if (!gameOver && !isRepeat) {
 		playerHandler->Player(gu->myPlayerNum)->currentStats.keyPresses++;
 	}
 
-	// Get the list of possible key actions
-	const CKeySet ks(key, false);
-	const CKeyBindings::ActionList& actionList = keyBindings->GetActionList(ks);
-
 	if (!hotBinding.empty()) {
-		if (key == SDLK_ESCAPE) {
+		if (k == SDLK_ESCAPE) {
 			hotBinding.clear();
 		}
-		else if (!keyCodes->IsModifier(key) && (key != keyBindings->GetFakeMetaKey())) {
+		else if (!keyCodes->IsModifier(k) && (k != keyBindings->GetFakeMetaKey())) {
+			CKeySet ks(k, false);
 			string cmd = "bind";
-			cmd += " " + ks.GetString(false);
+			cmd += " " + ks.GetString(false) ;
 			cmd += " " + hotBinding;
 			keyBindings->Command(cmd);
 			hotBinding.clear();
@@ -733,15 +730,173 @@ int CGame::KeyPressed(unsigned short key, bool isRepeat)
 		return 0;
 	}
 
+	// Get the list of possible key actions
+	CKeySet ks(k, false);
+	const CKeyBindings::ActionList& actionList = keyBindings->GetActionList(ks);
+
 	if (userWriting) {
-		for (unsigned int actionIndex = 0; actionIndex < actionList.size(); actionIndex++) {
-			if (ProcessKeyPressAction(key, actionList[actionIndex])) {
-				// the key was used, ignore it (ex: alt+a)
-				ignoreNextChar = (actionIndex < (actionList.size() - 1));
+		unsigned int actionIndex;
+		for (actionIndex = 0; actionIndex < actionList.size(); actionIndex++) {
+			const Action& action = actionList[actionIndex];
+
+			if (action.command == "edit_return") {
+				userWriting=false;
+				writingPos = 0;
+
+				if (k == SDLK_RETURN) {
+					keyInput->SetKeyState(k, 0); //prevent game start when server chats
+				}
+				if (chatting) {
+					string command;
+					if ((userInput.find_first_of("aAsS") == 0) && (userInput[1] == ':')) {
+						command = userInput.substr(2);
+					} else {
+						command = userInput;
+					}
+					if ((command[0] == '/') && (command[1] != '/')) {
+						// execute an action
+						consoleHistory->AddLine(command);
+						const string actionLine = command.substr(1); // strip the '/'
+						chatting = false;
+						userInput = "";
+						writingPos = 0;
+						logOutput.Print(command);
+						CKeySet ks(k, false);
+						Action fakeAction(actionLine);
+						ActionPressed(fakeAction, ks, isRepeat);
+					}
+				}
+				break;
+			}
+			else if ((action.command == "edit_escape") &&
+			         (chatting || inMapDrawer->wantLabel)) {
+				if (chatting) {
+					consoleHistory->AddLine(userInput);
+				}
+				userWriting=false;
+				chatting=false;
+				inMapDrawer->wantLabel=false;
+				userInput="";
+				writingPos = 0;
+				break;
+			}
+			else if (action.command == "edit_complete") {
+				string head = userInput.substr(0, writingPos);
+				string tail = userInput.substr(writingPos);
+				const vector<string> &partials = wordCompletion->Complete(head);
+				userInput = head + tail;
+				writingPos = (int)head.length();
+				if (!partials.empty()) {
+					string msg;
+					for (unsigned int i = 0; i < partials.size(); i++) {
+						msg += "  ";
+						msg += partials[i];
+					}
+					logOutput.Print(msg);
+				}
+				break;
+			}
+			else if (action.command == "chatswitchall") {
+				if ((userInput.find_first_of("aAsS") == 0) && (userInput[1] == ':')) {
+					userInput = userInput.substr(2);
+					writingPos = std::max(0, writingPos - 2);
+				}
+				userInputPrefix = "";
+				break;
+			}
+			else if (action.command == "chatswitchally") {
+				if ((userInput.find_first_of("aAsS") == 0) && (userInput[1] == ':')) {
+					userInput[0] = 'a';
+				} else {
+					userInput = "a:" + userInput;
+					writingPos += 2;
+				}
+				userInputPrefix = "a:";
+				break;
+			}
+			else if (action.command == "chatswitchspec") {
+				if ((userInput.find_first_of("aAsS") == 0) && (userInput[1] == ':')) {
+					userInput[0] = 's';
+				} else {
+					userInput = "s:" + userInput;
+					writingPos += 2;
+				}
+				userInputPrefix = "s:";
+				break;
+			}
+			else if (action.command == "pastetext") {
+				if (!action.extra.empty()) {
+					userInput.insert(writingPos, action.extra);
+					writingPos += action.extra.length();
+				} else {
+					PasteClipboard();
+				}
+				break;
+			}
+
+			else if (action.command == "edit_backspace") {
+				if (!userInput.empty() && (writingPos > 0)) {
+					userInput.erase(writingPos - 1, 1);
+					writingPos--;
+				}
+				break;
+			}
+			else if (action.command == "edit_delete") {
+				if (!userInput.empty() && (writingPos < (int)userInput.size())) {
+					userInput.erase(writingPos, 1);
+				}
+				break;
+			}
+			else if (action.command == "edit_home") {
+				writingPos = 0;
+				break;
+			}
+			else if (action.command == "edit_end") {
+				writingPos = (int)userInput.length();
+				break;
+			}
+			else if (action.command == "edit_prev_char") {
+				writingPos = std::max(0, std::min((int)userInput.length(), writingPos - 1));
+				break;
+			}
+			else if (action.command == "edit_next_char") {
+				writingPos = std::max(0, std::min((int)userInput.length(), writingPos + 1));
+				break;
+			}
+			else if (action.command == "edit_prev_word") {
+				// prev word
+				const char* s = userInput.c_str();
+				int p = writingPos;
+				while ((p > 0) && !isalnum(s[p - 1])) { p--; }
+				while ((p > 0) &&  isalnum(s[p - 1])) { p--; }
+				writingPos = p;
+				break;
+			}
+			else if (action.command == "edit_next_word") {
+				const int len = (int)userInput.length();
+				const char* s = userInput.c_str();
+				int p = writingPos;
+				while ((p < len) && !isalnum(s[p])) { p++; }
+				while ((p < len) &&  isalnum(s[p])) { p++; }
+				writingPos = p;
+				break;
+			}
+			else if ((action.command == "edit_prev_line") && chatting) {
+				userInput = consoleHistory->PrevLine(userInput);
+				writingPos = (int)userInput.length();
+				break;
+			}
+			else if ((action.command == "edit_next_line") && chatting) {
+				userInput = consoleHistory->NextLine(userInput);
+				writingPos = (int)userInput.length();
 				break;
 			}
 		}
 
+		if (actionIndex != actionList.size()) {
+			ignoreNextChar = true; // the key was used, ignore it  (ex: alt+a)
+		}
+
 		return 0;
 	}
 
@@ -749,15 +904,15 @@ int CGame::KeyPressed(unsigned short key, bool isRepeat)
 	std::deque<CInputReceiver*>& inputReceivers = GetInputReceivers();
 	std::deque<CInputReceiver*>::iterator ri;
 	for (ri = inputReceivers.begin(); ri != inputReceivers.end(); ++ri) {
-		CInputReceiver* recv = *ri;
-		if (recv && recv->KeyPressed(key, isRepeat)) {
+		CInputReceiver* recv=*ri;
+		if (recv && recv->KeyPressed(k, isRepeat)) {
 			return 0;
 		}
 	}
 
 	// try our list of actions
-	for (unsigned int i = 0; i < actionList.size(); ++i) {
-		if (ActionPressed(key, actionList[i], isRepeat)) {
+	for (int i = 0; i < (int)actionList.size(); ++i) {
+		if (ActionPressed(actionList[i], ks, isRepeat)) {
 			return 0;
 		}
 	}
@@ -1609,12 +1764,9 @@ void CGame::SimFrame() {
 		m_validateAllAllocUnits();
 #endif
 
-	// Important: gs->frameNum must be updated *before* GameFrame is called,
-	// or any call-outs called by Lua will see a stale gs->frameNum.
-	// (e.g. effective TTL of CEGs emitted in GameFrame will be reduced...)
-	// It must still be passed the old frameNum because Lua may depend on it.
-	// (e.g. initialization in frame 0...)
-	eventHandler.GameFrame(gs->frameNum++);
+	eventHandler.GameFrame(gs->frameNum);
+
+	gs->frameNum++;
 
 	if (!skipping) {
 		infoConsole->Update();
diff --git a/rts/Game/Game.h b/rts/Game/Game.h
index bfcb914..5837b90 100644
--- a/rts/Game/Game.h
+++ b/rts/Game/Game.h
@@ -47,7 +47,6 @@ private:
 
 public:
 	CGame(const std::string& mapname, const std::string& modName, ILoadSaveHandler* saveFile);
-	virtual ~CGame();
 
 	bool Draw();
 	bool DrawMT();
@@ -57,22 +56,20 @@ public:
 	/// Called when a key is released by the user
 	int KeyReleased(unsigned short k);
 	/// Called when the key is pressed by the user (can be called several times due to key repeat)
-	int KeyPressed(unsigned short k, bool isRepeat);
+	int KeyPressed(unsigned short k,bool isRepeat);
 	void ResizeEvent();
+	virtual ~CGame();
 
-
-	bool ProcessCommandText(unsigned int key, const std::string& command);
-	bool ProcessKeyPressAction(unsigned int key, const Action& action);
-
-	bool ActionPressed(unsigned int key, const Action&, bool isRepeat);
+	bool ActionPressed(const Action&, const CKeySet& ks, bool isRepeat);
 	bool ActionReleased(const Action&);
-
+	
 	bool HasLag() const;
 
+	volatile bool finishedLoading;
+
 	/// show GameEnd-window, calculate mouse movement etc.
 	void GameEnd(const std::vector<unsigned char>& winningAllyTeams);
 
-
 	enum GameDrawMode {
 		gameNotDrawing     = 0,
 		gameNormalDraw     = 1,
@@ -120,7 +117,6 @@ public:
 	bool showMTInfo;
 	/// Prevents spectator msgs from being seen by players
 	bool noSpectatorChat;
-	volatile bool finishedLoading;
 
 	unsigned char gameID[16];
 
@@ -142,7 +138,7 @@ private:
 	void SendNetChat(std::string message, int destination = -1);
 	/// Format and display a chat message received over network
 	void HandleChatMsg(const ChatMessage& msg);
-
+	
 	/// synced actions (received from server) go in here
 	void ActionReceived(const Action&, int playernum);
 
diff --git a/rts/Game/GameHelper.cpp b/rts/Game/GameHelper.cpp
index 404c9af..53b72de 100644
--- a/rts/Game/GameHelper.cpp
+++ b/rts/Game/GameHelper.cpp
@@ -829,7 +829,7 @@ public:
 
 
 
-void CGameHelper::GenerateWeaponTargets(const CWeapon* weapon, const CUnit* lastTargetUnit, std::multimap<float, CUnit*>& targets)
+void CGameHelper::GenerateWeaponTargets(const CWeapon* weapon, CUnit* lastTarget, std::multimap<float, CUnit*>& targets)
 {
 	GML_RECMUTEX_LOCK(qnum); // GenerateTargets
 
@@ -845,7 +845,7 @@ void CGameHelper::GenerateWeaponTargets(const CWeapon* weapon, const CUnit* last
 
 	const std::vector<int>& quads = qf->GetQuads(pos, radius + (aHeight - std::max(0.f, readmap->minheight)) * heightMod);
 
-	const int tempNum = gs->tempNum++;
+	int tempNum = gs->tempNum++;
 
 	typedef std::vector<int>::const_iterator VectorIt;
 	typedef std::list<CUnit*>::const_iterator ListIt;
@@ -893,16 +893,13 @@ void CGameHelper::GenerateWeaponTargets(const CWeapon* weapon, const CUnit* last
 					const float modRange = radius + (aHeight - targPos.y) * heightMod;
 
 					if ((pos - targPos).SqLength2D() <= modRange * modRange) {
-						const float dist2D = (pos - targPos).Length2D();
-						const float rangeMul = (dist2D * weapon->weaponDef->proximityPriority + modRange * 0.4f + 100.0f);
-						const float damageMul = weapon->weaponDef->damages[targetUnit->armorType] * targetUnit->curArmorMultiple;
-
-						targetPriority *= rangeMul;
+						float dist2d = (pos - targPos).Length2D();
+						targetPriority *= (dist2d * weapon->weaponDef->proximityPriority + modRange * 0.4f + 100.0f);
 
 						if (targetLOSState & LOS_INLOS) {
 							targetPriority *= (secDamage + targetUnit->health);
 
-							if (targetUnit == lastTargetUnit) {
+							if (targetUnit == lastTarget) {
 								targetPriority *= weapon->avoidTarget ? 10.0f : 0.4f;
 							}
 
@@ -918,7 +915,9 @@ void CGameHelper::GenerateWeaponTargets(const CWeapon* weapon, const CUnit* last
 						}
 
 						if (targetLOSState & LOS_PREVLOS) {
-							targetPriority /= (damageMul * targetUnit->power * (0.7f + gs->randFloat() * 0.6f));
+							targetPriority /=
+								weapon->weaponDef->damages[targetUnit->armorType] * targetUnit->curArmorMultiple *
+								targetUnit->power * (0.7f + gs->randFloat() * 0.6f);
 
 							if (targetUnit->category & weapon->badTargetCategory) {
 								targetPriority *= 100.0f;
@@ -945,6 +944,7 @@ void CGameHelper::GenerateWeaponTargets(const CWeapon* weapon, const CUnit* last
 		tracefile << "\n";
 	}
 #endif
+
 }
 
 CUnit* CGameHelper::GetClosestUnit(const float3 &pos, float searchRadius)
diff --git a/rts/Game/GameHelper.h b/rts/Game/GameHelper.h
index b079d10..8d4845d 100644
--- a/rts/Game/GameHelper.h
+++ b/rts/Game/GameHelper.h
@@ -55,7 +55,7 @@ public:
 	CUnit* GetClosestFriendlyUnit(const float3& pos, float searchRadius, int searchAllyteam);
 	CUnit* GetClosestEnemyAircraft(const float3& pos, float searchRadius, int searchAllyteam);
 
-	void GenerateWeaponTargets(const CWeapon* attacker, const CUnit* lastTarget, std::multimap<float, CUnit*>& targets);
+	void GenerateWeaponTargets(const CWeapon* attacker, CUnit* lastTarget, std::multimap<float, CUnit*>& targets);
 	float TraceRay(const float3& start, const float3& dir, float length, float power, const CUnit* owner, const CUnit*& hit, int collisionFlags = 0, const CFeature** hitFeature = NULL);
 	float GuiTraceRay(const float3& start, const float3& dir, float length, const CUnit*& hit, bool useRadar, const CUnit* exclude = NULL);
 	float GuiTraceRayFeature(const float3& start, const float3& dir, float length, const CFeature*& feature);
diff --git a/rts/Game/PreGame.cpp b/rts/Game/PreGame.cpp
index 69c4927..476d066 100644
--- a/rts/Game/PreGame.cpp
+++ b/rts/Game/PreGame.cpp
@@ -214,23 +214,19 @@ void CPreGame::UpdateClientNet()
 					std::string message;
 					pckt >> message;
 					logOutput.Print(message);
-					handleerror(NULL, "Remote requested quit: " + message, "Quit message", MBF_OK | MBF_EXCL);
+					handleerror(NULL, message, "Quit message", MBF_OK | MBF_EXCL);
 				} catch (netcode::UnpackPacketException &e) {
 					logOutput.Print("Got invalid QuitMessage: %s", e.err.c_str());
 				}		
 				break;
 			}
-			case NETMSG_CREATE_NEWPLAYER: {
-				// server will send this first if we're using mid-game join
-				// feature, to let us know about ourself (we won't be in
-				// gamedata), otherwise skip to gamedata
+			case NETMSG_CREATE_NEWPLAYER: { // server will send this first if we're using midgame join feature, to let us know about ourself (we won't be in gamedata), otherwise skip to gamedata
 				try {
 					netcode::UnpackPacket pckt(packet, 3);
 					unsigned char spectator, team, playerNum;
 					std::string name;
-					// since the >> operator uses dest size to extract data from
-					// the packet, we need to use temp variables of the same
-					// size of the packet, before converting to dest variable
+					// since the >> operator uses dest size to extract data from the packet, we need to use temp variables
+					// of the same size of the packet, then convert to dest variable
 					pckt >> playerNum;
 					pckt >> spectator;
 					pckt >> team;
@@ -241,30 +237,22 @@ void CPreGame::UpdateClientNet()
 					player.spectator = spectator;
 					player.team = team;
 					player.playerNum = playerNum;
-					// add ourself, to avoid crashing if our player num gets
-					// queried we will receive the same message later, in the
-					// game class, which is the global broadcast version
-					// the global broadcast will overwrite the user with the
-					// same values as here
+					// add ourself, to avoid crashing if our player num gets queried
+					// we'll receive the same message later, in the game class, which is the global broadcast version
+					// the global broadcast will overwrite the user with the same values as here
 					playerHandler->AddPlayer(player);
 				} catch (netcode::UnpackPacketException &e) {
 					logOutput.Print("Got invalid New player message: %s", e.err.c_str());
 				}
 				break;
 			}
-			case NETMSG_GAMEDATA: {
-				// server first sends this to let us know about teams, allyteams
-				// etc.
-				// (not if we are joining mid-game as extra players)
-				// see NETMSG_SETPLAYERNUM
+			case NETMSG_GAMEDATA: { // server first ( not if we're joining midgame as extra players ) sends this to let us know about teams, allyteams etc.
 				if (gameSetup)
 					throw content_error("Duplicate game data received from server");
 				GameDataReceived(packet);
 				break;
 			}
-			case NETMSG_SETPLAYERNUM: {
-				// this is sent after NETMSG_GAMEDATA, to let us know which
-				// playernum we have
+			case NETMSG_SETPLAYERNUM: { // this is sent afterwards to let us know which playernum we have
 				if (!gameSetup)
 					throw content_error("No game data received from server");
 
diff --git a/rts/Game/UI/GuiHandler.cpp b/rts/Game/UI/GuiHandler.cpp
index 0c298c2..87be01c 100644
--- a/rts/Game/UI/GuiHandler.cpp
+++ b/rts/Game/UI/GuiHandler.cpp
@@ -1412,7 +1412,8 @@ void CGuiHandler::RunCustomCommands(const std::vector<std::string>& cmds, bool r
 
 				Action action(copy);
 				if (!ProcessLocalActions(action)) {
-					game->ActionPressed(-1, action, false /*isRepeat*/);
+					CKeySet ks;
+					game->ActionPressed(action, ks, false /*isRepeat*/);
 				}
 
 				keyInput->SetKeyState(SDLK_LALT,   tmpAlt);
diff --git a/rts/Game/UnsyncedGameCommands.cpp b/rts/Game/UnsyncedGameCommands.cpp
index d7aae2a..ffb936b 100644
--- a/rts/Game/UnsyncedGameCommands.cpp
+++ b/rts/Game/UnsyncedGameCommands.cpp
@@ -15,7 +15,7 @@
 #include "PlayerRoster.h"
 #include "TimeProfiler.h"
 #include "IVideoCapturing.h"
-#include "WordCompletion.h"
+#include "Game/UI/UnitTracker.h"
 #ifdef _WIN32
 #  include "winerror.h"
 #endif
@@ -55,7 +55,6 @@
 #include "UI/SelectionKeyHandler.h"
 #include "UI/ShareBox.h"
 #include "UI/TooltipConsole.h"
-#include "UI/UnitTracker.h"
 #include "UI/ProfileDrawer.h"
 #include "System/ConfigHandler.h"
 #include "System/EventHandler.h"
@@ -89,186 +88,9 @@ static std::vector<std::string> _local_strSpaceTokenize(const std::string& text)
 }
 
 
-
-bool CGame::ProcessCommandText(unsigned int key, const std::string& command) {
-	if (command.size() <= 2)
-		return false;
-
-	if ((command[0] == '/') && (command[1] != '/')) {
-		const string actionLine = command.substr(1); // strip the '/'
-
-		Action fakeAction(actionLine);
-		ActionPressed(key, fakeAction, false);
-		return true;
-	}
-
-	return false;
-}
-
-bool CGame::ProcessKeyPressAction(unsigned int key, const Action& action) {
-	if (action.command == "edit_return") {
-		userWriting = false;
-		writingPos = 0;
-
-		if (key == SDLK_RETURN) {
-			// prevent game start when host enters a chat message
-			keyInput->SetKeyState(key, 0);
-		}
-		if (chatting) {
-			string command;
-
-			if ((userInput.find_first_of("aAsS") == 0) && (userInput[1] == ':')) {
-				command = userInput.substr(2);
-			} else {
-				command = userInput;
-			}
-
-			if (ProcessCommandText(key, command)) {
-				// execute an action
-				consoleHistory->AddLine(command);
-				logOutput.Print(command);
-
-				chatting = false;
-				userInput = "";
-				writingPos = 0;
-			}
-		}
-		return true;
-	}
-	else if ((action.command == "edit_escape") && (chatting || inMapDrawer->wantLabel)) {
-		if (chatting) {
-			consoleHistory->AddLine(userInput);
-		}
-		userWriting = false;
-		chatting = false;
-		inMapDrawer->wantLabel = false;
-		userInput = "";
-		writingPos = 0;
-		return true;
-	}
-	else if (action.command == "edit_complete") {
-		string head = userInput.substr(0, writingPos);
-		string tail = userInput.substr(writingPos);
-		const vector<string> &partials = wordCompletion->Complete(head);
-		userInput = head + tail;
-		writingPos = (int)head.length();
-
-		if (!partials.empty()) {
-			string msg;
-			for (unsigned int i = 0; i < partials.size(); i++) {
-				msg += "  ";
-				msg += partials[i];
-			}
-			logOutput.Print(msg);
-		}
-		return true;
-	}
-	else if (action.command == "chatswitchall") {
-		if ((userInput.find_first_of("aAsS") == 0) && (userInput[1] == ':')) {
-			userInput = userInput.substr(2);
-			writingPos = std::max(0, writingPos - 2);
-		}
-
-		userInputPrefix = "";
-		return true;
-	}
-	else if (action.command == "chatswitchally") {
-		if ((userInput.find_first_of("aAsS") == 0) && (userInput[1] == ':')) {
-			userInput[0] = 'a';
-		} else {
-			userInput = "a:" + userInput;
-			writingPos += 2;
-		}
-
-		userInputPrefix = "a:";
-		return true;
-	}
-	else if (action.command == "chatswitchspec") {
-		if ((userInput.find_first_of("aAsS") == 0) && (userInput[1] == ':')) {
-			userInput[0] = 's';
-		} else {
-			userInput = "s:" + userInput;
-			writingPos += 2;
-		}
-
-		userInputPrefix = "s:";
-		return true;
-	}
-	else if (action.command == "pastetext") {
-		if (!action.extra.empty()) {
-			userInput.insert(writingPos, action.extra);
-			writingPos += action.extra.length();
-		} else {
-			PasteClipboard();
-		}
-		return true;
-	}
-
-	else if (action.command == "edit_backspace") {
-		if (!userInput.empty() && (writingPos > 0)) {
-			userInput.erase(writingPos - 1, 1);
-			writingPos--;
-		}
-		return true;
-	}
-	else if (action.command == "edit_delete") {
-		if (!userInput.empty() && (writingPos < (int)userInput.size())) {
-			userInput.erase(writingPos, 1);
-		}
-		return true;
-	}
-	else if (action.command == "edit_home") {
-		writingPos = 0;
-		return true;
-	}
-	else if (action.command == "edit_end") {
-		writingPos = (int)userInput.length();
-		return true;
-	}
-	else if (action.command == "edit_prev_char") {
-		writingPos = std::max(0, std::min((int)userInput.length(), writingPos - 1));
-		return true;
-	}
-	else if (action.command == "edit_next_char") {
-		writingPos = std::max(0, std::min((int)userInput.length(), writingPos + 1));
-		return true;
-	}
-	else if (action.command == "edit_prev_word") {
-		// prev word
-		const char* s = userInput.c_str();
-		int p = writingPos;
-		while ((p > 0) && !isalnum(s[p - 1])) { p--; }
-		while ((p > 0) &&  isalnum(s[p - 1])) { p--; }
-		writingPos = p;
-		return true;
-	}
-	else if (action.command == "edit_next_word") {
-		const int len = (int)userInput.length();
-		const char* s = userInput.c_str();
-		int p = writingPos;
-		while ((p < len) && !isalnum(s[p])) { p++; }
-		while ((p < len) &&  isalnum(s[p])) { p++; }
-		writingPos = p;
-		return true;
-	}
-	else if ((action.command == "edit_prev_line") && chatting) {
-		userInput = consoleHistory->PrevLine(userInput);
-		writingPos = (int)userInput.length();
-		return true;
-	}
-	else if ((action.command == "edit_next_line") && chatting) {
-		userInput = consoleHistory->NextLine(userInput);
-		writingPos = (int)userInput.length();
-		return true;
-	}
-
-	return false;
-}
-
-
-
 // FOR UNSYNCED MESSAGES
-bool CGame::ActionPressed(unsigned int key, const Action& action, bool isRepeat)
+bool CGame::ActionPressed(const Action& action,
+                          const CKeySet& ks, bool isRepeat)
 {
 	// we may need these later
 	CBaseGroundDrawer* gd = readmap->GetGroundDrawer();
@@ -409,7 +231,7 @@ bool CGame::ActionPressed(unsigned int key, const Action& action, bool isRepeat)
 		if (pos.x >= 0) {
 			inMapDrawer->keyPressed = false;
 			inMapDrawer->PromptLabel(pos);
-			if ((key >= SDLK_SPACE) && (key <= SDLK_DELETE)) {
+			if ((ks.Key() >= SDLK_SPACE) && (ks.Key() <= SDLK_DELETE)) {
 				ignoreNextChar=true;
 			}
 		}
@@ -803,7 +625,7 @@ bool CGame::ActionPressed(unsigned int key, const Action& action, bool isRepeat)
 	else if (((cmd == "chat")     || (cmd == "chatall") ||
 	         (cmd == "chatally") || (cmd == "chatspec")) &&
 	         // if chat is bound to enter and we're waiting for user to press enter to start game, ignore.
-				  (key != SDLK_RETURN || playing || !keyInput->IsKeyPressed(SDLK_LCTRL))) {
+				  (ks.Key() != SDLK_RETURN || playing || !keyInput->IsKeyPressed(SDLK_LCTRL))) {
 
 		if (cmd == "chatall")  { userInputPrefix = ""; }
 		if (cmd == "chatally") { userInputPrefix = "a:"; }
@@ -814,7 +636,7 @@ bool CGame::ActionPressed(unsigned int key, const Action& action, bool isRepeat)
 		writingPos = (int)userInput.length();
 		chatting = true;
 
-		if (key != SDLK_RETURN) {
+		if (ks.Key() != SDLK_RETURN) {
 			ignoreNextChar = true;
 		}
 
@@ -1646,3 +1468,5 @@ bool CGame::ActionPressed(unsigned int key, const Action& action, bool isRepeat)
 
 	return true;
 }
+
+
diff --git a/rts/Rendering/Env/AdvSky.cpp b/rts/Rendering/Env/AdvSky.cpp
index 08393fe..680fa0f 100644
--- a/rts/Rendering/Env/AdvSky.cpp
+++ b/rts/Rendering/Env/AdvSky.cpp
@@ -46,7 +46,6 @@ CAdvSky::CAdvSky()
 	domeWidth = sin(2*PI/32)*400*1.7f;
 
 	UpdateSkyDir();
-	InitSun();
 	UpdateSunDir();
 
 	for(int a=0;a<CLOUD_DETAIL;a++)
@@ -65,15 +64,11 @@ CAdvSky::CAdvSky()
 	CreateClouds();
 	dynamicSky = configHandler->Get("DynamicSky", false);
 
+	InitSun();
 	oldCoverBaseX=-5;
 
 	cloudFP = LoadFragmentProgram("ARB/clouds.fp");
 
-	CreateSkyDomeList();
-}
-
-void CAdvSky::CreateSkyDomeList()
-{
 	glGetError();
 	skyDomeList = glGenLists(1);
 	glNewList(skyDomeList, GL_COMPILE);
@@ -665,7 +660,7 @@ void CAdvSky::InitSun()
 	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
 	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
 //	gluBuild2DMipmaps(GL_TEXTURE_2D,1 ,32, 2, GL_ALPHA, GL_UNSIGNED_BYTE, mem);
-	glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE ,32, 4, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, mem);
+	glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE ,32, 4,0, GL_LUMINANCE, GL_UNSIGNED_BYTE, mem);
 
 	delete [] mem;
 }
diff --git a/rts/Rendering/Env/AdvSky.h b/rts/Rendering/Env/AdvSky.h
index f963437..5686ce9 100644
--- a/rts/Rendering/Env/AdvSky.h
+++ b/rts/Rendering/Env/AdvSky.h
@@ -21,7 +21,6 @@ public:
 	void UpdateSkyTexture();
 
 private:
-	void CreateSkyDomeList();
 	void InitSun();
 	void UpdateSunFlare();
 	void CreateCover(int baseX, int baseY, float* buf);
diff --git a/rts/Rendering/Env/BaseTreeDrawer.cpp b/rts/Rendering/Env/BaseTreeDrawer.cpp
index bcfcd20..31ebe22 100644
--- a/rts/Rendering/Env/BaseTreeDrawer.cpp
+++ b/rts/Rendering/Env/BaseTreeDrawer.cpp
@@ -7,7 +7,6 @@
 #include "BasicTreeDrawer.h"
 #include "AdvTreeDrawer.h"
 #include "Game/Camera.h"
-#include "Rendering/GlobalRendering.h"
 #include "Sim/Features/FeatureHandler.h"
 #include "Sim/Features/Feature.h"
 #include "Sim/Misc/GlobalConstants.h"
@@ -83,8 +82,7 @@ void CBaseTreeDrawer::DrawShadowPass()
 
 void CBaseTreeDrawer::Draw(bool drawReflection)
 {
-	const float maxDistance = CGlobalRendering::MAX_VIEW_RANGE / (SQUARE_SIZE * TREE_SQUARE_SIZE);
-	const float treeDistance = Clamp(baseTreeDistance, 1.0f, maxDistance);
+	const float treeDistance = Clamp(baseTreeDistance, 1.0f, (float)MAX_VIEW_RANGE / (SQUARE_SIZE * TREE_SQUARE_SIZE));
 	Draw(treeDistance, drawReflection);
 }
 
diff --git a/rts/Rendering/Env/BasicSky.cpp b/rts/Rendering/Env/BasicSky.cpp
index 67621f5..10b2b3e 100644
--- a/rts/Rendering/Env/BasicSky.cpp
+++ b/rts/Rendering/Env/BasicSky.cpp
@@ -48,7 +48,6 @@ CBasicSky::CBasicSky()
 	domeWidth=sin(PI/16)*400*1.7f;
 
 	UpdateSkyDir();
-	InitSun();
 	UpdateSunDir();
 
 	cloudDensity = 0.25f + mapInfo->atmosphere.cloudDensity * 0.5f;
@@ -67,9 +66,8 @@ CBasicSky::CBasicSky()
 	CreateClouds();
 	dynamicSky = configHandler->Get("DynamicSky", false);
 
+	InitSun();
 	oldCoverBaseX=-5;
-
-	CreateSkyDomeList();
 }
 
 void CBasicSky::CreateSkyDomeList()
@@ -514,7 +512,7 @@ void CBasicSky::DrawSun()
 
 	glDisable(GL_DEPTH_TEST);
 	glDisable(GL_ALPHA_TEST);
-	static unsigned char buf[32];
+	static unsigned char buf[128];
 	glEnable(GL_TEXTURE_2D);
 
 	float3 modCamera=sundir1*camera->pos.x+sundir2*camera->pos.z;
@@ -538,6 +536,7 @@ void CBasicSky::DrawSun()
 	}
 
 	float mid=0;
+	unsigned char *bf=buf+32, *bf2=buf+64;
 	for(int x=0;x<32;++x){
 		float cx1=(*cvs++)*(1-fx)+(*cvs1++)*fx;
 		float cx2=(*cvs2++)*(1-fx)+(*cvs3++)*fx;
@@ -546,6 +545,9 @@ void CBasicSky::DrawSun()
 		if(cover>127.5f)
 			cover=127.5f;
 		mid+=cover;
+
+		(*bf++)=(unsigned char)(255-cover*2);
+		(*bf2++)=(unsigned char)(128-cover);
 	}
 	mid*=1.0f/32;
 	for(int x=0;x<32;++x){
@@ -553,7 +555,7 @@ void CBasicSky::DrawSun()
 	}
 
 	glBindTexture(GL_TEXTURE_2D, sunFlareTex);
-	glTexSubImage2D(GL_TEXTURE_2D,0,0,0,32,1,GL_LUMINANCE,GL_UNSIGNED_BYTE,buf);
+	glTexSubImage2D(GL_TEXTURE_2D,0,0,0,32,3,GL_LUMINANCE,GL_UNSIGNED_BYTE,buf);
 
 	const float si = skyLight->GetLightIntensity();
 	const float3 sc = sunColor * si;
@@ -584,9 +586,9 @@ void CBasicSky::UpdateSunFlare() {
 			float dx=sin(x*2*PI/256.0f);
 			float dy=cos(x*2*PI/256.0f);
 
-			glTexCoord2f(x/256.0f,0.25f);
+			glTexCoord2f(x/256.0f,0.125f);
 			glVertexf3(modSunDir*5+ldir*dx*0.0014f+udir*dy*0.0014f);
-			glTexCoord2f(x/256.0f,0.75f);
+			glTexCoord2f(x/256.0f,0.875f);
 			glVertexf3(modSunDir*5+ldir*dx*4+udir*dy*4);
 		}
 		glEnd();
@@ -621,7 +623,7 @@ void CBasicSky::InitSun()
 	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
 	glBuildMipmaps(GL_TEXTURE_2D,GL_RGBA8 ,128, 128, GL_RGBA, GL_UNSIGNED_BYTE, mem);
 
-	for(int y=0;y<2;++y){
+	for(int y=0;y<4;++y){
 		for(int x=0;x<32;++x){
 			if(y==0 && x%2)
 				mem[(y*32+x)]=255;
@@ -635,7 +637,7 @@ void CBasicSky::InitSun()
 	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
 	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
 	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
-	glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE ,32, 2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, mem);
+	glTexImage2D(GL_TEXTURE_2D,0, GL_LUMINANCE, 32, 4, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, mem);
 
 	delete[] mem;
 }
@@ -793,13 +795,14 @@ void CBasicSky::UpdateTexPartDot3(int x, int y, unsigned char (*texp)[4]) {
 
 	const float sunInt = skyLight->GetLightIntensity();
 	const float sunDist = acos(dir.dot(skyLight->GetLightDir())) * 50;
-	const float sunMod = sunInt * (0.3f / math::sqrt(sunDist) + 2.0f / sunDist);
+	const float sunMod = sunInt * (0.3f / math::sqrt(sunDist) + 3.0f / (1 + sunDist));
 
 	const float green = std::min(1.0f, (0.55f + sunMod));
+	const float blue  = 203 - sunInt * (40.0f / (3 + sunDist));
 
 	texp[x][0] = (unsigned char) (sunInt * (255 - std::min(255.0f, sunDist))); // sun on borders
 	texp[x][1] = (unsigned char) (green * 255); // sun light through
-	texp[x][2] = (unsigned char)  203; // ambient
+	texp[x][2] = (unsigned char)  blue; // ambient
 	texp[x][3] = 255;
 }
 
diff --git a/rts/Rendering/FarTextureHandler.cpp b/rts/Rendering/FarTextureHandler.cpp
old mode 100644
new mode 100755
index 3a7978a..74f20f4
--- a/rts/Rendering/FarTextureHandler.cpp
+++ b/rts/Rendering/FarTextureHandler.cpp
@@ -181,7 +181,7 @@ void CFarTextureHandler::CreateFarTexture(const CSolidObject* obj)
 	unitDrawer->SetupForUnitDrawing();
 	unitDrawer->GetOpaqueModelRenderer(model->type)->PushRenderState();
 
-	if (model->type == MODELTYPE_S3O || model->type == MODELTYPE_OBJ) {
+	if (model->type == MODELTYPE_S3O || model->type == MODELTYPE_OBJ || model->type == MODELTYPE_ASS) {
 		// FIXME for some strange reason we need to invert the culling, why?
 		if (model->type == MODELTYPE_S3O) {
 			glCullFace(GL_FRONT);
diff --git a/rts/Rendering/FeatureDrawer.cpp b/rts/Rendering/FeatureDrawer.cpp
old mode 100644
new mode 100755
index 4385a71..b88eab3
--- a/rts/Rendering/FeatureDrawer.cpp
+++ b/rts/Rendering/FeatureDrawer.cpp
@@ -229,7 +229,7 @@ void CFeatureDrawer::DrawOpaqueFeatures(int modelType)
 	FeatureSet::iterator featureSetIt;
 
 	for (featureBinIt = featureBin.begin(); featureBinIt != featureBin.end(); ++featureBinIt) {
-		if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ) {
+		if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ || modelType == MODELTYPE_ASS) {
 			texturehandlerS3O->SetS3oTexture(featureBinIt->first);
 		}
 
@@ -371,7 +371,7 @@ void CFeatureDrawer::DrawFadeFeaturesHelper(int modelType) {
 		FeatureRenderBin& featureBin = cloakedModelRenderers[modelType]->GetFeatureBinMutable();
 
 		for (FeatureRenderBinIt it = featureBin.begin(); it != featureBin.end(); ++it) {
-			if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ) {
+			if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ || modelType == MODELTYPE_ASS) {
 				texturehandlerS3O->SetS3oTexture(it->first);
 			}
 
diff --git a/rts/Rendering/GlobalRendering.cpp b/rts/Rendering/GlobalRendering.cpp
index 60bd337..86d328b 100644
--- a/rts/Rendering/GlobalRendering.cpp
+++ b/rts/Rendering/GlobalRendering.cpp
@@ -22,8 +22,6 @@
  */
 CGlobalRendering* globalRendering;
 
-const float CGlobalRendering::MAX_VIEW_RANGE = 8000.0f;
-const float CGlobalRendering::NEAR_PLANE = 2.8f;
 
 CR_BIND(CGlobalRendering, );
 
diff --git a/rts/Rendering/GlobalRendering.h b/rts/Rendering/GlobalRendering.h
index 38525b0..e60ba46 100644
--- a/rts/Rendering/GlobalRendering.h
+++ b/rts/Rendering/GlobalRendering.h
@@ -4,6 +4,8 @@
 #define _GLOBAL_RENDERING_H
 
 #include "System/creg/creg_cond.h"
+#include "System/float4.h"
+#include "System/Matrix44f.h"
 
 /**
  * @brief Globally accessible unsynced, rendering related data
@@ -180,18 +182,6 @@ public:
 	 * @brief full-screen or windowed rendering
 	 */
 	bool fullScreen;
-
-
-
-	/**
-	* @brief max view range in elmos
-	*/
-	static const float MAX_VIEW_RANGE;
-
-	/**
-	* @brief near z-plane distance in elmos
-	*/
-	static const float NEAR_PLANE;
 };
 
 extern CGlobalRendering* globalRendering;
diff --git a/rts/Rendering/Models/3DModel.cpp b/rts/Rendering/Models/3DModel.cpp
old mode 100644
new mode 100755
index 5c3d98d..02df7b7
--- a/rts/Rendering/Models/3DModel.cpp
+++ b/rts/Rendering/Models/3DModel.cpp
@@ -18,6 +18,18 @@
 
 
 //////////////////////////////////////////////////////////////////////
+// S3DModel
+//
+
+S3DModelPiece* S3DModel::FindPiece( std::string name )
+{
+    const ModelPieceMap::const_iterator it = pieces.find(name);
+    if (it != pieces.end()) return it->second;
+    return NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////
 // S3DModelPiece
 //
 
diff --git a/rts/Rendering/Models/3DModel.h b/rts/Rendering/Models/3DModel.h
old mode 100644
new mode 100755
index ba577b7..0501a41
--- a/rts/Rendering/Models/3DModel.h
+++ b/rts/Rendering/Models/3DModel.h
@@ -5,23 +5,26 @@
 
 #include <vector>
 #include <string>
-
+#include <set>
+#include <map>
 #include "System/Matrix44f.h"
 
 
-
 const int
 	MODELTYPE_3DO   = 0,
 	MODELTYPE_S3O   = 1,
 	MODELTYPE_OBJ   = 2,
-	MODELTYPE_OTHER = 3;
+	MODELTYPE_ASS	= 3, // Model loaded by Assimp library
+	MODELTYPE_OTHER	= 4; // For future use. Still used in some parts of code.
 
 struct CollisionVolume;
 struct S3DModel;
 struct S3DModelPiece;
 struct LocalModel;
 struct LocalModelPiece;
+struct aiScene;
 
+typedef std::map<std::string, S3DModelPiece*> ModelPieceMap;
 
 struct S3DModelPiece {
 	S3DModelPiece(): type(-1) {
@@ -53,8 +56,10 @@ struct S3DModelPiece {
 
 
 	std::string name;
+	std::string parentName;
 	std::vector<S3DModelPiece*> childs;
 
+    S3DModel* model;
 	S3DModelPiece* parent;
 	CollisionVolume* colvol;
 
@@ -68,23 +73,32 @@ struct S3DModelPiece {
 	float3 maxs;
 	float3 offset;    // wrt. parent
 	float3 goffset;   // wrt. root
+	float3 rot;
+	float3 scale;
 };
 
 
 struct S3DModel
 {
 	S3DModel(): id(-1), type(-1), textureType(-1) {
-		numPieces = 0;
-
-		radius = 0.0f;
-		height = 0.0f;
-
-		rootPiece = NULL;
+        height = 0.0f;
+        radius = 0.0f;
+        relMidPos = float3(0.0f, 0.0f, 0.0f);
+        mins = float3(10000.0f, 10000.0f, 10000.0f);
+        maxs = float3(-10000.0f, -10000.0f, -10000.0f);
+        tex1 = "default.png";
+        tex2 = "";
+        flipTexY = false;
+        invertTexAlpha = false;
+        numPieces = 0;
+        rootPiece = NULL;
+        scene = NULL;
 	}
 
 	S3DModelPiece* GetRootPiece() { return rootPiece; }
 	void SetRootPiece(S3DModelPiece* p) { rootPiece = p; }
 	void DrawStatic() const { rootPiece->DrawStatic(); }
+	S3DModelPiece* FindPiece( std::string name );
 
 	std::string name;
 	std::string tex1;
@@ -93,8 +107,9 @@ struct S3DModel
 	int id;                 //! unsynced ID, starting with 1
 	int type;               //! MODELTYPE_*
 	int textureType;        //! FIXME: MAKE S3O ONLY (0 = 3DO, otherwise S3O or OBJ)
+	bool flipTexY;			//! Turn both textures upside down before use
+	bool invertTexAlpha;	//! Invert teamcolor alpha channel in S3O texture 1
 
-	int numPieces;
 	float radius;
 	float height;
 
@@ -102,7 +117,10 @@ struct S3DModel
 	float3 maxs;
 	float3 relMidPos;
 
-	S3DModelPiece* rootPiece;
+	int numPieces;
+    S3DModelPiece* rootPiece;   //! The piece at the base of the model hierarchy
+    ModelPieceMap pieces;       //! Lookup table for pieces by name
+    const aiScene* scene;       //! Assimp scene containing all loaded model data. NULL for S30/3DO.
 };
 
 
diff --git a/rts/Rendering/Models/3DModelLog.h b/rts/Rendering/Models/3DModelLog.h
new file mode 100644
index 0000000..c1b38e2
--- /dev/null
+++ b/rts/Rendering/Models/3DModelLog.h
@@ -0,0 +1,14 @@
+/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
+
+#ifndef _3DMODELLOG_H_
+#define _3DMODELLOG_H_
+
+#include "System/LogOutput.h"
+
+// Enabling the "Model" and "Piece" log subsystems will help debug model loading. "Model-detail" is extremely verbose.
+static CLogSubsystem LOG_MODEL("Model");
+static CLogSubsystem LOG_MODEL_DETAIL("Model-detail");
+static CLogSubsystem LOG_PIECE("Piece");
+static CLogSubsystem LOG_PIECE_DETAIL("Piece-detail");
+
+#endif // _3DMODELLOG_H_
diff --git a/rts/Rendering/Models/AssIO.cpp b/rts/Rendering/Models/AssIO.cpp
new file mode 100644
index 0000000..9a2a1cb
--- /dev/null
+++ b/rts/Rendering/Models/AssIO.cpp
@@ -0,0 +1,96 @@
+#include "StdAfx.h"
+
+#include <string>
+
+#include "AssIO.h"
+
+#include "FileSystem/FileHandler.h"
+
+AssVFSStream::AssVFSStream(const std::string& pFile, const std::string& pMode)
+{
+	file = new CFileHandler(pFile, pMode);
+}
+
+AssVFSStream::~AssVFSStream(void)
+{
+	delete file;
+}
+
+size_t AssVFSStream::Read( void* pvBuffer, size_t pSize, size_t pCount)
+{
+	// Spring VFS only supports reading chars. Need to convert.
+	int length = pSize * pCount;
+	return file->Read(pvBuffer, length);
+}
+
+size_t AssVFSStream::Write( const void* pvBuffer, size_t pSize, size_t pCount)
+{
+	// FAKE. Shouldn't need to write back to VFS
+	return pSize * pCount;
+}
+
+aiReturn AssVFSStream::Seek( size_t pOffset, aiOrigin pOrigin)
+{
+	switch(pOrigin){
+		case aiOrigin_SET: // from start of file
+			if ( pOffset >= file->FileSize() ) return AI_FAILURE;
+			file->Seek( pOffset, std::ios_base::beg );
+			break;
+		case aiOrigin_CUR: // from current position in file
+			if ( pOffset >= ( file->FileSize() + file->GetPos() ) ) return AI_FAILURE;
+			file->Seek( pOffset, std::ios_base::cur );
+			break;
+		case aiOrigin_END: // reverse from end of file
+			if ( pOffset >= file->FileSize() ) return AI_FAILURE;
+			file->Seek( pOffset, std::ios_base::end );
+			break;
+	}
+	return AI_SUCCESS;
+}
+
+size_t AssVFSStream::Tell() const
+{
+	return file->GetPos();
+}
+
+
+size_t AssVFSStream::FileSize() const
+{
+	int filesize = file->FileSize();
+	if ( filesize < 0 ) filesize = 0;
+	return filesize;
+}
+
+void AssVFSStream::Flush () // FAKE
+{
+}
+
+
+// Check whether a specific file exists
+bool AssVFSSystem::Exists( const char* pFile) const
+{
+	CFileHandler file(pFile);
+	return file.FileExists();
+}
+
+// Get the path delimiter character we'd like to get
+char AssVFSSystem::getOsSeparator() const
+{
+	return '/';
+}
+
+bool AssVFSSystem::ComparePaths (const std::string& one, const std::string& second) const
+{
+	return one == second; // too naive? probably should convert to absolute paths
+}
+
+// open a custom stream
+Assimp::IOStream* AssVFSSystem::Open( const char* pFile, const char* pMode)
+{
+	return new AssVFSStream( pFile, pMode );
+}
+
+void AssVFSSystem::Close( Assimp::IOStream* pFile)
+{
+	delete pFile;
+}
diff --git a/rts/Rendering/Models/AssIO.h b/rts/Rendering/Models/AssIO.h
new file mode 100644
index 0000000..7b34efa
--- /dev/null
+++ b/rts/Rendering/Models/AssIO.h
@@ -0,0 +1,52 @@
+#ifndef ASSIO_H
+#define ASSIO_H
+
+#include "IOStream.h"
+#include "IOSystem.h"
+class CFileHandler;
+
+// Custom implementation of Assimp IOStream to support Spring's VFS
+// Required because Assimp models often need to load textures from other files
+
+class AssVFSStream : public Assimp::IOStream
+{
+	friend class AssVFSSystem;
+	CFileHandler* file;
+
+protected:
+	// Constructor protected for private usage by AssVFSSystem
+	AssVFSStream(const std::string& pFile, const std::string& pMode);
+
+public:
+	~AssVFSStream(void);
+	size_t Read( void* pvBuffer, size_t pSize, size_t pCount);
+	size_t Write( const void* pvBuffer, size_t pSize, size_t pCount);
+	aiReturn Seek( size_t pOffset, aiOrigin pOrigin);
+	size_t Tell() const;
+	size_t FileSize() const;
+	void Flush ();
+};
+
+
+// Spring VFS Filesystem Wrapper for Assimp
+
+class AssVFSSystem : public Assimp::IOSystem
+{
+public:
+	AssVFSSystem() { }
+	~AssVFSSystem() { }
+
+	// Check whether a specific file exists
+	bool Exists( const char* pFile) const;
+
+	// Get the path delimiter character we'd like to get
+	char getOsSeparator() const;
+	bool ComparePaths (const std::string& one, const std::string& second) const;
+
+	// open a custom stream
+	Assimp::IOStream* Open( const char* pFile, const char* pMode  = "rb" );
+
+	void Close( Assimp::IOStream* pFile);
+};
+
+#endif
diff --git a/rts/Rendering/Models/AssParser.cpp b/rts/Rendering/Models/AssParser.cpp
new file mode 100755
index 0000000..2678a01
--- /dev/null
+++ b/rts/Rendering/Models/AssParser.cpp
@@ -0,0 +1,565 @@
+// AssParser.cpp: implementation of the CAssParser class. Reads 3D formats supported by Assimp lib.
+//
+//////////////////////////////////////////////////////////////////////
+//#include "StdAfx.h"
+
+#include "Util.h"
+#include "LogOutput.h"
+#include "Platform/errorhandler.h"
+#include "System/Exceptions.h"
+#include "Sim/Misc/CollisionVolume.h"
+#include "FileSystem/FileHandler.h"
+#include "Lua/LuaParser.h"
+#include "3DModel.h"
+#include "3DModelLog.h"
+#include "S3OParser.h"
+#include "AssIO.h"
+#include "AssParser.h"
+
+#include "assimp.hpp"
+#include "aiDefines.h"
+#include "aiTypes.h"
+#include "aiScene.h"
+#include "aiPostProcess.h"
+#include "DefaultLogger.h"
+#include "Rendering/Textures/S3OTextureHandler.h"
+
+#define IS_QNAN(f) (f != f)
+#define DEGTORAD 0.0174532925
+
+// triangulate guarantees the most complex mesh is a triangle
+// sortbytype ensure only 1 type of primitive type per mesh is used
+#define ASS_POSTPROCESS_OPTIONS \
+    aiProcess_RemoveComponent               | \
+	aiProcess_FindInvalidData				| \
+	aiProcess_CalcTangentSpace				| \
+	aiProcess_GenSmoothNormals				| \
+	aiProcess_SplitLargeMeshes				| \
+	aiProcess_Triangulate					| \
+	aiProcess_GenUVCoords             		| \
+	aiProcess_SortByPType					| \
+	aiProcess_JoinIdenticalVertices
+
+//aiProcess_ImproveCacheLocality
+
+// Convert Assimp quaternion to radians around x, y and z
+float3 QuaternionToRadianAngles( aiQuaternion q1 )
+{
+    float sqw = q1.w*q1.w;
+    float sqx = q1.x*q1.x;
+    float sqy = q1.y*q1.y;
+    float sqz = q1.z*q1.z;
+	float unit = sqx + sqy + sqz + sqw; // if normalised is one, otherwise is correction factor
+	float test = q1.x*q1.y + q1.z*q1.w;
+
+	float3 result(0.0f, 0.0f, 0.0f);
+
+	if (test > 0.499f * unit) { // singularity at north pole
+		result.x = 2 * atan2(q1.x,q1.w);
+		result.y = PI/2;
+	} else if (test < -0.499f * unit) { // singularity at south pole
+		result.x = -2 * atan2(q1.x,q1.w);
+		result.y = -PI/2;
+	} else {
+        result.x = atan2(2*q1.y*q1.w-2*q1.x*q1.z , sqx - sqy - sqz + sqw);
+        result.y = asin(2*test/unit);
+        result.z = atan2(2*q1.x*q1.w-2*q1.y*q1.z , -sqx + sqy - sqz + sqw);
+	}
+	return result;
+}
+
+// Convert float3 rotations in degrees to radians
+void DegreesToRadianAngles( float3& angles )
+{
+    angles.x *= DEGTORAD;
+    angles.y *= DEGTORAD;
+    angles.z *= DEGTORAD;
+}
+
+class AssLogStream : public Assimp::LogStream
+{
+public:
+	AssLogStream() {}
+	~AssLogStream() {}
+	void write(const char* message)
+	{
+		logOutput.Print (LOG_MODEL_DETAIL, "Assimp: %s", message);
+	}
+};
+
+
+
+S3DModel* CAssParser::Load(const std::string& modelFileName)
+{
+	logOutput.Print (LOG_MODEL, "Loading model: %s\n", modelFileName.c_str() );
+	std::string modelPath = modelFileName.substr(0, modelFileName.find_last_of('/'));
+    std::string modelFileNameNoPath = modelFileName.substr(modelPath.length()+1, modelFileName.length());
+    std::string modelName = modelFileNameNoPath.substr(0, modelFileNameNoPath.find_last_of('.'));
+    std::string modelExt = modelFileNameNoPath.substr(modelFileNameNoPath.find_last_of('.'), modelFileName.length());
+
+    // LOAD METADATA
+	// Load the lua metafile. This contains properties unique to Spring models and must return a table
+	std::string metaFileName = modelFileName + ".lua";
+	CFileHandler* metaFile = new CFileHandler(metaFileName);
+	if (!metaFile->FileExists()) {
+	    // Try again without the model file extension
+        metaFileName = modelPath + '/' + modelName + ".lua";
+        metaFile = new CFileHandler(metaFileName);
+	}
+	LuaParser metaFileParser(metaFileName, SPRING_VFS_MOD_BASE, SPRING_VFS_ZIP);
+	if (!metaFileParser.Execute()) {
+		if (!metaFile->FileExists()) {
+			logOutput.Print(LOG_MODEL, "No meta-file '%s'. Using defaults.", metaFileName.c_str());
+		} else {
+			logOutput.Print(LOG_MODEL, "ERROR in '%s': %s. Using defaults.", metaFileName.c_str(), metaFileParser.GetErrorLog().c_str());
+		}
+	}
+	// Get the (root-level) model table
+	const LuaTable& metaTable = metaFileParser.GetRoot();
+    if (metaTable.IsValid()) logOutput.Print(LOG_MODEL, "Found valid model metadata in '%s'", metaFileName.c_str());
+
+
+    // LOAD MODEL DATA
+	// Create a model importer instance
+ 	Assimp::Importer importer;
+
+	// Create a logger for debugging model loading issues
+	Assimp::DefaultLogger::create("",Assimp::Logger::VERBOSE);
+	const unsigned int severity = Assimp::Logger::DEBUGGING|Assimp::Logger::INFO|Assimp::Logger::ERR|Assimp::Logger::WARN;
+	Assimp::DefaultLogger::get()->attachStream( new AssLogStream(), severity );
+
+	// Give the importer an IO class that handles Spring's VFS
+	importer.SetIOHandler( new AssVFSSystem() );
+
+    // Speed-up processing by skipping things we don't need
+    importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, aiComponent_CAMERAS|aiComponent_LIGHTS|aiComponent_TEXTURES|aiComponent_ANIMATIONS);
+
+	// Read the model file to build a scene object
+	logOutput.Print(LOG_MODEL, "Importing model file: %s\n", modelFileName.c_str() );
+	const aiScene* scene = importer.ReadFile( modelFileName, ASS_POSTPROCESS_OPTIONS );
+	if (scene != NULL) {
+		logOutput.Print(LOG_MODEL, "Processing scene for model: %s (%d meshes / %d materials / %d textures)", modelFileName.c_str(), scene->mNumMeshes, scene->mNumMaterials, scene->mNumTextures );
+    } else {
+		logOutput.Print (LOG_MODEL, "Model Import Error: %s\n",  importer.GetErrorString());
+	}
+
+    S3DModel* model = new S3DModel;
+    model->name = modelFileName;
+	model->type = MODELTYPE_ASS;
+	model->numPieces = 0;
+	model->scene = scene;
+    //model->meta = &metaTable;
+
+    // Assign textures
+    // The S3O texture handler uses two textures.
+    // The first contains diffuse color (RGB) and teamcolor (A)
+    // The second contains glow (R), reflectivity (G) and 1-bit Alpha (A).
+    if (metaTable.KeyExists("tex1")) {
+        model->tex1 = metaTable.GetString("tex1", "default.png");
+    } else {
+        // Search for a texture
+        std::vector<std::string> files = CFileHandler::FindFiles("unittextures/", modelName + ".*");
+        for(std::vector<std::string>::iterator fi = files.begin(); fi != files.end(); ++fi) {
+            std::string texPath = std::string(*fi);
+            model->tex1 = texPath.substr(texPath.find('/')+1, texPath.length());
+            break; // there can be only one!
+        }
+    }
+    if (metaTable.KeyExists("tex2")) {
+        model->tex2 = metaTable.GetString("tex2", "");
+    } else {
+        // Search for a texture
+        std::vector<std::string> files = CFileHandler::FindFiles("unittextures/", modelName + "2.*");
+        for(std::vector<std::string>::iterator fi = files.begin(); fi != files.end(); ++fi) {
+            std::string texPath = std::string(*fi);
+            model->tex2 = texPath.substr(texPath.find('/')+1, texPath.length());
+            break; // there can be only one!
+        }
+    }
+    model->flipTexY = metaTable.GetBool("fliptextures", true); // Flip texture upside down
+    model->invertTexAlpha = metaTable.GetBool("invertteamcolor", true); // Reverse teamcolor levels
+
+    // Load textures
+    logOutput.Print(LOG_MODEL, "Loading textures. Tex1: '%s' Tex2: '%s'", model->tex1.c_str(), model->tex2.c_str());
+    texturehandlerS3O->LoadS3OTexture(model);
+
+    // Load all pieces in the model
+    logOutput.Print(LOG_MODEL, "Loading pieces from root node '%s'", scene->mRootNode->mName.data);
+    LoadPiece( model, scene->mRootNode, metaTable );
+
+    // Update piece hierarchy based on metadata
+    BuildPieceHierarchy( model );
+
+    // Simplified dimensions used for rough calculations
+    model->radius = metaTable.GetFloat("radius", model->radius);
+    model->height = metaTable.GetFloat("height", model->height);
+    model->relMidPos = metaTable.GetFloat3("midpos", model->relMidPos);
+    model->mins = metaTable.GetFloat3("mins", model->mins);
+    model->maxs = metaTable.GetFloat3("maxs", model->maxs);
+
+    // Calculate model dimensions if not set
+    if (!metaTable.KeyExists("mins") || !metaTable.KeyExists("maxs")) CalculateMinMax( model->rootPiece );
+    if (model->radius < 0.0001f) CalculateRadius( model );
+    if (model->height < 0.0001f) CalculateHeight( model );
+
+    // Verbose logging of model properties
+    logOutput.Print(LOG_MODEL_DETAIL, "model->name: %s", model->name.c_str());
+    logOutput.Print(LOG_MODEL_DETAIL, "model->numobjects: %d", model->numPieces);
+    logOutput.Print(LOG_MODEL_DETAIL, "model->radius: %f", model->radius);
+    logOutput.Print(LOG_MODEL_DETAIL, "model->height: %f", model->height);
+    logOutput.Print(LOG_MODEL_DETAIL, "model->mins: (%f,%f,%f)", model->mins[0], model->mins[1], model->mins[2]);
+    logOutput.Print(LOG_MODEL_DETAIL, "model->maxs: (%f,%f,%f)", model->maxs[0], model->maxs[1], model->maxs[2]);
+
+    logOutput.Print (LOG_MODEL, "Model %s Imported.", model->name.c_str());
+    return model;
+}
+
+SAssPiece* CAssParser::LoadPiece(S3DModel* model, aiNode* node, const LuaTable& metaTable)
+{
+	// Create new piece
+	model->numPieces++;
+	SAssPiece* piece = new SAssPiece;
+	piece->type = MODELTYPE_OTHER;
+	piece->node = node;
+	piece->model = model;
+	piece->isEmpty = node->mNumMeshes == 0;
+
+    if (node->mParent) {
+        piece->name = std::string(node->mName.data);
+	} else {
+        piece->name = "root"; // The real model root
+	}
+	logOutput.Print(LOG_PIECE, "Converting node '%s' to piece '%s' (%d meshes).", node->mName.data, piece->name.c_str(), node->mNumMeshes);
+
+    // Load additional piece properties from metadata
+    const LuaTable& pieceTable = metaTable.SubTable("pieces").SubTable(piece->name);
+    if (pieceTable.IsValid()) logOutput.Print(LOG_PIECE, "Found metadata for piece '%s'", piece->name.c_str());
+
+    // Process transforms
+    float3 rotate, scale, offset;
+	aiVector3D _scale, _offset;
+ 	aiQuaternion _rotate;
+	node->mTransformation.Decompose(_scale,_rotate,_offset);
+
+	logOutput.Print(LOG_PIECE, "(%d:%s) Assimp offset (%f,%f,%f), rotate (%f,%f,%f), scale (%f,%f,%f)", model->numPieces, piece->name.c_str(),
+		_offset.x, _offset.y, _offset.z,
+		_rotate.x, _rotate.y, _rotate.z,
+		_scale.x, _scale.y, _scale.z
+	);
+
+	offset = pieceTable.GetFloat3("offset", float3(_offset.x, _offset.y, _offset.z));
+    offset.x = pieceTable.GetFloat("offsetx", offset.x);
+    offset.y = pieceTable.GetFloat("offsety", offset.y);
+    offset.z = pieceTable.GetFloat("offsetz", offset.z);
+
+    if (pieceTable.KeyExists("rotate")) {
+        rotate = pieceTable.GetFloat3("rotate", float3(0.0f, 0.0f, 0.0f));
+        DegreesToRadianAngles(rotate);
+    } else {
+        rotate = QuaternionToRadianAngles(_rotate);
+        rotate = float3(rotate.z, rotate.x, rotate.y);
+    }
+    if (pieceTable.KeyExists("rotatex")) rotate.x = pieceTable.GetFloat("rotatex", 0.0f) * DEGTORAD;
+    if (pieceTable.KeyExists("rotatey")) rotate.y = pieceTable.GetFloat("rotatey", 0.0f) * DEGTORAD;
+    if (pieceTable.KeyExists("rotatez")) rotate.z = pieceTable.GetFloat("rotatez", 0.0f) * DEGTORAD;
+
+	scale = pieceTable.GetFloat3("scale", float3(_scale.x, _scale.z, _scale.y));
+    scale.x = pieceTable.GetFloat("scalex", scale.x);
+    scale.y = pieceTable.GetFloat("scaley", scale.y);
+    scale.z = pieceTable.GetFloat("scalez", scale.z);
+
+	logOutput.Print(LOG_PIECE, "(%d:%s) Relative offset (%f,%f,%f), rotate (%f,%f,%f), scale (%f,%f,%f)", model->numPieces, piece->name.c_str(),
+		offset.x, offset.y, offset.z,
+		rotate.x, rotate.y, rotate.z,
+		scale.x, scale.y, scale.z
+	);
+	piece->offset = offset;
+	piece->rot = rotate;
+	piece->scale = scale;
+
+	// Get vertex data from node meshes
+	for ( unsigned meshListIndex = 0; meshListIndex < node->mNumMeshes; meshListIndex++ ) {
+		unsigned int meshIndex = node->mMeshes[meshListIndex];
+		logOutput.Print(LOG_PIECE_DETAIL, "Fetching mesh %d from scene", meshIndex );
+		aiMesh* mesh = model->scene->mMeshes[meshIndex];
+		std::vector<unsigned> mesh_vertex_mapping;
+		// extract vertex data
+		logOutput.Print(LOG_PIECE_DETAIL, "Processing vertices for mesh %d (%d vertices)", meshIndex, mesh->mNumVertices );
+		logOutput.Print(LOG_PIECE_DETAIL, "Normals: %s Tangents/Bitangents: %s TexCoords: %s",
+				(mesh->HasNormals())?"Y":"N",
+				(mesh->HasTangentsAndBitangents())?"Y":"N",
+				(mesh->HasTextureCoords(0)?"Y":"N")
+		);
+		for ( unsigned vertexIndex= 0; vertexIndex < mesh->mNumVertices; vertexIndex++) {
+			SAssVertex vertex;
+
+			// vertex coordinates
+			logOutput.Print(LOG_PIECE_DETAIL, "Fetching vertex %d from mesh", vertexIndex );
+			aiVector3D& aiVertex = mesh->mVertices[vertexIndex];
+			vertex.pos.x = aiVertex.x;
+			vertex.pos.y = aiVertex.y;
+			vertex.pos.z = aiVertex.z;
+
+			// update piece min/max extents
+			piece->mins.x = std::min(piece->mins.x, aiVertex.x);
+			piece->mins.y = std::min(piece->mins.y, aiVertex.y);
+			piece->mins.z = std::min(piece->mins.z, aiVertex.z);
+			piece->maxs.x = std::max(piece->maxs.x, aiVertex.x);
+			piece->maxs.y = std::max(piece->maxs.y, aiVertex.y);
+			piece->maxs.z = std::max(piece->maxs.z, aiVertex.z);
+
+			logOutput.Print(LOG_PIECE_DETAIL, "vertex position %d: %f %f %f", vertexIndex, vertex.pos.x, vertex.pos.y, vertex.pos.z );
+
+			// vertex normal
+			logOutput.Print(LOG_PIECE_DETAIL, "Fetching normal for vertex %d", vertexIndex );
+			aiVector3D& aiNormal = mesh->mNormals[vertexIndex];
+			vertex.hasNormal = !IS_QNAN(aiNormal);
+			if ( vertex.hasNormal ) {
+				vertex.normal.x = aiNormal.x;
+				vertex.normal.y = aiNormal.y;
+				vertex.normal.z = aiNormal.z;
+				logOutput.Print(LOG_PIECE_DETAIL, "vertex normal %d: %f %f %f",vertexIndex, vertex.normal.x, vertex.normal.y,vertex.normal.z );
+			}
+
+			// vertex tangent, x is positive in texture axis
+			if (mesh->HasTangentsAndBitangents()) {
+				logOutput.Print(LOG_PIECE_DETAIL, "Fetching tangent for vertex %d", vertexIndex );
+				aiVector3D& aiTangent = mesh->mTangents[vertexIndex];
+				vertex.hasTangent = !IS_QNAN(aiTangent);
+				if ( vertex.hasTangent ) {
+					float3 tangent;
+					tangent.x = aiTangent.x;
+					tangent.y = aiTangent.y;
+					tangent.z = aiTangent.z;
+					logOutput.Print(LOG_PIECE_DETAIL, "vertex tangent %d: %f %f %f",vertexIndex, tangent.x, tangent.y,tangent.z );
+					piece->sTangents.push_back(tangent);
+					// bitangent is cross product of tangent and normal
+					float3 bitangent;
+					if ( vertex.hasNormal ) {
+						bitangent = tangent.cross(vertex.normal);
+						logOutput.Print(LOG_PIECE_DETAIL, "vertex bitangent %d: %f %f %f",vertexIndex, bitangent.x, bitangent.y,bitangent.z );
+						piece->tTangents.push_back(bitangent);
+					}
+				}
+			} else {
+				vertex.hasTangent = false;
+			}
+
+			// vertex texcoords
+			if (mesh->HasTextureCoords(0)) {
+				vertex.textureX = mesh->mTextureCoords[0][vertexIndex].x;
+				vertex.textureY = mesh->mTextureCoords[0][vertexIndex].y;
+				logOutput.Print(LOG_PIECE_DETAIL, "vertex texcoords %d: %f %f", vertexIndex, vertex.textureX, vertex.textureY );
+			}
+
+			mesh_vertex_mapping.push_back(piece->vertices.size());
+			piece->vertices.push_back(vertex);
+		}
+
+		// extract face data
+		if ( mesh->HasFaces() ) {
+			logOutput.Print(LOG_PIECE_DETAIL, "Processing faces for mesh %d (%d faces)", meshIndex, mesh->mNumFaces);
+			for ( unsigned faceIndex = 0; faceIndex < mesh->mNumFaces; faceIndex++ ) {
+                aiFace& face = mesh->mFaces[faceIndex];
+				// get the vertex belonging to the mesh
+				for ( unsigned vertexListID = 0; vertexListID < face.mNumIndices; vertexListID++ ) {
+					unsigned int vertexID = mesh_vertex_mapping[face.mIndices[vertexListID]];
+					logOutput.Print(LOG_PIECE_DETAIL, "face %d vertex %d", faceIndex, vertexID );
+					piece->vertexDrawOrder.push_back(vertexID);
+				}
+			}
+		}
+	}
+
+    // Check if piece is special (ie, used to set Spring model properties)
+    if (strcmp(node->mName.data, "SpringHeight") == 0) {
+        // Set the model height to this nodes Z value
+        if (!metaTable.KeyExists("height")) {
+            model->height = piece->offset.z;
+            logOutput.Print (LOG_MODEL, "Model height of %f set by special node 'SpringHeight'", model->height);
+        }
+        return NULL;
+    }
+    if (strcmp(node->mName.data, "SpringRadius") == 0) {
+        if (!metaTable.KeyExists("midpos")) {
+            model->relMidPos = float3(piece->offset.x, piece->offset.z, piece->offset.y); // Y and Z are swapped because this piece isn't rotated
+            logOutput.Print (LOG_MODEL, "Model midpos of (%f,%f,%f) set by special node 'SpringRadius'", model->relMidPos.x, model->relMidPos.y, model->relMidPos.z);
+        }
+        if (!metaTable.KeyExists("radius")) {
+            if (piece->maxs.x <= 0.00001f) {
+                model->radius = piece->scale.x; // the blender import script only sets the scale property
+            } else {
+                model->radius = piece->maxs.x; // use the transformed mesh extents
+            }
+            logOutput.Print (LOG_MODEL, "Model radius of %f set by special node 'SpringRadius'", model->radius);
+        }
+        return NULL;
+    }
+
+	// collision volume for piece (not sure about these coords)
+	const float3 cvScales = (piece->maxs) - (piece->mins);
+	const float3 cvOffset = (piece->maxs - piece->offset) + (piece->mins - piece->offset);
+	//const float3 cvOffset(piece->offset.x, piece->offset.y, piece->offset.z);
+	piece->colvol = new CollisionVolume("box", cvScales, cvOffset, CollisionVolume::COLVOL_HITTEST_CONT);
+
+    // Get parent name from metadata or model
+    if (pieceTable.KeyExists("parent")) {
+        piece->parentName = pieceTable.GetString("parent", "");
+    } else if (node->mParent) {
+        if (node->mParent->mParent) {
+            piece->parentName = std::string(node->mParent->mName.data);
+        } else { // my parent is the root, which gets renamed
+            piece->parentName = "root";
+        }
+    } else {
+        piece->parentName = "";
+    }
+
+    logOutput.Print(LOG_PIECE, "Loaded model piece: %s with %d meshes\n", piece->name.c_str(), node->mNumMeshes );
+
+    // Verbose logging of piece properties
+    logOutput.Print(LOG_PIECE, "piece->name: %s", piece->name.c_str());
+    logOutput.Print(LOG_PIECE, "piece->parent: %s", piece->parentName.c_str());
+
+	// Recursively process all child pieces
+	for (unsigned int i = 0; i < node->mNumChildren; ++i) {
+		LoadPiece(model, node->mChildren[i], metaTable);
+	}
+
+	model->pieces[piece->name] = piece;
+	return piece;
+}
+
+// Because of metadata overrides we don't know the true hierarchy until all pieces have been loaded
+void CAssParser::BuildPieceHierarchy( S3DModel* model )
+{
+    // Loop through all pieces and create missing hierarchy info
+    ModelPieceMap::const_iterator end = model->pieces.end();
+    for (ModelPieceMap::const_iterator it = model->pieces.begin(); it != end; ++it)
+    {
+        S3DModelPiece* piece = it->second;
+        if (piece->name == "root") {
+            piece->parent = NULL;
+            model->SetRootPiece(piece);
+        } else if (piece->parentName != "") {
+            piece->parent = model->FindPiece(piece->parentName);
+            if (piece->parent == NULL) {
+                logOutput.Print( LOG_PIECE, "Error: Missing piece '%s' declared as parent of '%s'.\n", piece->parentName.c_str(), piece->name.c_str() );
+            } else {
+                piece->parent->childs.push_back(piece);
+            }
+        } else {
+            // A piece with no parent that isn't the root (orphan)
+            piece->parent = model->FindPiece("root");
+            if (piece->parent == NULL) {
+                logOutput.Print( LOG_PIECE, "Error: Missing root piece.\n" );
+            } else {
+                piece->parent->childs.push_back(piece);
+            }
+        }
+    }
+}
+
+// Iterate over the model and calculate its overall dimensions
+void CAssParser::CalculateMinMax( S3DModelPiece* piece )
+{
+    piece->goffset = piece->parent ? piece->parent->goffset + piece->offset : piece->offset;
+
+	// update model min/max extents
+	piece->model->mins.x = std::min(piece->goffset.x + piece->mins.x, piece->model->mins.x);
+	piece->model->mins.y = std::min(piece->goffset.y + piece->mins.y, piece->model->mins.y);
+	piece->model->mins.z = std::min(piece->goffset.z + piece->mins.z, piece->model->mins.z);
+	piece->model->maxs.x = std::max(piece->goffset.x + piece->maxs.x, piece->model->maxs.x);
+	piece->model->maxs.y = std::max(piece->goffset.y + piece->maxs.y, piece->model->maxs.y);
+	piece->model->maxs.z = std::max(piece->goffset.z + piece->maxs.z, piece->model->maxs.z);
+
+	// Repeat with childs
+	for (unsigned int i = 0; i < piece->childs.size(); i++) {
+		CalculateMinMax(piece->childs[i]);
+	}
+}
+
+// Calculate model radius from the min/max extents
+void CAssParser::CalculateRadius( S3DModel* model )
+{
+    model->radius = std::max(model->radius, model->maxs.x);
+    model->radius = std::max(model->radius, model->maxs.y);
+    model->radius = std::max(model->radius, model->maxs.z);
+}
+
+// Calculate model height from the min/max extents
+void CAssParser::CalculateHeight( S3DModel* model )
+{
+    model->height = model->maxs.z;
+}
+
+void DrawPiecePrimitive( const S3DModelPiece* o)
+{
+	if (o->isEmpty) {
+		return;
+	}
+	logOutput.Print(LOG_PIECE_DETAIL, "Compiling piece %s", o->name.c_str());
+	// Add GL commands to the pieces displaylist
+
+	const SAssPiece* so = static_cast<const SAssPiece*>(o);
+	const SAssVertex* sAssV = static_cast<const SAssVertex*>(&so->vertices[0]);
+
+
+	// pass the tangents as 3D texture coordinates
+	// (array elements are float3's, which are 12
+	// bytes in size and each represent a single
+	// xyz triple)
+	// TODO: test if we have this many texunits
+	// (if not, could only send the s-tangents)?
+
+	if (!so->sTangents.empty()) {
+		glClientActiveTexture(GL_TEXTURE5);
+		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+		glTexCoordPointer(3, GL_FLOAT, sizeof(float3), &so->sTangents[0].x);
+	}
+	if (!so->tTangents.empty()) {
+		glClientActiveTexture(GL_TEXTURE6);
+		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+		glTexCoordPointer(3, GL_FLOAT, sizeof(float3), &so->tTangents[0].x);
+	}
+
+	glClientActiveTexture(GL_TEXTURE0);
+	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+	glTexCoordPointer(2, GL_FLOAT, sizeof(SAssVertex), &sAssV->textureX);
+
+	glEnableClientState(GL_VERTEX_ARRAY);
+	glVertexPointer(3, GL_FLOAT, sizeof(SAssVertex), &sAssV->pos.x);
+
+	glEnableClientState(GL_NORMAL_ARRAY);
+	glNormalPointer(GL_FLOAT, sizeof(SAssVertex), &sAssV->normal.x);
+
+	// since aiProcess_SortByPType is being used, we're sure we'll get only 1 type here, so combination check isn't needed, also anything more complex than triangles is being split thanks to aiProcess_Triangulate
+	glDrawElements(GL_TRIANGLES, so->vertexDrawOrder.size(), GL_UNSIGNED_INT, &so->vertexDrawOrder[0]);
+
+	if (!so->sTangents.empty()) {
+		glClientActiveTexture(GL_TEXTURE6);
+		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+	}
+	if (!so->tTangents.empty()) {
+		glClientActiveTexture(GL_TEXTURE5);
+		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+	}
+
+	glClientActiveTexture(GL_TEXTURE0);
+	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+
+	glDisableClientState(GL_VERTEX_ARRAY);
+	glDisableClientState(GL_NORMAL_ARRAY);
+
+	logOutput.Print(LOG_PIECE_DETAIL, "Completed compiling piece %s", o->name.c_str());
+}
+
+void CAssParser::Draw( const S3DModelPiece* o) const
+{
+	DrawPiecePrimitive( o );
+}
+
+void SAssPiece::DrawList() const
+{
+	DrawPiecePrimitive(this);
+}
diff --git a/rts/Rendering/Models/AssParser.h b/rts/Rendering/Models/AssParser.h
new file mode 100755
index 0000000..e7a5b70
--- /dev/null
+++ b/rts/Rendering/Models/AssParser.h
@@ -0,0 +1,51 @@
+#ifndef ASSPARSER_H
+#define ASSPARSER_H
+
+#include <map>
+#include "IModelParser.h"
+#include "float3.h"
+
+struct aiNode;
+struct aiScene;
+class LuaTable;
+
+struct SAssVertex {
+	float3 pos;
+	float3 normal;
+	float textureX;
+	float textureY;
+	bool hasNormal;
+	bool hasTangent;
+};
+
+struct SAssPiece: public S3DModelPiece
+{
+public:
+	aiNode* node;
+
+	const float3& GetVertexPos(int idx) const { return vertices[idx].pos; }
+	void DrawList() const;
+	std::vector<SAssVertex> vertices;
+
+	std::vector<unsigned int> vertexDrawOrder;
+	// cannot store these in SAssVertex
+	std::vector<float3> sTangents; // == T(angent) dirs
+	std::vector<float3> tTangents; // == B(itangent) dirs
+};
+
+
+class CAssParser: public IModelParser
+{
+public:
+	S3DModel* Load(const std::string& modelFileName);
+	void Draw(const S3DModelPiece* o) const;
+
+private:
+	SAssPiece* LoadPiece(S3DModel* model, aiNode* node, const LuaTable& metaTable);
+	void BuildPieceHierarchy(S3DModel* model);
+    void CalculateRadius( S3DModel* model );
+    void CalculateHeight( S3DModel* model );
+    void CalculateMinMax( S3DModelPiece* piece );
+};
+
+#endif /* ASSPARSER_H */
diff --git a/rts/Rendering/Models/IModelParser.cpp b/rts/Rendering/Models/IModelParser.cpp
old mode 100644
new mode 100755
index aff59ad..e9513ee
--- a/rts/Rendering/Models/IModelParser.cpp
+++ b/rts/Rendering/Models/IModelParser.cpp
@@ -8,9 +8,12 @@
 
 #include "IModelParser.h"
 #include "3DModel.h"
+#include "3DModelLog.h"
 #include "3DOParser.h"
 #include "S3OParser.h"
 #include "OBJParser.h"
+#include "AssParser.h"
+#include "assimp.hpp"
 #include "Sim/Misc/CollisionVolume.h"
 #include "Sim/Units/Unit.h"
 #include "System/FileSystem/FileSystem.h"
@@ -18,10 +21,8 @@
 #include "System/LogOutput.h"
 #include "System/Exceptions.h"
 
-
 C3DModelLoader* modelParser = NULL;
 
-
 //////////////////////////////////////////////////////////////////////
 // C3DModelLoader
 //
@@ -31,7 +32,26 @@ C3DModelLoader::C3DModelLoader()
 	// file-extension should be lowercase
 	parsers["3do"] = new C3DOParser();
 	parsers["s3o"] = new CS3OParser();
-	parsers["obj"] = new COBJParser();
+	//parsers["obj"] = new COBJParser(); // replaced by Assimp
+
+	// assimp library
+	CAssParser* unitassparser = new CAssParser();
+	std::string extensionlist;
+
+	Assimp::Importer importer;
+	importer.GetExtensionList(extensionlist); // get a ";" separated list of wildcards
+	char* charextensionlist = new char[extensionlist.size() +1];
+	strcpy (charextensionlist, extensionlist.c_str());
+	logOutput.Print("Assimp: Supported model formats: %s", extensionlist.c_str());
+	char* extensionchar = strtok( charextensionlist, ";" );
+	while( extensionchar )
+	{
+		std::string extension = extensionchar;
+		extension = extension.substr( 2 ); // strip wildcard and dot
+		parsers[extension] = unitassparser; // register extension
+		extensionchar = strtok( NULL, ";" );
+	}
+	delete charextensionlist;
 }
 
 
@@ -48,11 +68,15 @@ C3DModelLoader::~C3DModelLoader()
 	cache.clear();
 
 	// delete parsers
+	std::set<IModelParser*> dedupe_parsers; // this is to avoid deleting the same parser twice, if it's assigned to multiple model formats
 	std::map<std::string, IModelParser*>::iterator pi;
 	for (pi = parsers.begin(); pi != parsers.end(); ++pi) {
+		if ( dedupe_parsers.count( pi->second ) != 0 ) continue;
+		dedupe_parsers.insert( pi->second );
 		delete pi->second;
 	}
 	parsers.clear();
+	dedupe_parsers.clear();
 
 #if defined(USE_GML) && GML_ENABLE_SIM
 	createLists.clear();
@@ -67,7 +91,7 @@ inline int ModelExtToModelType(const std::string& ext) {
 	if (ext == "3do") { return MODELTYPE_3DO; }
 	if (ext == "s3o") { return MODELTYPE_S3O; }
 	if (ext == "obj") { return MODELTYPE_OBJ; }
-	return -1;
+	return MODELTYPE_ASS; // FIXME: Return -1 if Assimp cant handle extension
 }
 inline S3DModelPiece* ModelTypeToModelPiece(int type) {
 	if (type == MODELTYPE_3DO) { return (new S3DOPiece()); }
diff --git a/rts/Rendering/Models/WorldObjectModelRenderer.cpp b/rts/Rendering/Models/WorldObjectModelRenderer.cpp
old mode 100644
new mode 100755
index 778f11b..bd8237b
--- a/rts/Rendering/Models/WorldObjectModelRenderer.cpp
+++ b/rts/Rendering/Models/WorldObjectModelRenderer.cpp
@@ -14,6 +14,7 @@ IWorldObjectModelRenderer* IWorldObjectModelRenderer::GetInstance(int modelType)
 		case MODELTYPE_3DO: { return (new WorldObjectModelRenderer3DO()); } break;
 		case MODELTYPE_S3O: { return (new WorldObjectModelRendererS3O()); } break;
 		case MODELTYPE_OBJ: { return (new WorldObjectModelRendererOBJ()); } break;
+		case MODELTYPE_ASS: { return (new WorldObjectModelRendererASS()); } break;
 		default: { return (new IWorldObjectModelRenderer(MODELTYPE_OTHER)); } break;
 	}
 }
@@ -226,7 +227,18 @@ void WorldObjectModelRendererOBJ::PopRenderState()
 	// WRITEME
 }
 
-
+void WorldObjectModelRendererASS::PushRenderState()
+{
+	#if (WORLDOBJECT_MODEL_RENDERER_DEBUG == 1)
+	#endif
+	// WRITEME
+}
+void WorldObjectModelRendererASS::PopRenderState()
+{
+	#if (WORLDOBJECT_MODEL_RENDERER_DEBUG == 1)
+	#endif
+	// WRITEME
+}
 
 void WorldObjectModelRendererS3O::DrawModel(const CUnit* u)
 {
diff --git a/rts/Rendering/Models/WorldObjectModelRenderer.h b/rts/Rendering/Models/WorldObjectModelRenderer.h
old mode 100644
new mode 100755
index 386da99..11a7644
--- a/rts/Rendering/Models/WorldObjectModelRenderer.h
+++ b/rts/Rendering/Models/WorldObjectModelRenderer.h
@@ -117,4 +117,11 @@ public:
 	void PopRenderState();
 };
 
+class WorldObjectModelRendererASS: public IWorldObjectModelRenderer {
+public:
+	WorldObjectModelRendererASS(): IWorldObjectModelRenderer(MODELTYPE_ASS) {}
+	void PushRenderState();
+	void PopRenderState();
+};
+
 #endif
diff --git a/rts/Rendering/ProjectileDrawer.cpp b/rts/Rendering/ProjectileDrawer.cpp
old mode 100644
new mode 100755
diff --git a/rts/Rendering/Textures/Bitmap.cpp b/rts/Rendering/Textures/Bitmap.cpp
old mode 100644
new mode 100755
index 2445c31..0e31ad6
--- a/rts/Rendering/Textures/Bitmap.cpp
+++ b/rts/Rendering/Textures/Bitmap.cpp
@@ -282,9 +282,9 @@ bool CBitmap::LoadGrayscale(const std::string& filename)
 	delete[] mem;
 	mem = new unsigned char[xsize * ysize];
 	memcpy(mem, ilGetData(), xsize * ysize);
-	
+
 	ilDeleteImages(1, &ImageName);
-	
+
 	return true;
 }
 
@@ -738,6 +738,18 @@ void CBitmap::InvertColors()
 }
 
 
+void CBitmap::InvertAlpha()
+{
+	if (type != BitmapTypeStandardRGBA) return; // Don't try to invert DDS
+	for (int y = 0; y < ysize; ++y) {
+		for (int x = 0; x < xsize; ++x) {
+			const int base = ((y * xsize) + x) * 4;
+			mem[base + 3] = 0xFF - mem[base + 3];
+		}
+	}
+}
+
+
 void CBitmap::GrayScale()
 {
 	if (type != BitmapTypeStandardRGBA) {
@@ -786,6 +798,7 @@ void CBitmap::Tint(const float tint[3])
 
 void CBitmap::ReverseYAxis()
 {
+    if (type != BitmapTypeStandardRGBA) return; // don't try to flip DDS
 	unsigned char* tmpLine = new unsigned char[channels * xsize];
 	for (int y=0; y < (ysize / 2); ++y) {
 		const int pixelLow  = (((y            ) * xsize) + 0) * channels;
diff --git a/rts/Rendering/Textures/Bitmap.h b/rts/Rendering/Textures/Bitmap.h
old mode 100644
new mode 100755
index 9b1577b..9d984f9
--- a/rts/Rendering/Textures/Bitmap.h
+++ b/rts/Rendering/Textures/Bitmap.h
@@ -83,6 +83,7 @@ public:
 	CBitmap CreateRescaled(int newx, int newy) const;
 	void ReverseYAxis();
 	void InvertColors();
+	void InvertAlpha();
 	void GrayScale();
 	void Tint(const float tint[3]);
 };
diff --git a/rts/Rendering/Textures/S3OTextureHandler.cpp b/rts/Rendering/Textures/S3OTextureHandler.cpp
old mode 100644
new mode 100755
index 857406b..abb6a02
--- a/rts/Rendering/Textures/S3OTextureHandler.cpp
+++ b/rts/Rendering/Textures/S3OTextureHandler.cpp
@@ -20,6 +20,15 @@
 #include "System/Exceptions.h"
 #include "System/LogOutput.h"
 
+static CLogSubsystem LOG_TEXTURE("Texture");
+
+// The S3O texture handler uses two textures.
+// The first contains diffuse color (RGB) and teamcolor (A)
+// The second contains glow (R), reflectivity (G) and 1-bit Alpha (A).
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
 
 CS3OTextureHandler* texturehandlerS3O = NULL;
 
@@ -52,6 +61,10 @@ void CS3OTextureHandler::Update() {
 int CS3OTextureHandler::LoadS3OTextureNow(const S3DModel* model)
 {
 	GML_STDMUTEX_LOCK(model); // LoadS3OTextureNow
+	logOutput.Print(LOG_TEXTURE, "Load S3O texture now (Flip Y Axis: %s, Invert Team Alpha: %s)", 
+		model->flipTexY ? "yes" : "no",
+		model->invertTexAlpha ? "yes" : "no"
+	);
 
 	const string totalName = model->tex1 + model->tex2;
 
@@ -74,6 +87,8 @@ int CS3OTextureHandler::LoadS3OTextureNow(const S3DModel* model)
 		tex1bm.mem[2] =   0;
 		tex1bm.mem[3] = 255;
 	}
+	if (model->flipTexY) tex1bm.ReverseYAxis();
+	if (model->invertTexAlpha) tex1bm.InvertAlpha();
 
 	tex.num       = s3oTextures.size();
 	tex.tex1      = tex1bm.CreateTexture(true);
@@ -92,6 +107,7 @@ int CS3OTextureHandler::LoadS3OTextureNow(const S3DModel* model)
 		tex2bm.mem[2] =   0; // unused
 		tex2bm.mem[3] = 255; // team-color
 	}
+	if (model->flipTexY) tex2bm.ReverseYAxis();
 
 	tex.tex2      = tex2bm.CreateTexture(true);
 	tex.tex2SizeX = tex2bm.xsize;
diff --git a/rts/Rendering/Textures/S3OTextureHandler.h b/rts/Rendering/Textures/S3OTextureHandler.h
old mode 100644
new mode 100755
diff --git a/rts/Rendering/UnitDrawer.cpp b/rts/Rendering/UnitDrawer.cpp
old mode 100644
new mode 100755
index 9dc973b..f6ca177
--- a/rts/Rendering/UnitDrawer.cpp
+++ b/rts/Rendering/UnitDrawer.cpp
@@ -519,7 +519,7 @@ void CUnitDrawer::DrawOpaqueUnits(int modelType, const CUnit* excludeUnit, bool
 	UnitSet::const_iterator unitSetIt;
 
 	for (unitBinIt = unitBin.begin(); unitBinIt != unitBin.end(); ++unitBinIt) {
-		if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ) {
+		if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ || modelType == MODELTYPE_ASS) {
 			texturehandlerS3O->SetS3oTexture(unitBinIt->first);
 		}
 
@@ -779,13 +779,13 @@ inline void CUnitDrawer::DrawOpaqueUnitShadow(CUnit* unit) {
 		#define S3O_TEX(model) \
 			texturehandlerS3O->GetS3oTex(model->textureType)
 		#define PUSH_SHADOW_TEXTURE_STATE(model)                                  \
-			if (model->type == MODELTYPE_S3O || model->type == MODELTYPE_OBJ) {   \
+			if (model->type == MODELTYPE_S3O || model->type == MODELTYPE_OBJ || model->type == MODELTYPE_ASS) {   \
 				glActiveTexture(GL_TEXTURE0);                                     \
 				glEnable(GL_TEXTURE_2D);                                          \
 				glBindTexture(GL_TEXTURE_2D, S3O_TEX(model)->tex2);               \
 			}
 		#define POP_SHADOW_TEXTURE_STATE(model)                                   \
-			if (model->type == MODELTYPE_S3O || model->type == MODELTYPE_OBJ) {   \
+			if (model->type == MODELTYPE_S3O || model->type == MODELTYPE_OBJ || model->type == MODELTYPE_ASS) {   \
 				glBindTexture(GL_TEXTURE_2D, 0);                                  \
 				glDisable(GL_TEXTURE_2D);                                         \
 				glActiveTexture(GL_TEXTURE0);                                     \
@@ -1042,7 +1042,7 @@ void CUnitDrawer::DrawCloakedUnitsHelper(int modelType)
 
 		// cloaked units
 		for (UnitRenderBinIt it = unitBin.begin(); it != unitBin.end(); ++it) {
-			if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ) {
+			if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ || modelType == MODELTYPE_ASS) {
 				texturehandlerS3O->SetS3oTexture(it->first);
 			}
 
@@ -1099,7 +1099,7 @@ inline void CUnitDrawer::DrawCloakedUnit(CUnit* unit, int modelType, bool drawGh
 		glTranslatef3(unit->pos);
 		glRotatef(unit->buildFacing * 90.0f, 0, 1, 0);
 
-		if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ) {
+		if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ || modelType == MODELTYPE_ASS) {
 			// the units in liveGhostedBuildings[modelType] are not
 			// sorted by textureType, but we cannot merge them with
 			// cloakedModelRenderers[modelType] since they are not
@@ -1183,7 +1183,7 @@ void CUnitDrawer::DrawGhostedBuildings(int modelType)
 				glTranslatef3((*it)->pos);
 				glRotatef((*it)->facing * 90.0f, 0, 1, 0);
 
-				if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ)
+				if (modelType == MODELTYPE_S3O || modelType == MODELTYPE_OBJ || modelType == MODELTYPE_ASS)
 					texturehandlerS3O->SetS3oTexture((*it)->model->textureType);
 
 				SetTeamColour((*it)->team, cloakAlpha1);
@@ -1559,7 +1559,7 @@ void CUnitDrawer::DrawIndividual(CUnit* unit)
 		SetupForUnitDrawing();
 		opaqueModelRenderers[MDL_TYPE(unit)]->PushRenderState();
 
-		if (MDL_TYPE(unit) == MODELTYPE_S3O || MDL_TYPE(unit) == MODELTYPE_OBJ) {
+		if (MDL_TYPE(unit) == MODELTYPE_S3O || MDL_TYPE(unit) == MODELTYPE_OBJ || MDL_TYPE(unit) == MODELTYPE_ASS) {
 			texturehandlerS3O->SetS3oTexture(TEX_TYPE(unit));
 		}
 
@@ -1599,7 +1599,8 @@ void CUnitDrawer::DrawBuildingSample(const UnitDef* unitdef, int side, float3 po
 			texturehandler3DO->Set3doAtlases();
 		} break;
 		case MODELTYPE_S3O:
-		case MODELTYPE_OBJ: {
+		case MODELTYPE_OBJ:
+		case MODELTYPE_ASS: {
 			texturehandlerS3O->SetS3oTexture(model->textureType);
 		} break;
 		default: {
@@ -1987,7 +1988,7 @@ inline void CUnitDrawer::UpdateUnitIconState(CUnit* unit) {
 #ifdef USE_GML
 		if (showHealthBars && !unit->noDraw &&
 			(unit->health < unit->maxHealth || unit->paralyzeDamage > 0.0f || unit->limExperience > 0.0f ||
-			unit->beingBuilt || unit->stockpileWeapon || unit->group) && 
+			unit->beingBuilt || unit->stockpileWeapon || unit->group) &&
 			((unit->pos - camera->pos).SqLength() < (unitDrawDistSqr * 500.0f)))
 			drawStat.insert(unit);
 #endif
diff --git a/rts/Sim/Misc/GlobalConstants.h b/rts/Sim/Misc/GlobalConstants.h
index 78c5305..6006a4e 100644
--- a/rts/Sim/Misc/GlobalConstants.h
+++ b/rts/Sim/Misc/GlobalConstants.h
@@ -39,6 +39,13 @@ const int UNIT_SLOWUPDATE_RATE = 16;
 const int TEAM_SLOWUPDATE_RATE = 32;
 
 /**
+ * @brief max view range
+ *
+ * Defines the maximum view range as 8000
+ */
+const int MAX_VIEW_RANGE = 8000;
+
+/**
  * @brief max teams
  *
  * Defines the maximum number of teams
@@ -56,10 +63,9 @@ const int MAX_PLAYERS = 251;
 /**
  * @brief max units
  *
- * Defines the absolute global maximum number of units allowed to exist in a game.
- * MUST be <= SHRT_MAX ((1 << ((sizeof(short) * 8) - 1)) - 1) because current net
- * code transmits unit ID's as signed shorts. The effective global unit limit is
- * stored in UnitHandler::maxUnits and is always clamped to this value.
+ * Defines the maximum number of untis that may be set as maximum for a game.
+ * The real maximum of the game is stored in uh->maxUnits,
+ * and may not be higher then this value.
  */
 const int MAX_UNITS = 32000;
 
@@ -69,6 +75,13 @@ const int MAX_UNITS = 32000;
 const int MAX_WEAPONS_PER_UNIT = 32;
 
 /**
+ * @brief near plane
+ *
+ * Defines the near plane as 2.8f
+ */
+const float NEAR_PLANE = 2.8f;
+
+/**
  * @brief randint max
  *
  * Defines the maximum random integer as 0x7fff
diff --git a/rts/Sim/Misc/Team.cpp b/rts/Sim/Misc/Team.cpp
index 407c17f..3b84cc6 100644
--- a/rts/Sim/Misc/Team.cpp
+++ b/rts/Sim/Misc/Team.cpp
@@ -27,52 +27,56 @@
 
 CR_BIND(CTeam,);
 CR_REG_METADATA(CTeam, (
-	// from CTeamBase
-	CR_MEMBER(leader),
-	CR_MEMBER(color),
-	CR_MEMBER(incomeMultiplier),
-	CR_MEMBER(side),
-	CR_MEMBER(startPos),
-	CR_MEMBER(teamStartNum),
-	CR_MEMBER(teamAllyteam),
-	// CR_MEMBER(customValues),
-	// from CTeam
-	CR_MEMBER(teamNum),
-	CR_MEMBER(isDead),
-	CR_MEMBER(gaia),
-	CR_MEMBER(origColor),
-	CR_MEMBER(units),
-	CR_MEMBER(metal),
-	CR_MEMBER(energy),
-	CR_MEMBER(metalPull),
-	CR_MEMBER(prevMetalPull),
-	CR_MEMBER(metalIncome),
-	CR_MEMBER(prevMetalIncome),
-	CR_MEMBER(metalExpense),
-	CR_MEMBER(prevMetalExpense),
-	CR_MEMBER(energyPull),
-	CR_MEMBER(prevEnergyPull),
-	CR_MEMBER(energyIncome),
-	CR_MEMBER(prevEnergyIncome),
-	CR_MEMBER(energyExpense),
-	CR_MEMBER(prevEnergyExpense),
-	CR_MEMBER(metalStorage),
-	CR_MEMBER(energyStorage),
-	CR_MEMBER(metalShare),
-	CR_MEMBER(energyShare),
-	CR_MEMBER(delayedMetalShare),
-	CR_MEMBER(delayedEnergyShare),
-	CR_MEMBER(metalSent),
-	CR_MEMBER(metalReceived),
-	CR_MEMBER(energySent),
-	CR_MEMBER(energyReceived),
-	//CR_MEMBER(currentStats),
-	CR_MEMBER(nextHistoryEntry),
-	//CR_MEMBER(statHistory),
-	CR_MEMBER(modParams),
-	CR_MEMBER(modParamsMap),
-	CR_RESERVED(64)
-));
+// from CTeamBase
+				CR_MEMBER(leader),
+				CR_MEMBER(color),
+				CR_MEMBER(incomeMultiplier),
+				CR_MEMBER(side),
+				CR_MEMBER(startPos),
+				CR_MEMBER(teamStartNum),
+				CR_MEMBER(teamAllyteam),
+//				CR_MEMBER(customValues),
+// from CTeam
+				CR_MEMBER(teamNum),
+				CR_MEMBER(isDead),
+				CR_MEMBER(gaia),
+				CR_MEMBER(origColor),
+				CR_MEMBER(units),
+				CR_MEMBER(metal),
+				CR_MEMBER(energy),
+				CR_MEMBER(metalPull),
+				CR_MEMBER(prevMetalPull),
+				CR_MEMBER(metalIncome),
+				CR_MEMBER(prevMetalIncome),
+				CR_MEMBER(metalExpense),
+				CR_MEMBER(prevMetalExpense),
+				CR_MEMBER(metalUpkeep),
+				CR_MEMBER(prevMetalUpkeep),
+				CR_MEMBER(energyPull),
+				CR_MEMBER(prevEnergyPull),
+				CR_MEMBER(energyIncome),
+				CR_MEMBER(prevEnergyIncome),
+				CR_MEMBER(energyExpense),
+				CR_MEMBER(prevEnergyExpense),
+				CR_MEMBER(energyUpkeep),
+				CR_MEMBER(prevEnergyUpkeep),
+				CR_MEMBER(metalStorage),
+				CR_MEMBER(energyStorage),
+				CR_MEMBER(metalShare),
+				CR_MEMBER(energyShare),
+				CR_MEMBER(delayedMetalShare),
+				CR_MEMBER(delayedEnergyShare),
+				CR_MEMBER(metalSent),
+				CR_MEMBER(metalReceived),
+				CR_MEMBER(energySent),
+				CR_MEMBER(energyReceived),
+				//CR_MEMBER(currentStats),
+				CR_MEMBER(nextHistoryEntry),
+				//CR_MEMBER(statHistory),
+				CR_MEMBER(modParams),
+				CR_MEMBER(modParamsMap),
+				CR_RESERVED(64)
+				));
 
 
 //////////////////////////////////////////////////////////////////////
@@ -88,9 +92,11 @@ CTeam::CTeam() :
 	metalPull(0.0f),     prevMetalPull(0.0f),
 	metalIncome(0.0f),   prevMetalIncome(0.0f),
 	metalExpense(0.0f),  prevMetalExpense(0.0f),
+	metalUpkeep(0.0f),   prevMetalUpkeep(0.0f),
 	energyPull(0.0f),    prevEnergyPull(0.0f),
 	energyIncome(0.0f),  prevEnergyIncome(0.0f),
 	energyExpense(0.0f), prevEnergyExpense(0.0f),
+	energyUpkeep(0.0f),  prevEnergyUpkeep(0.0f),
 	metalStorage(1000000),
 	energyStorage(1000000),
 	metalShare(0.99f),
@@ -114,10 +120,14 @@ CTeam::CTeam() :
 }
 
 
+CTeam::~CTeam()
+{
+}
+
 
 bool CTeam::UseMetal(float amount)
 {
-	if (metal >= amount) {
+	if ((metal - (prevMetalUpkeep * 10)) >= amount) {
 		metal -= amount;
 		metalExpense += amount;
 		return true;
@@ -125,9 +135,10 @@ bool CTeam::UseMetal(float amount)
 	return false;
 }
 
+
 bool CTeam::UseEnergy(float amount)
 {
-	if (energy >= amount) {
+	if ((energy - (prevEnergyUpkeep * 10)) >= amount) {
 		energy -= amount;
 		energyExpense += amount;
 		return true;
@@ -136,6 +147,29 @@ bool CTeam::UseEnergy(float amount)
 }
 
 
+bool CTeam::UseMetalUpkeep(float amount)
+{
+	if (metal >= amount) {
+		metal -= amount;
+		metalExpense += amount;
+		metalUpkeep += amount;
+		return true;
+	}
+	return false;
+}
+
+
+bool CTeam::UseEnergyUpkeep(float amount)
+{
+	if (energy >= amount) {
+		energy -= amount;
+		energyExpense += amount;
+		energyUpkeep += amount;
+		return true;
+	}
+	return false;
+}
+
 
 void CTeam::AddMetal(float amount, bool useIncomeMultiplier)
 {
@@ -143,24 +177,23 @@ void CTeam::AddMetal(float amount, bool useIncomeMultiplier)
 	metal += amount;
 	metalIncome += amount;
 	if (metal > metalStorage) {
-		delayedMetalShare += (metal - metalStorage);
+		delayedMetalShare += metal - metalStorage;
 		metal = metalStorage;
 	}
 }
 
+
 void CTeam::AddEnergy(float amount, bool useIncomeMultiplier)
 {
 	if (useIncomeMultiplier) { amount *= GetIncomeMultiplier(); }
 	energy += amount;
 	energyIncome += amount;
 	if (energy > energyStorage) {
-		delayedEnergyShare += (energy - energyStorage);
+		delayedEnergyShare += energy - energyStorage;
 		energy = energyStorage;
 	}
 }
 
-
-
 void CTeam::GiveEverythingTo(const unsigned toTeam)
 {
 	CTeam* target = teamHandler->Team(toTeam);
@@ -220,6 +253,10 @@ void CTeam::Died()
 	eventHandler.TeamDied(teamNum);
 }
 
+void CTeam::StartposMessage(const float3& pos)
+{
+	startPos = pos;
+}
 
 CTeam& CTeam::operator=(const TeamBase& base)
 {
@@ -227,36 +264,43 @@ CTeam& CTeam::operator=(const TeamBase& base)
 	return *this;
 }
 
-
-
-void CTeam::ResetResourceState()
+void CTeam::ResetFrameVariables()
 {
-	// reset all state variables that were
-	// potentially modified during the last
-	// <TEAM_SLOWUPDATE_RATE> frames
-	prevMetalPull     = metalPull;     metalPull     = 0.0f;
-	prevMetalIncome   = metalIncome;   metalIncome   = 0.0f;
-	prevMetalExpense  = metalExpense;  metalExpense  = 0.0f;
-	prevEnergyPull    = energyPull;    energyPull    = 0.0f;
-	prevEnergyIncome  = energyIncome;  energyIncome  = 0.0f;
-	prevEnergyExpense = energyExpense; energyExpense = 0.0f;
-
-	// reset the sharing accumulators
-	metalSent = 0.0f; metalReceived = 0.0f;
-	energySent = 0.0f; energyReceived = 0.0f;
+	prevMetalPull     = metalPull;
+	prevMetalIncome   = metalIncome;
+	prevMetalExpense  = metalExpense;
+	prevMetalUpkeep   = metalUpkeep;
+	prevEnergyPull    = energyPull;
+	prevEnergyIncome  = energyIncome;
+	prevEnergyExpense = energyExpense;
+	prevEnergyUpkeep  = energyUpkeep;
+
+	metalPull = 0;
+	metalIncome = 0;
+	metalExpense = 0;
+	metalUpkeep = 0;
+	energyPull = 0;
+	energyIncome = 0;
+	energyExpense = 0;
+	energyUpkeep = 0;
+
+	metalSent = 0;
+	energySent = 0;
+	metalReceived = 0;
+	energyReceived = 0;
 }
 
 void CTeam::SlowUpdate()
 {
-	float eShare = 0.0f, mShare = 0.0f;
+	currentStats->metalProduced  += prevMetalIncome;
+	currentStats->energyProduced += prevEnergyIncome;
+	currentStats->metalUsed  += prevMetalUpkeep + prevMetalExpense;
+	currentStats->energyUsed += prevEnergyUpkeep + prevEnergyExpense;
 
-	// calculate the total amount of resources that all
-	// (allied) teams can collectively receive through
-	// sharing
+	float eShare = 0.0f, mShare = 0.0f;
 	for (int a = 0; a < teamHandler->ActiveTeams(); ++a) {
-		CTeam* team = teamHandler->Team(a);
-
 		if ((a != teamNum) && (teamHandler->AllyTeam(teamNum) == teamHandler->AllyTeam(a))) {
+			CTeam* team = teamHandler->Team(a);
 			if (team->isDead)
 				continue;
 
@@ -265,25 +309,21 @@ void CTeam::SlowUpdate()
 		}
 	}
 
+	metal += delayedMetalShare;
+	energy += delayedEnergyShare;
+	delayedMetalShare = 0;
+	delayedEnergyShare = 0;
 
-	currentStats->metalProduced  += prevMetalIncome;
-	currentStats->energyProduced += prevEnergyIncome;
-	currentStats->metalUsed  += prevMetalExpense;
-	currentStats->energyUsed += prevEnergyExpense;
-
-	metal  += delayedMetalShare;  delayedMetalShare  = 0.0f;
-	energy += delayedEnergyShare; delayedEnergyShare = 0.0f;
-
-
-	// calculate how much we can share in total (any and all excess resources)
 	const float eExcess = std::max(0.0f, energy - (energyStorage * energyShare));
 	const float mExcess = std::max(0.0f, metal  - (metalStorage  * metalShare));
 
 	float de = 0.0f, dm = 0.0f;
-	if (eShare > 0.0f) { de = std::min(1.0f, eExcess / eShare); }
-	if (mShare > 0.0f) { dm = std::min(1.0f, mExcess / mShare); }
-
-	// now evenly distribute our excess resources among allied teams
+	if (eShare > 0.0f) {
+		de = std::min(1.0f, eExcess/eShare);
+	}
+	if (mShare > 0.0f) {
+		dm = std::min(1.0f, mExcess/mShare);
+	}
 	for (int a = 0; a < teamHandler->ActiveTeams(); ++a) {
 		if ((a != teamNum) && (teamHandler->AllyTeam(teamNum) == teamHandler->AllyTeam(a))) {
 			CTeam* team = teamHandler->Team(a);
@@ -291,19 +331,23 @@ void CTeam::SlowUpdate()
 				continue;
 
 			const float edif = std::max(0.0f, (team->energyStorage * 0.99f) - team->energy) * de;
-			const float mdif = std::max(0.0f, (team->metalStorage * 0.99f) - team->metal) * dm;
+			energy -= edif;
+			energySent += edif;
+			currentStats->energySent += edif;
+			team->energy += edif;
+			team->energyReceived += edif;
+			team->currentStats->energyReceived += edif;
 
-			energy     -= edif; team->energy         += edif;
-			energySent += edif; team->energyReceived += edif;
-			metal      -= mdif; team->metal          += mdif;
-			metalSent  += mdif; team->metalReceived  += mdif;
-
-			currentStats->energySent += edif; team->currentStats->energyReceived += edif;
-			currentStats->metalSent  += mdif; team->currentStats->metalReceived  += mdif;
+			const float mdif = std::max(0.0f, (team->metalStorage * 0.99f) - team->metal) * dm;
+			metal -= mdif;
+			metalSent += mdif;
+			currentStats->metalSent += mdif;
+			team->metal += mdif;
+			team->metalReceived += mdif;
+			team->currentStats->metalReceived += mdif;
 		}
 	}
 
-	// clamp resource levels to storage capacity
 	if (metal > metalStorage) {
 		currentStats->metalExcess += (metal - metalStorage);
 		metal = metalStorage;
@@ -325,10 +369,11 @@ void CTeam::SlowUpdate()
 		nextHistoryEntry = gs->frameNum + statsFrames;
 		currentStats->frame = nextHistoryEntry;
 	}
+
 }
 
 
-void CTeam::AddUnit(CUnit* unit, AddType type)
+void CTeam::AddUnit(CUnit* unit,AddType type)
 {
 	units.insert(unit);
 	switch (type) {
@@ -348,7 +393,7 @@ void CTeam::AddUnit(CUnit* unit, AddType type)
 }
 
 
-void CTeam::RemoveUnit(CUnit* unit, RemoveType type)
+void CTeam::RemoveUnit(CUnit* unit,RemoveType type)
 {
 	units.erase(unit);
 	switch (type) {
diff --git a/rts/Sim/Misc/Team.h b/rts/Sim/Misc/Team.h
index fdd6bcf..f390cbc 100644
--- a/rts/Sim/Misc/Team.h
+++ b/rts/Sim/Misc/Team.h
@@ -21,20 +21,28 @@ class CTeam : public TeamBase, private boost::noncopyable //! cannot allow shall
 	CR_DECLARE(CTeam);
 public:
 	CTeam();
+	~CTeam();
+public:
 
-	void ResetResourceState();
+	/**
+	 * This has to be called for every team before SlowUpdates start,
+	 * otherwise values get overwritten.
+	 */
+	void ResetFrameVariables();
 	void SlowUpdate();
 
 	void AddMetal(float amount, bool useIncomeMultiplier = true);
 	void AddEnergy(float amount, bool useIncomeMultiplier = true);
 	bool UseEnergy(float amount);
 	bool UseMetal(float amount);
+	bool UseEnergyUpkeep(float amount);
+	bool UseMetalUpkeep(float amount);
 
 	void GiveEverythingTo(const unsigned toTeam);
 
 	void Died();
 
-	void StartposMessage(const float3& pos) { startPos = pos; }
+	void StartposMessage(const float3& pos);
 
 	CTeam& operator=(const TeamBase& base);
 
@@ -71,10 +79,12 @@ public:
 	float metalPull,    prevMetalPull;
 	float metalIncome,  prevMetalIncome;
 	float metalExpense, prevMetalExpense;
+	float metalUpkeep,  prevMetalUpkeep;
 
 	float energyPull,    prevEnergyPull;
 	float energyIncome,  prevEnergyIncome;
 	float energyExpense, prevEnergyExpense;
+	float energyUpkeep,  prevEnergyUpkeep;
 
 	SyncedFloat metalStorage, energyStorage;
 
diff --git a/rts/Sim/Misc/TeamHandler.cpp b/rts/Sim/Misc/TeamHandler.cpp
index 0bbbf99..04e5bd0 100644
--- a/rts/Sim/Misc/TeamHandler.cpp
+++ b/rts/Sim/Misc/TeamHandler.cpp
@@ -25,7 +25,7 @@ CR_REG_METADATA(CTeamHandler, (
 ));
 
 
-CTeamHandler* teamHandler = NULL;
+CTeamHandler* teamHandler;
 
 
 CTeamHandler::CTeamHandler():
@@ -88,12 +88,12 @@ void CTeamHandler::LoadFromSetup(const CGameSetup* setup)
 
 void CTeamHandler::GameFrame(int frameNum)
 {
-	if ((frameNum % TEAM_SLOWUPDATE_RATE) == 0) {
+	if (!(frameNum & (TEAM_SLOWUPDATE_RATE-1))) {
 		for (int a = 0; a < ActiveTeams(); ++a) {
-			teams[a]->ResetResourceState();
+			Team(a)->ResetFrameVariables();
 		}
 		for (int a = 0; a < ActiveTeams(); ++a) {
-			teams[a]->SlowUpdate();
+			Team(a)->SlowUpdate();
 		}
 	}
 }
diff --git a/rts/Sim/MoveTypes/AAirMoveType.cpp b/rts/Sim/MoveTypes/AAirMoveType.cpp
index 5cabd4e..67943ce 100644
--- a/rts/Sim/MoveTypes/AAirMoveType.cpp
+++ b/rts/Sim/MoveTypes/AAirMoveType.cpp
@@ -39,9 +39,9 @@ AAirMoveType::AAirMoveType(CUnit* unit) :
 	wantedHeight(80.0f),
 	collide(true),
 	useSmoothMesh(false),
-	autoLand(true),
 	lastColWarning(NULL),
 	lastColWarningType(0),
+	autoLand(true),
 	lastFuelUpdateFrame(0)
 {
 	useHeading = false;
diff --git a/rts/Sim/MoveTypes/AirMoveType.cpp b/rts/Sim/MoveTypes/AirMoveType.cpp
index f0aade2..bc09cbf 100644
--- a/rts/Sim/MoveTypes/AirMoveType.cpp
+++ b/rts/Sim/MoveTypes/AirMoveType.cpp
@@ -1088,7 +1088,7 @@ void CAirMoveType::SetState(AAirMoveType::AircraftState state)
 
 
 
-void CAirMoveType::ImpulseAdded(const float3&)
+void CAirMoveType::ImpulseAdded(void)
 {
 	if (aircraftState == AIRCRAFT_FLYING) {
 		owner->speed += owner->residualImpulse;
diff --git a/rts/Sim/MoveTypes/AirMoveType.h b/rts/Sim/MoveTypes/AirMoveType.h
index 2f70cab..bc3db18 100644
--- a/rts/Sim/MoveTypes/AirMoveType.h
+++ b/rts/Sim/MoveTypes/AirMoveType.h
@@ -32,7 +32,7 @@ public:
 			float engine, const float3& engineVector);
 	void SetState(AircraftState state);
 	void UpdateTakeOff(float wantedHeight);
-	void ImpulseAdded(const float3&);
+	void ImpulseAdded();
 	float3 FindLandingPos() const;
 
 	void DependentDied(CObject* o);
diff --git a/rts/Sim/MoveTypes/GroundMoveType.cpp b/rts/Sim/MoveTypes/GroundMoveType.cpp
index 58e8670..984aa85 100644
--- a/rts/Sim/MoveTypes/GroundMoveType.cpp
+++ b/rts/Sim/MoveTypes/GroundMoveType.cpp
@@ -81,11 +81,12 @@ CR_REG_METADATA(CGroundMoveType, (
 	CR_MEMBER(canReverse),
 	CR_MEMBER(useMainHeading),
 
+	CR_MEMBER(skidRotSpeed),
+
 	CR_MEMBER(waypointDir),
 	CR_MEMBER(flatFrontDir),
 
 	CR_MEMBER(skidRotVector),
-	CR_MEMBER(skidRotSpeed),
 	CR_MEMBER(skidRotSpeed2),
 	CR_MEMBER(skidRotPos2),
 	CR_ENUM_MEMBER(oldPhysState),
@@ -110,10 +111,10 @@ CGroundMoveType::CGroundMoveType(CUnit* owner):
 	maxReverseSpeed(0.0f),
 	wantedSpeed(0.0f),
 	currentSpeed(0.0f),
-	requestedSpeed(0.0f),
 	deltaSpeed(0.0f),
 	deltaHeading(0),
 
+	flatFrontDir(0.0f, 0.0, 1.0f),
 	pathId(0),
 	goalRadius(0),
 
@@ -121,7 +122,15 @@ CGroundMoveType::CGroundMoveType(CUnit* owner):
 	nextWaypoint(ZeroVector),
 	atGoal(false),
 	haveFinalWaypoint(false),
+
+	requestedSpeed(0.0f),
 	currentDistanceToWaypoint(0),
+	pathRequestDelay(0),
+	numIdlingUpdates(0),
+	numIdlingSlowUpdates(0),
+
+	nextDeltaSpeedUpdate(0),
+	nextObstacleAvoidanceUpdate(0),
 
 	skidding(false),
 	flying(false),
@@ -130,22 +139,12 @@ CGroundMoveType::CGroundMoveType(CUnit* owner):
 	canReverse(owner->unitDef->rSpeed > 0.0f),
 	useMainHeading(false),
 
-	skidRotVector(UpVector),
 	skidRotSpeed(0.0f),
+	skidRotVector(UpVector),
 	skidRotSpeed2(0.0f),
 	skidRotPos2(0.0f),
 	oldPhysState(CSolidObject::OnGround),
-
-	flatFrontDir(0.0f, 0.0, 1.0f),
-	mainHeadingPos(ZeroVector),
-
-	nextDeltaSpeedUpdate(0),
-	nextObstacleAvoidanceUpdate(0),
-
-	pathRequestDelay(0),
-
-	numIdlingUpdates(0),
-	numIdlingSlowUpdates(0)
+	mainHeadingPos(0.0f, 0.0f, 0.0f)
 {
 	if (owner) {
 		moveSquareX = owner->pos.x / MIN_WAYPOINT_DISTANCE;
@@ -560,17 +559,23 @@ void CGroundMoveType::ChangeHeading(short wantedHeading) {
 	tracefile << "unit " << owner->id << " changed heading to " << heading << " from " << _oldheading << " (wantedHeading: " << wantedHeading << ")\n";
 #endif
 
-	owner->SetDirectionFromHeading();
+	owner->frontdir = GetVectorFromHeading(heading);
+	if (owner->upright) {
+		owner->updir = UpVector;
+		owner->rightdir = owner->frontdir.cross(owner->updir);
+	} else {
+		owner->updir = ground->GetNormal(owner->pos.x, owner->pos.z);
+		owner->rightdir = owner->frontdir.cross(owner->updir);
+		owner->rightdir.Normalize();
+		owner->frontdir = owner->updir.cross(owner->rightdir);
+	}
 
 	flatFrontDir = owner->frontdir;
 	flatFrontDir.y = 0.0f;
 	flatFrontDir.Normalize();
 }
 
-
-
-
-void CGroundMoveType::ImpulseAdded(const float3&)
+void CGroundMoveType::ImpulseAdded()
 {
 	if (owner->beingBuilt || owner->unitDef->movedata->moveType == MoveData::Ship_Move)
 		return;
@@ -583,34 +588,25 @@ void CGroundMoveType::ImpulseAdded(const float3&)
 		impulse = ZeroVector;
 	}
 
-	const float3& groundNormal = ground->GetNormal(owner->pos.x, owner->pos.z);
-	const float groundImpulseScale = impulse.dot(groundNormal);
+	float3 groundNormal = ground->GetNormal(owner->pos.x, owner->pos.z);
+
+	if (impulse.dot(groundNormal) < 0)
+		impulse -= groundNormal * impulse.dot(groundNormal);
 
-	if (groundImpulseScale < 0.0f)
-		impulse -= (groundNormal * groundImpulseScale);
+	const float sqstrength = impulse.SqLength();
 
-	if (impulse.SqLength() > 9.0f || groundImpulseScale > 0.3f) {
+	if (sqstrength > 9 || impulse.dot(groundNormal) > 0.3f) {
 		skidding = true;
 		speed += impulse;
 		impulse = ZeroVector;
 
-		// FIXME: impulse should not cause a _random_ rotational component
 		skidRotSpeed += (gs->randFloat() - 0.5f) * 1500;
-		skidRotSpeed2 = 0.0f;
-		skidRotPos2 = 0.0f;
-
-		float3 skidDir;
-
-		if (speed.SqLength2D() >= 0.01f) {
-			skidDir = speed;
-			skidDir.y = 0.0f;
+		skidRotPos2 = 0;
+		skidRotSpeed2 = 0;
+		float3 skidDir(speed);
+			skidDir.y = 0;
 			skidDir.Normalize();
-		} else {
-			skidDir = owner->frontdir;
-		}
-
 		skidRotVector = skidDir.cross(UpVector);
-
 		oldPhysState = owner->physicalState;
 		owner->physicalState = CSolidObject::Flying;
 		owner->moveType->useHeading = false;
@@ -690,8 +686,8 @@ void CGroundMoveType::UpdateSkid()
 				speed *= (speedf - speedReduction) / speedf;
 			}
 
-			const float remTime = speedf / speedReduction - 1.0f;
-			const float rp = floor(skidRotPos2 + skidRotSpeed2 * remTime + 0.5f);
+			float remTime = speedf / speedReduction - 1.0f;
+			float rp = floor(skidRotPos2 + skidRotSpeed2 * remTime + 0.5f);
 
 			skidRotSpeed2 = (remTime + 1.0f == 0.0f ) ? 0.0f : (rp - skidRotPos2) / (remTime + 1.0f);
 
@@ -720,7 +716,13 @@ void CGroundMoveType::UpdateSkid()
 		}
 	}
 	CalcSkidRot();
-	owner->MoveMidPos(speed);
+
+	midPos += speed;
+	pos = midPos -
+		owner->frontdir * owner->relMidPos.z -
+		owner->updir    * owner->relMidPos.y -
+		owner->rightdir * owner->relMidPos.x;
+	owner->midPos = midPos;
 
 	CheckCollisionSkid();
 	ASSERT_SYNCED_FLOAT3(owner->midPos);
@@ -733,17 +735,25 @@ void CGroundMoveType::UpdateControlledDrop()
 	SyncedFloat3& midPos = owner->midPos;
 
 	if (owner->falling) {
-		speed.y += (mapInfo->map.gravity * owner->fallSpeed);
-		speed.y = std::min(speed.y, 0.0f);
+		speed.y += mapInfo->map.gravity * owner->fallSpeed;
 
-		owner->MoveMidPos(speed);
+		if (owner->speed.y > 0) //sometimes the dropped unit gets an upward force, still unsure where its coming from
+			owner->speed.y = 0;
 
-		if (midPos.y < 0.0f)
-			speed *= 0.90;
+		midPos += speed;
+		pos = midPos -
+			owner->frontdir * owner->relMidPos.z -
+			owner->updir * owner->relMidPos.y -
+			owner->rightdir * owner->relMidPos.x;
+
+		owner->midPos.y = owner->pos.y + owner->relMidPos.y;
+
+		if(midPos.y < 0)
+			speed*=0.90;
 
 		const float wh = GetGroundHeight(midPos);
 
-		if (wh > (midPos.y - owner->relMidPos.y)) {
+		if (wh > midPos.y - owner->relMidPos.y) {
 			owner->falling = false;
 			midPos.y = wh + owner->relMidPos.y - speed.y * 0.8;
 			owner->script->Landed(); //stop parachute animation
@@ -781,8 +791,12 @@ void CGroundMoveType::CheckCollisionSkid()
 			const float impactSpeed = -owner->speed.dot(dif);
 
 			if (impactSpeed > 0.0f) {
-				owner->MoveMidPos(dif * impactSpeed);
-				owner->speed += ((dif * impactSpeed) * 1.8f);
+				midPos += dif * impactSpeed;
+				pos = midPos -
+					owner->frontdir * owner->relMidPos.z -
+					owner->updir    * owner->relMidPos.y -
+					owner->rightdir * owner->relMidPos.x;
+				owner->speed += dif * (impactSpeed * 1.8f);
 
 				// damage the collider
 				if (impactSpeed > ownerUD->minCollisionSpeed && ownerUD->minCollisionSpeed >= 0) {
@@ -799,10 +813,17 @@ void CGroundMoveType::CheckCollisionSkid()
 			const float impactSpeed = (u->speed - owner->speed).dot(dif) * 0.5f;
 
 			if (impactSpeed > 0.0f) {
-				owner->MoveMidPos(dif * (impactSpeed * (1 - part) * 2));
+				midPos += dif * (impactSpeed * (1 - part) * 2);
+				pos = midPos -
+					owner->frontdir * owner->relMidPos.z -
+					owner->updir    * owner->relMidPos.y -
+					owner->rightdir * owner->relMidPos.x;
 				owner->speed += dif * (impactSpeed * (1 - part) * 2);
-
-				u->MoveMidPos(dif * (impactSpeed * part * -2));
+				u->midPos -= dif * (impactSpeed * part * 2);
+				u->pos = u->midPos -
+					u->frontdir * u->relMidPos.z -
+					u->updir    * u->relMidPos.y -
+					u->rightdir * u->relMidPos.x;
 				u->speed -= dif * (impactSpeed * part * 2);
 
 				if (CGroundMoveType* mt = dynamic_cast<CGroundMoveType*>(u->moveType)) {
@@ -845,8 +866,12 @@ void CGroundMoveType::CheckCollisionSkid()
 		const float impactSpeed = -owner->speed.dot(dif);
 
 		if (impactSpeed > 0.0f) {
-			owner->MoveMidPos(dif * impactSpeed);
-			owner->speed += ((dif * impactSpeed) * 1.8f);
+			midPos += dif * impactSpeed;
+			pos = midPos -
+				owner->frontdir * owner->relMidPos.z -
+				owner->updir    * owner->relMidPos.y -
+				owner->rightdir * owner->relMidPos.x;
+			owner->speed += dif * (impactSpeed * 1.8f);
 
 			if (impactSpeed > ownerUD->minCollisionSpeed && ownerUD->minCollisionSpeed >= 0) {
 				owner->DoDamage(DamageArray(impactSpeed * owner->mass * 0.2f), 0, ZeroVector);
@@ -859,7 +884,18 @@ void CGroundMoveType::CheckCollisionSkid()
 void CGroundMoveType::CalcSkidRot()
 {
 	owner->heading += (short int) skidRotSpeed;
-	owner->SetDirectionFromHeading();
+
+	owner->frontdir = GetVectorFromHeading(owner->heading);
+
+	if (owner->upright) {
+		owner->updir = UpVector;
+		owner->rightdir = owner->frontdir.cross(owner->updir);
+	} else {
+		owner->updir = ground->GetSmoothNormal(owner->pos.x, owner->pos.z);
+		owner->rightdir = owner->frontdir.cross(owner->updir);
+		owner->rightdir.Normalize();
+		owner->frontdir = owner->updir.cross(owner->rightdir);
+	}
 
 	skidRotPos2 += skidRotSpeed2;
 
@@ -868,25 +904,22 @@ void CGroundMoveType::CalcSkidRot()
 
 	float3 f1 = skidRotVector * skidRotVector.dot(owner->frontdir);
 	float3 f2 = owner->frontdir - f1;
+	f2 = f2 * cosp + f2.cross(skidRotVector) * sinp;
+	owner->frontdir = f1 + f2;
 
 	float3 r1 = skidRotVector * skidRotVector.dot(owner->rightdir);
 	float3 r2 = owner->rightdir - r1;
+	r2 = r2 * cosp + r2.cross(skidRotVector) * sinp;
+	owner->rightdir = r1 + r2;
 
 	float3 u1 = skidRotVector * skidRotVector.dot(owner->updir);
 	float3 u2 = owner->updir - u1;
-
-	f2 = f2 * cosp + f2.cross(skidRotVector) * sinp;
-	r2 = r2 * cosp + r2.cross(skidRotVector) * sinp;
 	u2 = u2 * cosp + u2.cross(skidRotVector) * sinp;
-
-	owner->frontdir = f1 + f2;
-	owner->rightdir = r1 + r2;
-	owner->updir    = u1 + u2;
+	owner->updir = u1 + u2;
 }
 
 
 
-
 /*
  * Dynamic obstacle avoidance, helps the unit to
  * follow the path even when it's not perfect.
diff --git a/rts/Sim/MoveTypes/GroundMoveType.h b/rts/Sim/MoveTypes/GroundMoveType.h
index 30ee478..ad8c805 100644
--- a/rts/Sim/MoveTypes/GroundMoveType.h
+++ b/rts/Sim/MoveTypes/GroundMoveType.h
@@ -29,7 +29,7 @@ public:
 
 	void SetMaxSpeed(float speed);
 
-	void ImpulseAdded(const float3&);
+	void ImpulseAdded();
 
 	void KeepPointingTo(float3 pos, float distance, bool aggressive);
 	void KeepPointingTo(CUnit* unit, float distance, bool aggressive);
diff --git a/rts/Sim/MoveTypes/MoveType.h b/rts/Sim/MoveTypes/MoveType.h
index b89ff88..c47428c 100644
--- a/rts/Sim/MoveTypes/MoveType.h
+++ b/rts/Sim/MoveTypes/MoveType.h
@@ -23,7 +23,7 @@ public:
 	virtual void KeepPointingTo(float3 pos, float distance, bool aggressive) = 0;
 	virtual void KeepPointingTo(CUnit* unit, float distance, bool aggressive);
 	virtual void StopMoving() = 0;
-	virtual void ImpulseAdded(const float3&) {}
+	virtual void ImpulseAdded(void) {}
 	virtual void ReservePad(CAirBaseHandler::LandingPad* lp);
 
 	virtual void SetGoal(const float3& pos);
diff --git a/rts/Sim/MoveTypes/ScriptMoveType.h b/rts/Sim/MoveTypes/ScriptMoveType.h
index 1195710..904fd91 100644
--- a/rts/Sim/MoveTypes/ScriptMoveType.h
+++ b/rts/Sim/MoveTypes/ScriptMoveType.h
@@ -34,6 +34,7 @@ class CScriptMoveType : public AMoveType
 		void KeepPointingTo(CUnit* unit, float distance, bool aggressive) {}
 		void StopMoving() {}
 
+		void ImpulseAdded() {}
 		void SetGoal(float3 pos) {}
 		void SetMaxSpeed(float speed) {}
 		void SetWantedMaxSpeed(float speed) {}
diff --git a/rts/Sim/Objects/SolidObject.cpp b/rts/Sim/Objects/SolidObject.cpp
index f3ec270..949f734 100644
--- a/rts/Sim/Objects/SolidObject.cpp
+++ b/rts/Sim/Objects/SolidObject.cpp
@@ -60,8 +60,8 @@ CSolidObject::CSolidObject():
 	isMoving(false),
 	isUnderWater(false),
 	isMarkedOnBlockingMap(false),
-	speed(ZeroVector),
-	residualImpulse(ZeroVector),
+	speed(0, 0, 0),
+	residualImpulse(0, 0, 0),
 	allyteam(0),
 	team(0),
 	mobility(NULL),
diff --git a/rts/Sim/Path/Default/PathEstimator.cpp b/rts/Sim/Path/Default/PathEstimator.cpp
index b612a2b..e64fb78 100644
--- a/rts/Sim/Path/Default/PathEstimator.cpp
+++ b/rts/Sim/Path/Default/PathEstimator.cpp
@@ -1,9 +1,6 @@
 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
 
 #include "StdAfx.h"
-
-#include "lib/gml/gml.h" // FIXME: linux for some reason does not compile without this
-
 #include "PathEstimator.h"
 
 #include <fstream>
diff --git a/rts/Sim/Projectiles/Projectile.h b/rts/Sim/Projectiles/Projectile.h
index ab75359..b0385c1 100644
--- a/rts/Sim/Projectiles/Projectile.h
+++ b/rts/Sim/Projectiles/Projectile.h
@@ -3,7 +3,7 @@
 #ifndef PROJECTILE_H
 #define PROJECTILE_H
 
-#include "lib/gml/gml.h" // for GML_ENABLE_SIM
+#include "Rendering/GL/myGL.h"
 
 #ifdef _MSC_VER
 #pragma warning(disable:4291)
diff --git a/rts/Sim/Units/Unit.cpp b/rts/Sim/Units/Unit.cpp
index 7802b41..07ac36a 100644
--- a/rts/Sim/Units/Unit.cpp
+++ b/rts/Sim/Units/Unit.cpp
@@ -120,7 +120,6 @@ CUnit::CUnit() : CSolidObject(),
 	stunned(false),
 	useHighTrajectory(false),
 	dontUseWeapons(false),
-	dontFire(false),
 	deathScriptFinished(false),
 	delayedWreckLevel(-1),
 	restTime(0),
@@ -201,6 +200,7 @@ CUnit::CUnit() : CSolidObject(),
 	commandShotCount(-1),
 	fireState(FIRESTATE_FIREATWILL),
 	moveState(MOVESTATE_MANEUVER),
+	dontFire(false),
 	activated(false),
 	crashing(false),
 	isDead(false),
@@ -233,10 +233,6 @@ CUnit::CUnit() : CSolidObject(),
 	myTrack(NULL),
 	lastFlareDrop(0),
 	currentFuel(0.0f),
-	maxSpeed(0.0f),
-	maxReverseSpeed(0.0f),
-	alphaThreshold(0.1f),
-	cegDamage(1),
 	luaDraw(false),
 	noDraw(false),
 	noSelect(false),
@@ -244,8 +240,12 @@ CUnit::CUnit() : CSolidObject(),
 	leaveTracks(false),
 	isIcon(false),
 	iconRadius(0.0f),
+	maxSpeed(0.0f),
+	maxReverseSpeed(0.0f),
 	lodCount(0),
-	currentLOD(0)
+	currentLOD(0),
+	alphaThreshold(0.1f),
+	cegDamage(1)
 #ifdef USE_GML
 	, lastDrawFrame(-30)
 #endif
@@ -593,6 +593,45 @@ void CUnit::ForcedSpin(const float3& newDir)
 	ForcedMove(pos); // lazy, don't need to update the quadfield, etc...
 }
 
+
+/*
+void CUnit::SetFront(const SyncedFloat3& newDir)
+{
+	frontdir = newDir;
+	frontdir.Normalize();
+	rightdir = frontdir.cross(updir);
+	rightdir.Normalize();
+	updir = rightdir.cross(frontdir);
+	updir.Normalize();
+	heading = GetHeadingFromVector(frontdir.x, frontdir.z);
+	UpdateMidPos();
+}
+
+void CUnit::SetUp(const SyncedFloat3& newDir)
+{
+	updir = newDir;
+	updir.Normalize();
+	frontdir = updir.cross(rightdir);
+	frontdir.Normalize();
+	rightdir = frontdir.cross(updir);
+	rightdir.Normalize();
+	heading = GetHeadingFromVector(frontdir.x, frontdir.z);
+	UpdateMidPos();
+}
+
+void CUnit::SetRight(const SyncedFloat3& newDir)
+{
+	rightdir = newDir;
+	rightdir.Normalize();
+	updir = rightdir.cross(frontdir);
+	updir.Normalize();
+	frontdir = updir.cross(rightdir);
+	frontdir.Normalize();
+	heading = GetHeadingFromVector(frontdir.x, frontdir.z);
+	UpdateMidPos();
+}
+*/
+
 void CUnit::SetDirectionFromHeading()
 {
 	if (GetTransporter() == NULL) {
@@ -619,14 +658,6 @@ void CUnit::UpdateMidPos()
 		(rightdir * relMidPos.x);
 }
 
-void CUnit::MoveMidPos(const float3& deltaPos) {
-	midPos += deltaPos;
-	pos = midPos -
-		(frontdir * relMidPos.z) -
-		(updir    * relMidPos.y) -
-		(rightdir * relMidPos.x);
-}
-
 
 void CUnit::Drop(const float3& parentPos, const float3& parentDir, CUnit* parent)
 {
@@ -1054,21 +1085,18 @@ void CUnit::SlowUpdateWeapons() {
 
 void CUnit::DoWaterDamage()
 {
-	if (mapInfo->water.damage <= 0.0f) {
-		return;
-	}
-	if (!pos.IsInBounds()) {
-		return;
-	}
-
-	const int  px            = pos.x / (SQUARE_SIZE * 2);
-	const int  pz            = pos.z / (SQUARE_SIZE * 2);
-	const bool isFloating    = (physicalState == CSolidObject::Floating);
-	const bool onGround      = (physicalState == CSolidObject::OnGround);
-	const bool isWaterSquare = (readmap->mipHeightmap[1][pz * gs->hmapx + px] <= 0.0f);
-
-	if ((pos.y <= 0.0f) && isWaterSquare && (isFloating || onGround)) {
-		DoDamage(DamageArray(mapInfo->water.damage), 0, ZeroVector, -1);
+	if (uh->waterDamage > 0.0f) {
+		if (pos.IsInBounds()) {
+			const int  px            = int(pos.x / (SQUARE_SIZE * 2));
+			const int  pz            = int(pos.z / (SQUARE_SIZE * 2));
+			const bool isFloating    = (physicalState == CSolidObject::Floating);
+			const bool onGround      = (physicalState == CSolidObject::OnGround);
+			const bool isWaterSquare = (readmap->mipHeightmap[1][pz * gs->hmapx + px] <= 0.0f);
+
+			if ((pos.y <= 0.0f) && isWaterSquare && (isFloating || onGround)) {
+				DoDamage(DamageArray(uh->waterDamage), 0, ZeroVector, -1);
+			}
+		}
 	}
 }
 
@@ -1128,7 +1156,8 @@ void CUnit::DoDamage(const DamageArray& damages, CUnit* attacker, const float3&
 		damage = newDamage;
 	}
 
-	AddImpulse((impulse * impulseMult) / mass);
+	residualImpulse += ((impulse * impulseMult) / mass);
+	moveType->ImpulseAdded();
 
 	if (paralyzeTime == 0) { // real damage
 		if (damage > 0.0f) {
@@ -1149,7 +1178,8 @@ void CUnit::DoDamage(const DamageArray& damages, CUnit* attacker, const float3&
 				stunned = false;
 			}
 		}
-	} else { // paralyzation
+	}
+	else { // paralyzation
 		experienceMod *= 0.1f; // reduced experience
 		if (damage > 0.0f) {
 			// paralyzeDamage may not get higher than maxHealth * (paralyzeTime + 1),
@@ -1223,13 +1253,6 @@ void CUnit::Kill(const float3& impulse) {
 	DoDamage(da, NULL, impulse, -1);
 }
 
-void CUnit::AddImpulse(const float3& addedImpulse) {
-	residualImpulse += addedImpulse;
-
-	if (addedImpulse.SqLength() >= 0.01f)
-		moveType->ImpulseAdded(addedImpulse);
-}
-
 
 
 /******************************************************************************/
diff --git a/rts/Sim/Units/Unit.h b/rts/Sim/Units/Unit.h
index 3aa74a0..31871cb 100644
--- a/rts/Sim/Units/Unit.h
+++ b/rts/Sim/Units/Unit.h
@@ -3,8 +3,6 @@
 #ifndef UNIT_H
 #define UNIT_H
 
-#include "lib/gml/gml.h" // for GML_ENABLE_SIM
-
 #include <map>
 #include <vector>
 #include <string>
@@ -82,7 +80,6 @@ public:
 	                      const float3& impulse, int weaponId = -1);
 	virtual void DoWaterDamage();
 	virtual void Kill(const float3& impulse);
-	virtual void AddImpulse(const float3&);
 	virtual void FinishedBuilding();
 
 	bool AttackGround(const float3& pos, bool wantDGun, bool fpsMode = false);
@@ -150,7 +147,6 @@ public:
 	void UpdateTerrainType();
 
 	void UpdateMidPos();
-	void MoveMidPos(const float3&);
 
 	bool IsNeutral() const {
 		return neutral;
diff --git a/rts/Sim/Units/UnitHandler.cpp b/rts/Sim/Units/UnitHandler.cpp
index 2c8326e..ab14579 100644
--- a/rts/Sim/Units/UnitHandler.cpp
+++ b/rts/Sim/Units/UnitHandler.cpp
@@ -1,18 +1,20 @@
 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
 
 #include "StdAfx.h"
-#include <cassert>
+#include <assert.h>
 #include "mmgr.h"
 
 #include "lib/gml/gml.h"
 #include "UnitHandler.h"
 #include "Unit.h"
 #include "UnitDefHandler.h"
-#include "CommandAI/BuilderCAI.h"
+#include "UnitLoader.h"
 #include "CommandAI/Command.h"
+#include "CommandAI/BuilderCAI.h"
 #include "Game/GameSetup.h"
 #include "Lua/LuaUnsyncedCtrl.h"
 #include "Map/Ground.h"
+#include "Map/MapInfo.h"
 #include "Map/ReadMap.h"
 #include "Sim/Features/Feature.h"
 #include "Sim/Features/FeatureDef.h"
@@ -27,6 +29,7 @@
 #include "System/LogOutput.h"
 #include "System/TimeProfiler.h"
 #include "System/myMath.h"
+#include "System/LoadSave/LoadSaveInterface.h"
 #include "System/Sync/SyncTracer.h"
 #include "System/creg/STL_Deque.h"
 #include "System/creg/STL_List.h"
@@ -39,24 +42,29 @@ using std::max;
 // Construction/Destruction
 //////////////////////////////////////////////////////////////////////
 
-CUnitHandler* uh = NULL;
+CUnitHandler* uh;
 
-CR_BIND(CUnitHandler, );
+CR_BIND(CUnitHandler, (true));
 CR_REG_METADATA(CUnitHandler, (
 	CR_MEMBER(activeUnits),
 	CR_MEMBER(units),
-	CR_MEMBER(freeUnitIDs),
-	CR_MEMBER(maxUnits),
-	CR_MEMBER(maxUnitsPerTeam),
+	CR_MEMBER(freeIDs),
+	CR_MEMBER(waterDamage),
+	CR_MEMBER(unitsPerTeam),
 	CR_MEMBER(maxUnitRadius),
-	CR_MEMBER(unitsToBeRemoved),
+	CR_MEMBER(toBeRemoved),
 	CR_MEMBER(morphUnitToFeature),
+//	CR_MEMBER(toBeRemoved),
 	CR_MEMBER(builderCAIs),
 	CR_MEMBER(unitsByDefs),
 	CR_POSTLOAD(PostLoad),
 	CR_SERIALIZER(Serialize)
-));
+	));
+
 
+void CUnitHandler::Serialize(creg::ISerializer& s)
+{
+}
 
 
 void CUnitHandler::PostLoad()
@@ -66,55 +74,38 @@ void CUnitHandler::PostLoad()
 }
 
 
-CUnitHandler::CUnitHandler()
+CUnitHandler::CUnitHandler(bool serializing)
 :
 	maxUnitRadius(0.0f),
 	morphUnitToFeature(true)
 {
-	assert(teamHandler->ActiveTeams() > 0);
-
-	// note: the number of active teams can change at run-time, so
-	// the team unit limit should be recalculated whenever one dies
-	// or spawns (but would get complicated)
-	maxUnits = std::min(gameSetup->maxUnits * teamHandler->ActiveTeams(), MAX_UNITS);
-	maxUnitsPerTeam = maxUnits / teamHandler->ActiveTeams();
-
-	// ensure each team can make at least one unit
-	assert(maxUnits >= teamHandler->ActiveTeams());
-	assert(maxUnitsPerTeam >= 1);
-
-	units.resize(maxUnits, NULL);
-	unitsByDefs.resize(teamHandler->ActiveTeams(), std::vector<CUnitSet>(unitDefHandler->unitDefs.size()));
+	const size_t maxUnitsTemp = std::min(gameSetup->maxUnits * teamHandler->ActiveTeams(), MAX_UNITS);
+	units.resize(maxUnitsTemp);
+	unitsPerTeam = maxUnitsTemp / teamHandler->ActiveTeams() - 5;
+
+	freeIDs.reserve(units.size()-1);
+	for (size_t a = 1; a < units.size(); a++) {
+		freeIDs.push_back(a);
+		units[a] = NULL;
+	}
+	units[0] = NULL;
 
-	{
-		std::vector<unsigned int> freeIDs(units.size());
+	slowUpdateIterator = activeUnits.end();
 
-		// id's are used as indices, so they must lie in [0, units.size() - 1]
-		// (furthermore all id's are treated equally, none have special status)
-		for (unsigned int id = 0; id < units.size(); id++) {
-			freeIDs[id] = id;
-		}
+	waterDamage = mapInfo->water.damage;
 
-		// randomize the unit ID's so that Lua widgets can not
-		// easily determine enemy unit counts from ID's alone
-		// (shuffle twice for good measure)
-		SyncedRNG rng;
+	if (!serializing) {
+		airBaseHandler = new CAirBaseHandler;
 
-		std::random_shuffle(freeIDs.begin(), freeIDs.end(), rng);
-		std::random_shuffle(freeIDs.begin(), freeIDs.end(), rng);
-		std::copy(freeIDs.begin(), freeIDs.end(), std::front_inserter(freeUnitIDs));
+		unitsByDefs.resize(teamHandler->ActiveTeams(), std::vector<CUnitSet>(unitDefHandler->unitDefs.size()));
 	}
-
-	slowUpdateIterator = activeUnits.end();
-	airBaseHandler = new CAirBaseHandler();
 }
 
 
 CUnitHandler::~CUnitHandler()
 {
 	for (std::list<CUnit*>::iterator usi = activeUnits.begin(); usi != activeUnits.end(); ++usi) {
-		// ~CUnit dereferences featureHandler which is destroyed already
-		(*usi)->delayedWreckLevel = -1;
+		(*usi)->delayedWreckLevel = -1; // dont create wreckages since featureHandler may be destroyed already
 		delete (*usi);
 	}
 
@@ -122,44 +113,40 @@ CUnitHandler::~CUnitHandler()
 }
 
 
-bool CUnitHandler::AddUnit(CUnit *unit)
+int CUnitHandler::AddUnit(CUnit *unit)
 {
-	if (freeUnitIDs.empty()) {
-		// should be unreachable (all code that goes through
-		// UnitLoader::LoadUnit --> Unit::PreInit checks the
-		// unit limit first)
-		assert(false);
-		return false;
-	}
-
-	unit->id = freeUnitIDs.front();
-	units[unit->id] = unit;
-
+	int num = (int)(gs->randFloat()) * ((int)activeUnits.size() - 1);
 	std::list<CUnit*>::iterator ui = activeUnits.begin();
-
-	if (ui != activeUnits.end()) {
-		// randomize this to make the slow-update order random (good if one
-		// builds say many buildings at once and then many mobile ones etc)
-		const unsigned int insertionPos = gs->randFloat() * activeUnits.size();
-
-		for (unsigned int n = 0; n < insertionPos; ++n) {
-			++ui;
-		}
+	for (int a = 0; a < num;++a) {
+		++ui;
 	}
 
+	// randomize this to make the order in slowupdate random (good if one
+	// builds say many buildings at once and then many mobile ones etc)
 	activeUnits.insert(ui, unit);
-	freeUnitIDs.pop_front();
 
+	// randomize the unitID assignment so that lua widgets can
+	// not easily determine enemy unit counts from unitIDs alone
+	assert(!freeIDs.empty());
+	const unsigned int freeSlot = gs->randInt() % freeIDs.size();
+	const unsigned int freeMax  = freeIDs.size() - 1;
+	unit->id = freeIDs[freeSlot]; // set the unit ID
+	freeIDs[freeSlot] = freeIDs[freeMax];
+	freeIDs.resize(freeMax);
+
+	units[unit->id] = unit;
 	teamHandler->Team(unit->team)->AddUnit(unit, CTeam::AddBuilt);
 	unitsByDefs[unit->team][unit->unitDef->id].insert(unit);
 
 	maxUnitRadius = max(unit->radius, maxUnitRadius);
-	return true;
+
+	return unit->id;
 }
 
+
 void CUnitHandler::DeleteUnit(CUnit* unit)
 {
-	unitsToBeRemoved.push_back(unit);
+	toBeRemoved.push_back(unit);
 	(eventBatchHandler->GetUnitCreatedDestroyedBatch()).dequeue_synced(unit);
 }
 
@@ -187,12 +174,13 @@ void CUnitHandler::DeleteUnitNow(CUnit* delUnit)
 
 			activeUnits.erase(usi);
 			units[delUnit->id] = 0;
-			freeUnitIDs.push_back(delUnit->id);
+			freeIDs.push_back(delUnit->id);
 			teamHandler->Team(delTeam)->RemoveUnit(delUnit, CTeam::RemoveDied);
 
 			unitsByDefs[delTeam][delType].erase(delUnit);
 
 			delete delUnit;
+
 			break;
 		}
 	}
@@ -215,7 +203,7 @@ void CUnitHandler::Update()
 	{
 		GML_STDMUTEX_LOCK(runit); // Update
 
-		if (!unitsToBeRemoved.empty()) {
+		if (!toBeRemoved.empty()) {
 			GML_RECMUTEX_LOCK(unit); // Update - for anti-deadlock purposes.
 			GML_RECMUTEX_LOCK(sel);  // Update - unit is removed from selectedUnits in ~CObject, which is too late.
 			GML_RECMUTEX_LOCK(quad); // Update - make sure unit does not get partially deleted before before being removed from the quadfield
@@ -223,9 +211,9 @@ void CUnitHandler::Update()
 
 			eventHandler.DeleteSyncedUnits();
 
-			while (!unitsToBeRemoved.empty()) {
-				CUnit* delUnit = unitsToBeRemoved.back();
-				unitsToBeRemoved.pop_back();
+			while (!toBeRemoved.empty()) {
+				CUnit* delUnit = toBeRemoved.back();
+				toBeRemoved.pop_back();
 
 				DeleteUnitNow(delUnit);
 			}
@@ -237,7 +225,7 @@ void CUnitHandler::Update()
 	GML_UPDATE_TICKS();
 
 	{
-		SCOPED_TIMER("Unit::MoveType::Update");
+		SCOPED_TIMER("Unit Movetype update");
 		std::list<CUnit*>::iterator usi;
 		for (usi = activeUnits.begin(); usi != activeUnits.end(); ++usi) {
 			CUnit* unit = *usi;
@@ -252,7 +240,7 @@ void CUnitHandler::Update()
 	}
 
 	{
-		SCOPED_TIMER("Unit::Update");
+		SCOPED_TIMER("Unit update");
 		std::list<CUnit*>::iterator usi;
 		for (usi = activeUnits.begin(); usi != activeUnits.end(); ++usi) {
 			CUnit* unit = *usi;
@@ -272,7 +260,7 @@ void CUnitHandler::Update()
 	}
 
 	{
-		SCOPED_TIMER("Unit::SlowUpdate");
+		SCOPED_TIMER("Unit slow update");
 		if (!(gs->frameNum & (UNIT_SLOWUPDATE_RATE - 1))) {
 			slowUpdateIterator = activeUnits.begin();
 		}
@@ -285,10 +273,10 @@ void CUnitHandler::Update()
 }
 
 
-float CUnitHandler::GetBuildHeight(const float3& pos, const UnitDef* unitdef)
+float CUnitHandler::GetBuildHeight(float3 pos, const UnitDef* unitdef)
 {
-	float minh = -5000.0f;
-	float maxh =  5000.0f;
+	float minh=-5000;
+	float maxh=5000;
 	int numBorder=0;
 	float borderh=0;
 	const float* heightmap=readmap->GetHeightmap();
@@ -477,9 +465,10 @@ void CUnitHandler::AddBuilderCAI(CBuilderCAI* b)
 {
 	GML_STDMUTEX_LOCK(cai); // AddBuilderCAI
 
-	builderCAIs.insert(builderCAIs.end(), b);
+	builderCAIs.insert(builderCAIs.end(),b);
 }
 
+
 void CUnitHandler::RemoveBuilderCAI(CBuilderCAI* b)
 {
 	GML_STDMUTEX_LOCK(cai); // RemoveBuilderCAI
@@ -488,51 +477,46 @@ void CUnitHandler::RemoveBuilderCAI(CBuilderCAI* b)
 }
 
 
+void CUnitHandler::LoadSaveUnits(CLoadSaveInterface* file, bool loading)
+{
+}
+
 
 /**
  * Returns a build Command that intersects the ray described by pos and dir from
  * the command queues of the units 'units' on team number 'team'.
  * @brief returns a build Command that intersects the ray described by pos and dir
- * @return the build Command, or a Command with id 0 if none is found
+ * @return the build Command, or a Command wiht id 0 if none is found
  */
-Command CUnitHandler::GetBuildCommand(const float3& pos, const float3& dir) {
+Command CUnitHandler::GetBuildCommand(float3 pos, float3 dir){
 	float3 tempF1 = pos;
 
 	GML_STDMUTEX_LOCK(cai); // GetBuildCommand
 
 	CCommandQueue::iterator ci;
-	for (std::list<CUnit*>::const_iterator ui = activeUnits.begin(); ui != activeUnits.end(); ++ui) {
-		const CUnit* unit = *ui;
-
-		if (unit->team != gu->myTeam) {
-			continue;
-		}
-
-		ci = unit->commandAI->commandQue.begin();
-
-		for (; ci != unit->commandAI->commandQue.end(); ++ci) {
-			const Command& cmd = *ci;
-
-			if (cmd.id < 0 && cmd.params.size() >= 3) {
-				BuildInfo bi(cmd);
-				tempF1 = pos + dir * ((bi.pos.y - pos.y) / dir.y) - bi.pos;
-
-				if (bi.def && (bi.GetXSize() / 2) * SQUARE_SIZE > fabs(tempF1.x) && (bi.GetZSize() / 2) * SQUARE_SIZE > fabs(tempF1.z)) {
-					return cmd;
+	for(std::list<CUnit*>::iterator ui = this->activeUnits.begin(); ui != this->activeUnits.end(); ++ui){
+		if((*ui)->team == gu->myTeam){
+			ci = (*ui)->commandAI->commandQue.begin();
+			for(; ci != (*ui)->commandAI->commandQue.end(); ++ci){
+				if((*ci).id < 0 && (*ci).params.size() >= 3){
+					BuildInfo bi(*ci);
+					tempF1 = pos + dir*((bi.pos.y - pos.y)/dir.y) - bi.pos;
+					if(bi.def && bi.GetXSize()/2*SQUARE_SIZE > fabs(tempF1.x) && bi.GetZSize()/2*SQUARE_SIZE > fabs(tempF1.z)){
+						return (*ci);
+					}
 				}
 			}
 		}
 	}
-
 	Command c;
-	c.id = CMD_STOP;
+	c.id = 0;
 	return c;
 }
 
 
 bool CUnitHandler::CanBuildUnit(const UnitDef* unitdef, int team)
 {
-	if (teamHandler->Team(team)->units.size() >= maxUnitsPerTeam) {
+	if (teamHandler->Team(team)->units.size() >= unitsPerTeam) {
 		return false;
 	}
 	if (unitsByDefs[team][unitdef->id].size() >= unitdef->maxThisUnit) {
diff --git a/rts/Sim/Units/UnitHandler.h b/rts/Sim/Units/UnitHandler.h
index 0f348d7..407f4c7 100644
--- a/rts/Sim/Units/UnitHandler.h
+++ b/rts/Sim/Units/UnitHandler.h
@@ -24,15 +24,14 @@ class CUnitHandler
 	CR_DECLARE(CUnitHandler)
 
 public:
-	CUnitHandler();
-	~CUnitHandler();
-
 	void Update();
 	void DeleteUnit(CUnit* unit);
 	void DeleteUnitNow(CUnit* unit);
-	bool AddUnit(CUnit* unit);
-	void Serialize(creg::ISerializer& s) {}
+	int AddUnit(CUnit* unit);
+	CUnitHandler(bool serializing=false);
+	void Serialize(creg::ISerializer& s);
 	void PostLoad();
+	virtual ~CUnitHandler();
 
 	///< test if a unit can be built at specified position
 	///<   return values for the following is
@@ -56,12 +55,20 @@ public:
 
 	void AddBuilderCAI(CBuilderCAI*);
 	void RemoveBuilderCAI(CBuilderCAI*);
-	float GetBuildHeight(const float3& pos, const UnitDef* unitdef);
+	float GetBuildHeight(float3 pos, const UnitDef* unitdef);
+
+	void LoadSaveUnits(CLoadSaveInterface* file, bool loading);
+	Command GetBuildCommand(float3 pos, float3 dir);
 
-	Command GetBuildCommand(const float3& pos, const float3& dir);
+	int MaxUnitsPerTeam() const
+	{
+		return unitsPerTeam;
+	};
 
-	unsigned int MaxUnitsPerTeam() const { return maxUnitsPerTeam; }
-	unsigned int MaxUnits() const { return maxUnits; }
+	size_t MaxUnits() const
+	{
+		return units.size();
+	};
 
 
 	// note: negative ID's are implicitly converted
@@ -72,19 +79,23 @@ public:
 	std::vector< std::vector<CUnitSet> > unitsByDefs; ///< units sorted by team and unitDef
 
 	std::list<CUnit*> activeUnits;                    ///< used to get all active units
+	std::vector<int> freeIDs;
 	std::vector<CUnit*> units;                        ///< used to get units from IDs (0 if not created)
+
+	std::vector<CUnit*> toBeRemoved;                  ///< units that will be removed at start of next update
+
+	std::list<CUnit*>::iterator slowUpdateIterator;
+
 	std::list<CBuilderCAI*> builderCAIs;
 
+	float waterDamage;
+
 	float maxUnitRadius; ///< largest radius seen so far
+
 	bool morphUnitToFeature;
 
 private:
-	std::list<unsigned int> freeUnitIDs;
-	std::vector<CUnit*> unitsToBeRemoved;            ///< units that will be removed at start of next update
-	std::list<CUnit*>::iterator slowUpdateIterator;
-
-	unsigned int maxUnits;
-	unsigned int maxUnitsPerTeam;
+	int unitsPerTeam;
 };
 
 extern CUnitHandler* uh;
diff --git a/rts/Sim/Units/UnitLoader.cpp b/rts/Sim/Units/UnitLoader.cpp
index 47210de..fa95845 100644
--- a/rts/Sim/Units/UnitLoader.cpp
+++ b/rts/Sim/Units/UnitLoader.cpp
@@ -315,8 +315,8 @@ void CUnitLoader::GiveUnits(const std::vector<std::string>& args, int team)
 	}
 
 	if (objectName == "all") {
-		unsigned int numRequestedUnits = unitDefHandler->unitDefs.size() - 1; /// defid=0 is not valid
-		unsigned int currentNumUnits = teamHandler->Team(team)->units.size();
+		int numRequestedUnits = unitDefHandler->unitDefs.size() - 1; /// defid=0 is not valid
+		int currentNumUnits = teamHandler->Team(team)->units.size();
 
 		// make sure team unit-limit is not exceeded
 		if ((currentNumUnits + numRequestedUnits) > uh->MaxUnitsPerTeam()) {
@@ -345,14 +345,11 @@ void CUnitLoader::GiveUnits(const std::vector<std::string>& args, int team)
 			}
 		}
 	} else {
-		unsigned int numRequestedUnits = amount;
-		unsigned int currentNumUnits = teamHandler->Team(team)->units.size();
+		int numRequestedUnits = amount;
+		int currentNumUnits = teamHandler->Team(team)->units.size();
 
 		if (currentNumUnits >= uh->MaxUnitsPerTeam()) {
-			logOutput.Print(
-				"[%s] unable to give more units to team %d (current: %u, team limit: %u, global limit: %u)",
-				__FUNCTION__, team, currentNumUnits, uh->MaxUnitsPerTeam(), uh->MaxUnits()
-			);
+			logOutput.Print("[%s] unable to give more units to team %d (current: %d, max: %d)", __FUNCTION__, team, currentNumUnits, uh->MaxUnits());
 			return;
 		}
 
diff --git a/rts/Sim/Units/UnitTypes/TransportUnit.cpp b/rts/Sim/Units/UnitTypes/TransportUnit.cpp
index 72faaa3..470d6f9 100644
--- a/rts/Sim/Units/UnitTypes/TransportUnit.cpp
+++ b/rts/Sim/Units/UnitTypes/TransportUnit.cpp
@@ -77,19 +77,16 @@ void CTransportUnit::Update()
 		if (unitDef->holdSteady) {
 			// slave transportee orientation to piece
 			if (ti->piece >= 0) {
-				const CMatrix44f& transMat = GetTransformMatrix(true);
 				const CMatrix44f& pieceMat = script->GetPieceMatrix(ti->piece);
-				const CMatrix44f  slaveMat = transMat.Mul(pieceMat);
-
-				SyncedFloat3& xdir = transportee->rightdir;
-				SyncedFloat3& ydir = transportee->updir;
-				SyncedFloat3& zdir = transportee->frontdir;
-
-				zdir.x = slaveMat[8]; zdir.y = slaveMat[9]; zdir.z = slaveMat[10];
-				xdir.x = slaveMat[0]; xdir.y = slaveMat[1]; xdir.z = slaveMat[ 2];
-				ydir.x = slaveMat[4]; ydir.y = slaveMat[5]; ydir.z = slaveMat[ 6];
-
-				transportee->heading = GetHeadingFromVector(zdir.x, zdir.z);
+				const float3 pieceDir =
+					frontdir * pieceMat[10] +
+					updir    * pieceMat[ 6] +
+					rightdir * pieceMat[ 2];
+
+				transportee->heading  = GetHeadingFromVector(pieceDir.x, pieceDir.z);
+				transportee->frontdir = pieceDir;
+				transportee->rightdir = transportee->frontdir.cross(UpVector);
+				transportee->updir    = transportee->rightdir.cross(transportee->frontdir);
 			}
 		} else {
 			// slave transportee orientation to body
diff --git a/rts/Sim/Weapons/Weapon.cpp b/rts/Sim/Weapons/Weapon.cpp
index bc60b64..aa7bf15 100644
--- a/rts/Sim/Weapons/Weapon.cpp
+++ b/rts/Sim/Weapons/Weapon.cpp
@@ -474,7 +474,7 @@ bool CWeapon::AttackGround(float3 pos, bool userTarget)
 		return false;
 	if (targetUnit) {
 		DeleteDeathDependence(targetUnit);
-		targetUnit = NULL;
+		targetUnit=0;
 	}
 
 	haveUserTarget = userTarget;
@@ -531,7 +531,7 @@ bool CWeapon::AttackUnit(CUnit* unit, bool userTarget)
 
 	if (targetUnit) {
 		DeleteDeathDependence(targetUnit);
-		targetUnit = NULL;
+		targetUnit = 0;
 	}
 
 	haveUserTarget = userTarget;
@@ -549,7 +549,7 @@ void CWeapon::HoldFire()
 {
 	if (targetUnit) {
 		DeleteDeathDependence(targetUnit);
-		targetUnit = NULL;
+		targetUnit = 0;
 	}
 	targetType = Target_None;
 	haveUserTarget = false;
@@ -668,7 +668,7 @@ void CWeapon::SlowUpdate(bool noAutoTargetOverride)
 		// use targets from the thing we are slaved to
 		if (targetUnit) {
 			DeleteDeathDependence(targetUnit);
-			targetUnit = NULL;
+			targetUnit = 0;
 		}
 		targetType = Target_None;
 
@@ -698,34 +698,22 @@ void CWeapon::SlowUpdate(bool noAutoTargetOverride)
 		lastTargetRetry = gs->frameNum;
 
 		std::multimap<float, CUnit*> targets;
-		std::multimap<float, CUnit*>::const_iterator nextTargetIt;
-		std::multimap<float, CUnit*>::const_iterator lastTargetIt;
-
 		helper->GenerateWeaponTargets(this, targetUnit, targets);
 
-		if (!targets.empty())
-			lastTargetIt = --targets.end();
+		for (std::multimap<float, CUnit*>::const_iterator ti = targets.begin(); ti != targets.end(); ++ti) {
+			CUnit* nextTarget = ti->second;
 
-		for (nextTargetIt = targets.begin(); nextTargetIt != targets.end(); ++nextTargetIt) {
-			CUnit* nextTargetUnit = nextTargetIt->second;
-
-			if (nextTargetUnit->neutral && (owner->fireState <= FIRESTATE_FIREATWILL)) {
+			if (nextTarget->neutral && (owner->fireState <= FIRESTATE_FIREATWILL)) {
 				continue;
 			}
-
-			// when only one target is available, <nextTarget> can equal <targetUnit>
-			// and we want to attack whether it is in our bad target category or not
-			// (if only bad targets are available and this is the last, just pick it)
-			if (nextTargetUnit != targetUnit && (nextTargetUnit->category & badTargetCategory)) {
-				if (nextTargetIt != lastTargetIt) {
-					continue;
-				}
+			if (targetUnit && (nextTarget->category & badTargetCategory)) {
+				continue;
 			}
 
-			const float weaponLead = weaponDef->targetMoveError * GAME_SPEED * nextTargetUnit->speed.Length();
+			const float weaponLead = weaponDef->targetMoveError * GAME_SPEED * nextTarget->speed.Length();
 			const float weaponError = weaponLead * (1.0f - owner->limExperience);
 
-			float3 nextTargetPos = nextTargetUnit->midPos + (errorVector * weaponError);
+			float3 nextTargetPos = nextTarget->midPos + (errorVector * weaponError);
 
 			const float appHeight = ground->GetApproximateHeight(nextTargetPos.x, nextTargetPos.z) + 2.0f;
 
@@ -733,13 +721,13 @@ void CWeapon::SlowUpdate(bool noAutoTargetOverride)
 				nextTargetPos.y = appHeight;
 			}
 
-			if (TryTarget(nextTargetPos, false, nextTargetUnit)) {
+			if (TryTarget(nextTargetPos, false, nextTarget)) {
 				if (targetUnit) {
 					DeleteDeathDependence(targetUnit);
 				}
 
 				targetType = Target_Unit;
-				targetUnit = nextTargetUnit;
+				targetUnit = nextTarget;
 				targetPos = nextTargetPos;
 
 				AddDeathDependence(targetUnit);
@@ -755,7 +743,7 @@ void CWeapon::SlowUpdate(bool noAutoTargetOverride)
 		}
 	} else {
 		// if we can't target anything, try switching aim point
-		if (useWeaponPosForAim == 1) {
+		if (useWeaponPosForAim && (useWeaponPosForAim == 1)) {
 			useWeaponPosForAim = 0;
 		} else {
 			useWeaponPosForAim = 1;