diff --git a/src/client/entities.qc b/src/client/entities.qc
index 420bc08c..a13e3737 100644
--- a/src/client/entities.qc
+++ b/src/client/entities.qc
@@ -133,6 +133,9 @@ Entity_EntityUpdate(float type, float new)
 	case ENT_CONVEYOR:
 		func_conveyor_ReadEntity(new);
 		break;
+	case ENT_WAYPOINT:
+		info_waypoint_ReadEntity(new);
+		break;
 	case ENT_PUSH:
 		trigger_push_ReadEntity(new);
 		break;
diff --git a/src/gs-entbase/shared/info_waypoint.qc b/src/gs-entbase/shared/info_waypoint.qc
index dd7bcd6d..ac21b8d4 100644
--- a/src/gs-entbase/shared/info_waypoint.qc
+++ b/src/gs-entbase/shared/info_waypoint.qc
@@ -14,6 +14,16 @@
  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
 
+#define WAYPOINT_METER 52.49344f
+
+enumflags
+{
+	INFWAY_CHANGED_ORIGIN,
+	INFWAY_CHANGED_IMAGE,
+	INFWAY_CHANGED_TEXT,
+	INFWAY_CHANGED_STATE
+};
+
 /*!QUAKED info_waypoint (0 1 0) (-8 -8 -8) (8 8 8)
 # OVERVIEW
 When active, will display an icon and text at its position that can be seen
@@ -32,14 +42,31 @@ by players.
 This entity was introduced in Obsidian Conflict (2006).
 */
 class
-info_waypoint
+info_waypoint:NSPointTrigger
 {
 public:
 	void info_waypoint(void);
 
+#ifdef SERVER
+	virtual void SpawnKey(string,string);
+	virtual void Save(float);
+	virtual void Restore(string,string);
+	virtual void Input(entity,string,string);
+	virtual void Trigger(entity, triggermode_t);
+
+	virtual void EvaluateEntity(void);
+	virtual float SendEntity(entity,float);
+#endif
+
+#ifdef CLIENT
+	virtual void ReceiveEntity(float, float);
+	virtual void postdraw(void);
+#endif
+
 private:
-	string m_strIcon;
-	string m_strText;
+	PREDICTED_STRING(m_strIcon)
+	PREDICTED_STRING(m_strText)
+	PREDICTED_BOOL(m_bEnabled)
 };
 
 void
