/*
	sv_ents.c

	(description)

	Copyright (C) 1996-1997  Id Software, Inc.

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

	See the GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to:

		Free Software Foundation, Inc.
		59 Temple Place - Suite 330
		Boston, MA  02111-1307, USA

*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif

#include "QF/cvar.h"
#include "QF/msg.h"
#include "QF/sys.h"

#include "qw/msg_ucmd.h"

#include "compat.h"
#include "server.h"
#include "sv_progs.h"


/*
	The PVS must include a small area around the client to allow head
	bobbing or other small motion on the client side.  Otherwise, a bob
	might cause an entity that should be visible to not show up, especially
	when the bob crosses a waterline.
*/

byte		fatpvs[MAX_MAP_LEAFS / 8];
int			fatbytes;


static void
SV_AddToFatPVS (vec3_t org, mnode_t *node)
{
	byte	   *pvs;
	int			i;
	float		d;
	plane_t    *plane;

	while (1) {
		// if this is a leaf, accumulate the pvs bits
		if (node->contents < 0) {
			if (node->contents != CONTENTS_SOLID) {
				pvs = Mod_LeafPVS ((mleaf_t *) node, sv.worldmodel);
				for (i = 0; i < fatbytes; i++)
					fatpvs[i] |= pvs[i];
			}
			return;
		}

		plane = node->plane;
		d = DotProduct (org, plane->normal) - plane->dist;
		if (d > 8)
			node = node->children[0];
		else if (d < -8)
			node = node->children[1];
		else {							// go down both
			SV_AddToFatPVS (org, node->children[0]);
			node = node->children[1];
		}
	}
}

/*
	SV_FatPVS

	Calculates a PVS that is the inclusive or of all leafs within 8 pixels
	of the given point.
*/
static byte *
SV_FatPVS (vec3_t org)
{
	fatbytes = (sv.worldmodel->numleafs + 31) >> 3;
	memset (fatpvs, 0, fatbytes);
	SV_AddToFatPVS (org, sv.worldmodel->nodes);
	return fatpvs;
}

// nails are plentiful, so there is a special network protocol for them
#define	MAX_NAILS	32
edict_t	   *nails[MAX_NAILS];
int			numnails;
int         nailcount;


static qboolean
SV_AddNailUpdate (edict_t *ent)
{
	if (SVfloat (ent, modelindex) != sv_nailmodel
		&& SVfloat (ent, modelindex) != sv_supernailmodel)
		return false;
	if (numnails == MAX_NAILS)
		return true;
	nails[numnails] = ent;
	numnails++;
	return true;
}

static void
SV_EmitNailUpdate (sizebuf_t *msg, qboolean recorder)
{
	byte	   *buf;				// [48 bits] xyzpy 12 12 12 4 8
	int			n, p, x, y, z, yaw;
	int         bpn = recorder ? 7 : 6;	// bytes per nail
	edict_t	   *ent;

	if (!numnails)
		return;

	buf =  SZ_GetSpace (msg, numnails * bpn + 2);
	*buf++ = recorder ? svc_nails2 : svc_nails;
	*buf++ = numnails;

	for (n = 0; n < numnails; n++) {
		ent = nails[n];
		if (recorder) {
			if (!SVfloat (ent, colormap)) {
				if (!((++nailcount) & 255))
					nailcount++;
				SVfloat (ent, colormap) = nailcount&255;
			}
			*buf++ = (byte)SVfloat (ent, colormap);
		}

		x   = ((int) (SVvector (ent, origin)[0] + 4096 + 1) >> 1) & 0xfff;
		y   = ((int) (SVvector (ent, origin)[1] + 4096 + 1) >> 1) & 0xfff;
		z   = ((int) (SVvector (ent, origin)[2] + 4096 + 1) >> 1) & 0xfff;
		p   =  (int) (SVvector (ent, angles)[0] * (16.0 / 360.0)) & 0x00f;
		yaw = (int) (SVvector (ent, angles)[1] * (256.0 / 360.0)) & 0x0ff;

		*buf++ = x;
		*buf++ = (x >> 8) | (y << 4);
		*buf++ = (y >> 4);
		*buf++ = z;
		*buf++ = (z >> 8) | (p << 4);
		*buf++ = yaw;
	}
}

