From a185c11f2397c0296a4b62cc266b4fa00a63c1e2 Mon Sep 17 00:00:00 2001
From: Stijn Buys <ingar@osirion.org>
Date: Wed, 14 May 2008 21:07:10 +0000
Subject: console, camera & interpolation

---
 osirion.kdevelop            |   2 +-
 osirion.kdevelop.pcs        | Bin 669682 -> 669199 bytes
 osirion.kdevses             |  15 +-
 src/client/camera.cc        | 129 ++++++++---
 src/client/chat.cc          |  51 ++++-
 src/client/client.cc        |  16 +-
 src/client/console.cc       | 536 +++++++++++++++++++++-----------------------
 src/client/console.h        |  84 ++++---
 src/client/input.cc         |  20 +-
 src/client/view.cc          |  10 +-
 src/core/application.cc     |  26 ++-
 src/core/application.h      |   4 +
 src/core/cvar.cc            |   1 +
 src/core/cvar.h             |   2 +
 src/core/gameconnection.cc  |   4 +-
 src/core/gameinterface.cc   | 207 +++++++++--------
 src/core/gameinterface.h    |   4 +-
 src/core/gameserver.cc      |  10 +-
 src/core/gameserver.h       |   1 +
 src/core/netconnection.cc   |  14 +-
 src/game/game.cc            |   2 +-
 src/math/functions.cc       |  10 +-
 src/render/draw.cc          |   4 +-
 src/server/console.cc       |  75 +------
 src/server/console.h        |  13 +-
 src/server/server.cc        |   5 +-
 src/sys/consoleinterface.cc | 129 +++++++++++
 src/sys/consoleinterface.h  |  39 ++--
 src/sys/sys.cc              |  16 +-
 src/sys/sys.h               |   4 +-
 30 files changed, 810 insertions(+), 623 deletions(-)

diff --git a/osirion.kdevelop b/osirion.kdevelop
index 9a3f9c4..25d8c79 100644
--- a/osirion.kdevelop
+++ b/osirion.kdevelop
@@ -21,7 +21,7 @@
   </general>
   <kdevautoproject>
     <general>
-      <activetarget>src/aux/libaux.la</activetarget>
+      <activetarget>src/osirion</activetarget>
       <useconfiguration>debug</useconfiguration>
     </general>
     <run>
diff --git a/osirion.kdevelop.pcs b/osirion.kdevelop.pcs
index 5c7fc9b..6ec8ca8 100644
Binary files a/osirion.kdevelop.pcs and b/osirion.kdevelop.pcs differ
diff --git a/osirion.kdevses b/osirion.kdevses
index faa9110..e8415d1 100644
--- a/osirion.kdevses
+++ b/osirion.kdevses
@@ -1,20 +1,7 @@
 <?xml version = '1.0' encoding = 'UTF-8'?>
 <!DOCTYPE KDevPrjSession>
 <KDevPrjSession>
- <DocsAndViews NumberOfDocuments="4" >
-  <Doc0 NumberOfViews="1" URL="file:///home/ingar/projects/osirion/osirion-work/src/client/console.cc" >
-   <View0 Encoding="" line="299" Type="Source" />
-  </Doc0>
-  <Doc1 NumberOfViews="1" URL="file:///home/ingar/projects/osirion/osirion-work/src/client/view.cc" >
-   <View0 Encoding="" line="262" Type="Source" />
-  </Doc1>
-  <Doc2 NumberOfViews="1" URL="file:///home/ingar/projects/osirion/osirion-work/src/core/application.cc" >
-   <View0 Encoding="" line="132" Type="Source" />
-  </Doc2>
-  <Doc3 NumberOfViews="1" URL="file:///home/ingar/projects/osirion/osirion-work/src/client/chat.cc" >
-   <View0 Encoding="" line="81" Type="Source" />
-  </Doc3>
- </DocsAndViews>
+ <DocsAndViews NumberOfDocuments="0" />
  <pluginList>
   <kdevdebugger>
    <breakpointList/>
diff --git a/src/client/camera.cc b/src/client/camera.cc
index 29310fc..21bf930 100644
--- a/src/client/camera.cc
+++ b/src/client/camera.cc
@@ -28,6 +28,7 @@ math::Vector3f target;
 math::Vector3f eye;
 math::Axis axis;
 
+const float MIN_DELTA = 10e-10;
 
 // current camera mode
 Mode mode;