@@ -47,4 +74,188 @@ info_waypoint::info_waypoint(void)
 {
 	m_strIcon =
 	m_strText = __NULL__;
-}
\ No newline at end of file
+	m_bEnabled = false;
+}
+
+#ifdef SERVER
+void
+info_waypoint::SpawnKey(string strKey, string strValue)
+{
+	switch (strKey) {
+	case "image":
+		m_strIcon = strValue;
+		break;
+	case "text":
+		m_strText = strValue;
+		break;
+	default:
+		super::SpawnKey(strKey, strValue);
+	}
+}
+
+void
+info_waypoint::Save(float handle)
+{
+	super::Save(handle);
+	SaveString(handle, "m_strIcon", m_strIcon);
+	SaveString(handle, "m_strText", m_strText);
+	SaveBool(handle, "m_bEnabled", m_bEnabled);
+}
+
+void
+info_waypoint::Restore(string strKey, string strValue)
+{
+	switch (strKey) {
+	case "m_strIcon":
+		m_strIcon = ReadString(strValue);
+		break;
+	case "m_strText":
+		m_strText = ReadString(strValue);
+		break;
+	case "m_bEnabled":
+		m_bEnabled = ReadBool(strValue);
+		break;
+	default:
+		super::Restore(strKey, strValue);
+	}
+}
+
+void
+info_waypoint::Trigger(entity act, triggermode_t state)
+{
+	switch (state) {
+	case TRIG_OFF:
+		m_bEnabled = false;
+		break;
+	case TRIG_ON:
+		m_bEnabled = true;
+		break;
+	default:
+		m_bEnabled = m_bEnabled ? false : true;
+	}
+}
+
+void
+info_waypoint::Input(entity eAct, string strInput, string strData)
+{
+	switch (strInput) {
+	case "Enable":
+		Trigger(eAct, TRIG_ON);
+		break;
+	case "Disable":
+		Trigger(eAct, TRIG_OFF);
+		break;
+	case "Toggle":
+		Trigger(eAct, TRIG_TOGGLE);
+		break;
+	default:
+		super::Input(eAct, strInput, strData);
+	}
+}
+
+void
+info_waypoint::EvaluateEntity(void)
+{
+	EVALUATE_VECTOR(origin, 0, INFWAY_CHANGED_ORIGIN)
+	EVALUATE_VECTOR(origin, 1, INFWAY_CHANGED_ORIGIN)
+	EVALUATE_VECTOR(origin, 2, INFWAY_CHANGED_ORIGIN)
+	EVALUATE_FIELD(m_strIcon, INFWAY_CHANGED_IMAGE)
+	EVALUATE_FIELD(m_strText, INFWAY_CHANGED_TEXT)
+	EVALUATE_FIELD(m_bEnabled, INFWAY_CHANGED_STATE)
+}
+
+float
+info_waypoint::SendEntity(entity ePEnt, float flChanged)
+{
+	WriteByte(MSG_ENTITY, ENT_WAYPOINT);
+	WriteFloat(MSG_ENTITY, flChanged);
+	SENDENTITY_COORD(origin[0], INFWAY_CHANGED_ORIGIN)
+	SENDENTITY_COORD(origin[1], INFWAY_CHANGED_ORIGIN)
+	SENDENTITY_COORD(origin[2], INFWAY_CHANGED_ORIGIN)
+	SENDENTITY_STRING(m_strIcon, INFWAY_CHANGED_IMAGE)
+	SENDENTITY_STRING(m_strText, INFWAY_CHANGED_TEXT)
+	SENDENTITY_BYTE(m_bEnabled, INFWAY_CHANGED_STATE)
+	return true;
+}
+#endif
+
+#ifdef CLIENT
+void
+info_waypoint::ReceiveEntity(float flNew, float flChanged)
+{
+	READENTITY_COORD(origin[0], INFWAY_CHANGED_ORIGIN)
+	READENTITY_COORD(origin[1], INFWAY_CHANGED_ORIGIN)
+	READENTITY_COORD(origin[2], INFWAY_CHANGED_ORIGIN)
+	READENTITY_STRING(m_strIcon, INFWAY_CHANGED_IMAGE)
+	READENTITY_STRING(m_strText, INFWAY_CHANGED_TEXT)
+	READENTITY_BYTE(m_bEnabled, INFWAY_CHANGED_STATE)
+	setorigin(this, origin);
+}
+
+void
+info_waypoint::postdraw(void)
+{
+	static float
+	drawicon_visible(vector p1) {
+		vector delta;
+		float fov;
+		vector p2 = g_view.GetCameraOrigin();
+		vector ang = g_view.GetCameraAngle();
+
+		makevectors(ang);
+		delta = normalize (p1 - p2);
+		fov = delta * v_forward;
+
+		/* within field of view... */
+		if (fov > (g_view.GetAFOV()/180)) {
+			traceline(p2, p1, MOVE_WORLDONLY, self);
+			if (trace_fraction == 1.0) {
+				return (1);
+			} else {
+				return (2);
+			}
+		}
+		return (0);
+	}
+
+	float visible;
+
+	if (!m_bEnabled)
+		return;
+
+	visible = drawicon_visible(origin);
+
+	if (drawicon_visible(origin) != 0) {
+		float textLength = Font_StringWidth(m_strText, true, FONT_CON);
+		vector vecProj = project(origin) - [32, 32];
+		vector projectedPos = project(origin) - (textLength/2) + [0, 114];
+		float a = (visible == 2) ? 0.25 : 1.0f;
+		float dist = vlen(origin - g_view.GetCameraOrigin()) / WAYPOINT_METER;
+		string distText = sprintf("Distance: %d m", dist);
+
+		drawpic(vecProj, m_strIcon, [64, 64], [1,1,1], a);
+
+		Font_DrawText_RGBA(projectedPos + [1,1], distText, [0,0,0], a, FONT_CON);
+		Font_DrawText_RGBA(projectedPos, distText, [1,1,1], a, FONT_CON);
+
+		projectedPos[1] += Font_GetHeight(FONT_CON);
+
+		Font_DrawText_RGBA(projectedPos + [1,1], m_strText, [0,0,0], a, FONT_CON);
+		Font_DrawText_RGBA(projectedPos, m_strText, [1,1,1], a, FONT_CON);
+	}
+}
+
+void
+info_waypoint_ReadEntity(bool new)
+{
+	float fl;
+
+	info_waypoint rend = (info_waypoint)self;
+	if (new) {
+		spawnfunc_info_waypoint();
+	}
+
+	fl = readfloat();
+	rend.ReceiveEntity(new, fl);
+}
+#endif
\ No newline at end of file
diff --git a/src/shared/entities.h b/src/shared/entities.h
index f5cee000..3914b7f9 100644
--- a/src/shared/entities.h
+++ b/src/shared/entities.h
@@ -45,8 +45,9 @@ typedef enum
 	ENT_VEH_4WHEEL,	/**< of type prop_vehicle_driveable */
 	ENT_PROPROPE,	/**< of type prop_rope */
 	ENT_BUBBLES,	/**< of type env_bubbles */
-	ENT_CONVEYOR,
-	ENT_PUSH,
+	ENT_CONVEYOR,	/**< of type func_conveyor */
+	ENT_WAYPOINT,	/**< of type info_waypoint */
+	ENT_PUSH,		/**< of type trigger_push */
 	ENT_SEPARATOR,	/**< This is a separator. This separator is used by you to add game-specific networked entities. When declaring your own entity-update types, you want the first value to equal ENT_SEPARATOR at all times to ensure you'll not be overriding existing slots. */
 } entupdate_t;