/*
	SV_WriteDelta

	Writes part of a packetentities message.
	Can delta from either a baseline or a previous packet_entity
*/
static void
SV_WriteDelta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg,
			   qboolean force, int stdver)
{
	int			bits, i;
	float		miss;

	// send an update
	bits = 0;

	for (i = 0; i < 3; i++) {
		miss = to->origin[i] - from->origin[i];
		if (miss < -0.1 || miss > 0.1)
			bits |= U_ORIGIN1 << i;
	}

	if (to->angles[0] != from->angles[0])
		bits |= U_ANGLE1;

	if (to->angles[1] != from->angles[1])
		bits |= U_ANGLE2;

	if (to->angles[2] != from->angles[2])
		bits |= U_ANGLE3;

	if (to->colormap != from->colormap)
		bits |= U_COLORMAP;

	if (to->skinnum != from->skinnum)
		bits |= U_SKIN;

	if (to->frame != from->frame)
		bits |= U_FRAME;

	if ((to->effects & 0xff) != (from->effects & 0xff))
		bits |= U_EFFECTS;

	if (to->modelindex != from->modelindex)
		bits |= U_MODEL;

	// LordHavoc: cleaned up Endy's coding style, and added missing effects
// Ender (QSG - Begin)
	if (stdver > 1) {
		if (to->alpha != from->alpha)
			bits |= U_ALPHA;

		if (to->scale != from->scale)
			bits |= U_SCALE;

		if ((to->effects & 0xff00) != (from->effects & 0xff00))
			bits |= U_EFFECTS2;

		if (to->glow_size != from->glow_size)
			bits |= U_GLOWSIZE;

		if (to->glow_color != from->glow_color)
			bits |= U_GLOWCOLOR;

		if (to->colormod != from->colormod)
			bits |= U_COLORMOD;

		if (to->frame > 255)
			bits |= U_EFFECTS2;
	}

	if (bits >= 16777216)
		bits |= U_EXTEND2;

	if (bits >= 65536)
		bits |= U_EXTEND1;
// Ender (QSG - End)
	if (bits & 511)
		bits |= U_MOREBITS;

	if (to->flags & U_SOLID)
		bits |= U_SOLID;

	// write the message
	if (!to->number)
		Sys_Error ("Unset entity number");
	if (to->number >= 512)
		Sys_Error ("Entity number >= 512");

	if (!bits && !force)
		return;							// nothing to send!
	i = to->number | (bits & ~511);
	if (i & U_REMOVE)
		Sys_Error ("U_REMOVE");
	MSG_WriteShort (msg, i);

	if (bits & U_MOREBITS)
		MSG_WriteByte (msg, bits & 255);

	// LordHavoc: cleaned up Endy's tabs
// Ender (QSG - Begin)
	if (bits & U_EXTEND1)
		MSG_WriteByte (msg, bits >> 16);
	if (bits & U_EXTEND2)
		MSG_WriteByte (msg, bits >> 24);
// Ender (QSG - End)

	if (bits & U_MODEL)
		MSG_WriteByte (msg, to->modelindex);
	if (bits & U_FRAME)
		MSG_WriteByte (msg, to->frame);
	if (bits & U_COLORMAP)
		MSG_WriteByte (msg, to->colormap);
	if (bits & U_SKIN)
		MSG_WriteByte (msg, to->skinnum);
	if (bits & U_EFFECTS)
		MSG_WriteByte (msg, to->effects);
	if (bits & U_ORIGIN1)
		MSG_WriteCoord (msg, to->origin[0]);
	if (bits & U_ANGLE1)
		MSG_WriteAngle (msg, to->angles[0]);
	if (bits & U_ORIGIN2)
		MSG_WriteCoord (msg, to->origin[1]);
	if (bits & U_ANGLE2)
		MSG_WriteAngle (msg, to->angles[1]);
	if (bits & U_ORIGIN3)
		MSG_WriteCoord (msg, to->origin[2]);
	if (bits & U_ANGLE3)
		MSG_WriteAngle (msg, to->angles[2]);

	// LordHavoc: cleaned up Endy's tabs, rearranged bytes, and implemented
	// missing effects
// Ender (QSG - Begin)
	if (bits & U_ALPHA)
		MSG_WriteByte (msg, to->alpha);
	if (bits & U_SCALE)
		MSG_WriteByte (msg, to->scale);
	if (bits & U_EFFECTS2)
		MSG_WriteByte (msg, (to->effects >> 8));
	if (bits & U_GLOWSIZE)
		MSG_WriteByte (msg, to->glow_size);
	if (bits & U_GLOWCOLOR)
		MSG_WriteByte (msg, to->glow_color);
	if (bits & U_COLORMOD)
		MSG_WriteByte (msg, to->colormod);
	if (bits & U_FRAME2)
		MSG_WriteByte (msg, (to->frame >> 8));
// Ender (QSG - End)
}