@@ -86,10 +87,18 @@ void set_mode(Mode newmode) {
 	target_pitch = 0.0f;
 	distance = 0.4f;
 
+	axis.clear();
+
 	switch(newmode) {
 	case Track:
 		// switch camera to Track mode
 		mode = Track;
+		if (core::localcontrol()) {
+			if (core::localcontrol()->state())
+				axis.assign(core::localcontrol()->state()->axis());
+			else
+				axis.assign(core::localcontrol()->axis());
+		}
 		break;
 
 	case Free:
@@ -148,7 +157,7 @@ void next_mode()
 void draw(float seconds) 
 {	
 	math::Matrix4f matrix;
-
+	math::Axis target_axis;
 	float d = 0;
 
 	if (!core::localcontrol()) {
@@ -165,37 +174,102 @@ void draw(float seconds)
 		distance = 20.0f;
 
 	} else {
-
 		if (mode == Overview)
 			set_mode(Track);
 
 		if (core::localcontrol()->state()) {
 			target.assign(core::localcontrol()->state()->location());
-			axis.assign(core::localcontrol()->state()->axis());
+			target_axis.assign(core::localcontrol()->state()->axis());
 		} else {
 			target.assign(core::localcontrol()->location());
-			axis.assign(core::localcontrol()->axis());
+			target_axis.assign(core::localcontrol()->axis());
 		}
-
+		
 		if (mode == Track) {
- 			if (core::localcontrol()->state() && core::localcontrol()->model()) {
-				target -= (core::localcontrol()->model()->maxbbox().x + 0.15f) * core::localcontrol()->state()->axis().forward();
-
-				target += (core::localcontrol()->model()->maxbbox().z + 0.3f ) *
-				core::localcontrol()->state()->axis().up();
+			float cosangle;
+			float angle;
+			float side;
+			float u;
+			const float cam_speed = seconds * 4 ;
+
+			math::Vector3f n;
+			math::Vector3f p;
+
+			// camera axis: pitch
+
+			// project target_axis.up() into the plane with axis->left() normal
+			n = axis.left();
+			p = target_axis.up();
+			u = p[0]*n[0] + p[1]*n[1] + p[2]*n[2] / (-n[0]*n[0] - n[1]*n[1] - n[2] * n[2]);
+			p = target_axis.up() + u * n;
+	
+			side = axis.forward().x * p.x + 
+				axis.forward().y * p.y +
+				axis.forward().z * p.z;
+
+			if ((fabs(side) - MIN_DELTA > 0)) {
+				
+				cosangle = math::dotproduct(p, axis.up());
+				if (fabs(cosangle) + MIN_DELTA < 1 ) {
+					angle = acos(cosangle) * 180.0f / M_PI;
+					angle = math::sgnf(side) * angle * cam_speed;
+					axis.change_pitch(-angle);
+				}
 			}
 
-			// make the camera swing while turning
-			target_direction = core::localcontrol()->target_direction;
-			target_pitch = core::localcontrol()->target_pitch;
+			// camera axis: direction
+
+			// project target_axis.forward() into the plane with axis.up() normal
+			n = axis.up();
+			p = target_axis.forward();
+			u = p[0]*n[0] + p[1]*n[1] + p[2]*n[2] / (-n[0]*n[0] - n[1]*n[1] - n[2] * n[2]);
+			p = target_axis.forward() + u * n;
+
+			side = axis.left().x * p.x + 
+				axis.left().y * p.y +
+				axis.left().z * p.z;
+
+			if ((fabs(side) - MIN_DELTA > 0)) {
+				
+				cosangle = math::dotproduct(p, axis.forward());
+				if (fabs(cosangle) + MIN_DELTA < 1 ) {
+					angle = acos(cosangle) * 180.0f / M_PI;
+					angle = math::sgnf(side) * angle * cam_speed;
+					axis.change_direction(angle);
+				}
+			}
 
-			yaw_target =  - 30 * target_direction;
-			pitch_target = - 30 * target_pitch;
-			//pitch_target = pitch_track - 30 * target_pitch;
+			// camera axis: roll
+
+			// project target_axis.up() into the plane with axis.forward() normal
+			n = axis.forward();
+			p = target_axis.up();
+			u = p[0]*n[0] + p[1]*n[1] + p[2]*n[2] / (-n[0]*n[0] - n[1]*n[1] - n[2] * n[2]);
+			p = target_axis.up() + u * n;
+
+			side = axis.left().x * p.x + 
+				axis.left().y * p.y +
+				axis.left().z * p.z;
+
+			if ((fabs(side) - MIN_DELTA > 0)) {
+				
+				cosangle = math::dotproduct(p, axis.up());
+				if (fabs(cosangle) + MIN_DELTA < 1 ) {
+					angle = acos(cosangle) * 180.0f / M_PI;
+					angle = math::sgnf(side) * angle * cam_speed;
+					axis.change_roll(angle);
+				}
+			}
 
 			distance = 0.0f;
 
+			if (core::localcontrol()->model()) {
+ 				target -= (core::localcontrol()->model()->maxbbox().x + 0.1f) * axis.forward();
+				target += (core::localcontrol()->model()->maxbbox().z + 0.1f ) * axis.up();
+			}
+
 		} else if (mode == Free) {
+			axis.assign(target_axis);
 
 			yaw_target =  yaw_current - 90 * target_direction;
 			pitch_target = pitch_current - 90 * target_pitch;
@@ -205,26 +279,25 @@ void draw(float seconds)
 			} else {
 				distance = 1.0f;
 			}
-		
-		} else if (mode == Cockpit) {
-			if (core::localcontrol()->state() && core::localcontrol()->model())
-				target += (core::localcontrol()->model()->maxbbox().x+0.05) *
-					 core::localcontrol()->state()->axis().forward();
-			
-			distance = 0.0f;
-		}
 
-		
-		if (mode != Cockpit) {
 			// adjust direction
 			d = degrees180f(yaw_current - yaw_target);
 			yaw_current = degrees360f( yaw_current -  d * seconds);
 			axis.change_direction(yaw_current);
 
-			// adjust pitch target
+			// adjust pitch 
 			d = degrees180f(pitch_current - pitch_target);
-			pitch_current = degrees360f(pitch_current -  d *seconds);
+			pitch_current = degrees360f(pitch_current -  d * seconds);
 			axis.change_pitch(pitch_current);
+		
+		} else if (mode == Cockpit) {
+			axis.assign(target_axis);
+
+			if (core::localcontrol()->state() && core::localcontrol()->model())
+				target += (core::localcontrol()->model()->maxbbox().x+0.05) *
+					 core::localcontrol()->state()->axis().forward();
+			
+			distance = 0.0f;
 		}
 	}
 
diff --git a/src/client/chat.cc b/src/client/chat.cc
index 1953390..9171387 100644
--- a/src/client/chat.cc
+++ b/src/client/chat.cc
@@ -4,6 +4,7 @@
    the terms and conditions of the GNU General Public License version 2 
 */
 
+#include "auxiliary/functions.h"
 #include "core/core.h"
 #include "client/chat.h"
 #include "client/console.h"
@@ -71,24 +72,54 @@ void draw()
 {
 	using namespace render;
 
-	if (console::visible() || !visible())
+	if (console()->visible() || !visible())
 		return;
 
-	size_t width = (size_t) (video::width / Text::fontwidth()) - 7;
-	size_t draw_pos = 0;
-	while (input_pos - draw_pos > width)
-		draw_pos += 2;
+	size_t width = (size_t) (video::width / Text::fontwidth()) - 8;
+	float y = video::height*8/10;
+	Text::draw(4, y, "^BSay^F:^B ");
 
-	// draw the chat input
-	float y = video::height * 8.0 / 10.0;
+	std::string firstpart((*history_pos).substr(0, input_pos));
+	size_t draw_width = 0;
+	const char *c = firstpart.c_str();
 
-	Text::draw(4 , y, "^Bsay^F:^B");
-	Text::draw(4+Text::fontwidth()*5 , y, (*history_pos).substr(draw_pos, width));
+	while (*c) {
+		if (aux::is_color_code(c)) {
+			c++;
+		} else {
+			draw_width++;
+		}
+		c++;
+	}
+
+	c = firstpart.c_str();
+	while (*c && draw_width > width - 2) {
+		if (aux::is_color_code(c)) {
+			c++;
+			Text::setcolor(*c);
+		} else {
+			draw_width--;
+		}
+		c++;
+	}
+
+	if (*c) {
+		Text::draw(4+5*Text::fontwidth(), y, c);
+	}
+
+	if (input_pos < (*history_pos).size()) {
+		// FIXME limit to width
+		if (input_pos > 1 && aux::is_color_code((*history_pos).c_str() + input_pos -1)) {
+			Text::setcolor((*history_pos)[input_pos]);
+		}
+		c = (*history_pos).c_str() + input_pos;
+		Text::draw(4+Text::fontwidth()*(draw_width+5), y, c);
+	}
 
 	// draw cursor
 	if ((core::application()->time() - ::floorf(core::application()->time())) < 0.5f) {
 		std::string cursor("^B_");
-		Text::draw(4+Text::fontwidth()*(input_pos - draw_pos+5), y, cursor);
+		Text::draw(4+Text::fontwidth()*(draw_width+5), y , cursor);
 	}
 }
 
diff --git a/src/client/client.cc b/src/client/client.cc
index 97c0128..85dec27 100644
--- a/src/client/client.cc
+++ b/src/client/client.cc
@@ -25,6 +25,7 @@ namespace client
 core::Cvar *cl_framerate = 0;
 
 //--- private definition ------------------------------------------
+
 /// client application implementation
 class Client : public core::Application
 {
@@ -49,10 +50,8 @@ Client app;
 void func_r_restart(std::stringstream &args)
 {
 	video::shutdown();
-	console::flush();
 
 	if (!video::init()) {
-		console::flush();
 		app.quit(1);
 	}
 }
@@ -87,6 +86,7 @@ void Client::init(int count, char **arguments)
 	// initialize core
 	core::Cvar::sv_dedicated = core::Cvar::set("sv_private", "0");
 	core::Application::init(count, arguments);
+	Console::init();
 
 	// client variables
 	core::Cvar *cvar = 0;
@@ -104,12 +104,10 @@ void Client::init(int count, char **arguments)
 
 	// Initialize the video subsystem
 	if (!video::init()) {
-		console::flush();
 		quit(1);
 	}
 
 	// initialize console
-	console::init();
 	chat::init();
 	
 	// initialize input
@@ -128,6 +126,9 @@ void Client::run()
 	Uint32 client_framerate =  (Uint32)(1000/120);
 	Uint32 elapsed = 0;
 	
+	console()->flush();
+	console()->clear_notify();
+
 	while (true) {
 		Uint32 chrono = SDL_GetTicks();
 
@@ -155,24 +156,19 @@ void Client::run()
 void Client::shutdown()
 {
 	con_print << "^BShutting down client..." << std::endl;
-	console::flush();
 
 	// remove engine functions
 	core::Func::remove("r_restart");
 
 	chat::shutdown();
 
-	console::shutdown();
-	console::flush();
+	Console::shutdown();
 
 	input::shutdown();
-	console::flush();
 
 	video::shutdown();
-	console::flush();
 
 	core::Application::shutdown();
-	console::flush();
 
 	quit(0);
 }
diff --git a/src/client/console.cc b/src/client/console.cc
index 4c65581..f692db4 100644
--- a/src/client/console.cc
+++ b/src/client/console.cc
@@ -4,12 +4,12 @@
    the terms and conditions of the GNU General Public License version 2 
 */
 
+#include "client/console.h"
 #include "auxiliary/functions.h"
 #include "core/core.h"
 #include "filesystem/filesystem.h"
 #include "render/render.h"
 #include "render/textures.h"
-#include "client/console.h"
 #include "client/video.h"
 #include "client/keyboard.h"
 
@@ -19,116 +19,225 @@
 
 namespace client {
 
-namespace console {
+Console client_console;
 
-//--- private definition ------------------------------------------
+Console *console() {
+	return &client_console;
+}
 
+//--- engine functions --------------------------------------------
 
-/// private client console implementation
-class Console : public sys::ConsoleInterface {
-public:
-	/// stream to send normal messages too
-	virtual std::ostream & messagestream();
+void func_con_toggle(std::string const &args)
+{
+	
+	console()->toggle();
+}
 
-	/// stream to send warning messages too
-	virtual std::ostream & warningstream();
+//--- public ------------------------------------------------------
 
-	/// stream to send error messages too
-	virtual std::ostream & errorstream();
-	
-	/// stream to send debug messages too
-	virtual std::ostream & debugstream();
+void Console::init()
+{
+	con_print << "^BInitializing console..." << std::endl;	
 
-	/// flush buffered messages
-	virtual void flush();
+	core::Func *func = core::Func::add("con_toggle", (core::FuncPtr) func_con_toggle);
+	func->set_info("toggle console on or off");
+	console()->load_history();
+}
+
+void Console::shutdown()
+{
+	con_print << "^BShutting down console..." << std::endl;
 
-	/// console text buffer
-	std::stringstream buffer;
-};
+	console()->save_history();
 
-// private client console object
-Console console;
+	// remove engine functions
+	core::Func::remove("con_toggle");
+}
+
+//--- Console -----------------------------------------------------
+
+Console::Console()
+{
+	clear();
+}
 
-// console text data
-std::deque<std::string> text;
+Console::~Console()
+{
+	history.clear();
+}
 
-// input history
-std::deque<std::string> history;
-std::deque<std::string>::reverse_iterator history_pos;
-size_t input_pos = 0;
+void Console::clear()
+{
+	console_visible = false;
+	console_scroll = 0;	
+	history.clear();
+	history.push_back("");
+	history_pos = history.rbegin();
+	input_pos = 0;
 
-// console visibility
-bool console_visible = false;
-size_t console_scroll = 0;
+	clear_notify();
 
-// notifications
-size_t notify_pos = 0;
-std::string notify_text[MAXNOTIFYLINES];
-float notify_time[MAXNOTIFYLINES];
+}
 
-//--- engine functions --------------------------------------------
+void Console::draw() {
+	if (visible())
+		draw_console();
+	else
+		draw_notify();
+}
 
-void func_con_toggle(std::string const &args)
+void Console::toggle()
 {
+	console_visible = !console_visible;
 	
-	std::istringstream argstream(args);
-	int i;
+	if (console_visible) {
+		console_scroll = 0;
+		input_pos = 0;
+
+		history_pos = history.rbegin();
+		(*history_pos).clear();
 
-	if (argstream >> i) {
-		if (i) console_visible = false; else console_visible = true;
+		SDL_WM_GrabInput(SDL_GRAB_OFF);
+		SDL_ShowCursor(SDL_ENABLE);
+	} else {
+		SDL_WM_GrabInput(SDL_GRAB_ON);
+		SDL_ShowCursor(SDL_DISABLE);
+		clear_notify();
 	}
-	toggle();
+	
+	setkeyboardmode(visible());
 }
 
-//--- public ------------------------------------------------------
-
-void clear_notify()
+void Console::keypressed(int key)
 {
-	for (size_t i=0; i < MAXNOTIFYLINES; i++) {
-		notify_text[i].clear();
-		notify_time[i] = 0;
+	std::deque<std::string>::reverse_iterator upit;
+
+	switch( key ) {
+	case SDLK_TAB:
+		core::CommandBuffer::complete( (*history_pos), input_pos);
+		break;
+	case SDLK_RETURN:
+		if ((*history_pos).size()) {
+			// store input into history
+			while (history.size() >= MAXHISTOLINES) {
+				history.pop_front();
+			}
+
+			core::cmd() << (*history_pos) << std::endl;
+			con_print << "^B>" << (*history_pos) << std::endl;
+			(*history.rbegin()) = (*history_pos);
+
+			history.push_back("");
+			history_pos = history.rbegin();
+			input_pos = 0;
+		}
+		break;
+	case SDLK_UP:
+		upit = history_pos;
+		++upit;
+		if (upit != history.rend()) {
+			history_pos = upit;
+			input_pos = (*history_pos).size();
+		}
+		break;
+	case SDLK_DOWN:
+		if (history_pos != history.rbegin()) {
+			--history_pos;
+			input_pos = (*history_pos).size();
+		}
+		break;
+	case SDLK_HOME:
+		input_pos = 0;
+		break;
+	case SDLK_END:		
+		input_pos = (*history_pos).size();
+		break;
+	case SDLK_LEFT:
+		if (input_pos > 0)
+			input_pos--;
+		break;
+	case SDLK_RIGHT:
+		if (input_pos < (*history_pos).size())
+			input_pos++;
+		break;
+	case SDLK_BACKSPACE:
+		if ((*history_pos).size() && input_pos) {
+			(*history_pos).erase(input_pos-1, 1);
+			input_pos--;
+		}
+		break;
+	case SDLK_PAGEUP:
+		console_scroll +=5;
+		if (console_scroll > consoleinterface_text.size()) console_scroll = consoleinterface_text.size();
+		break;
+	case SDLK_PAGEDOWN:
+		if (console_scroll > 5)	console_scroll -=5;
+		else console_scroll = 0;
+		break;
+	default:
+		if ((key >= 32 ) && (key <175)) {
+			if (input_pos == (*history_pos).size())
+				(*history_pos) += (char)key;
+			else
+				(*history_pos).insert(input_pos, 1, (char)key);
+			input_pos++;
+		}
+		break;
 	}
+
+	
 }
 
-void init()
+void Console::save_history()
 {
-	con_print << "^BInitializing console..." << std::endl;
-	
-	console_visible = false;	
-	
-	// add engine functions
-	core::Func *func = core::Func::add("con_toggle", (core::FuncPtr) func_con_toggle);
-	func->set_info("toggle console on or off");
 
-	clear_notify();	
-	text.clear();
-	console_scroll = 0;	
+	if (history.size() <= 1)
+		return;
 
-	history.clear();
-	history.push_back("");
-	history_pos = history.rbegin();
-	input_pos = 0;
+	std::string filename(filesystem::writedir);
+	filename.append("history.txt");
+	std::ofstream ofs(filename.c_str());
 
-	load_history();
+	if (!ofs.is_open()) {
+		con_warn << "Could not write " << filename << std::endl;
+		return;
+	}
+	std::deque<std::string>::iterator it;
+	size_t l = 1;
+	for (it = history.begin(); it != history.end(); it++) {
+		if (l < history.size())
+			ofs << (*it) << std::endl;
+		l++;
+	}
+
+	ofs.close();
 }
 
-void shutdown()
+void Console::load_history()
 {
-	con_print << "^BShutting down console..." << std::endl;
-
-	save_history();
+	std::string filename(filesystem::writedir);
+	filename.append("history.txt");
+	std::ifstream ifs(filename.c_str(), std::ifstream::in);
 
-	// remove engine functions
-	//core::Func::remove("con_toggle");
-	
-	text.clear();
-	console_scroll = 0;	
+	if (!ifs.is_open()) {
+		con_warn << "Could not read " << filename << std::endl;
+		return;
+	}
 
 	history.clear();
+	char line[MAXCMDSIZE];
+	while (ifs.getline(line, MAXCMDSIZE-1)) {
+		history.push_back(line);
+	}
+
+	ifs.close();
+
+	history.push_back("");
+	history_pos = history.rbegin();
 	input_pos = 0;
 }
 
-void draw_notify()
+void Console::draw_notify()
 {
 	using namespace render;
 
@@ -150,6 +259,7 @@ void draw_notify()
 
 			const char *c = linedata.c_str();
 			char pen = 'N';
+			char wordpen = 'N';
 
 			while (*c) {
 
@@ -170,7 +280,7 @@ void draw_notify()
 							h += Text::fontheight();
 							line.clear();
 							line += '^';
-							line += pen;
+							line += wordpen;
 							line_length = 0;
 						}
 					}
@@ -180,6 +290,7 @@ void draw_notify()
 
 					word.clear();
 					word_length = 0;
+					wordpen = pen;
 									
 					// new line
 					if (*c == '\n' ) {
@@ -205,7 +316,7 @@ void draw_notify()
 							h += Text::fontheight();
 							line.clear();
 							line += '^';
-							line += pen;
+							line += wordpen;
 							line_length = 0;
 						}
 
@@ -214,6 +325,7 @@ void draw_notify()
 
 						word.clear();
 						word_length = 0;
+						wordpen = pen;
 					}
 				}
 
@@ -225,7 +337,7 @@ void draw_notify()
 	}
 }
 
-void draw_console()
+void Console::draw_console()
 {
 	using namespace render;
 
@@ -251,17 +363,17 @@ void draw_console()
 	gl::color(0.0f, 1.0f, 0.0f, 0.5f);
 	Text::draw(video::width-Text::fontwidth()*(version.size()+1), video::height*con_height-Text::fontheight()-4, version);
 
-	// draw the console text
-	if (console_scroll > text.size())
-		console_scroll = text.size();
+	// draw the console consoleinterface_text
+	if (console_scroll > consoleinterface_text.size())
+		console_scroll = consoleinterface_text.size();
 	
 	size_t height = (size_t) (video::height * con_height / Text::fontheight()) -1;
 	size_t width = (size_t) ((video::width-8) / Text::fontwidth());
-	size_t bottom = text.size() - console_scroll;
+	size_t bottom = consoleinterface_text.size() - console_scroll;
 	size_t current_line = 0;
 
 	std::deque<std::string>	lines;
-	for (std::deque<std::string>::iterator it = text.begin(); it != text.end() && current_line < bottom; it++) {
+	for (std::deque<std::string>::iterator it = consoleinterface_text.begin(); it != consoleinterface_text.end() && current_line < bottom; it++) {
 		if (current_line >= bottom - height) {
 			std::string linedata(*it);
 			linedata += '\n';
@@ -274,6 +386,7 @@ void draw_console()
 
 			const char *c = linedata.c_str();
 			char pen = 'N';
+			char wordpen = 'N';
 
 			while (*c) {
 
@@ -293,7 +406,7 @@ void draw_console()
 							lines.push_back(line);
 							line.clear();
 							line += '^';
-							line += pen;
+							line += wordpen;
 							line_length = 0;
 						}
 					}
@@ -303,6 +416,7 @@ void draw_console()
 
 					word.clear();
 					word_length = 0;
+					wordpen = pen;
 									
 					// new line
 					if (*c == '\n' ) {
@@ -326,7 +440,7 @@ void draw_console()
 							lines.push_back(line);
 							line.clear();
 							line += '^';
-							line += pen;
+							line += wordpen;
 							line_length = 0;
 						}
 
@@ -335,6 +449,7 @@ void draw_console()
 
 						word.clear();
 						word_length = 0;
+						wordpen = pen;
 					}
 				}
 
@@ -354,233 +469,86 @@ void draw_console()
 	
 
 	// draw the console input
-	size_t draw_pos = 0;
 	y = video::height*con_height - Text::fontheight() - 4;
-
-	while (input_pos - draw_pos > width)
-		draw_pos += 2;
-
 	Text::draw(4, y, "^B>");
-	Text::draw(4+Text::fontwidth(), y , (*history_pos).substr(draw_pos, width-1));
 
-	// draw cursor
-	if ((core::application()->time() - ::floorf(core::application()->time())) < 0.5f) {
-		std::string cursor("^B_");
-		Text::draw(4+Text::fontwidth()*(input_pos-draw_pos+1), y , cursor);
-	}
-
-}
-
-void draw(){
-	if (visible())
-		draw_console();
-	else
-		draw_notify();
-}
+	std::string firstpart((*history_pos).substr(0, input_pos));
+	size_t draw_width = 0;
+	const char *c = firstpart.c_str();
 
-void flush() 
-{
-	char line[MAXCMDSIZE];
-	while(console.buffer.getline(line, MAXCMDSIZE-1)) {	
-		while (text.size() >= MAXCONLINES) {
-			text.pop_front();
+	while (*c) {
+		if (aux::is_color_code(c)) {
+			c++;
+		} else {
+			draw_width++;
 		}
-		text.push_back(std::string(line));
-
-		// save notification
-		notify_text[notify_pos] = line;
-		notify_time[notify_pos] = core::application()->time();
-		notify_pos = (notify_pos+1) % MAXNOTIFYLINES;
-
-		// print to stdout
-		std::cout << line << std::endl;
+		c++;
 	}
-	
-	console.buffer.clear();
-}
-
-void toggle()
-{
-	console_visible = !console_visible;
-	
-	if (console_visible) {
-		console_scroll = 0;
-		input_pos = 0;
 
-		history_pos = history.rbegin();
-		(*history_pos).clear();
-
-		SDL_WM_GrabInput(SDL_GRAB_OFF);
-		SDL_ShowCursor(SDL_ENABLE);
-	} else {
-		SDL_WM_GrabInput(SDL_GRAB_ON);
-		SDL_ShowCursor(SDL_DISABLE);
-		clear_notify();
+	c = firstpart.c_str();
+	while (*c && draw_width > width - 2) {
+		if (aux::is_color_code(c)) {
+			c++;
+			Text::setcolor(*c);
+		} else {
+			draw_width--;
+		}
+		c++;
 	}
-	
-	setkeyboardmode(console::visible());
-}
-
-void keypressed(int key)
-{
-	std::deque<std::string>::reverse_iterator upit;
-
-	switch( key ) {
-	case SDLK_TAB:
-		core::CommandBuffer::complete( (*history_pos), input_pos);
-		break;
-	case SDLK_RETURN:
-		if ((*history_pos).size()) {
-			// store input into history
-			while (history.size() >= MAXHISTOLINES) {
-				history.pop_front();
-			}
 
-			core::cmd() << (*history_pos) << std::endl;
-			con_print << "^B>" << (*history_pos) << std::endl;
-			(*history.rbegin()) = (*history_pos);
+	if (*c) {
+		Text::draw(4+Text::fontwidth(), y, c);
+	}
 
-			history.push_back("");
-			history_pos = history.rbegin();
-			input_pos = 0;
-		}
-		break;
-	case SDLK_UP:
-		upit = history_pos;
-		++upit;
-		if (upit != history.rend()) {
-			history_pos = upit;
-			input_pos = (*history_pos).size();
-		}
-		break;
-	case SDLK_DOWN:
-		if (history_pos != history.rbegin()) {
-			--history_pos;
-			input_pos = (*history_pos).size();
+	if (input_pos < (*history_pos).size()) {
+		// FIXME limit to width
+		if (input_pos > 1 && aux::is_color_code((*history_pos).c_str() + input_pos -1)) {
+			Text::setcolor((*history_pos)[input_pos]);
 		}
-		break;
-	case SDLK_HOME:
-		input_pos = 0;
-		break;
-	case SDLK_END:		
-		input_pos = (*history_pos).size();
-		break;
-	case SDLK_LEFT:
-		if (input_pos > 0)
-			input_pos--;
-		break;
-	case SDLK_RIGHT:
-		if (input_pos < (*history_pos).size())
-			input_pos++;
-		break;
-	case SDLK_BACKSPACE:
-		if ((*history_pos).size() && input_pos) {
-			(*history_pos).erase(input_pos-1, 1);
-			input_pos--;
-		}
-		break;
-	case SDLK_PAGEUP:
-		console_scroll +=5;
-		if (console_scroll > text.size()) console_scroll = text.size();
-		break;
-	case SDLK_PAGEDOWN:
-		if (console_scroll > 5)	console_scroll -=5;
-		else console_scroll = 0;
-		break;
-	default:
-		if ((key >= 32 ) && (key <175)) {
-			if (input_pos == (*history_pos).size())
-				(*history_pos) += (char)key;
-			else
-				(*history_pos).insert(input_pos, 1, (char)key);
-			input_pos++;
-		}
-		break;
+		c = (*history_pos).c_str() + input_pos;
+		Text::draw(4+Text::fontwidth()*(draw_width+1), y, c);
 	}
 
-	
-}
-
-bool visible()
-{
-	return console_visible;
-}
-
-void save_history()
-{
-
-	if (history.size() <= 1)
-		return;
-
-	std::string filename(filesystem::writedir);
-	filename.append("history.txt");
-	std::ofstream ofs(filename.c_str());
-
-	if (!ofs.is_open()) {
-		con_warn << "Could not write " << filename << std::endl;
-		return;
-	}
-	std::deque<std::string>::iterator it;
-	size_t l = 1;
-	for (it = history.begin(); it != history.end(); it++) {
-		if (l < history.size())
-			ofs << (*it) << std::endl;
-		l++;
+	// draw cursor
+	if ((core::application()->time() - ::floorf(core::application()->time())) < 0.5f) {
+		std::string cursor("^B_");
+		Text::draw(4+Text::fontwidth()*(draw_width+1), y , cursor);
 	}
 
-	ofs.close();
 }
 
-void load_history()
-{
-	std::string filename(filesystem::writedir);
-	filename.append("history.txt");
-	std::ifstream ifs(filename.c_str(), std::ifstream::in);
 
-	if (!ifs.is_open()) {
-		con_warn << "Could not read " << filename << std::endl;
-		return;
-	}
-
-	history.clear();
-	char line[MAXCMDSIZE];
-	while (ifs.getline(line, MAXCMDSIZE-1)) {
-		history.push_back(line);
+void Console::clear_notify()
+{
+	for (size_t i=0; i < MAXNOTIFYLINES; i++) {
+		notify_text[i].clear();
+		notify_time[i] = 0;
 	}
-
-	ifs.close();
-
-	history.push_back("");
-	history_pos = history.rbegin();
-	input_pos = 0;
+	notify_pos = 0;
 }
-//--- private -----------------------------------------------------
 
 void Console::flush()
 {
-	console::flush();
-}
+	char line[MAXCMDSIZE];
 
-std::ostream & Console::messagestream()
-{
-	return (buffer << "^N");
-}
+	while(consoleinterface_buffer.getline(line, MAXCMDSIZE-1)) {	
 
-std::ostream & Console::warningstream()
-{
-	return (buffer << "^W");
-}
+		while (consoleinterface_text.size() >= sys::MAXCONLINES) {
+			consoleinterface_text.pop_front();
+		}
+		consoleinterface_text.push_back(std::string(line));
 
-std::ostream & Console::errorstream()
-{
-	return (buffer << "^R");
-}
+		// save notification
+		notify_text[notify_pos] = line;
+		notify_time[notify_pos] = core::application()->time();
+		notify_pos = (notify_pos+1) % MAXNOTIFYLINES;
 
-std::ostream & Console::debugstream()
-{
-	return (buffer << "^D");
+		// print to stdout
+		print_ansi(line);
+		std::cout << std::endl;
+	}
+	
+	consoleinterface_buffer.clear();
 }
 
-} // namespace console
-
 } // namespace client
diff --git a/src/client/console.h b/src/client/console.h
index dc831b7..2f8d6a0 100644
--- a/src/client/console.h
+++ b/src/client/console.h
@@ -9,54 +9,70 @@
 
 #include "sys/consoleinterface.h"
 
-#include <sstream>
-#include <deque>
+namespace client {
 
-const size_t MAXCONLINES = 2048;
-const size_t MAXHISTOLINES = 512;
 const size_t MAXNOTIFYLINES = 3;
+const size_t MAXHISTOLINES = 512;
 
-namespace client {
+/// client console implementation
+class Console : public sys::ConsoleInterface {
+public:
+	Console();
+	virtual ~Console();
 
-/// the client console 
-namespace console {
+	virtual void flush();
 
-/// initialize client console
-/** Adds the engine functions for the client console
- */
-void init();
+	void draw_console();
 
-/// shutdown the client console
-/** Removes the engine functions for the client console
- */
-void shutdown();
+	void draw_notify();
 
-/// flush buffer messages and print to stdout
-void flush();
-	
-/// draw the console
-void draw();
+	/// clear notifications
+	void clear_notify();
 
-/// toggle the console on or off
-void toggle();
+	/// draw client notifications or console text
+	void draw();
 
-/// handle keyboard input
-void keypressed(int key);
+	/// toggle the console on or off
+	void toggle();
 
-/// true of the console is visible
-bool visible();
+	/// handle keyboard input
+	void keypressed(int key);
 
-/// load input history
-void load_history();
+	/// true of the console is visible
+	inline bool visible() { return console_visible; }
 
-/// save input history
-void save_history();
+	/// load input history
+	void load_history();
 
-extern size_t notify_pos;
-extern std::string notify_text[MAXNOTIFYLINES];
-extern float notify_time[MAXNOTIFYLINES];
+	/// save input history
+	void save_history();
 
-}
+	/// clear console
+	void clear();
+
+	/// initialize client console
+	static void init();
+
+	/// shutdown the client console
+	static void shutdown();
+
+private:
+	// notifications
+	size_t 			notify_pos;
+	std::string 		notify_text[MAXNOTIFYLINES];
+	float 			notify_time[MAXNOTIFYLINES];
+
+	// input history
+	std::deque<std::string> history;
+	std::deque<std::string>::reverse_iterator history_pos;
+	size_t input_pos;
+
+	bool			console_visible;
+	size_t 			console_scroll;
+
+};
+
+Console *console();
 
 }
 
diff --git a/src/client/input.cc b/src/client/input.cc
index f40591c..22db6bc 100644
--- a/src/client/input.cc
+++ b/src/client/input.cc
@@ -221,31 +221,31 @@ void frame(float seconds)
 				mouse_y = event.motion.y;
 				break;
 			case SDL_MOUSEBUTTONDOWN:
-				if (!console::visible() && core::application()->connected() && core::localcontrol())
+				if (!console()->visible() && core::application()->connected() && core::localcontrol())
 					mousebuttonpressed(event.button);
 
 				break;
 			case SDL_KEYUP:
 				if (event.key.keysym.sym == SDLK_PRINT) {
 					video::screenshot();
-				} else if (!chat::visible() && !console::visible() && 
+				} else if (!chat::visible() && !console()->visible() && 
 					core::application()->connected() && core::localcontrol())
 
 					// send key events to the game world
 					keyreleased(event.key.keysym);
 				break;
 			case SDL_KEYDOWN:
-				if (chat::visible() && !console::visible() && (event.key.keysym.sym == SDLK_ESCAPE)) {
+				if (chat::visible() && !console()->visible() && (event.key.keysym.sym == SDLK_ESCAPE)) {
 					chat::toggle();
 				} else if (event.key.keysym.sym == '`' || event.key.keysym.sym == '~' || (event.key.keysym.sym == SDLK_ESCAPE)) {
 					//last_control = 0;
-					console::toggle();
+					console()->toggle();
 					
-					if (console::visible() && chat::visible())
+					if (console()->visible() && chat::visible())
 						chat::toggle();
-				} else if (console::visible()) {
+				} else if (console()->visible()) {
 					// send key events to the console
-					console::keypressed(translate_keysym(event.key.keysym));
+					console()->keypressed(translate_keysym(event.key.keysym));
 
 				} else if (chat::visible()) {
 					if(event.key.keysym.sym == SDLK_ESCAPE) {
@@ -270,7 +270,7 @@ void frame(float seconds)
 		
 	}
 
-	if (!console::visible() && core::application()->connected() && core::localcontrol()) {
+	if (!console()->visible() && core::application()->connected() && core::localcontrol()) {
 		
 		if (cl_mousecontrol->value()) {
 			// the mouse will not react if it is in the deadzone
@@ -302,8 +302,8 @@ void frame(float seconds)
 			}
 
 			if ((camera::mode == camera::Track) || (camera::mode == camera::Cockpit)) {
-				local_direction = mouse_direction;
-				local_pitch = mouse_pitch;
+				local_direction = mouse_direction * math::absf(mouse_direction);
+				local_pitch = mouse_pitch * math::absf(mouse_pitch);
 			} else if (camera::mode == camera::Free) {
 				// squared values to smoothen camera movement
 				camera::set_direction( -mouse_direction * math::absf(mouse_direction));
diff --git a/src/client/view.cc b/src/client/view.cc
index 63c373e..e2c8687 100644
--- a/src/client/view.cc
+++ b/src/client/view.cc
@@ -128,7 +128,7 @@ void draw_status()
 {
 	using namespace render;
 
-	if (console::visible())
+	if (console()->visible())
 		return;
 
 	// print the status in the upper left corner
@@ -171,7 +171,7 @@ void draw_status()
 
 void draw_cursor()
 {
-	if (!core::localcontrol() || console::visible()) 
+	if (!core::localcontrol() || console()->visible()) 
 		return;
 
 	float crosshair_size = 48.0f;
@@ -222,7 +222,7 @@ void frame(float seconds)
 		fps = 9999;
 
 	// flush console messages
-	console::flush();
+	console()->flush();
 
 	// Clear the color and depth buffers.
 	gl::clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@@ -234,7 +234,7 @@ void frame(float seconds)
 		gl::loadidentity();
 
 		// FIXME width must always be one
-		const float frustumsize = 0.5f;
+		const float frustumsize = 0.25f;
 		gl::frustum(-frustumsize*video::aspect, frustumsize*video::aspect, -frustumsize, frustumsize, 1.0f, 1024.0f);
 
 		gl::matrixmode(GL_MODELVIEW);	// map world to screen coordinates
@@ -263,7 +263,7 @@ void frame(float seconds)
 
 	// draw text elements
 	Text::setfont("bitmaps/fonts/console", 12, 18);
-	console::draw();
+	console()->draw();
 	chat::draw();
 
 	Text::setfont("bitmaps/fonts/gui", 16, 24);
diff --git a/src/core/application.cc b/src/core/application.cc
index 79f4a28..67bb952 100644
--- a/src/core/application.cc
+++ b/src/core/application.cc
@@ -100,10 +100,10 @@ Application::Application()
 	application_game = 0;
 
 #ifndef _WIN32
-	//sys::signal(SIGHUP, signal_handler);
-	//sys::signal(SIGINT, signal_handler);
-	//sys::signal(SIGQUIT, signal_handler);
-	//sys::signal(SIGTERM, signal_handler);
+	sys::signal(SIGHUP, signal_handler);
+	sys::signal(SIGINT, signal_handler);
+	sys::signal(SIGQUIT, signal_handler);
+	sys::signal(SIGTERM, signal_handler);
 #endif
 	
 }
@@ -117,7 +117,7 @@ void Application::init(int count, char **arguments)
 {
 	con_debug << "Debug messages enabled\n";
 	con_print << "^BInitializing core...\n";
-	
+
 	filesystem::init();
 
 	CommandBuffer::init();
@@ -156,6 +156,14 @@ void Application::init(int count, char **arguments)
 	Cvar::net_framerate = Cvar::get("net_framerate", "25", Cvar::Archive);
 	Cvar::net_framerate->set_info("[int] network framerate in frames/sec");
 
+#ifdef _WIN32
+	Cvar::con_ansi = Cvar::get("con_ansi", "0", Cvar::Archive);
+#else
+	Cvar::con_ansi = Cvar::get("con_ansi", "1", Cvar::Archive);
+#endif
+	Cvar::con_ansi->set_info("[bool] console ANSI colors");
+	sys::ConsoleInterface::instance()->set_ansi(Cvar::con_ansi->value());
+
 #ifdef _WIN32
 	// Initialize win32 socket library
 	WSADATA wsa_data;
@@ -217,6 +225,8 @@ void Application::shutdown()
 
 void Application::quit(int status)
 {
+	console()->flush();
+
 	sys::quit(status);
 }
 
@@ -264,6 +274,8 @@ void Application::disconnect()
 
 void Application::frame(float seconds)
 {
+	console()->flush();
+
 	// execute commands in the buffer
 	CommandBuffer::exec();
 
@@ -278,9 +290,11 @@ void Application::frame(float seconds)
 	
 	// run a game interface frame	
 	application_game->frame(seconds);
-			
+	
 	if (!application_game->running()) 
 		disconnect();
+
+	console()->flush();
 }
 
 void Application::save_config()
diff --git a/src/core/application.h b/src/core/application.h
index 01f34cf..bec53d9 100644
--- a/src/core/application.h
+++ b/src/core/application.h
@@ -7,6 +7,7 @@
 #ifndef __INCLUDED_CORE_APPLICATION_H__
 #define __INCLUDED_CORE_APPLICATION_H__
 
+#include "sys/sys.h"
 #include "core/commandbuffer.h"
 #include "core/netserver.h"
 #include "core/netconnection.h"
@@ -86,6 +87,9 @@ inline Application *application() { return Application::instance(); }
 /// pointer to the current GameInterface
 inline GameInterface *game() { return Application::instance()->game(); }
 
+/// pointer to the console
+inline sys::ConsoleInterface *console() { return sys::ConsoleInterface::instance(); };
+
 } // namespace core
 
 #endif // __INCLUDED_CORE_APPLICATION_H__
diff --git a/src/core/cvar.cc b/src/core/cvar.cc
index 7593ac3..f8ea746 100644
--- a/src/core/cvar.cc
+++ b/src/core/cvar.cc
@@ -17,6 +17,7 @@
 namespace core
 {
 
+Cvar				*Cvar::con_ansi = 0;
 Cvar				*Cvar::sv_dedicated = 0;
 Cvar				*Cvar::sv_private = 0;
 Cvar				*Cvar::sv_framerate = 0;
diff --git a/src/core/cvar.h b/src/core/cvar.h
index 4125d3c..69fcae1 100644
--- a/src/core/cvar.h
+++ b/src/core/cvar.h
@@ -113,6 +113,8 @@ public:
 	static Cvar		*sv_private;	// client with private server
 	static Cvar		*sv_framerate;	// server framerate
 
+	static Cvar		*con_ansi;	// console ANSI colors
+
 	static Cvar		*net_host;	// network server ip (default binds to all interfaces)
 	static Cvar		*net_port;	// network port
 	static Cvar		*net_maxclients;// maximum number of connected clients
diff --git a/src/core/gameconnection.cc b/src/core/gameconnection.cc
index 1d022c5..6090a52 100644
--- a/src/core/gameconnection.cc
+++ b/src/core/gameconnection.cc
@@ -88,9 +88,7 @@ void GameConnection::frame(float seconds)
 		return;
 	}
 
-	if (!Cvar::sv_dedicated->value()) {
-		update_clientstate(seconds);
-	}
+	update_clientstate(seconds);
 
 	connection_network->frame(seconds);
 
diff --git a/src/core/gameinterface.cc b/src/core/gameinterface.cc
index 1f4a5cd..f50c1cc 100644
--- a/src/core/gameinterface.cc
+++ b/src/core/gameinterface.cc
@@ -18,6 +18,8 @@
 namespace core
 {
 
+const float MIN_DELTA = 10e-10;
+
 void func_list_model(std::string const &args)
 {
 	model::Model::list();
@@ -97,12 +99,10 @@ void GameInterface::clear()
 	game_clientframetime = 0;
 }
 
-void GameInterface::reset_clientstate(float servertime)
+void GameInterface::reset_clientstate(float timestamp, float prevtimestamp)
 {
-	game_previousframetime = game_serverframetime;
-	game_clientframetime = game_serverframetime;
-
-	game_serverframetime = servertime;
+	game_previousframetime = prevtimestamp;
+	game_serverframetime = timestamp;
 	
 	std::map<unsigned int, core::Entity *>::iterator it;
 	for (it=core::Entity::registry.begin(); it != core::Entity::registry.end(); it++) {
@@ -112,121 +112,130 @@ void GameInterface::reset_clientstate(float servertime)
 		if (entity->state() && !(entity->flags() & core::Entity::Static))
 			entity->state()->assign(entity);
 	}
-}
-
-void GameInterface::update_clientstate(float seconds)
-{
-	game_clientframetime += seconds;
 
-	if (game_clientframetime > game_serverframetime)
+	if ( game_clientframetime < game_previousframetime)
+		game_clientframetime = game_previousframetime;
+	else if ( game_clientframetime > game_serverframetime)
 		game_clientframetime = game_serverframetime;
 
-	std::map<unsigned int, Entity *>::iterator it;
-	for (it=Entity::registry.begin(); it != Entity::registry.end(); it++) {
+}
+
 
-		Entity *entity = (*it).second;
+void GameInterface::update_entity_clientstate(Entity *entity)
+{
+	if (!entity->state()) {
+		entity->entity_clientstate = new core::ClientState(entity);
+		entity->entity_clientstate->assign(entity);
+	}
 
-		// update client state
-		if (!entity->state()) {
-			entity->entity_clientstate = new core::ClientState(entity);
-		}
+	if (!(entity->flags() & core::Entity::Static))
+		return;
+
+	if (game_clientframetime < game_serverframetime) {
 		
 		if (!(entity->flags() & core::Entity::Static)) {
-
+	
 			// clientstate location
 			entity->state()->state_location = entity->state()->previouslocation() + 
-			(entity->location() - entity->state()->previouslocation()) * core::game()->timeoffset();
-
-			if (core::game()->clientframetime() < core::game()->serverframetime()) {
-
-				// http://local.wasp.uwa.edu.au/~pbourke/geometry/planeline/
-				float cosangle;
-				float angle;
-				float side;
-				float u;
-
-				math::Vector3f n;
-				math::Vector3f p;
-
-				// clientstate axis: pitch
-
-				// project entity->axis().up() into the plane with entity->state()->axis()->left() normal
-				n = entity->state()->axis().left();
-				p = entity->axis().up();
-				u = p[0]*n[0] + p[1]*n[1] + p[2]*n[2] / (-n[0]*n[0] - n[1]*n[1] - n[2] * n[2]);
-				p = entity->axis().up() + u * n;
-		
-				side = entity->state()->axis().forward().x * p.x + 
-				       entity->state()->axis().forward().y * p.y +
-				       entity->state()->axis().forward().z * p.z;
-
-				if (fabs(side) > 0.00005f) {
-					cosangle = math::dotproduct(p, entity->state()->axis().up());
-					
-					if (fabs(cosangle) < 0.99995f) {
-						angle = acos(cosangle) * 180.0f / M_PI;
-						angle = math::sgnf(side) * angle * seconds / 
-							(core::game()->serverframetime() - core::game()->clientframetime());
-
-						entity->state()->state_axis.change_pitch(-angle);
-					}
+			(entity->location() - entity->state()->previouslocation()) * timeoffset();
+	
+			// http://local.wasp.uwa.edu.au/~pbourke/geometry/planeline/
+			float cosangle;
+			float angle;
+			float side;
+			float u;
+			float l;
+	
+			math::Vector3f n;
+			math::Vector3f p;
+	
+			entity->state()->state_axis.assign(entity->state()->previousaxis());
+			// clientstate axis: pitch
+	
+			// project entity->axis().up() into the plane with entity->state()->axis()->left() normal
+			n = entity->state()->axis().left();
+			p = entity->axis().up();
+			u = p[0]*n[0] + p[1]*n[1] + p[2]*n[2] / (-n[0]*n[0] - n[1]*n[1] - n[2] * n[2]);
+			p = entity->axis().up() + u * n;
+	
+			side = entity->state()->axis().forward().x * p.x + 
+				entity->state()->axis().forward().y * p.y +
+				entity->state()->axis().forward().z * p.z;
+	
+			l = p.length();
+			if ((fabs(side) - MIN_DELTA > 0)) {
+				cosangle = math::dotproduct(p, entity->state()->axis().up());		
+				if (fabs(cosangle) + MIN_DELTA < 1 ) {
+					angle = acos(cosangle) * 180.0f / M_PI;
+					angle = math::sgnf(side) * angle *  timeoffset();
+					entity->state()->state_axis.change_pitch(-angle);
 				}
-
-				// clientstate axis: direction
-
-				// project entity->axis().forward() into the plane with entity->state()->axis().up() normal
-				n = entity->state()->axis().up();
-				p = entity->axis().forward();
-				u = p[0]*n[0] + p[1]*n[1] + p[2]*n[2] / (-n[0]*n[0] - n[1]*n[1] - n[2] * n[2]);
-				p = entity->axis().forward() + u * n;
-
-				side = entity->state()->axis().left().x * p.x + 
-				       entity->state()->axis().left().y * p.y +
-				       entity->state()->axis().left().z * p.z;
-
-				if (fabs(side) > 0.00005f) {
-					cosangle = math::dotproduct(p, entity->state()->axis().forward());
-					if (fabs(cosangle) < 0.99995f) {
-						angle = acos(cosangle) * 180.0f / M_PI;
-						angle = math::sgnf(side) * angle * seconds / 
-							(core::game()->serverframetime() - core::game()->clientframetime());
-
-						entity->state()->state_axis.change_direction(angle);
-					}
+			}
+	
+			// clientstate axis: direction
+	
+			// project entity->axis().forward() into the plane with entity->state()->axis().up() normal
+			n = entity->state()->axis().up();
+			p = entity->axis().forward();
+			u = p[0]*n[0] + p[1]*n[1] + p[2]*n[2] / (-n[0]*n[0] - n[1]*n[1] - n[2] * n[2]);
+			p = entity->axis().forward() + u * n;
+	
+			side = entity->state()->axis().left().x * p.x + 
+				entity->state()->axis().left().y * p.y +
+				entity->state()->axis().left().z * p.z;
+	
+			l = p.length();
+			if ((fabs(side) - MIN_DELTA > 0)) {
+				cosangle = math::dotproduct(p, entity->state()->axis().forward());
+				if (fabs(cosangle) + MIN_DELTA < 1 ) {
+					angle = acos(cosangle) * 180.0f / M_PI;
+					angle = math::sgnf(side) * angle *  timeoffset();
+					entity->state()->state_axis.change_direction(angle);
 				}
-
-				// clientstate axis: roll
-
-				// project entity->axis().up() into the plane with entity->state()->axis().forward() normal
-				n = entity->state()->axis().forward();
-				p = entity->axis().up();
-				u = p[0]*n[0] + p[1]*n[1] + p[2]*n[2] / (-n[0]*n[0] - n[1]*n[1] - n[2] * n[2]);
-				p = entity->axis().up() + u * n;
-
-				side = entity->state()->axis().left().x * p.x + 
-				       entity->state()->axis().left().y * p.y +
-				       entity->state()->axis().left().z * p.z;
-
-				if (fabs(side) > 0.00005f) {
-					cosangle = math::dotproduct(p, entity->state()->axis().up());
+			}
+	
+			// clientstate axis: roll
+	
+			// project entity->axis().up() into the plane with entity->state()->axis().forward() normal
+			n = entity->state()->axis().forward();
+			p = entity->axis().up();
+			u = p[0]*n[0] + p[1]*n[1] + p[2]*n[2] / (-n[0]*n[0] - n[1]*n[1] - n[2] * n[2]);
+			p = entity->axis().up() + u * n;
+	
+			side = entity->state()->axis().left().x * p.x + 
+				entity->state()->axis().left().y * p.y +
+				entity->state()->axis().left().z * p.z;
+	
+			l = p.length();
+			if ((fabs(side) - MIN_DELTA > 0)) {
+				cosangle = math::dotproduct(p, entity->state()->axis().up());
+				if (fabs(cosangle) + MIN_DELTA < 1 ) {
 					angle = acos(cosangle) * 180.0f / M_PI;
-					if (fabs(cosangle) < 0.99995f) {						
-						angle = math::sgnf(side) * angle * seconds / 
-							(core::game()->serverframetime() - core::game()->clientframetime());
-
-						entity->state()->state_axis.change_roll(angle);
-					}
+					angle = math::sgnf(side) * angle *  timeoffset();
+					entity->state()->state_axis.change_roll(angle);
 				}
-
 			}
 			
- 		}
+		}
+	
+	} else {
+		entity->state()->assign(entity);
+	}
+}
 
+void GameInterface::update_clientstate(float seconds)
+{
+	std::map<unsigned int, core::Entity *>::iterator it;
+	for (it=core::Entity::registry.begin(); it != core::Entity::registry.end(); it++) {
+		update_entity_clientstate((*it).second);
 	}
 }
 
 float GameInterface::timeoffset() {
 
+	if (game_clientframetime > game_serverframetime)
+		return 1;
+
 	float d = game_serverframetime - game_previousframetime;
 	if (d <= 0)
 		return 0;
diff --git a/src/core/gameinterface.h b/src/core/gameinterface.h
index 0edcb1b..19c6886 100644
--- a/src/core/gameinterface.h
+++ b/src/core/gameinterface.h
@@ -52,11 +52,13 @@ public:
 	void clear();
 
 	/// reset the client state
-	void reset_clientstate(float servertime);
+	void reset_clientstate(float timestamp, float prevtimestamp);
 
 	/// update the client state timers
 	void update_clientstate(float seconds);
 
+	void update_entity_clientstate(Entity *entity);
+
 /*----- virtual mutators ------------------------------------------ */
 	
 	/// run one game time frame
diff --git a/src/core/gameserver.cc b/src/core/gameserver.cc
index 11a8b13..fb828e2 100644
--- a/src/core/gameserver.cc
+++ b/src/core/gameserver.cc
@@ -34,6 +34,7 @@ GameServer::GameServer() : GameInterface()
 	server_instance = this;
 	server_network = 0;
 	server_time = 0;
+	server_previoustime = 0;
 	server_frametime = 0.0f;
 	server_maxplayerid = 1;
 
@@ -278,7 +279,7 @@ void GameServer::frame(float seconds)
 	
 	// copy the previous entity state to the client state
 	if (!Cvar::sv_dedicated->value()) {
-		reset_clientstate(server_time);
+		reset_clientstate(server_time, server_previoustime);
 	}
 
 	// run a time frame on each entity
@@ -308,7 +309,7 @@ void GameServer::frame(float seconds)
 		// start server frame
 		std::ostringstream framehdr;
 		framehdr.str("");
-		framehdr << "frame " << server_time << "\n";
+		framehdr << "frame " << server_time << " " << server_previoustime << "\n";
 		server_network->broadcast(framehdr.str());
 
 		std::map<unsigned int, Entity *>::iterator it;
@@ -377,7 +378,12 @@ void GameServer::frame(float seconds)
 		}
 	}
 
+	if (!Cvar::sv_dedicated->value()) {
+		update_clientstate(0);
+	}
+
 	server_frametime = 0;
+	server_previoustime = server_time;
 }
 
 
diff --git a/src/core/gameserver.h b/src/core/gameserver.h
index 4fb9c47..bcd9cc3 100644
--- a/src/core/gameserver.h
+++ b/src/core/gameserver.h
@@ -76,6 +76,7 @@ private:
 	unsigned int		server_maxplayerid;
 	float			server_frametime;
 	float			server_time;
+	float			server_previoustime;
 };
 
 inline GameServer *server() { return GameServer::instance(); }
diff --git a/src/core/netconnection.cc b/src/core/netconnection.cc
index 2bcd6dc..22614d2 100644
--- a/src/core/netconnection.cc
+++ b/src/core/netconnection.cc
@@ -307,9 +307,9 @@ void NetConnection::parse_incoming_message(const std::string & message)
 	} else if (command == "ping") {
 
 	} else if (command == "frame") {
-		float timestamp;
-		if (msgstream >> timestamp) {
-			game()->reset_clientstate(timestamp);
+		float timestamp, prevtimestamp;
+		if ((msgstream >> timestamp) && (msgstream >> prevtimestamp)) {
+			game()->reset_clientstate(timestamp, prevtimestamp);
 		}
 
 	} else if (command == "die") {
@@ -329,16 +329,16 @@ void NetConnection::parse_incoming_message(const std::string & message)
 			switch (type) 
 			{
 			case Entity::Default:
-				new Entity(msgstream);
+				game()->update_entity_clientstate(new Entity(msgstream));
 				break;
 			case Entity::Dynamic:
-				new EntityDynamic(msgstream);
+				game()->update_entity_clientstate(new EntityDynamic(msgstream));
 				break;
 			case Entity::Controlable:
-				new EntityControlable(msgstream);
+				game()->update_entity_clientstate(new EntityControlable(msgstream));
 				break;
 			case Entity::Globe:
-				new EntityGlobe(msgstream);
+				game()->update_entity_clientstate(new EntityGlobe(msgstream));
 				break;
 			default:
 				con_warn << "Create for unknown entity type " << type << std::endl;
diff --git a/src/game/game.cc b/src/game/game.cc
index d2e5242..aa78e41 100644
--- a/src/game/game.cc
+++ b/src/game/game.cc
@@ -96,7 +96,7 @@ void func_buy(core::Player *player, std::string const &args)
 		player->player_control = new Ship(player, shipmodel);
 		player->control()->entity_color = player->color();
 
-		core::server()->broadcast("^N" + player->name() + " ^Bpurchased " + aux::article(shipmodel->name()));
+		core::server()->broadcast("^B" + player->name() + " ^Bpurchased " + aux::article(shipmodel->name()));
 		player->player_dirty = true;
 	} else {
 		core::server()->send(player, "Usage: buy <" + helpstr + "^N>");
diff --git a/src/math/functions.cc b/src/math/functions.cc
index e048659..40f0f4e 100644
--- a/src/math/functions.cc
+++ b/src/math/functions.cc
@@ -9,6 +9,8 @@
 namespace math
 {
 
+const float DELTA = 10e-10;
+
 float min(float a, float b)
 {
 	return (a < b ? a : b);
@@ -42,9 +44,9 @@ unsigned randomi(const unsigned int max)
 float degrees180f(float angle)
 {
 	float r = angle;
-	while (r < -180.0f)
+	while (r - DELTA < -180.0f)
 		r += 360.0f;
-	while (r > 180.0f)
+	while (r + DELTA > 180.0f)
 		r -= 360.0f;
 	return r;
 }
@@ -52,9 +54,9 @@ float degrees180f(float angle)
 float degrees360f(float angle)
 {
 	float r = angle;
-	while (r < 0.0f)
+	while (r - DELTA < 0)
 		r += 360.0f;
-	while (r > 360.0f)
+	while (r + DELTA > 360.0f)
 		r -= 360.0f;
 	return r;
 }
diff --git a/src/render/draw.cc b/src/render/draw.cc
index 7c35bf3..9292460 100644
--- a/src/render/draw.cc
+++ b/src/render/draw.cc
@@ -431,7 +431,7 @@ void draw_pass_model_fx()
 					t = (core::application()->time() + entity->state()->fuzz() - (*lit)->offset()) * (*lit)->frequency();
 
 				if (!(*lit)->strobe() || (( t - floorf(t)) <= (*lit)->time())) {
-					math::Vector3f location = entity->state()->location() + (entity->axis() * (*lit)->location());
+					math::Vector3f location = entity->state()->location() + (entity->state()->axis() * (*lit)->location());
 					float light_size = 0.0625 * (*lit)->radius();
 	
 					if ((*lit)->render_texture != flare_texture) {
@@ -482,7 +482,7 @@ void draw_pass_model_fx()
 				t = 0.75f + t/2;
 				
 				for(std::list<model::Engine*>::iterator eit = entity->model()->model_engine.begin(); eit != entity->model()->model_engine.end(); eit++) {
-					math::Vector3f location = entity->state()->location() + (entity->axis() * (*eit)->location());
+					math::Vector3f location = entity->state()->location() + (entity->state()->axis() * (*eit)->location());
 					float engine_size = 0.0625 * (*eit)->radius() * t;
 
 					math::Color color(1.0f, 0.0f, 0.0f, 0.9f * u);
diff --git a/src/server/console.cc b/src/server/console.cc
index b5d0aaf..4b7992e 100644
--- a/src/server/console.cc
+++ b/src/server/console.cc
@@ -11,86 +11,21 @@
 
 namespace server {
 
-namespace console {
+Console server_console;
 
-//--- private definition ------------------------------------------
-
-/// server console implementation
-class Console : public sys::ConsoleInterface {
-public:
-	/// stream to send normal messages too
-	virtual std::ostream & messagestream();
-
-	/// stream to send warning messages too
-	virtual std::ostream & warningstream();
-
-	/// stream to send warning messages too
-	virtual std::ostream & errorstream();
-	
-	/// stream to send debug messages too
-	virtual std::ostream & debugstream();
-
-	/// flush does nothing in this implementation
-	virtual void flush();
-
-	unsigned long ping;
-	
-};
-
-// private console object
-Console console;
-
-//--- engine functions --------------------------------------------
-
-extern "C" void func_con_ping(std::stringstream &args)
+Console *console()
 {
-	con_print << "Ping!" << std::endl;
-	console.ping++;
+	return (&server_console);
 }
-
-//--- public ------------------------------------------------------
-
-void init()
+void Console::init()
 {
 	con_print << "Initializing console..." << std::endl;
-
-	// register our engine functions
-	core::Func::add("con_ping", (core::FuncPtr) func_con_ping);
 }
 
-void shutdown()
+void Console::shutdown()
 {
 	con_print << "Shutting down console..." << std::endl;
-
-	// unregister our engine functions
-	core::Func::remove("con_ping");
-}
-
-//--- private -----------------------------------------------------
-
-void Console::flush()
-{
 }
 
-std::ostream & Console::messagestream()
-{
-	return std::cout;
-}
-
-std::ostream & Console::warningstream()
-{
-	return std::cerr;
 }
-std::ostream & Console::errorstream()
-{
-	return std::cerr;
-}
-
-std::ostream & Console::debugstream()
-{
-	return std::cout;
-}
-
-} // namespace console
 
-} // namespace server
diff --git a/src/server/console.h b/src/server/console.h
index 1d94ba6..d894bf1 100644
--- a/src/server/console.h
+++ b/src/server/console.h
@@ -11,15 +11,16 @@
 
 namespace server {
 
-namespace console {
+class Console : public sys::ConsoleInterface {
+public:
+	static void init();
 
-void init();
+	static void shutdown();
+};
 
-void shutdown();
+Console *console();
 
-} // namespace console
-
-} // namespace server
+}
 
 #endif // __INCLUDED_SERVER_CONSOLE_H__
 
diff --git a/src/server/server.cc b/src/server/server.cc
index 1c95ee8..ee54352 100644
--- a/src/server/server.cc
+++ b/src/server/server.cc
@@ -33,6 +33,7 @@ public:
 	virtual void quit(int status);
 };
 
+
 Server app;
 
 //--- public ------------------------------------------------------
@@ -60,7 +61,7 @@ void Server::init(int count, char **arguments)
 	core::Cvar::sv_private = core::Cvar::set("sv_dedicated", "1", core::Cvar::ReadOnly);
 	core::Application::init(count, arguments);	
 
-	console::init();
+	Console::init();
 
 	// the command line is in the buffer, execute it
 	core::CommandBuffer::exec();
@@ -103,7 +104,7 @@ void Server::shutdown()
 	con_debug << "  bytes sent     " << std::setw(6) << core::Stats::network_bytes_sent / 1024 << " Kb" << std::endl;
 	con_debug << "  bytes received " << std::setw(6) << core::Stats::network_bytes_received / 1024 << " Kb" << std::endl;
 	
-	console::shutdown();
+	Console::shutdown();
 	
 	core::Application::shutdown();
 	
diff --git a/src/sys/consoleinterface.cc b/src/sys/consoleinterface.cc
index f80939d..a300ea1 100644
--- a/src/sys/consoleinterface.cc
+++ b/src/sys/consoleinterface.cc
@@ -17,11 +17,15 @@ ConsoleInterface *ConsoleInterface::consoleinterface_instance = 0;
 
 ConsoleInterface::ConsoleInterface()
 {
+	consoleinterface_ansi = true;
 	if (consoleinterface_instance) {
 		std::cerr << "multiple sys::ConsoleInterface instances!" << std::endl;
 		sys::quit(2);
 	}
+
 	consoleinterface_instance = this;
+	consoleinterface_text.clear();
+	consoleinterface_buffer.clear();
 }
 
 ConsoleInterface::~ConsoleInterface()
@@ -34,4 +38,129 @@ ConsoleInterface *ConsoleInterface::instance()
 	return consoleinterface_instance;
 }
 
+std::ostream & ConsoleInterface::messagestream()
+{
+	flush();
+	return (consoleinterface_buffer << "^N");
+}
+
+std::ostream & ConsoleInterface::warningstream()
+{
+	flush();
+	return (consoleinterface_buffer << "^W");
+}
+
+std::ostream & ConsoleInterface::errorstream()
+{
+	flush();
+	return (consoleinterface_buffer << "^R");
+}
+
+std::ostream & ConsoleInterface::debugstream()
+{
+	flush();
+	return (consoleinterface_buffer << "^D");
+}
+
+void ConsoleInterface::flush()
+{
+	char line[MAXCMDSIZE];
+
+	while(consoleinterface_buffer.getline(line, MAXCMDSIZE-1)) {	
+
+		while (consoleinterface_text.size() >= MAXCONLINES) {
+			consoleinterface_text.pop_front();
+		}
+		consoleinterface_text.push_back(std::string(line));
+
+		// print to stdout
+		print_ansi(line);
+		std::cout << std::endl;
+	}
+	
+	consoleinterface_buffer.clear();
+}
+
+void ConsoleInterface::print_ansi(const char *line)
+{
+	if (consoleinterface_ansi)
+		std::cout << "\033[0;39m";
+
+	const char *c = line;
+	while (*c) {
+		
+		if ((*c == '^')) {
+			bool is_code = true;
+			int bold = 0;
+			int color = 39; 
+
+			switch (*(c+1)) {
+				case '0': // black
+					color = 0;
+                                	bold = 1;
+                                        break;
+                                case '1': // red
+                                	color = 31;
+                                        break;
+                                case '2': // green
+                                        color = 32;
+                                        break;
+                                case '3': // yellow
+					bold = 1;
+                                        color = 33;
+                                        break;
+                                case '4': // blue
+                                        color = 34;
+                                        break;
+                                case '5': // cyan
+                                        color = 36;
+                                        break;
+                                case '6': // magenta
+                                        color = 35;
+                                        break;
+                                case '7': // white is mapped to foreground color
+					bold = 1;
+                                        color = 39;
+					break;
+
+				case 'N': // normal
+					bold = 0;
+					color = 39;
+					break;
+				case 'B': // bold
+					bold = 1;
+					color = 39;
+					break;
+				case 'D': // debug
+					bold = 0;
+					color = 39;
+					break;
+				case 'R': // error
+					bold = 0;
+					color = 31;
+					break;
+				case 'W': // warning
+					bold = 1;
+					color = 33;
+					break;
+				case 'F': // fancy
+					bold = 0;
+					color = 32;
+					break;
+				default:
+					is_code = false;
+			}
+			if (is_code) {
+				c++;
+				if (consoleinterface_ansi)
+					std::cout <<  "\033[" << bold << ";" << color << "m";
+			} else
+				std::cout << *c;
+		} else {
+			std::cout << *c;
+		}
+		c++;
+	}
+}
+
 } // namespace sys
diff --git a/src/sys/consoleinterface.h b/src/sys/consoleinterface.h
index 33dc5b5..f874182 100644
--- a/src/sys/consoleinterface.h
+++ b/src/sys/consoleinterface.h
@@ -7,11 +7,11 @@
 #ifndef __INCLUDED_SYS_CONSOLEINTERFACE_H__
 #define __INCLUDED_SYS_CONSOLEINTERFACE_H__
 
-// project headers
-#include "sys/sys.h"
-
-// C++ headers
 #include <iostream>
+#include <sstream>
+#include <deque>
+
+#include "sys/sys.h"
 
 /// global define to send a message to the system console
 #define con_print sys::ConsoleInterface::instance()->messagestream()
@@ -30,6 +30,8 @@
 namespace sys
 {
 
+const size_t MAXCONLINES = 2048;
+
 /// interface for the client and server Console classes
 class ConsoleInterface
 {
@@ -40,27 +42,38 @@ public:
 	/// default destructor
 	virtual ~ConsoleInterface();
 
-	/// stream to send normal messages too
-	virtual std::ostream & messagestream() = 0;
+	/// stream to send normal messages to
+	std::ostream & messagestream();
 
-	/// stream to send warning messages too
-	virtual std::ostream & warningstream() = 0;
+	/// stream to send warning messages to
+	std::ostream & warningstream();
 
-	/// stream to send error messages too
-	virtual std::ostream & errorstream() = 0;
+	/// stream to send error messages to
+	std::ostream & errorstream();
 
-	/// stream to send debug messages too
-	virtual std::ostream & debugstream() = 0;
+	/// stream to send debug messages to
+	std::ostream & debugstream();
 
 	/// flush buffered messages
-	virtual void flush() = 0;
+	virtual void flush();
+
+	/// turn ANSI color codes on or off
+	inline void set_ansi(bool ansi) { consoleinterface_ansi = ansi; }
 
 	/// a pointer to the current console instance
 	static ConsoleInterface *instance();
 
+protected:
+	std::deque<std::string> consoleinterface_text;
+	std::stringstream 	consoleinterface_buffer;
+	
+	/// print a string to stdout with ansi color codes
+	void print_ansi(const char *line);
+
 private:
 	/// console singleton
 	static ConsoleInterface *consoleinterface_instance;
+	bool			consoleinterface_ansi;
 };
 
 } // namespace sys
diff --git a/src/sys/sys.cc b/src/sys/sys.cc
index 4c3f38f..4cb820d 100644
--- a/src/sys/sys.cc
+++ b/src/sys/sys.cc
@@ -20,25 +20,25 @@
 #endif
 
 #include <stdlib.h>
+#include <string>
 
 #include "sys/sys.h"
 
 namespace sys {
 
-void mkdir(const char *path)
+void mkdir(std::string const &path)
 {
 #ifdef _WIN32
-	mkdir(path);
+	string p(path);
+	for (size_t i = 0; i < p.lenght(); i++)
+		if (p[i] == '/') p[i] = '\\';
+	::mkdir(p.cstr());
 #else
-	::mkdir(path, 0777);
+	
+	::mkdir(path.c_str(), 0777);
 #endif
 }
 
-void mkdir(const std::string &path)
-{
-	mkdir(path.c_str());
-}
-
 void signal(int signum, signalfunc handler)
 {
 #ifndef _WIN32
diff --git a/src/sys/sys.h b/src/sys/sys.h
index 178fe60..e455b82 100644
--- a/src/sys/sys.h
+++ b/src/sys/sys.h
@@ -22,9 +22,7 @@ namespace sys
 typedef void (* signalfunc)(int signum);
 
 /// create a directory
-void mkdir(const char *path);
-
-void mkdir(const std::string &path);
+void mkdir(std::string const &path);
 
 /// intercept OS signals
 void signal(int signum, signalfunc handler);
-- 
cgit v1.2.3