/*
	SV_EmitPacketEntities

	Writes a delta update of a packet_entities_t to the message.
*/
static void
SV_EmitPacketEntities (delta_t *delta, packet_entities_t *to, sizebuf_t *msg,
					   int stdver)
{
	int			newindex, oldindex, newnum, oldnum, oldmax;
	edict_t	   *ent;
	client_frame_t *fromframe;
	packet_entities_t *from;

	// this is the frame that we are going to delta update from
	if (delta->delta_sequence != -1) {
		fromframe = &delta->frames[delta->delta_sequence & UPDATE_MASK];
		from = &fromframe->entities;
		oldmax = from->num_entities;

		MSG_WriteByte (msg, svc_deltapacketentities);
		MSG_WriteByte (msg, delta->delta_sequence);
	} else {
		oldmax = 0;						// no delta update
		from = NULL;

		MSG_WriteByte (msg, svc_packetentities);
	}

	newindex = 0;
	oldindex = 0;
//	SV_Printf ("---%i to %i ----\n", delta->delta_sequence & UPDATE_MASK,
//			   delta->netchan.outgoing_sequence & UPDATE_MASK);
	while (newindex < to->num_entities || oldindex < oldmax) {
		newnum = newindex >= to->num_entities ? 9999 :
			to->entities[newindex].number;
		oldnum = oldindex >= oldmax ? 9999 : from->entities[oldindex].number;

		if (newnum == oldnum) {			// delta update from old position
//			SV_Printf ("delta %i\n", newnum);
			SV_WriteDelta (&from->entities[oldindex], &to->entities[newindex],
						   msg, false, stdver);
			oldindex++;
			newindex++;
			continue;
		}

		if (newnum < oldnum) {
			// this is a new entity, send it from the baseline
			if (newnum == 9999) {
				Sys_Printf ("LOL, %d, %d, %d, %d %d %d\n", newnum, oldnum,
							to->num_entities, oldmax,
							delta->in_frame,
							delta->delta_sequence & UPDATE_MASK);
				if (!delta->client)
					Sys_Printf ("demo\n");
			}
			ent = EDICT_NUM (&sv_pr_state, newnum);
//			SV_Printf ("baseline %i\n", newnum);
			SV_WriteDelta (&SVdata (ent)->state, &to->entities[newindex], msg,
						   true, stdver);
			newindex++;
			continue;
		}

		if (newnum > oldnum) {			// the old entity isn't present in
										// the new message
//			SV_Printf ("remove %i\n", oldnum);
			MSG_WriteShort (msg, oldnum | U_REMOVE);
			oldindex++;
			continue;
		}
	}

	MSG_WriteShort (msg, 0);			// end of packetentities
}

static void
write_demoplayer (delta_t *delta, plent_state_t *from, plent_state_t *to,
				  sizebuf_t *msg, int mask, int full)
{
	int         flags;
	int         j;

	flags = to->flags >> 1;		// convert PF_(GIB|DEAD) to DF_(GIB|DEAD)
	flags &= DF_GIB | DF_DEAD;	// PF_MSEC and PF_COMMAND aren't wanted
	if (full) {
		flags |= DF_ORIGIN | (DF_ORIGIN << 1) | (DF_ORIGIN << 2)
				| DF_ANGLES | (DF_ANGLES << 1) | (DF_ANGLES << 2)
				| DF_EFFECTS | DF_SKINNUM | DF_WEAPONFRAME | DF_MODEL;
	} else {
		for (j = 0; j < 3; j++)
			if (from->origin[j] != to->origin[j])
				flags |= DF_ORIGIN << j;

		for (j = 0; j < 3; j++)
			if (from->cmd.angles[j] != to->cmd.angles[j])
				flags |= DF_ANGLES << j;

		if (from->modelindex != to->modelindex)
			flags |= DF_MODEL;
		if ((from->effects & 0xff) != (to->effects & 0xff))
			flags |= DF_EFFECTS;
		if (from->skinnum != to->skinnum)
			flags |= DF_SKINNUM;
		if (from->weaponframe != to->weaponframe)
			flags |= DF_WEAPONFRAME;
	}

	MSG_WriteByte (msg, svc_playerinfo);
	MSG_WriteByte (msg, to->number);
	MSG_WriteShort (msg, flags);

	MSG_WriteByte (msg, to->frame);

	for (j = 0; j < 3; j++)
		if (flags & (DF_ORIGIN << j))
			MSG_WriteCoord (msg, to->origin[j]);

	for (j = 0; j < 3; j++)
		if (flags & (DF_ANGLES << j))
			MSG_WriteAngle16 (msg, to->cmd.angles[j]);

	if (flags & DF_MODEL)
		MSG_WriteByte (msg, to->modelindex);

	if (flags & DF_SKINNUM)
		MSG_WriteByte (msg, to->skinnum);

	if (flags & DF_EFFECTS)
		MSG_WriteByte (msg, to->effects);

	if (flags & DF_WEAPONFRAME)
		MSG_WriteByte (msg, to->weaponframe);
}

static void
write_player (delta_t *delta, plent_state_t *from, plent_state_t *to,
			  sizebuf_t *msg, int mask, int full)
{
	int         flags = to->flags;
	int         qf_bits = 0;
	int         i;
	int         ds = delta->delta_sequence & 0x7f;

	if (full) {
		flags |= PF_VELOCITY1 | PF_VELOCITY2 | PF_VELOCITY3 | PF_MODEL
				| PF_SKINNUM | PF_EFFECTS | PF_WEAPONFRAME;
		if (full > 1) {
			qf_bits = PF_ALPHA | PF_SCALE | PF_EFFECTS2 | PF_GLOWSIZE
					  | PF_GLOWCOLOR | PF_COLORMOD | PF_FRAME2;
			flags |= PF_QF;
		}
		ds = -1;
	} else {
		for (i = 0; i < 3; i++)
			if (from->velocity[i] != to->velocity[i])
				flags |= PF_VELOCITY1 << i;
		if (from->modelindex != to->modelindex)
			flags |= PF_MODEL;
		if (from->skinnum != to->skinnum)
			flags |= PF_SKINNUM;
		if ((from->effects & 0xff) != (to->effects &0xff))
			flags |= PF_EFFECTS;
		if (from->weaponframe != to->weaponframe)
			flags |= PF_WEAPONFRAME;

		if (from->alpha != to->alpha)
			qf_bits |= PF_ALPHA;
		if (from->scale != to->scale)
			qf_bits |= PF_SCALE;
		if ((from->effects & 0xff00) != (to->effects & 0xff00))
			qf_bits |= PF_EFFECTS2;
		if (from->glow_size != to->glow_size)
			qf_bits |= PF_GLOWSIZE;
		if (from->glow_color != to->glow_color)
			qf_bits |= PF_GLOWCOLOR;
		if (from->colormod != to->colormod)
			qf_bits |= PF_COLORMOD;
		if ((from->frame & 0xff00) != (to->frame & 0xff00))
			qf_bits |= PF_FRAME2;
		if (qf_bits)
			flags |= PF_QF;
	}

	flags &= mask;

	MSG_WriteByte (msg, svc_playerinfo);
	if (delta->type == dt_tp_qtv)
		MSG_WriteByte (msg, ds);
	MSG_WriteByte (msg, to->number);
	MSG_WriteShort (msg, flags);

	MSG_WriteCoordV (msg, to->origin);

	MSG_WriteByte (msg, to->frame);

	if (flags & PF_MSEC)
		MSG_WriteByte (msg, to->msec);
	if (flags & PF_COMMAND)
		MSG_WriteDeltaUsercmd (msg, &from->cmd, &to->cmd);
	for (i = 0; i < 3; i++)
		if (flags & (PF_VELOCITY1 << i))
			MSG_WriteShort (msg, to->velocity[i]);
	if (flags & PF_MODEL)
		MSG_WriteByte (msg, to->modelindex);
	if (flags & PF_SKINNUM)
		MSG_WriteByte (msg, to->skinnum);
	if (flags & PF_EFFECTS)
		MSG_WriteByte (msg, to->effects);
	if (flags & PF_WEAPONFRAME)
		MSG_WriteByte (msg, to->weaponframe);

	if (flags & PF_QF) {
		MSG_WriteByte (msg, qf_bits);
		if (qf_bits & PF_ALPHA)
			MSG_WriteByte (msg, to->alpha);
		if (qf_bits & PF_SCALE)
			MSG_WriteByte (msg, to->scale);
		if (qf_bits & PF_EFFECTS2)
			MSG_WriteByte (msg, to->effects >> 8);
		if (qf_bits & PF_GLOWSIZE)
			MSG_WriteByte (msg, to->glow_size);
		if (qf_bits & PF_GLOWCOLOR)
			MSG_WriteByte (msg, to->glow_color);
		if (qf_bits & PF_COLORMOD)
			MSG_WriteByte (msg, to->colormod);
		if (qf_bits & PF_FRAME2)
			MSG_WriteByte (msg, to->frame);
	}
}

static void
SV_WritePlayersToClient (delta_t *delta, byte *pvs, sizebuf_t *msg)
{
	int			j, k;
	client_t   *cl;
	edict_t    *clent = 0;
	int         spec_track = 0;
	int         stdver = 2, full = stdver;
	edict_t	   *ent;
	edict_leaf_t *el;
	packet_players_t *pack;
	client_frame_t *frame = &delta->frames[delta->in_frame];
	packet_players_t *from_pack = 0;
	plent_state_t dummy_player_state, *state = &dummy_player_state;
	static plent_state_t null_player_state;
	void (*write) (delta_t *, plent_state_t *, plent_state_t *, sizebuf_t *,
				   int, int);

	if (!null_player_state.alpha) {
		null_player_state.alpha = 255;
		null_player_state.scale = 16;
		null_player_state.glow_size = 0;
		null_player_state.glow_color = 254;
		null_player_state.colormod = 255;
	}
	null_player_state.modelindex = 0;

	if (delta->client) {
		clent = delta->client->edict;
		spec_track = delta->client->spec_track;
		stdver = delta->client->stdver;
		null_player_state.modelindex = sv_playermodel;
		full = 0;		// normal qw clients don't get real deltas on players
	}

	write = write_player;
	if (!clent && delta->type == dt_tp_demo)
		write = write_demoplayer;

	if (delta->delta_sequence != -1) {
		client_frame_t *fromframe;
		fromframe = &delta->frames[delta->delta_sequence & UPDATE_MASK];
		from_pack = &fromframe->players;
		full = 0;
	}

	pack = &frame->players;
	pack->num_players = 0;

	for (j = k = 0, cl = svs.clients; j < MAX_CLIENTS; j++, cl++) {
		int         mask = ~PF_WEAPONFRAME;

		if (cl->state != cs_spawned && cl->state != cs_server)
			continue;

		ent = cl->edict;

		// ZOID visibility tracking
		// self or the the ent being tracked as a spectator always gets sent
		if (ent != clent && !(spec_track && spec_track - 1 == j)) {
			if (cl->spectator)
				continue;

			if (pvs) {
				// ignore if not touching a PV leaf
				for (el = SVdata (ent)->leafs; el; el = el->next) {
					if (pvs[el->leafnum >> 3] & (1 << (el->leafnum & 7)))
						break;
				}
				if (!el)
					continue;				// not visible
			}
		}

		if (pack->players)
			state = &pack->players[pack->num_players];
		pack->num_players++;

		state->number = j;
		state->flags = 0;
		VectorCopy (SVvector (ent, origin), state->origin);

		state->msec = min (255, 1000 * (sv.time - cl->localtime));

		state->cmd = cl->lastcmd;
		if (SVfloat (ent, health) <= 0) {	// don't show the corpse
											// looking around...
			state->cmd.angles[0] = 0;
			state->cmd.angles[1] = SVvector (ent, angles)[1];
			state->cmd.angles[0] = 0;
		}

		VectorCopy (SVvector (ent, velocity), state->velocity);
		state->modelindex = SVfloat (ent, modelindex);
		state->frame = SVfloat (ent, frame);
		state->skinnum = SVfloat (ent, skin);
		state->effects = SVfloat (ent, effects);
		state->weaponframe = SVfloat (ent, weaponframe);

		if (SVfloat (ent, health) <= 0)
			state->flags |= PF_DEAD;
		if (SVvector (ent, mins)[2] != -24)
			state->flags |= PF_GIB;
		state->flags |= PF_MSEC | PF_COMMAND;

		state->alpha = 255;
		state->scale = 16;
		state->glow_size = 0;
		state->glow_color = 254;
		state->colormod = 255;
		if (sv_fields.alpha != -1 && SVfloat (ent, alpha)) {
			float       alpha = SVfloat (ent, alpha);
			state->alpha = bound (0, alpha, 1) * 255.0;
		}
		if (sv_fields.scale != -1 && SVfloat (ent, scale)) {
			float       scale = SVfloat (ent, scale);
			state->scale = bound (0, scale, 15.9375) * 16;
		}
		if (sv_fields.glow_size != -1 && SVfloat (ent, glow_size)) {
			int         glow_size = SVfloat (ent, glow_size);
			state->glow_size = bound (-1024, glow_size, 1016) >> 3;
		}
		if (sv_fields.glow_color != -1 && SVfloat (ent, glow_color))
			state->glow_color = SVfloat (ent, glow_color);
		if (sv_fields.colormod != -1
			&& !VectorIsZero (SVvector (ent, colormod))) {
			float      *colormod= SVvector (ent, colormod);
			state->colormod = ((int) (bound (0, colormod[0], 1) * 7) << 5) |
							  ((int) (bound (0, colormod[1], 1) * 7) << 2) |
							   (int) (bound (0, colormod[2], 1) * 3);
		}

		if (cl->spectator) {
			// send only origin and velocity of spectators
			mask &= PF_VELOCITY1 | PF_VELOCITY2 | PF_VELOCITY3;
		} else if (ent == clent) {
			// don't send a lot of data on personal entity
			mask &= ~(PF_MSEC | PF_COMMAND);
			mask |= PF_WEAPONFRAME;
		}

		if (spec_track && spec_track - 1 == j)
			mask |= PF_WEAPONFRAME;

		if (from_pack && from_pack->players) {
			while (k < from_pack->num_players
				   && from_pack->players[k].number < state->number)
				k++;
			if (k < from_pack->num_players
				&& from_pack->players[k].number == state->number) {
				write (delta, &from_pack->players[k], state, msg, mask, full);
				continue;
			}
			full = 1;
		}
		write (delta, &null_player_state, state, msg, mask, full);
	}
}

static inline byte *
calc_pvs (delta_t *delta)
{
	byte       *pvs = 0;
	vec3_t      org;

	// find the client's PVS
	if (delta->pvs == dt_pvs_normal) {
		edict_t *clent = delta->client->edict;
		VectorAdd (SVvector (clent, origin), SVvector (clent, view_ofs), org);
		pvs = SV_FatPVS (org);
	} else if (delta->pvs == dt_pvs_fat) {
		// when recording a demo, send only entities that can be seen. Can help
		// shrink the mvd at expense of effectively lossy compressio (ents that
		// can't be seen by a player either won't get updated or will disappear
		// for people watching the mvd from viewpoints with no players around
		// (not sure yet:).
		client_t   *cl;
		int         i;

		for (i=0, cl = svs.clients; i<MAX_CLIENTS; i++, cl++) {
			if (cl->state != cs_spawned && cl->state != cs_server)
				continue;

			if (cl->spectator)
				continue;

			VectorAdd (SVvector (cl->edict, origin),
					   SVvector (cl->edict, view_ofs), org);
			if (pvs == NULL) {
				pvs = SV_FatPVS (org);
			} else {
				SV_AddToFatPVS (org, sv.worldmodel->nodes);
			}
		}
	}
	return pvs;
}

/*
	SV_WriteEntitiesToClient

	Encodes the current state of the world as
	a svc_packetentities messages and possibly
	a svc_nails message and
	svc_playerinfo messages
*/
void
SV_WriteEntitiesToClient (delta_t *delta, sizebuf_t *msg)
{
	byte	   *pvs = 0;
	int			e, num_edicts;
	int         max_packet_entities = MAX_DEMO_PACKET_ENTITIES;
	int         stdver = 1;
	client_frame_t *frame;
	edict_t	   *ent;
	edict_leaf_t *el;
	entity_state_t *state;
	packet_entities_t *pack;

	if (delta->client) {
		max_packet_entities = MAX_PACKET_ENTITIES;
		stdver = delta->client->stdver;
	}

	pvs = calc_pvs (delta);

	// send over the players in the PVS
	SV_WritePlayersToClient (delta, pvs, msg);

	// this is the frame we are creating
	frame = &delta->frames[delta->in_frame];

	// put other visible entities into either a packet_entities or a nails
	// message
	pack = &frame->entities;
	pack->num_entities = 0;

	numnails = 0;

	num_edicts = min (sv.num_edicts, 512);	//FIXME stupid protocol limit

	for (e = MAX_CLIENTS + 1, ent = EDICT_NUM (&sv_pr_state, e);
		 e < num_edicts; e++, ent = NEXT_EDICT (&sv_pr_state, ent)) {
		if (ent->free)
			continue;

		// ignore ents without visible models
		if (!SVfloat (ent, modelindex)
			|| !*PR_GetString (&sv_pr_state, SVstring (ent, model)))
			continue;

		if (pvs) {
			// ignore if not touching a PV leaf
			for (el = SVdata (ent)->leafs; el; el = el->next) {
				if (pvs[el->leafnum >> 3] & (1 << (el->leafnum & 7)))
					break;
			}
			if (!el)
				continue;				// not visible
		}

		if (SV_AddNailUpdate (ent))
			continue;					// added to the special update list

		// add to the packetentities
		if (pack->num_entities == max_packet_entities) {
			continue;					// all full
		}

		state = &pack->entities[pack->num_entities];
		pack->num_entities++;

		state->number = e;
		state->flags = 0;
		VectorCopy (SVvector (ent, origin), state->origin);
		VectorCopy (SVvector (ent, angles), state->angles);
		state->modelindex = SVfloat (ent, modelindex);
		state->frame = SVfloat (ent, frame);
		state->colormap = SVfloat (ent, colormap);
		state->skinnum = SVfloat (ent, skin);
		state->effects = SVfloat (ent, effects);

		// LordHavoc: cleaned up Endy's coding style, shortened the code,
		// and implemented missing effects
// Ender: EXTEND (QSG - Begin)
		{
			state->alpha = 255;
			state->scale = 16;
			state->glow_size = 0;
			state->glow_color = 254;
			state->colormod = 255;

			if (sv_fields.alpha != -1 && SVfloat (ent, alpha))
				state->alpha = bound (0, SVfloat (ent, alpha), 1) * 255.0;

			if (sv_fields.scale != -1 && SVfloat (ent, scale))
				state->scale = bound (0, SVfloat (ent, scale), 15.9375) * 16.0;

			if (sv_fields.glow_size != -1 && SVfloat (ent, glow_size))
				state->glow_size =
					bound (-1024, (int) SVfloat (ent, glow_size), 1016) >> 3;

			if (sv_fields.glow_color != -1 && SVfloat (ent, glow_color))
				state->glow_color = (int) SVfloat (ent, glow_color);

			if (sv_fields.colormod != -1 && SVvector (ent, colormod)[0]
				&& SVvector (ent, colormod)[1] && SVvector (ent, colormod)[2])
				state->colormod =
					((int) (bound (0, SVvector (ent, colormod)[0], 1) * 7.0)
					 << 5) |
					((int) (bound (0, SVvector (ent, colormod)[1], 1) * 7.0)
					 << 2) |
					(int) (bound (0, SVvector (ent, colormod)[2], 1) * 3.0);
		}
// Ender: EXTEND (QSG - End)
	}

	// encode the packet entities as a delta from the
	// last packetentities acknowledged by the client
	SV_EmitPacketEntities (delta, pack, msg, stdver);

	// now add the specialized nail update
	SV_EmitNailUpdate (msg, !delta->client);
}
