//-----------------------------------------------------------------------------
//
//  $Logfile:: /Quake 2 Engine/Sin/code/game/entity.cpp                       $
// $Revision:: 296                                                            $
//   $Author:: Jimdose                                                        $
//     $Date:: 11/18/98 5:22a                                                 $
//
// Copyright (C) 1997 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// $Log:: /Quake 2 Engine/Sin/code/game/entity.cpp                            $
// 
// 296   11/18/98 5:22a Jimdose
// CheckGround properly checks normal an alternate grav axis
// 
// 295   11/18/98 3:03a Jimdose
// made EV_Mutate a cheat
// 
// 294   11/18/98 12:22a Jimdose
// fixed fullmins and fullmaxs when gravaxis is non-0 in setSize
// 
// 293   11/15/98 11:31p Markd
// make sure all our children are deleted when removing
// 
// 292   11/14/98 5:29p Jimdose
// Made BroadcastSound search through the SentientList instead of using
// findradius.  This way, fewer entities are checked.
// 
// 291   11/13/98 10:20p Aldie
// Fix crash bug in attachevent
// 
// 290   11/13/98 1:47a Markd
// Don't set gravaxis if the same as before
// 
// 289   11/10/98 8:03p Jimdose
// made setSize and SetGravityAxis change fullmins and fullmaxs appropriately
// 
// 288   11/08/98 6:33p Jimdose
// added FLAG_IGNORE to the flag modifaction functions, making sure that
// illegal values don't cause flags to be modified
// 
// 287   11/04/98 8:40p Jimdose
// Made it so the Flags event will not screw the flags if the string is not
// recognized.
// 
// 286   10/26/98 9:41p Markd
// Fixed gotkill message not passing in gibbed parameter
// 
// 285   10/26/98 4:26p Aldie
// Added ghost command for models that are notsolid, and notvisible, but still
// need to get sent over.
// 
// 284   10/25/98 4:38a Aldie
// Moved link()
// 
// 283   10/25/98 12:01a Markd
// put in censored support
// 
// 282   10/24/98 5:43p Markd
// Fixed IfSkillEvent
// 
// 281   10/24/98 12:42a Markd
// changed origins to worldorigins where appropriate
// 
// 280   10/23/98 11:50p Jimdose
// fixed usagage of model as a temporary variable in AttachModelEvent
// 
// 279   10/23/98 5:38a Jimdose
// Added SetMassEvent
// 
// 278   10/22/98 4:57p Aldie
// Removed blastscale_z values
// 
// 277   10/22/98 1:40a Markd
// Added stealth mode
// 
// 276   10/21/98 1:29a Jimdose
// fixed crash bug with binding/unbinding and teams
// 
// 275   10/20/98 11:29p Markd
// Revamped Broadcast sound
// 
// 274   10/20/98 4:00p Aldie
// Put in teammaster checkwhen unbinding
// 
// 273   10/20/98 3:32a Jimdose
// Made setSolidType not error out when loading savegames
// added isBoundTo to test is an entity is affected by another entity via
// binding
// binding is no longer order dependent
// 
// 272   10/19/98 8:55p Markd
// Put check in setSolidType
// 
// 271   10/19/98 5:29p Aldie
// Zero out total_delta when stop animating is called
// 
// 270   10/19/98 12:07a Jimdose
// made all code use fast checks for inheritance (no text lookups when
// possible)
// isSubclassOf no longer requires ::_classinfo()
// 
// 269   10/18/98 8:40p Jimdose
// Added GetEntName
// Made setModel check if it's SOLID_BSP when it has no model
// 
// 268   10/17/98 11:02p Markd
// Added ifskill
// 
// 267   10/17/98 8:11p Jimdose
// Changed Damage to DamgeEvent
// 
// 266   10/15/98 3:39p Markd
// added forcefield ability 
// 
// 265   10/14/98 10:21p Markd
// Added debug code for AnimateFrame
// 
// 264   10/13/98 11:13p Markd
// Added hurt and mutate support
// 
// 263   10/13/98 5:25p Markd
// Added UseBoundingBoxEvent
// 
// 262   10/11/98 8:50p Jimdose
// Added RandomGlobalEntitySound and RandomGlobalEntitySoundEvent
// 
// 261   10/10/98 10:37p Markd
// made it so that targetnames with "$" could not be entered
// 
// 260   10/10/98 9:13p Markd
// Took out SetAliasPrefix
// 
// 259   10/10/98 3:24a Jimdose
// changed team to moveteam
// 
// 258   10/09/98 10:18p Jimdose
// made setSize check mins and maxs in the edict instead of the shadow
// variables in Entity to check if it should change the size
// 
// 257   10/09/98 8:58p Aldie
// Move air_finished to player
// 
// 256   10/09/98 8:02p Jimdose
// made SetModel only post processinitcommands when LoadingSavegame is false
// 
// 255   10/08/98 7:40p Aldie
// Added minlight
// 
// 254   10/08/98 7:25p Aldie
// minlight, gravity, lightoffset
// 
// 253   10/07/98 11:46p Jimdose
// Disabled ProcessInitCommands when loading savegames
// made setModel use str instead of char array
// 
// 252   10/06/98 9:59p Aldie
// Added an oxygenator
// 
// 251   10/06/98 9:39p Markd
// removed last_origin
// 
// 250   10/05/98 11:30p Markd
// Changed sound radius to header
// 
// 249   10/04/98 10:28p Aldie
// Added multiple weapon changes.  Damage, flashes, quantum stuff
// 
// 248   10/03/98 1:09p Aldie
// Added rendereffects flag
// 
// 247   10/02/98 10:59p Jimdose
// Added SetEntNum
// 
// 246   10/01/98 7:59p Markd
// Made dialog use NO_PHS
// 
// 245   9/23/98 11:00p Markd
// put in some garbage collection on stuff that wasn't freed up
// 
// 244   9/22/98 5:11p Jimdose
// Changed the radius of most of the sound events
// 
// 243   9/22/98 3:21p Markd
// put in parentmode lockout for blood and gibs
// 
// 242   9/22/98 2:59p Aldie
// Added effects command
// 
// 241   9/20/98 7:11p Aldie
// Added flags to particles
// 
// 240   9/20/98 3:45p Markd
// Changed default dialog channel from CHAN_DIALOG to CHAN_DIALOG_SECONDARY
// 
// 239   9/17/98 6:19p Jimdose
// Made BroadcastSound use centroid instead of worldorigin for basing it's
// radius and phs checks
// 
// 238   9/17/98 10:50a Markd
// fixed gun entity not being shown
// 
// 237   9/15/98 6:37p Markd
// Added RotatedBounds flag support
// 
// 236   9/12/98 11:27a Markd
// fixed check in Animateframe to go to the next animation
// 
// 235   9/11/98 11:25p Markd
// cleared out last_animation_time when stopanimating is called
// 
// 234   9/09/98 6:49p Markd
// forgot to check to see if num_frames_in_gun_anim was greater than one
// 
// 233   9/09/98 6:45p Markd
// put in world weapon model animations
// 
// 232   9/08/98 11:31p Jimdose
// Added AnimEvent
// 
// 231   9/08/98 8:11p Markd
// Fixed Animation timing so frames weren't skipped
// 
// 230   9/07/98 8:28p Markd
// Added fullradius
// 
// 229   8/29/98 9:40p Jimdose
// Added call info to G_Trace
// 
// 228   8/29/98 5:27p Markd
// added specialfx, replaced misc with specialfx where appropriate
// 
// 227   8/28/98 7:03p Markd
// Fixed Broadcast Sound
// 
// 226   8/28/98 4:13p Markd
// Increased VoiceSound radius
// 
// 225   8/27/98 9:04p Jimdose
// Moved a lot of small functions to the header as inline
// Made Centroid a variable
// 
// 224   8/24/98 6:51p Jimdose
// Added SetGravityAxis
// 
// 223   8/22/98 9:36p Jimdose
// Renamed spawn arg 
// 
// 222   8/22/98 8:55p Jimdose
// Added support for alternate gravity axis
// 
// 221   8/21/98 3:48p Markd
// Added new group "all" behavior
// 
// 220   8/19/98 8:48p Aldie
// Reduce lag for weapons and fixed assertion
// 
// 219   8/18/98 11:08p Markd
// Added new Alias System
// 
// 218   8/18/98 11:12a Markd
// Added "skin" event
// 
// 217   8/15/98 5:30p Jimdose
// Made RegisterAlias and RegisterAliasAndCache use a str for storing the name
// so we don't have any name length crashes
// 
// 216   8/09/98 5:52p Markd
// Attached models are ThrowObjects by default
// 
// 215   8/08/98 8:36p Markd
// forgot to return a value in attach function
// 
// 214   8/08/98 8:27p Markd
// Added attach and bind check for self binding/attaching
// 
// 213   8/08/98 7:50p Jimdose
// changed realWorld to world
// 
// 212   8/07/98 4:21p Aldie
// Allow kill from the console.
// 
// 211   8/06/98 10:53p Aldie
// Added weapon tweaks and kickback.  Also modified blast radius damage and
// rocket jumping.
// 
// 210   8/04/98 6:05p Aldie
// Added RF_DETAIL flag and removed some dead code
// 
// 209   7/31/98 8:08p Jimdose
// Script commands now include flags to indicate cheats and console commands
// 
// 208   7/29/98 2:31p Aldie
// Changed health to a float
// 
// 207   7/26/98 1:14a Markd
// rename entityflags to flags because we wern't using entityflags in any def
// files
// 
// 206   7/25/98 8:18p Markd
// Fixed animation bug
// 
// 205   7/25/98 3:57p Markd
// Added EV_GotKill
// 
// 204   7/24/98 6:18p Markd
// changed some CHAN_AUTO's to CHAN_BODY, changed CHAN_VOICE in dialog to
// CHAN_DIALOG
// 
// 203   7/24/98 6:17p Aldie
// Dialog checking
// 
// 202   7/23/98 6:17p Aldie
// Updated damage system and fixed some damage related bugs.  Also put tracers
// back to the way they were, and added gib event to funcscriptmodels
// 
// 201   7/22/98 10:41p Aldie
// Fixed tracers
// 
// 200   7/21/98 10:42p Markd
// Fixed entity tesselation
// 
// 199   7/21/98 10:04p Markd
// Added DIE_EXPLODE stuff to entity flags
// 
// 198   7/21/98 9:34p Jimdose
// Added AliasExists and PrefixAliasExists
// 
// 197   7/21/98 1:10p Aldie
// Added meansofdeath to obituaries
// 
// 196   7/20/98 3:52p Aldie
// Made a ProcessInitCommands which is called from the event.
// 
// 195   7/19/98 8:27p Jimdose
// Made setModel cancel any EV_ProcessInitCommands events
// 
// 194   7/18/98 11:16p Markd
// Added takedamage and nodamage events
// 
// 193   7/18/98 8:05p Markd
// Fixed bug with attached models
// 
// 192   7/18/98 4:05p Markd
// Added movetype events to attach, detach and attachmodel
// 
// 191   7/18/98 3:52p Markd
// Added attach, detach and attach model commands
// 
// 190   7/17/98 3:50p Markd
// Added HasAnim method
// 
// 189   7/15/98 11:22p Markd
// Added ProcessInitCommands when setting model
// 
// 188   7/14/98 11:35p Markd
// Added PHSSound and RandomPHSSound
// 
// 187   7/14/98 4:35p Markd
// fixed animation bug
// 
// 186   7/14/98 3:53p Markd
// Added proper frame 0 animation if immediately animating when calling
// RandomAnimate
// 
// 185   7/13/98 4:59p Aldie
// Added dead player bodies with gibbing
// 
// 184   7/11/98 2:48p Markd
// Added dialog event
// 
// 183   7/11/98 2:25p Markd
// Removed dialog event
// 
// 182   7/10/98 11:11p Markd
// Added DialogEvent
// 
// 181   7/09/98 11:52p Markd
// fixed greater than 10FPS support
// 
// 180   7/09/98 9:37p Jimdose
// Added getParentVector, since some calls to getLocalVector needed the
// transform in terms of the parent, and some to the local vector.  phew...
// hope that's that! :)
// 
// 179   7/09/98 12:55a Markd
// put in arbitrary frames per second support for models
// 
// 178   7/09/98 12:41a Markd
// Added support for greater than 10FPS
// 
// 177   7/09/98 12:18a Jimdose
// As soon as I checked in the file, I realized that getLocalVector was only
// incorrect in returning the vector untouched when it didn't have a
// bindmaster.  getLocalVector is independent of binding, so I fixed that and
// returned the bind call to getLocalVector to its original (correct) state.
// For binding, either way is correct, but getLocalVector is used in the
// physics for calculating a delta move in the coordinate system of the object,
// so this is more correct.
// 
// 176   7/09/98 12:11a Jimdose
// fixed getLocalVector to do the dot products agains the bindmaster's
// orientation instead of the object's.  This fixes a bug with binding to
// objects that are oriented.
// 
// 175   7/08/98 12:55p Jimdose
// Added classname event to clear up warnings when loading def files
// 
// 174   7/03/98 12:00p Aldie
// Fixed random animate to post endevent frametime in the future if anim not
// found
// 
// 173   6/27/98 2:17p Aldie
// Updated CanDamage
// 
// 172   6/25/98 4:58p Markd
// Fixed tesselation bug
// 
// 171   6/24/98 12:39p Markd
// Added default tesselation percentage
// 
// 170   6/20/98 7:49p Markd
// Added location to Killed and Pain events
// 
// 169   6/20/98 3:42p Markd
// Changed default damage tesselation
// 
// 168   6/19/98 7:24p Markd
// took out initialization of groups by setModel
// 
// 167   6/19/98 4:45p Jimdose
// Added Centroid, DistanceTo, and WithinDistance
// 
// 166   6/19/98 10:56a Markd
// re-ordered tesselation event
// 
// 165   6/18/98 8:46p Jimdose
// Added better event error handling
// Added source info to events
// 
// 164   6/18/98 6:14p Markd
// forgot to remove a warning
// 
// 163   6/18/98 2:00p Markd
// rewrote tesselation code
// 
// 162   6/17/98 7:34p Markd
// Fixed weird bug when world has targetname set!
// 
// 161   6/17/98 6:16p Markd
// Fixed broadcast sound NumArgs bug
// 
// 160   6/17/98 3:03p Markd
// Changed NumArgs back to previous behavior
// 
// 159   6/15/98 8:04p Markd
// put in Group_Flags support
// 
// 158   6/13/98 7:32p Markd
// put in default tesselation of 10 thick
// 
// 157   6/10/98 7:53p Markd
// Made NumArgs behave correctly like argc
// 
// 156   6/10/98 2:10p Aldie
// Updated damage function.
// 
// 155   6/09/98 6:41p Markd
// made static sounds default to ATTN_IDLE instead of ATTN_STATIC
// 
// 154   6/09/98 4:40p Markd
// Added additonal environment mapped effect
// 
// 153   6/08/98 8:20p Markd
// When RandomAnimate fails, the animdone event is still posted
// 
// 152   6/08/98 7:21p Aldie
// Fixed group command parsing
// 
// 151   6/08/98 5:18p Markd
// made group events use '+' and '-' behavior
// 
// 150   6/08/98 5:17p Aldie
// Moved defines to qshared.
// 
// 149   6/08/98 4:58p Markd
// Added GroupModelEvent for model dynamic modifications
// 
// 148   6/08/98 2:37p Markd
// changed some static sound stuff
// 
// 147   6/08/98 1:54p Jimdose
// Made sure that animdoneevent was freed if the animation was stopped or
// changed
// 
// 146   6/05/98 6:23p Aldie
// Added a location string to Damage
// 
// 145   6/04/98 4:36p Markd
// made all script sounds not use PHS so that sounds would be sent over no
// matter what
// 
// 144   5/27/98 8:35p Markd
// decreased health quotient
// 
// 143   5/27/98 7:34p Markd
// reduce damage on player by 50%
// 
// 142   5/27/98 4:55p Markd
// Added Inflictor message to Killed
// 
// 141   5/26/98 10:54p Markd
// Made sound flags SOUND_SYNCH default
// 
// 140   5/26/98 9:40p Markd
// Made damage appear instantly
// 
// 139   5/26/98 9:25p Aldie
// Added kill event
// 
// 138   5/26/98 8:45p Markd
// Initilialize number of groups for model
// 
// 137   5/26/98 4:22p Markd
// Added Target registration stuff
// 
// 136   5/26/98 1:29a Markd
// fixed rotated bounding boxes
// 
// 135   5/25/98 12:22p Aldie
// Inited waterlevel and watertype
// 
// 134   5/25/98 8:50p Markd
// Increased damage momentum for E3
// 
// 133   5/25/98 7:59p Markd
// Added RandomPositionedSound
// 
// 132   5/25/98 6:46p Jimdose
// Made animateframe, prethink and posthink into functions built into the base
// entity class
// 
// 131   5/25/98 4:43p Markd
// Added SpawnParticles to entity
// 
// 130   5/24/98 8:55p Jimdose
// Changed classname to a const char *
// 
// 129   5/24/98 8:46p Jimdose
// Made a lot of functions more str-friendly.
// Got rid of a lot of char * based strings
// Cleaned up get spawn arg functions and sound functions
// sound functions now use consistant syntax
// 
// 128   5/24/98 4:48p Jimdose
// Made char *'s const
// 
// 127   5/24/98 2:47p Markd
// Made char *'s into const char *'s
// 
// 126   5/24/98 1:06a Jimdose
// Added sound events for ai
// 
// 125   5/23/98 6:47p Aldie
// Fixed FADE_OUT
// 
// 124   5/20/98 1:42p Markd
// Added proper stopsound behavior
// 
// 123   5/20/98 11:11a Markd
// removed char * dependency
// 
// 122   5/15/98 6:46p Markd
// patched FLAGS command, also set max_health
// 
// 121   5/13/98 4:44p Jimdose
// Changed remove time in FadeOut
// Damage checks if inflictor or attacker are NULL.
// 
// 120   5/11/98 5:53p Markd
// Added aliascache command
// 
// 119   5/11/98 2:19p Markd
// Fixed randomsound stuff
// 
// 118   5/09/98 7:11p Markd
// Removed sound parameter from tesselate command
// 
// 117   5/08/98 7:00p Markd
// Added FL_DARKEN Support
// 
// 116   5/08/98 2:55p Markd
// Put in an additional randomsound method
// 
// 115   5/07/98 11:32p Markd
// Removed footstep event and command
// 
// 114   5/06/98 11:22a Markd
// Fixed up some sound stuff
// 
// 113   5/04/98 8:31p Markd
// Removed cachemodel and cachesound
// 
// 112   5/03/98 8:09p Markd
// Fixed calculate bounds bug in creator and also added precaching to
// modelindex and soundindex
// 
// 111   5/03/98 7:13p Markd
// Added precache to alias event
// 
// 110   5/03/98 4:31p Jimdose
// Changed Vector class.  No longer includes PointsTo
// 
// 109   5/03/98 2:41p Jimdose
// no change
// 
// 107   5/02/98 8:41p Markd
// Added cachemodel and cachesound and also entityflags events
// 
// 106   5/02/98 12:49a Jimdose
// added scale event
// 
// 105   5/01/98 8:32p Markd
// Added some precache comments
// 
// 104   5/01/98 8:17p Jimdose
// Init groundsurface to NULL
// 
// 103   5/01/98 7:39p Markd
// changed entsound and related functions to ambientsound etc.
// 
// 102   5/01/98 7:32p Jimdose
// Added groundplane, groundsurface, groundcontents
// 
// 101   5/01/98 5:02p Markd
// temporarily commented out setting of children in setorigin
// 
// 100   5/01/98 11:09a Markd
// Added sound to tesselation event
// 
// 99    4/29/98 10:44p Markd
// Added positioned_sound and random_sound with more parameters
// 
// 98    4/29/98 5:55p Jimdose
// Added "bind" spawn key to allow binding without using scripts
// 
// 97    4/16/98 1:59p Jimdose
// Added EndAnimEvent and PrevFrameEvent
// 
// 96    4/14/98 6:55p Markd
// Moved SetModel in Entity Constructor, added thickness to tesselation
// parameter
// 
// 95    4/14/98 5:25p Markd
// Fixed setsize support
// 
// 94    4/10/98 4:55p Jimdose
// fixed bug in tesselate
// damage no longer affects people in noclip mode
// 
// 93    4/10/98 1:22a Markd
// Added FL_TESSELATE damage func
// 
// 92    4/10/98 12:34a Jimdose
// RandomSound now uses CHAN_BODY
// Got rid of damage_inflictor
// 
// 91    4/09/98 8:45p Jimdose
// Made channel non-specific sound functions use CHAN_AUTO instead of
// CHAN_VOICE
// 
// 90    4/09/98 1:40p Markd
// Added a NextAnim(0) which I accidentally took out before, needed for single
// animation stuff
// 
// 89    4/08/98 6:03p Jimdose
// Changed momentum from damage
// 
// 88    4/07/98 8:00p Markd
// removed defhandle, changed all SINMDL calls to modelindex calls, removed
// SINMDL prefix
// 
// 87    4/07/98 5:40p Jimdose
// Made animdone events be posted 0 time in future instead of FRAMETIME / 2
// 
// 86    4/06/98 8:25p Markd
// Can't use centroid fix with B-models, only A-models
// 
// 85    4/06/98 7:21p Markd
// Fixed PVS tesselation bug, fixed it by sending over centroid instead.
// 
// 84    4/06/98 6:40p Jimdose
// BecomeSolid and BecomeNonSolid no longer change the movetype
// BecomeSolid now handles a-model and b-model entities
// 
// 83    4/06/98 5:44p Jimdose
// Moved the "angles" stuff to Object and Sentient
// 
// 82    4/06/98 12:02a Markd
// Grabbed a float as an integer instead
// 
// 81    4/05/98 10:42p Markd
// Added Tesselate Event
// 
// 80    4/05/98 10:17p Jimdose
// added lastorigin
// 
// 79    4/05/98 9:41p Markd
// Added "angles" and "angle" support
// 
// 78    4/05/98 7:19p Aldie
// Added dynamic lights.
// 
// 77    4/05/98 1:55a Jimdose
// Added SetModelEvent
// 
// 76    4/04/98 7:51p Jimdose
// Oops!  trace->surface->surfaceinfo is NULL if it's an a-model! :)
// 
// 75    4/04/98 7:29p Jimdose
// Added HitSky that takes a generic trace
// 
// 74    4/04/98 6:03p Jimdose
// Added HitSky and RandomSound
// 
// 73    4/02/98 4:51p Jimdose
// Added animation control events
// Made RandomSound default volume to 1
// Made droptofloor accept the max distance to fall
// 
// 72    3/31/98 5:40p Markd
// Added StartAnimatingEvent
// 
// 71    3/31/98 4:21p Jimdose
// Fixed angle mod
// 
// 70    3/31/98 3:03p Markd
// Fixed CalculateBounds and set proper solidtype for models
// 
// 69    3/31/98 2:26p Jimdose
// Got rid of some unneeded R_ConcatRotations
// 
// 68    3/31/98 2:04p Jimdose
// Fixed binding bug
// 
// 67    3/30/98 11:39p Markd
// Added modelIndex function
// 
// 66    3/30/98 11:20p Markd
// Added scale support to entity
// 
// 65    3/30/98 11:17p Markd
// Added sound and random sound support
// 
// 64    3/30/98 9:14p Markd
// Fixed attenuation of footstep sounds
// 
// 63    3/30/98 9:54p Jimdose
// Setmodel now prepends "models/" to model names.
// 
// 62    3/30/98 7:30p Markd
// Added FootStep method
// 
// 61    3/30/98 3:09p Jimdose
// made total_delta based on the entity's scale
// 
// 60    3/29/98 9:01p Markd
// Added Frame events and Init Events
// 
// 59    3/29/98 9:38p Jimdose
// Changed Killed and Pain to events
// Added damage event
// last_frame_in_anim wasn't being initialized when the entity was allocated
// 
// 58    3/27/98 7:01p Markd
// Added vieworigin and viewangle 
// 
// 57    3/26/98 8:24p Jimdose
// Added GetBone (not working yet)
// Changed groundentity to an edict_t *
// 
// 56    3/25/98 7:13p Markd
// Added detach to free event and made sure that parents exist when detaching
// 
// 55    3/25/98 3:24p Markd
// Added attach, detach, model binding variables and modified setorigin
// 
// 54    3/24/98 4:54p Jimdose
// Changed usage of GetToken to GetString so that script variables can be used
// 
// 53    3/23/98 1:31p Jimdose
// Revamped event and command system
// 
// 52    3/18/98 7:49p Jimdose
// Updated sound for new sound system
// 
// 51    3/18/98 7:19p Jimdose
// Added RandomAnimate
// Tweaked animation code
// 
// 50    3/11/98 6:51p Aldie
// Added alpha to the render state for an entity.
// 
// 49    3/11/98 11:32a Markd
// Added total_delta variable
// 
// 48    3/09/98 2:57p Aldie
// Fixed some alpha calculations in entity.
// 
// 47    3/09/98 2:49p Jimdose
// Working on euler-quat stuff
// 
// 46    3/07/98 2:04p Markd
// Got animation system up and running (no pun intended)
// 
// 45    3/05/98 2:49p Jimdose
// Made playsound command work again.
// 
// 44    3/02/98 8:49p Jimdose
// Changed the classid parameter of CLASS_DECLARATION to a quoted string so
// that you could have a NULL classid.
// 
// 43    3/02/98 5:51p Jimdose
// Added entname to edict_t.
// Copy classname into entname
// No longer set default targetname to edict#
// 
// 42    2/18/98 8:08p Jimdose
// Changed calls to getEntityFromScript to GetObject calls
// 
// 41    2/17/98 6:58p Jimdose
// Gave entities a default target name.
// no longer pass script into interpretCommand
// 
// 40    2/16/98 2:33p Jimdose
// Fixed bug in bind where an uninitialized pointer was referenced.
// 
// 39    2/16/98 1:58p Jimdose
// Added object teams
// Added true hierarchial binding
// Fixed bug with spawning using specific edicts.
// 
// 38    2/06/98 5:47p Jimdose
// Added link and unlink
// Removed touch and think functions
// Removed Spawn (all spawning done in constructor)
// Added client pointer
// No longer initialize mins and maxs to '0 0 0' since it screws up bmodels.
// 
// 37    2/03/98 10:57a Jimdose
// Updated to work with Quake 2 engine
// Moved initialization to constructor and removed Init function
// 
// 35    12/15/97 11:56a Markd
// zeroed out velocity in "notsolid"
// 
// 34    12/15/97 1:34a Jimdose
// Decreased the momentum added from damage
// 
// 33    12/13/97 5:42p Markd
// Made it so that ProcessNoteCommands no longer uses a static Script variable
// 
// 32    12/12/97 7:19p Markd
// Fixed model spawning stuff for decals and *models
// 
// 31    12/12/97 4:27p Markd
// Added "soundprefix"
// 
// 30    12/12/97 2:10p Markd
// Only calls animcallback if animate is still true
// 
// 29    12/12/97 1:19p Markd
// Moved model setArg to before setModel
// 
// 28    12/12/97 1:00p Jimdose
// Added warning to AnimateThink when no animation is set
// 
// 27    12/11/97 7:41p Markd
// moved note processing into setmodel
// 
// 26    12/11/97 3:34p Markd
// Fixed SetArg for "model", set "model" to "" when it was processed
// checked for global_loading in registersound
// 
// 25    12/08/97 4:28p Aldie
// Changed FadeOut factors so that the blood splats look ok.
// 
// 24    12/06/97 4:54p Markd
// Added default spawing behavior
// Added alpha, target, targetname, spawnflags, model, origin, angle, angles to
// interpretCommand
// 
// 23    12/05/97 3:03p Jimdose
// Now responds to EVENT_REMOVE.
// Made it so that if "model" is set when spawned then it precaches that model.
// 
// 22    12/03/97 6:40p Markd
// changed how alias stuff works for sounds
// 
// 21    11/24/97 6:54p Markd
// Added Register Sound and Random Sound, added Setsize
// 
// 20    11/18/97 5:30p Markd
// Added weighting in RandomAnimate, also commented out warnings in
// InterpretCommands (they were debug messages)
// 
// 19    11/17/97 5:45p Markd
// Added $owner command to ProcessFrameNotes routine
// 
// 18    11/15/97 6:53p Markd
// Added ProcessNoteCommands, Added RandomAnimate, added animloop_ variables
// for animation loop processing
// 
// 17    11/15/97 2:48p Jimdose
// Added ProcessEvent call which calls System->ProcessEvent
// 
// 16    11/14/97 4:44p Jimdose
// Added PostEvent
// 
// 15    10/30/97 7:42p Jimdose
// In Damage, now only add momentum if not a bsp model
// 
// 14    10/29/97 4:19p Jimdose
// Added FadeOut.
// 
// 13    10/28/97 4:13p Jimdose
// Added interpretCommand to make Entity be controllable by scripts via
// ScriptMaster.
// 
// 12    10/27/97 3:30p Jimdose
// Removed dependency on quakedef.h
// 
// 11    10/08/97 8:52p Jimdose
// Added EVENT_USE to Event()
// 
// 10    10/08/97 6:03p Jimdose
// Began vehicle support.
// 
// 9     10/01/97 10:27a Markd
// forgot to put break statement next to zero time animation
// 
// 8     10/01/97 10:23a Markd
// Fixed Animate bug, some animations had ZERO length animation times, still
// investigating that problem
// 
// 7     9/30/97 9:59p Markd
// Fixed AnimTime stuff
// 
// 6     9/30/97 5:55p Jimdose
// Damaged entities now get velocity from inflictor
// 
// 5     9/30/97 2:39p Markd
// Fixed Animate stuff
// 
// 4     9/29/97 6:18p Markd
// working on animate
// 
// 3     9/26/97 6:13p Jimdose
// Added standard Ritual headers
//
// DESCRIPTION:
// Base class for all enities that are controlled by Sin.  If you have any
// object that should be called on a periodic basis and it is not an entity,
// then you have to have an dummy entity that calls it.
//
// An entity in Sin is any object that is not part of the world.  Any non-world
// object that is visible in Sin is an entity, although it is not required that
// all entities be visible to the player.  Some objects are basically just virtual
// constructs that act as an instigator of certain actions, for example, some 
// triggers are invisible and cannot be touched, but when activated by other
// objects can cause things to happen.
//
// All entities are capable of receiving messages from Sin or from other entities.
// Messages received by an entity may be ignored, passed on to their superclass,
// or acted upon by the entity itself.  The programmer must decide on the proper
// action for the entity to take to any message.  There will be many messages
// that are completely irrelevant to an entity and should be ignored.  Some messages
// may require certain states to exist and if they are received by an entity when
// it these states don't exist may indicate a logic error on the part of the 
// programmer or map designer and should be reported as warnings (if the problem is
// not severe enough for the game to be halted) or as errors (if the problem should 
// not be ignored at any cost).
// 

#include "entity.h"
#include "worldspawn.h"
#include "scriptmaster.h"
#include "sentient.h"
#include "misc.h"
#include "specialfx.h"
#include "object.h"
#include "player.h"

CLASS_DECLARATION( Listener, Entity, NULL );

// Player events
Event EV_ClientConnect( "client_connect" );
Event EV_ClientDisconnect( "client_disconnect" );
Event EV_ClientKill( "client_kill" );
Event EV_ClientMove( "client_move" );
Event EV_ClientEndFrame( "client_endframe" );

// Generic entity events
Event EV_GetEntName( "getentname" );
Event EV_Classname( "classname" );
Event EV_Activate( "doActivate" );
Event EV_Use( "doUse" );
//Event EV_Footstep( "footstep" );
Event EV_FadeOut( "fadeout" );
Event EV_Fade( "fade" );
Event EV_Killed( "killed" );
Event EV_GotKill( "gotkill" );
Event EV_Pain( "pain" );
Event EV_Damage( "damage" );
Event EV_Kill( "kill", EV_CONSOLE );
Event EV_Gib( "gib" );
Event EV_Hurt( "hurt" );

Event EV_CourseAngles( "courseangles" );
Event EV_SmoothAngles( "smoothangles" );
Event EV_TakeDamage( "takedamage" );
Event EV_NoDamage( "nodamage" );

// Physics events
Event EV_MoveDone( "movedone" );
Event EV_Touch( "doTouch" );
Event EV_Blocked( "doBlocked" );
Event EV_UseBoundingBox( "usebbox" );

// Animation events
Event EV_NewAnim( "animChanged" );
Event EV_LastFrame( "lastFrame" );
Event EV_NextAnim( "nextanim" );
Event EV_NextFrame( "nextframe" );
Event EV_PrevFrame( "prevframe" );
Event EV_SetFrame( "setframe" );
Event EV_StopAnim( "stopanim" );
Event EV_EndAnim( "endanim" );
Event EV_ProcessInitCommands( "processinit" );
Event EV_Attach( "attach" );
Event EV_AttachModel( "attachmodel" );
Event EV_Detach( "detach" );

// script stuff
Event EV_Model( "model" );
Event EV_Hide( "hide" );
Event EV_Show( "show" );
Event EV_BecomeSolid( "solid" );
Event EV_BecomeNonSolid( "notsolid" );
Event EV_Ghost( "ghost" );
Event EV_PlaySound( "playsound" );
Event EV_PHSSound( "phssound" );
Event EV_StopSound( "stopsound" );
Event EV_GravityAxis( "gravityaxis", EV_CHEAT );
Event EV_Bind( "bind" );
Event EV_Unbind( "unbind" );
Event EV_JoinTeam( "joinTeam" );
Event EV_QuitTeam( "quitTeam" );
Event EV_SetHealth( "health", EV_CHEAT );
Event EV_SetScale( "scale" );
Event EV_SetSize( "setsize" );
Event EV_SetAlpha( "alpha" );
Event EV_SetOrigin( "origin" );
Event EV_SetTargetName( "targetname" );
Event EV_SetTarget( "target" );
Event EV_SetKillTarget( "killtarget" );
Event EV_SetAngles( "angles" );
Event EV_RegisterAlias( "alias" );
Event EV_RegisterAliasAndCache( "aliascache" );
Event EV_RandomSound( "randomsound" );
Event EV_RandomPHSSound( "randomphssound" );
Event EV_Tesselate( "shatter" );
Event EV_SetMass( "mass" );

//HACK HACK
Event EV_EntitySound( "ambientsound" );
Event EV_RandomGlobalEntitySound( "randomglobalambientsound" );
Event EV_RandomEntitySound( "randomambientsound" );
Event EV_StopEntitySound( "stopambientsound" );
Event EV_Anim( "anim" );
Event EV_StartAnimating( "animate" );
Event EV_GroupModelEvent( "group" );
Event EV_DialogEvent( "dialog" );
Event EV_SetSkin( "skin" );

// AI sound events
Event EV_WeaponSound( "weaponsound" );
Event EV_MovementSound( "movementsound" );
Event EV_PainSound( "painsound" );
Event EV_DeathSound( "deathsound" );
Event EV_BreakingSound( "breakingsound" );
Event EV_DoorSound( "doorsound" );
Event EV_MutantSound( "mutantsound" );
Event EV_VoiceSound( "voicesound" );
Event EV_MachineSound( "machinesound" );
Event EV_RadioSound( "radiosound" );

Event EV_HeardWeapon( "heardweapon" );
Event EV_HeardMovement( "heardmovement" );
Event EV_HeardPain( "heardpain" );
Event EV_HeardDeath( "hearddeath" );
Event EV_HeardBreaking( "heardbreaking" );
Event EV_HeardDoor( "hearddoor" );
Event EV_HeardMutant( "heardmutant" );
Event EV_HeardVoice( "heardvoice" );
Event EV_HeardMachine( "heardmachine" );
Event EV_HeardRadio( "heardradio" );

// Conditionals
Event EV_IfSkill( "ifskill" );

// Lighting
Event EV_SetLight( "light" );
Event EV_LightOn( "lightOn" );
Event EV_LightOff( "lightOff" );
Event EV_LightRed( "lightRed" );
Event EV_LightGreen( "lightGreen" );
Event EV_LightBlue( "lightBlue" );
Event EV_LightRadius( "lightRadius" );

Event EV_Lightoffset( "lightoffset" );
Event EV_Minlight( "minlight" );
Event EV_Gravity( "gravity" );

// Entity flag specific
Event EV_EntityFlags( "flags" );
Event EV_EntityRenderEffects( "rendereffects" );
Event EV_EntityEffects( "effects" );

// Special Effects
Event EV_SpawnParticles( "sparks" );

// Tesselation setup events
Event EV_Shatter_MinSize( "shatter_minsize" );
Event EV_Shatter_MaxSize( "shatter_maxsize" );
Event EV_Shatter_Thickness( "shatter_thickness" );
Event EV_Shatter_Percentage( "shatter_percentage" );

Event EV_Mutate( "mutate", EV_CHEAT );
Event EV_Censor( "censor" );

ResponseDef Entity::Responses[] =
	{
		{ &EV_Damage,				( Response )Entity::DamageEvent },
      { &EV_Kill, 				( Response )Entity::Kill },
	   { &EV_FadeOut,				( Response )Entity::FadeOut },
	   { &EV_Fade,				   ( Response )Entity::Fade },
	   { &EV_Hide,					( Response )Entity::EventHideModel },
	   { &EV_Show,					( Response )Entity::EventShowModel },
	   { &EV_BecomeSolid,		( Response )Entity::BecomeSolid },
	   { &EV_BecomeNonSolid,	( Response )Entity::BecomeNonSolid },
	   { &EV_Ghost,	         ( Response )Entity::Ghost },
	   { &EV_PlaySound,			( Response )Entity::PlaySound },
	   { &EV_StopSound,			( Response )Entity::StopSound },
		{ &EV_GravityAxis,		( Response )Entity::GravityAxisEvent },
	   { &EV_Bind,					( Response )Entity::BindEvent },
	   { &EV_Unbind,				( Response )Entity::EventUnbind },
	   { &EV_JoinTeam,			( Response )Entity::JoinTeam },
	   { &EV_QuitTeam,			( Response )Entity::EventQuitTeam },
	   { &EV_SetHealth,			( Response )Entity::SetHealth },
	   { &EV_SetSize,				( Response )Entity::SetSize },
		{ &EV_SetScale,			( Response )Entity::SetScale },
	   { &EV_SetAlpha,			( Response )Entity::SetAlpha },
	   { &EV_SetOrigin,			( Response )Entity::SetOrigin },
	   { &EV_SetTargetName,		( Response )Entity::SetTargetName },
	   { &EV_SetTarget,			( Response )Entity::SetTarget },
	   { &EV_SetKillTarget,		( Response )Entity::SetKillTarget },
	   { &EV_SetAngles,			( Response )Entity::SetAngles },
      { &EV_SetMass,          ( Response )Entity::SetMassEvent },

		{ &EV_CourseAngles,		( Response )Entity::CourseAnglesEvent },
		{ &EV_SmoothAngles,		( Response )Entity::SmoothAnglesEvent },

	   { &EV_RegisterAlias,	   ( Response )Entity::RegisterAlias },
	   { &EV_RegisterAliasAndCache, ( Response )Entity::RegisterAliasAndCache },
	   { &EV_RandomSound,	   ( Response )Entity::RandomSound },
	   { &EV_EntitySound,	   ( Response )Entity::EntitySound },
	   { &EV_RandomEntitySound,( Response )Entity::RandomEntitySound },
      { &EV_RandomGlobalEntitySound, ( Response )Entity::RandomGlobalEntitySoundEvent },
	   { &EV_StopEntitySound,	( Response )Entity::StopEntitySound },
      { &EV_Anim,             ( Response )Entity::AnimEvent },
	   { &EV_StartAnimating,	( Response )Entity::StartAnimatingEvent },
		{ &EV_NextAnim,			( Response )Entity::NextAnimEvent },
		{ &EV_NextFrame,			( Response )Entity::NextFrameEvent },
		{ &EV_PrevFrame,			( Response )Entity::PrevFrameEvent },
		{ &EV_SetFrame,			( Response )Entity::SetFrameEvent },
		{ &EV_StopAnim,			( Response )Entity::StopAnimatingEvent },
		{ &EV_EndAnim,				( Response )Entity::EndAnimEvent },
		{ &EV_Model,				( Response )Entity::SetModelEvent },
      { &EV_SetLight,			( Response )Entity::SetLight },
      { &EV_LightOn,				( Response )Entity::LightOn },
      { &EV_LightOff,		   ( Response )Entity::LightOff },
      { &EV_LightRed,		   ( Response )Entity::LightRed },
      { &EV_LightGreen,		   ( Response )Entity::LightGreen },
      { &EV_LightBlue,		   ( Response )Entity::LightBlue },
      { &EV_LightRadius,	   ( Response )Entity::LightRadius },
      { &EV_Tesselate,	      ( Response )Entity::Tesselate },
      { &EV_EntityFlags,	   ( Response )Entity::Flags },
      { &EV_EntityEffects,	   ( Response )Entity::Effects },
      { &EV_EntityRenderEffects,	   ( Response )Entity::RenderEffects },
	   { &EV_RandomPHSSound,	( Response )Entity::RandomPHSSound },
	   { &EV_PHSSound,	      ( Response )Entity::PHSSound },

		{ &EV_WeaponSound,		( Response )Entity::WeaponSound },
		{ &EV_MovementSound,		( Response )Entity::MovementSound },
		{ &EV_PainSound,			( Response )Entity::PainSound },
		{ &EV_DeathSound,			( Response )Entity::DeathSound },
		{ &EV_BreakingSound,		( Response )Entity::BreakingSound },
		{ &EV_DoorSound,			( Response )Entity::DoorSound },
		{ &EV_MutantSound,		( Response )Entity::MutantSound },
		{ &EV_VoiceSound,			( Response )Entity::VoiceSound },
		{ &EV_MachineSound,		( Response )Entity::MachineSound },
		{ &EV_RadioSound,			( Response )Entity::RadioSound },
	   { &EV_SpawnParticles,	( Response )Entity::SpawnParticles },
	   { &EV_GroupModelEvent,	( Response )Entity::GroupModelEvent },
      { &EV_DialogEvent,	   ( Response )Entity::DialogEvent },
      { &EV_ProcessInitCommands,( Response )Entity::ProcessInitCommandsEvent },
      { &EV_Attach,           ( Response )Entity::AttachEvent },
      { &EV_AttachModel,      ( Response )Entity::AttachModelEvent },
      { &EV_Detach,           ( Response )Entity::DetachEvent },
      { &EV_TakeDamage,       ( Response )Entity::TakeDamageEvent },
      { &EV_NoDamage,         ( Response )Entity::NoDamageEvent },
      { &EV_SetSkin,          ( Response )Entity::SetSkinEvent },
      { &EV_Lightoffset,      ( Response )Entity::Lightoffset },
      { &EV_Minlight,         ( Response )Entity::Minlight },
      { &EV_Gravity,          ( Response )Entity::Gravity },

	   { &EV_Shatter_MinSize,	( Response )Entity::SetShatterMinSize },
	   { &EV_Shatter_MaxSize,	( Response )Entity::SetShatterMaxSize },
	   { &EV_Shatter_Thickness,( Response )Entity::SetShatterThickness },
	   { &EV_Shatter_Percentage,( Response )Entity::SetShatterPercentage },

	   { &EV_UseBoundingBox,   ( Response )Entity::UseBoundingBoxEvent },
	   { &EV_Hurt,             ( Response )Entity::HurtEvent },
	   { &EV_IfSkill,          ( Response )Entity::IfSkillEvent },

      { &EV_GetEntName,       ( Response )Entity::GetEntName },
      { &EV_Censor,           ( Response )Entity::Censor },

		{ NULL, NULL }
	};

Entity::Entity()
	{
	const char *m;
	Event *ev;
   int   minlight;

	classname = this->getClassname();

	if ( game.force_entnum )
		{
		game.force_entnum = false;
		edict = &g_edicts[ game.spawn_entnum ];
		LL_Remove( edict, next, prev );
		G_InitEdict( edict );
		LL_Add( &active_edicts, edict, next, prev );
		}
	else
		{
		edict = G_Spawn ();
		}

	client = edict->client;
	edict->entity = this;
	entnum = edict->s.number;

	m = G_GetSpawnArg( "classname" );
	if ( m )
		{
		strncpy( edict->entname, m, sizeof( edict->entname ) - 1 );
		}

	// spawning variables
	spawnflags = G_GetIntArg( "spawnflags" );
   if ( spawnflags & SPAWNFLAG_DETAIL )
		{
      edict->s.renderfx |= RF_DETAIL;
		}

	// rendering variables
	setAlpha( G_GetFloatArg( "alpha", 1.0f ) );
	setScale( G_GetFloatArg( "scale", 1.0f ) );
   
   minlight = G_GetIntArg( "minlight", 0 );
   if ( minlight )
      edict->s.renderfx |= RF_MINLIGHT;
	
   edict->s.lightofs = G_GetFloatArg( "lightoffset", 0 );
   if ( edict->s.lightofs )
      edict->s.renderfx |= RF_LIGHTOFFSET;
      
   viewheight	= 0;
	light_level = 0;

	// Animation variables
   next_anim      = -1;
   next_frame     = -1;
   frame_delta    = "0 0 0";
   next_anim_delta = "0 0 0";
   next_anim_time = 0;
   total_delta    = "0 0 0";
	animDoneEvent	= NULL;
	animating		= false;
	last_frame_in_anim = 0;
   last_animation_time = -1;
	num_frames_in_gun_anim = 0;

	// team variables
	teamchain	= NULL;
	teammaster	= NULL;
	m		   	= G_GetSpawnArg( "team" );
	if ( m )
		{
      moveteam = str( m );
		}

	// physics variables
	contents						= 0;
	mass							= 0;
	gravity						= 1.0;
	groundentity				= NULL;
	groundsurface				= NULL;
	groundentity_linkcount	= 0;
	bindmaster					= NULL;
	velocity						= vec_zero;
	avelocity					= vec_zero;
	
	SetGravityAxis( G_GetIntArg( "gravityaxis", 0 ) );

   // model binding variables
   numchildren             = 0;
   memset( &children, 0, sizeof( children ) );

	setOrigin( G_GetSpawnArg( "origin" ) );
	worldorigin.copyTo( edict->s.old_origin );

	setMoveType( MOVETYPE_NONE );
	setSolidType( SOLID_NOT );

	// targeting variables
	SetTargetName( G_GetSpawnArg( "targetname" ) );
	SetTarget( G_GetSpawnArg( "target" ) );

	// Character state
	health		= 0;
	max_health	= 0;
	deadflag		= DEAD_NO;
	flags			= 0; 

	// underwater variables
	watertype		= 0;
	waterlevel		= 0;

	// Pain and damage variables
	takedamage				= DAMAGE_NO;
	enemy						= NULL;
	pain_finished			= 0;
	damage_debounce_time = 0;

	m = G_GetSpawnArg( "model" );
	if ( m )
		{
		setModel( m );
		}

   //
   // see if we have a mins and maxs set for this model
   //
   if ( gi.IsModel( edict->s.modelindex ) && !mins.length() && !maxs.length())
      {
      vec3_t tempmins, tempmaxs;
      gi.CalculateBounds( edict->s.modelindex, edict->s.scale, tempmins, tempmaxs );
      setSize( tempmins, tempmaxs );
      }

   //
   // get the number of groups for this model
   //
   edict->s.numgroups = gi.NumGroups( edict->s.modelindex );

	m = G_GetSpawnArg( "bind" );
	if ( m )
		{
		str name;

		ev = new Event( EV_Bind );

		// construct an object name
		name = "$";
		name += m;
		ev->AddString( name );

		// Wait until all entities are spawned.
		PostEvent( ev, 0 );
		}

   //
   // initialize tesselation variables
   //
   tess_max_size = size.length() / 4;
   tess_min_size = tess_max_size / 3;

   if ( tess_min_size < 8 )
		{
      tess_min_size = 8;
		}

   if ( tess_max_size <= tess_min_size )
		{
      tess_max_size = tess_min_size * 2;
		}

   tess_thickness = tess_min_size;
   tess_percentage = TESS_DEFAULT_PERCENT;
	}

Entity::~Entity()
	{
   Container<Entity *> bindlist;
   Entity *ent;
   int num;
   int i;

	// unbind any entities that are bound to me
   // can't unbind within this loop, so make an array
   // and unbind them outside of it.
   num = 0;
	for( ent = teamchain; ent; ent = ent->teamchain )
		{
      if ( ent->bindmaster == this )
         {
         bindlist.AddObject( ent );
         }
		}

   num = bindlist.NumObjects();
   for( i = 1; i <= num; i++ )
      {
      bindlist.ObjectAt( i )->unbind();
      }

   bindlist.FreeObjectList();

	unbind();
	quitTeam();
   detach();

   //
   // go through and set our children
   //
   num = numchildren;
   for( i = 0; ( i < MAX_MODEL_CHILDREN ) && num; i++ )
      {
      if ( !children[ i ] )
			{
         continue;
			}
      ent = ( Entity * )G_GetEntity( children[ i ] );
      ent->PostEvent( EV_Remove, 0 );
      num--;
      }

   if ( targetname.length() && world )
      {
		world->RemoveTargetEntity( targetname, this );
      }
	G_FreeEdict( edict );
	}

EXPORT_FROM_DLL void Entity::SetEntNum
   (
   int num
   )

   {
   if ( edict )
      {
      G_FreeEdict( edict );
      }

	edict = &g_edicts[ num ];
	LL_Remove( edict, next, prev );
	G_InitEdict( edict );
	LL_Add( &active_edicts, edict, next, prev );

	client = edict->client;
	edict->entity = this;
	entnum = num;
   }

EXPORT_FROM_DLL void Entity::GetEntName
	(
	Event *ev
   )

   {
   strncpy( edict->entname, getClassname(), sizeof( edict->entname ) - 1 );
	}

EXPORT_FROM_DLL void Entity::SetTarget
	(
	const char *text
	)

	{
   if ( text )
		{
      target = text;
		}
	else
		{
		target = "";
		}
	}

EXPORT_FROM_DLL void Entity::SetTargetName
	(
	const char *text
	)

	{
   if ( targetname.length() && world )
      {
      world->RemoveTargetEntity( targetname, this );
      }

   if ( text )
		{
      if ( text[ 0 ] == '$' )
         text++;
      targetname = text;
		}
	else
		{
		targetname = "";
		}

   if ( targetname.length() && world )
		{
      world->AddTargetEntity( targetname, this );
		}
	}

EXPORT_FROM_DLL void Entity::SetKillTarget
	( 
	const char *text
	)

	{
   if ( text )
		{
      killtarget = text;
		}
	else
		{
		killtarget = "";
		}
	}

EXPORT_FROM_DLL int Entity::modelIndex
	(
	const char *mdl
	)
   {
   str name;

	assert( mdl );

	if ( !mdl )
		{
		return 0;
		}

	// Prepend 'models/' to make things easier
	if ( !strchr( mdl, '*' ) && !strchr( mdl, '\\' ) && !strchr( mdl, '/' ) )
		{
		name = "models/";
		}

	name += mdl;

   return gi.modelindex( name.c_str() );
   }

EXPORT_FROM_DLL void Entity::setModel
	(
	const char *mdl
	)

	{
   str temp;

	if ( !mdl )
		{
		mdl = "";
		}
   
   // Prepend 'models/' to make things easier
   temp = "";
	if ( !strchr( mdl, '*' ) && !strchr( mdl, '\\' ) && !strchr( mdl, '/' ) )
		{
		temp = "models/";
		}
   temp += mdl;

   // we use a temp string so that if model was passed into here, we don't 
   // accidentally free up the string that we're using in the process.
   model = temp;

	gi.setmodel( edict, model.c_str() );

   if ( gi.IsModel( edict->s.modelindex ) )
      {
  	   Event *ev;

      edict->s.numgroups = gi.NumGroups( edict->s.modelindex );

      if ( !LoadingSavegame )
         {
		   CancelEventsOfType( EV_ProcessInitCommands );

         ev = new Event( EV_ProcessInitCommands );
         ev->AddInteger( edict->s.modelindex );
         PostEvent( ev, 0 );
         }
      }

	// Sanity check to see if we're expecting a B-Model
	assert( !( ( edict->solid == SOLID_BSP ) && !edict->s.modelindex ) );
	if ( ( edict->solid == SOLID_BSP ) && !edict->s.modelindex )
		{
      const char *name;

      name = getClassID();
      if ( !name )
         {
         name = getClassname();
         }
		gi.dprintf( "%s with SOLID_BSP and no model - '%s'(%d)\n", name, targetname.c_str(), entnum );

      // Make it non-solid so that the collision code doesn't kick us out.
      setSolidType( SOLID_NOT );
      }

	mins = edict->mins;
	maxs = edict->maxs;
	size = edict->size;
	}


EXPORT_FROM_DLL void Entity::ProcessInitCommands
	(
	int index
	)

	{
   sinmdl_cmd_t *cmds;

   if ( LoadingSavegame )
      {
      // Don't process init commands when loading a savegame since
      // it will cause items to be added to inventories unnecessarily.
      // All variables affected by the init commands will be set
      // by the unarchive functions.
      return;
      }

   cmds = gi.InitCommands( index );
   if (cmds)
      {
      int i, j;
   	Event		*event;
	
      for (i=0;i<cmds->num_cmds;i++)
         {
      	event = new Event( cmds->cmds[i].args[0] );
         for(j=1;j<cmds->cmds[i].num_args;j++)
            {
      		event->AddToken( cmds->cmds[i].args[j] );
            }
         ProcessEvent( event );
         }
      }
	}

EXPORT_FROM_DLL void Entity::ProcessInitCommandsEvent
	(
	Event *ev
	)

	{
   int index;

   index = ev->GetInteger( 1 );
   ProcessInitCommands( index );
	}

EXPORT_FROM_DLL void Entity::EventHideModel
	(
	Event *ev
	)

	{
	hideModel();
	}

EXPORT_FROM_DLL void Entity::EventShowModel
	(
	Event *ev
	)

	{
	showModel();
	}

EXPORT_FROM_DLL void Entity::setAlpha
	(
	float alpha
	)

	{
	if ( alpha > 1.0f )
		{
		alpha = 1.0f;
		}
	if ( alpha < 0 )
		{
		alpha = 0;
		}
	translucence = 1.0f - alpha;
   edict->s.alpha = alpha;
	edict->s.renderfx &= ~RF_TRANSLUCENT;

   if ( ( translucence > 0 ) && ( translucence <= 1.0 ) )
		{
		edict->s.renderfx |= RF_TRANSLUCENT;
		}
	}

EXPORT_FROM_DLL void Entity::setScale
	(
	float scale
	)

	{
	if ( scale > 255.0f )
		{
		scale = 255.0f;
		}
	if ( scale < 0.004f )
		{
		scale = 0.004f;
		}
   edict->s.scale = scale;
	}

EXPORT_FROM_DLL void Entity::setSolidType
	(
	solid_t type
	)

	{
	if ( 
         ( !LoadingSavegame ) &&
         ( type == SOLID_BSP ) && 
         ( this != world ) &&
         ( 
            !model.length() ||
            (
               ( model[ 0 ] != '*' ) && 
               ( !strstr( model.c_str(), ".bsp" ) )
            ) 
         )
      )
		{
      error( "setSolidType", "SOLID_BSP entity at x%.2f y%.2f z%.2f with no BSP model", worldorigin[ 0 ], worldorigin[ 1 ], worldorigin[ 2 ] );
		}
	edict->solid = type;
	link();

	edict->svflags &= ~SVF_NOCLIENT;
	if ( hidden() )
		{
		edict->svflags |= SVF_NOCLIENT;
		}
	}

EXPORT_FROM_DLL void Entity::setSize
	(
	Vector min,
	Vector max
	)

	{
   Vector delta;

   if ( flags & FL_ROTATEDBOUNDS )
      {
      vec3_t tempmins, tempmaxs;
      float mat[3][3];

      //
      // rotate the mins and maxs for the model
      //
	   min.copyTo( tempmins );
	   max.copyTo( tempmaxs );

      VectorCopy( orientation[ 0 ], mat[ 0 ] );
  	   VectorNegate( orientation[ 1 ], mat[ 1 ] );
      VectorCopy( orientation[ 2 ], mat[ 2 ] );

      CalculateRotatedBounds2( mat, tempmins, tempmaxs );

      mins = Vector( tempmins );
      maxs = Vector( tempmaxs );
	   size = max - min;

	   mins.copyTo( edict->mins );
	   maxs.copyTo( edict->maxs );
	   size.copyTo( edict->size );
      mins.copyTo( edict->fullmins );
      maxs.copyTo( edict->fullmaxs );
      edict->fullradius = size.length() * 0.5;
      }
   else
      {
      if ( ( min == edict->mins ) && ( max == edict->maxs ) )
         {
         return;
         }

	   mins = min;
	   maxs = max;
	   size = max - min;

	   mins.copyTo( edict->mins );
	   maxs.copyTo( edict->maxs );
	   size.copyTo( edict->size );

      //
      // get the full mins and maxs for this model
      //
      if ( gi.IsModel( edict->s.modelindex ) )
         {
         Vector delta;
         vec3_t fmins;
         vec3_t fmaxs;
        	const gravityaxis_t &grav = gravity_axis[ gravaxis ];

         gi.CalculateBounds( edict->s.modelindex, edict->s.scale, fmins, fmaxs );
         edict->fullmins[ 0 ] = fmins[ grav.x ];
         edict->fullmaxs[ 0 ] = fmaxs[ grav.x ];

         if ( grav.sign > 0 )
            {
            edict->fullmins[ 1 ] = fmins[ grav.y ];
            edict->fullmins[ 2 ] = fmins[ grav.z ];
            edict->fullmaxs[ 1 ] = fmaxs[ grav.y ];
            edict->fullmaxs[ 2 ] = fmaxs[ grav.z ];
            }
         else
            {
            edict->fullmins[ 1 ] = -fmaxs[ grav.y ];
            edict->fullmins[ 2 ] = -fmaxs[ grav.z ];
            edict->fullmaxs[ 1 ] = -fmins[ grav.y ];
            edict->fullmaxs[ 2 ] = -fmins[ grav.z ];
            }

         delta = Vector( edict->fullmaxs ) - Vector( edict->fullmins );
         edict->fullradius = delta.length() * 0.5f;
         }
      else
         {
         mins.copyTo( edict->fullmins );
         maxs.copyTo( edict->fullmaxs );
         edict->fullradius = size.length() * 0.5;
         }
      }

	link();
	}

EXPORT_FROM_DLL Vector Entity::getLocalVector
	(
	Vector vec
	)

	{
	Vector pos;

	pos[ 0 ] = vec * orientation[ 0 ];
	pos[ 1 ] = vec * orientation[ 1 ];
	pos[ 2 ] = vec * orientation[ 2 ];

	return pos;
	}

EXPORT_FROM_DLL Vector Entity::getParentVector
	(
	Vector vec
	)

	{
	Vector pos;

	if ( !bindmaster )
		{
		return vec;
		}

	pos[ 0 ] = vec * bindmaster->orientation[ 0 ];
	pos[ 1 ] = vec * bindmaster->orientation[ 1 ];
	pos[ 2 ] = vec * bindmaster->orientation[ 2 ];

	return pos;
	}

EXPORT_FROM_DLL void Entity::link
	(
	void
	)

	{
	gi.linkentity( edict );
	absmin = edict->absmin;
	absmax = edict->absmax;
	centroid = ( absmin + absmax ) * 0.5;
	centroid.copyTo( edict->centroid );

   // If this has a parent, then set the areanum the same
   // as the parent's
   if ( edict->s.parent )
      {
      edict->areanum = g_edicts[ edict->s.parent ].areanum;
      }
	}

EXPORT_FROM_DLL void Entity::setOrigin
	(
	Vector org
	)

	{
   Entity * ent;
   int i, num;

	origin = org;
	if ( bindmaster )
		{
		MatrixTransformVector( origin.vec3(), bindmaster->orientation, worldorigin.vec3() );
		worldorigin += bindmaster->worldorigin;
		worldorigin.copyTo( edict->s.vieworigin );
		}
   else if ( edict->s.parent )
      {
		org.copyTo( edict->s.vieworigin );
      ent = ( Entity * )G_GetEntity( edict->s.parent );
      worldorigin = ent->centroid;
      
      }
	else
		{
		worldorigin = origin;
		worldorigin.copyTo( edict->s.vieworigin );
		}

	worldorigin.copyTo( edict->s.origin );
	link();

   //
   // go through and set our children
   //
   num = numchildren;
   for( i = 0; ( i < MAX_MODEL_CHILDREN ) && num; i++ )
      {
      if ( !children[ i ] )
			{
         continue;
			}
      ent = ( Entity * )G_GetEntity( children[ i ] );
      ent->setOrigin( ent->origin );
      num--;
      }
	}

EXPORT_FROM_DLL qboolean Entity::GetBone
	(
	const char *name,
	Vector *pos,
	Vector *forward,
	Vector *right,
	Vector *up
	)

	{
   vec3_t	trans[ 3 ];
   vec3_t	p1, p2;
   vec3_t   orient;
   int		groupindex;
   int		tri_num;

   // get the bone information
   if ( !gi.GetBoneInfo( edict->s.modelindex, name, &groupindex, &tri_num, orient) )
		{
      return false;
		}
   if ( !gi.GetBoneTransform( edict->s.modelindex, groupindex, tri_num, orient, edict->s.anim, edict->s.frame, 
			edict->s.scale, trans, p1 ) )
		{
		return false;
		}

	if ( forward || right || up )
		{
		R_ConcatRotations( trans, orientation, trans );
		}

	if ( pos )
		{
		MatrixTransformVector( p1, orientation, p2 );
		*pos = Vector( p2 );
		}
	if ( forward )
		{
		*forward = Vector( trans[ 0 ] );
		}
	if ( right )
		{
		*right = Vector( trans[ 1 ] );
		}
	if ( up )
		{
		*up = Vector( trans[ 2 ] );
		}

	return true;
   }

EXPORT_FROM_DLL void Entity::setAngles
	(
	Vector ang
	)

	{
   Entity * ent;
	float	mat[3][3];
   int num,i;

	angles[ 0 ] = angmod( ang[ 0 ] );
	angles[ 1 ] = angmod( ang[ 1 ] );
	angles[ 2 ] = angmod( ang[ 2 ] );

	if ( bindmaster )
		{
		AnglesToMat( angles.vec3(), mat );
		R_ConcatRotations( mat, bindmaster->orientation, orientation );
		MatrixToEulerAngles( orientation, worldangles.vec3() );
		worldangles.copyTo( edict->s.viewangles );
		}
   else if (edict->s.parent)
      {
      float trans[3][3];
      float local_trans[3][3];
      vec3_t p1;

      ent = ( Entity * )G_GetEntity( edict->s.parent );
		ang.copyTo( edict->s.viewangles );

      if ( gi.GetBoneTransform( ent->edict->s.modelindex, edict->s.bone.group_num, edict->s.bone.tri_num, edict->s.bone.orientation,
         ent->edict->s.anim, ent->edict->s.frame, ent->edict->s.scale, trans, p1 ) )
         {
		   AnglesToMat( angles.vec3(), mat );
		   R_ConcatRotations( mat, trans, local_trans );
		   R_ConcatRotations( local_trans, ent->orientation, orientation );
		   MatrixToEulerAngles( orientation, worldangles.vec3() );
         }
      }
	else
		{
		worldangles = angles;
		AnglesToMat( worldangles.vec3(), orientation );
		worldangles.copyTo( edict->s.viewangles );
		}

	worldangles.copyTo( edict->s.angles );

	// Fill the edicts matrix
	VectorCopy( orientation[ 0 ], edict->s.mat[ 0 ] );
	VectorCopy( orientation[ 1 ], edict->s.mat[ 1 ] );
	VectorCopy( orientation[ 2 ], edict->s.mat[ 2 ] );

   //
   // go through and set our children
   //
   num = numchildren;
   for (i=0;(i < MAX_MODEL_CHILDREN) && num;i++)
      {
      if (!children[i])
         continue;
      ent = ( Entity * )G_GetEntity( children[i] );
      ent->setAngles( ent->angles );
      num--;
      }
	}

EXPORT_FROM_DLL qboolean Entity::droptofloor
	(
	float maxfall
	)

	{
	trace_t	trace;
	Vector	end;
	
	end = origin;
	end[ 2 ]-= maxfall;
	origin += "0 0 1";

	trace = G_Trace( origin, mins, maxs, end, this, MASK_SOLID, "Entity::droptofloor" );
	if ( trace.fraction == 1 || trace.allsolid )
		{
		groundentity = NULL;
		return false;
		}

	setOrigin( trace.endpos );

	groundentity				= trace.ent;
	groundentity_linkcount	= trace.ent->linkcount;

	return true;
	}

void Entity::Damage
   (
   Entity *inflictor,
   Entity *attacker,
   int damage,
   Vector position,
   Vector direction,
   Vector normal,
   int knockback,
   int dflags,
   int meansofdeath,
   int groupnum,
   int trinum,
   float damage_multiplier
   )

   {
	Event	*ev;

	if ( !attacker )
		{
		attacker = world;
		}
	if ( !inflictor )
		{
		inflictor = world;
		}

	ev = new Event( EV_Damage );
	ev->AddInteger( damage );
	ev->AddEntity ( inflictor );
	ev->AddEntity ( attacker );
   ev->AddVector ( position );
   ev->AddVector ( direction );
   ev->AddVector ( normal );
   ev->AddInteger( knockback );   
   ev->AddInteger( dflags );
   ev->AddInteger( meansofdeath );
   ev->AddInteger( groupnum );
   ev->AddInteger( trinum );
   ev->AddFloat  ( damage_multiplier );
   ProcessEvent  ( ev );
   }

void Entity::DamageEvent
	(
	Event *ev
	)

	{
	Entity	*inflictor;
	Entity	*attacker;
	int		damage;
	Vector	dir;
	Vector	momentum;
	Event		*event;
	float		m;

	if ( ( takedamage == DAMAGE_NO ) || ( movetype == MOVETYPE_NOCLIP ) )
		{
		return;
		}

	damage		= ev->GetInteger( 1 );
	inflictor	= ev->GetEntity( 2 );
	attacker		= ev->GetEntity( 3 );

	// figure momentum add
	if ( ( inflictor != world ) &&
		( movetype != MOVETYPE_NONE ) && 
		( movetype != MOVETYPE_BOUNCE ) && 
		( movetype != MOVETYPE_PUSH ) && 
		( movetype != MOVETYPE_STOP ) )
		{
		dir = worldorigin - ( inflictor->worldorigin + ( inflictor->mins + inflictor->maxs ) * 0.5 );
		dir.normalize();

		if ( mass < 50)
			{
			m = 50;
			}
		else
			{
			m = mass;
			}

		momentum = dir * damage * ( 1700.0 / m );
		velocity += momentum;
		}

	// check for godmode or invincibility
	if ( flags & FL_GODMODE )
		{
		return;
		}

   // Forcefields make objects invulnerable
   if ( flags & FL_FORCEFIELD )
      {
      float    alpha;
      float    radius;
      Entity   *forcefield;
      //
      // spawn forcefield
      //
      forcefield = new Entity;

      radius = ( centroid - worldorigin ).length();
      forcefield->setModel( "sphere2.def" );
      forcefield->setOrigin( centroid );
      forcefield->worldorigin.copyTo(forcefield->edict->s.old_origin);
      forcefield->setMoveType( MOVETYPE_NONE );
      forcefield->setSolidType( SOLID_NOT );
      forcefield->edict->s.scale = radius / 16;
      alpha = damage / 100;
      if ( alpha > 1 )
         alpha = 1;
      if ( alpha < 0.15f )
         alpha = 0.15f;
      forcefield->edict->s.alpha = alpha;
		forcefield->edict->s.renderfx |= RF_TRANSLUCENT;
      forcefield->PostEvent( EV_Remove, 0.1f );
      return;
      }

	// team play damage avoidance
	//if ( ( global->teamplay == 1 ) && ( edict->team > 0 ) && ( edict->team == attacker->edict->team ) )
	//	{
	//	return;
	//	}

   if ( !deathmatch->value && isSubclassOf( Player ) )
      {
      damage *= 0.15;
      }

   if ( deadflag )
      {
      // Check for gib.
      if ( inflictor->isSubclassOf( Projectile ) )
         {
         Event *gibEv;

         health -= damage;

         gibEv = new Event( EV_Gib );
         gibEv->AddEntity( this );
         gibEv->AddFloat( health );
         ProcessEvent( gibEv );
         }
      return;
      }

	// do the damage
	health -= damage;
	if ( health <= 0 )
		{
      if ( attacker )
         {
		   event = new Event( EV_GotKill );
		   event->AddEntity( this );
		   event->AddInteger( damage );
		   event->AddEntity( inflictor );
         // location based damage
         event->AddString( ev->GetString( 4 ) );
         event->AddInteger( ev->GetInteger( 9 ) );
         event->AddInteger( 0 );
		   attacker->ProcessEvent( event );
         }

		event = new Event( EV_Killed );
		event->AddEntity( attacker );
		event->AddInteger( damage );
		event->AddEntity( inflictor );
      // location based damage
      event->AddString( ev->GetString( 4 ) );
		ProcessEvent( event );
		return;
		}

   if (flags & FL_TESSELATE)
      {
      TesselateModel
         (
         this,
         tess_min_size,
         tess_max_size,
         dir,
         damage,
         tess_percentage*0.5f,
         tess_thickness,
         ev->GetVector( 5 )
         );
      }

   if (flags & FL_DARKEN)
      {
      edict->s.renderfx |= RF_LIGHTOFFSET;
      if ( max_health )
         {
         edict->s.lightofs = - ( 40.0f * ( (float)(max_health - health) / (float)max_health ) );
         }
      else
         {
         edict->s.lightofs -= damage;
         }
      if ( edict->s.lightofs < -127 )
         edict->s.lightofs = -127;
      if ( edict->s.lightofs > 127 )
         edict->s.lightofs = 127;
      }

	event = new Event( EV_Pain );
	event->AddFloat( damage );
	event->AddEntity( attacker );
   // location based damage
   event->AddString( ev->GetString( 4 ) );
	ProcessEvent( event );
	}

/*
============
CanDamage

Returns true if the inflictor can directly damage the target.  Used for
explosions and melee attacks.
============
*/
qboolean Entity::CanDamage
	(
	Entity *target
	)
	
	{
	trace_t	trace;
	Vector	pos;

	// bmodels need special checking because their origin is 0,0,0
	if ( target->getMoveType() == MOVETYPE_PUSH )
		{
		pos = ( target->absmin + target->absmax ) * 0.5;
      trace = G_Trace( origin, vec_origin, vec_origin, pos, this, MASK_SHOT, "Entity::CanDamage 1" );
		if ( trace.fraction == 1 || trace.ent == target->edict )
			{
			return true;
			}
		return false;
		}

   trace = G_Trace( origin, vec_origin, vec_origin, target->centroid, this, MASK_SHOT, "Entity::CanDamage 2" );
	if ( trace.fraction == 1 || trace.ent == target->edict )
		{
		return true;
		}
	pos = target->centroid + Vector( 15, 15, 0 );
   trace = G_Trace( origin, vec_origin, vec_origin, pos, this, MASK_SHOT, "Entity::CanDamage 3" );
	if ( trace.fraction == 1 || trace.ent == target->edict )
		{
		return true;
		}
	pos = target->centroid + Vector( -15, 15, 0 );
   trace = G_Trace( origin, vec_zero, vec_zero, pos, this, MASK_SHOT, "Entity::CanDamage 4" );
	if ( trace.fraction == 1 || trace.ent == target->edict )
		{
		return true;
		}
	pos = target->centroid + Vector( 15, -15, 0 );
   trace = G_Trace( origin, vec_zero, vec_zero, pos, this, MASK_SHOT, "Entity::CanDamage 5" );
	if ( trace.fraction == 1 || trace.ent == target->edict )
		{
		return true;
		}
  	pos = target->centroid + Vector( -15, -15, 0 );
   trace = G_Trace( origin, vec_zero, vec_zero, pos, this, MASK_SHOT, "Entity::CanDamage 6" );
	if ( trace.fraction == 1 || trace.ent == target->edict )
		{
		return true;
		}

	return false;
	}

EXPORT_FROM_DLL qboolean Entity::IsTouching
	(
	Entity *e1
	)

	{
	if ( e1->absmin.x > absmax.x )
		{
		return false;
		}
	if ( e1->absmin.y > absmax.y )
		{
		return false;
		}
	if ( e1->absmin.z > absmax.z )
		{
		return false;
		}
	if ( e1->absmax.x < absmin.x )
		{
		return false;
		}
	if ( e1->absmax.y < absmin.y )
		{
		return false;
		}
	if ( e1->absmax.z < absmin.z )
		{
		return false;
		}

	return true;
	}

void Entity::StopAnimating
	(
	void
	)

   {
	// Cancel all animating events
   last_animation_time = -1;
   animating = false;
   total_delta = vec_zero;
   if ( animDoneEvent )
		{
      CancelEventsOfType( animDoneEvent );
		delete animDoneEvent;
		animDoneEvent = NULL;
		}
   }

void Entity::StartAnimating
	(
	void
	)

   {
	// start animating
	AnimateFrame();
   animating = true;
   }

void Entity::NextAnim
	(
	int animnum
	)

   {
   if ( ( animnum >= 0 ) && ( animnum < gi.NumAnims( edict->s.modelindex ) ) )
		{
      next_anim = animnum;
		}
   else 
		{
		// bad value
      return;
		}
   
	// get the next anim delta
	gi.Anim_Delta( edict->s.modelindex, next_anim, next_anim_delta.vec3() );
   next_anim_delta *= edict->s.scale;

   // get the next anim time
	next_anim_time = gi.Anim_Time( edict->s.modelindex, next_anim );
	NextFrame( 0 );
   }

void Entity::NextFrame
	(
	int framenum
	)

   {
   if ( ( framenum >= 0 ) && ( framenum <= last_frame_in_anim ) )
		{
      next_frame = framenum;
		}
   else
		{
		// bad value
      return;
		}
   }

void Entity::AnimateFrame
	(
	void
	)

   {
   float delta;
   sinmdl_cmd_t * cmds;
   Event *ev;
   int i;
	int j;

   //
   // see if we have already animated this frame
   //
   if ( 
         ( level.time == last_animation_time ) && 
         ( next_anim < 0 ) &&
         ( next_frame == edict->s.frame + 1 ) 
      )
		{
      return;
		}

   // see if we have an anim change pending
   if (next_anim >= 0)
      {
      edict->s.anim = next_anim;
      last_frame_in_anim = gi.Anim_NumFrames( edict->s.modelindex, edict->s.anim ) - 1;
      next_anim = -1;
      if ( edict->s.gunmodelindex )
         {
         const char * animname;
         animname = gi.Anim_NameForNum( edict->s.modelindex, edict->s.anim );
         //
         // see if the anim exists in the world model
         //
         edict->s.gunanim        = gi.Anim_Random( edict->s.gunmodelindex, animname );
         if ( edict->s.gunanim < 0 )
            {
            //
            // see if at least we have an idle 
            //
            edict->s.gunanim        = gi.Anim_Random( edict->s.gunmodelindex, "idle" );
            }
         if ( edict->s.gunanim >= 0 )
            {
            num_frames_in_gun_anim = gi.Anim_NumFrames( edict->s.gunmodelindex, edict->s.gunanim );
            }
         else
            {
            edict->s.gunanim = 0;
            num_frames_in_gun_anim = 0;
            }
         }
      }

   // see if we have a frame change pending
   if (next_frame >= 0)
      {
      edict->s.frame = next_frame;
      next_frame = -1;
      }
#if 0
      {
   const char * animname;
   animname = gi.Anim_NameForNum( edict->s.modelindex, edict->s.anim );
   warning( "aframe", "%d anim %s frame %d", entnum, animname, edict->s.frame );
      }
#endif

   delta = gi.Frame_Time( edict->s.modelindex, edict->s.anim, edict->s.frame );
   if ( !delta )
		{
      delta = FRAMETIME;
		}
   next_frame = edict->s.frame + ( int )( ( float )FRAMETIME / delta );

   // should never be greater...but just in case
   if ( ( edict->s.frame >= last_frame_in_anim ) || ( next_frame > last_frame_in_anim ) )
      {
      PostEvent( EV_LastFrame, 0 );
		if ( animDoneEvent )
			{
	      PostEvent( animDoneEvent, 0 );
			animDoneEvent = NULL;
			}
      next_frame -= last_frame_in_anim+1;
      }

   // get the current frame delta
	gi.Frame_Delta( edict->s.modelindex, edict->s.anim, edict->s.frame, frame_delta.vec3() );
   total_delta += frame_delta * edict->s.scale;
   cmds = gi.Frame_Commands( edict->s.modelindex, edict->s.anim, edict->s.frame );
   if ( cmds )
      {
      for( i = 0; i < cmds->num_cmds; i++ )
         {
      	ev = new Event( cmds->cmds[ i ].args[ 0 ] );
         for( j = 1; j < cmds->cmds[ i ].num_args; j++ )
            {
      		ev->AddToken( cmds->cmds[ i ].args[ j ] );
            }
         ProcessEvent( ev );
         }
      }

   last_animation_time = level.time;
   //
   // check to see if we have a secondary animation system going on here
   //
   if ( edict->s.gunmodelindex )
      {
      if ( num_frames_in_gun_anim > 1 )
         {
         edict->s.gunframe = ( edict->s.frame * num_frames_in_gun_anim ) / ( last_frame_in_anim + 1 );
         }
      else
         {
         edict->s.gunframe = 0;
         }
      }
   }

void Entity::RandomAnimate
	(
   const char *animname,
	Event *endevent
   )

   {
   int num;

	num = gi.Anim_Random( edict->s.modelindex, animname );

   //
   // if we have an event that hasn't been processed, kill the current one
   //
	if ( animDoneEvent )
		{
      CancelEventsOfType( animDoneEvent );
		delete animDoneEvent;
		animDoneEvent = NULL;
		}
   //
   // see if we even have a valid animation at all
   //
   if ( num == -1 )
      {
      if ( endevent )
			{
	      PostEvent( endevent, FRAMETIME );
			}

		animDoneEvent = NULL;
      return;
      }

	NextAnim( num );

	animDoneEvent = endevent;
	if ( !animating )
		{
		StartAnimating();
		}
   last_animation_time = -1;
   }

EXPORT_FROM_DLL void Entity::joinTeam
	(
	Entity *teammember
	)

	{
	Entity *ent;
	Entity *master;
   Entity *prev;
   Entity *next;

	if ( teammaster && ( teammaster != this ) )
		{
		quitTeam();
		}

	assert( teammember );
	if ( !teammember )
		{
		warning( "joinTeam", "Null entity" );
		return;
		}

	master = teammember->teammaster;
	if ( !master )
		{
		master = teammember;
		teammember->teammaster = teammember;
      teammember->teamchain = this;

      // make anyone who's bound to me part of the new team
      for( ent = teamchain; ent != NULL; ent = ent->teamchain )
         {
         ent->teammaster = master;
         }
      }
   else
      {
      // skip past the chain members bound to the entity we're teaming up with
      prev = teammember;
	   next = teammember->teamchain;
      if ( bindmaster )
         {
         // if we have a bindmaster, joing after any entities bound to the entity
         // we're joining
	      while( next && next->isBoundTo( teammember ) )
		      {
            prev = next;
		      next = next->teamchain;
		      }
         }
      else
         {
         // if we're not bound to someone, then put us at the end of the team
	      while( next )
		      {
            prev = next;
		      next = next->teamchain;
		      }
         }

      // make anyone who's bound to me part of the new team and
      // also find the last member of my team
      for( ent = this; ent->teamchain != NULL; ent = ent->teamchain )
         {
         ent->teamchain->teammaster = master;
         }

    	prev->teamchain = this;
      ent->teamchain = next;
      }

   teammaster = master;
	flags |= FL_TEAMSLAVE;
	}

EXPORT_FROM_DLL void Entity::quitTeam
	(
	void
	)

	{
	Entity *ent;

	if ( !teammaster )
		{
		return;
		}

	if ( teammaster == this )
		{
      if ( !teamchain->teamchain )
         {
         teamchain->teammaster = NULL;
         }
      else
         {
		   // make next teammate the teammaster
		   for( ent = teamchain; ent; ent = ent->teamchain )
			   {
			   ent->teammaster = teamchain;
			   }
         }

      teamchain->flags &= ~FL_TEAMSLAVE;
		}
	else
		{
		assert( flags & FL_TEAMSLAVE );
		assert( teammaster->teamchain );

		ent = teammaster;
		while( ent->teamchain != this )
			{
			// this should never happen
			assert( ent->teamchain );

			ent = ent->teamchain;
			}

		ent->teamchain = teamchain;

		if ( !teammaster->teamchain )
			{
			teammaster->teammaster = NULL;
			}
		}

	teammaster = NULL;
	teamchain = NULL;
	flags &= ~FL_TEAMSLAVE;
	}

EXPORT_FROM_DLL void Entity::EventQuitTeam
	(
	Event *ev
	)

	{
	quitTeam();
	}

qboolean Entity::isBoundTo
   (
   Entity *master
   )

   {
   Entity *ent;

   for( ent = bindmaster; ent != NULL; ent = ent->bindmaster )
      {
      if ( ent == master )
         {
         return true;
         }
      }

   return false;
   }

EXPORT_FROM_DLL void Entity::bind
	(
	Entity *master
	)

	{
	float  mat[ 3 ][ 3 ];
	float  local[ 3 ][ 3 ];
	Vector ang;

	assert( master );
	if ( !master )
		{
		warning( "bind", "Null master entity" );
		return;
		}

   if ( master == this )
      {
      warning( "bind", "Trying to bind to oneself." );
      return;
      }

   // unbind myself from my master
   unbind();

	bindmaster = master;

   // We are now separated from our previous team and are either
   // an individual, or have a team of our own.  Now we can join
   // the new bindmaster's team.  Bindmaster must be set before
   // joining the team, or we will be placed in the wrong position
   // on the team.
   joinTeam( master );

	// calculate local angles
	TransposeMatrix( bindmaster->orientation, mat );
	R_ConcatRotations( mat, orientation, local );
	MatrixToEulerAngles( local, ang.vec3() );
	setAngles( ang );

	setOrigin( getParentVector( origin - bindmaster->worldorigin ) );

   return;
   }

EXPORT_FROM_DLL void Entity::unbind
	(
	void
	)

	{
   Entity *prev;
   Entity *next;
   Entity *last;
   Entity *ent;

	if ( !bindmaster )
		{
		return;
		}

	//bindmaster = NULL;

	origin = Vector( edict->s.origin );
	angles = Vector( edict->s.angles );

   if ( !teammaster )
      {
      bindmaster = NULL;
      //Teammaster already has been freed
      return;
      }
   
   // We're still part of a team, so that means I have to extricate myself
   // and any entities that are bound to me from the old team.
   // Find the node previous to me in the team
   prev = teammaster;

	for( ent = teammaster->teamchain; ent && ( ent != this ); ent = ent->teamchain )
		{
      prev = ent;
		}

   // If ent is not pointing to me, then something is very wrong.
   assert( ent );
   if ( !ent )
      {
      error( "unbind", "corrupt team chain\n" );
      }

   // Find the last node in my team that is bound to me.
   // Also find the first node not bound to me, if one exists.
   last = this;
   for( next = teamchain; next != NULL; next = next->teamchain )
      {
      if ( !next->isBoundTo( this ) )
         {
         break;
         }

      // Tell them I'm now the teammaster
      next->teammaster = this;
      last = next;
		}

   // disconnect the last member of our team from the old team
   last->teamchain = NULL;

   // connect up the previous member of the old team to the node that
   // follow the last node bound to me (if one exists).
   if ( teammaster != this )
      {
      prev->teamchain = next;
      if ( !next && ( teammaster == prev ) )
         {
         prev->teammaster = NULL;
         }
      }
   else if ( next )
      {
      // If we were the teammaster, then the nodes that were not bound to me are now
      // a disconnected chain.  Make them into their own team.
		for( ent = next; ent->teamchain != NULL; ent = ent->teamchain )
         {
         ent->teammaster = next;
			}
      next->teammaster = next;
	   next->flags &= ~FL_TEAMSLAVE;
      }

   // If we don't have anyone on our team, then clear the team variables.
   if ( teamchain )
      {
      // make myself my own team
      teammaster = this;
      }
   else
      {
      // no longer a team
      teammaster = NULL;
      }

	flags &= ~FL_TEAMSLAVE;
   bindmaster = NULL;
	}

EXPORT_FROM_DLL void Entity::EventUnbind
	(
	Event *ev
	)

	{
	unbind();
	}

EXPORT_FROM_DLL void Entity::FadeOut
	(
	Event *ev
	)

	{
	PostEvent( EV_FadeOut, 0.1f );

	edict->s.renderfx |= RF_TRANSLUCENT;
   translucence += 0.03f;
	if ( translucence >= 0.96f )
		{
		PostEvent( EV_Remove, 0 );
		}

   setAlpha( 1.0f - translucence );
	}

EXPORT_FROM_DLL void Entity::Fade
	(
	Event *ev
	)

	{
   float rate = ev->GetFloat( 1 );

	edict->s.renderfx |= RF_TRANSLUCENT;
   translucence += rate;
   setAlpha( 1.0f - translucence );

   if ( translucence <= 1 )
      PostEvent( EV_FadeOut, 0.1f );
	}

EXPORT_FROM_DLL void Entity::SetMassEvent
	(
	Event *ev
	)

	{
   mass = ev->GetFloat( 1 );
	}

void Entity::CheckGround
	(
   void
	)
	
	{
	Vector	point;
	trace_t	trace;
	const gravityaxis_t &grav = gravity_axis[ gravaxis ];

	if ( flags & ( FL_SWIM | FL_FLY ) )
		{
		return;
		}

	if ( velocity[ grav.z ] > 100 )
		{
		groundentity = NULL;
		return;
		}

	// if the hull point one-quarter unit down is solid the entity is on ground
	point = worldorigin;
	point[ grav.z ] -= 0.25 * grav.sign;
	trace = G_Trace( worldorigin, mins, maxs, point, this, MASK_MONSTERSOLID, "Entity::CheckGround" );

	// check steepness
	if ( ( ( trace.plane.normal[ grav.z ] * grav.sign ) <= 0.7 ) && !trace.startsolid )
		{
		groundentity = NULL;
		return;
		}

	groundentity = trace.ent;
	groundentity_linkcount = trace.ent->linkcount;
	groundplane = trace.plane;
	groundsurface = trace.surface;
	groundcontents = trace.contents;

	if ( !trace.startsolid && !trace.allsolid )
		{
		setOrigin( trace.endpos );
		velocity[ grav.z ] = 0;
		}
	}

EXPORT_FROM_DLL void Entity::BecomeSolid
	(
	Event *ev
	)

	{
	if ( ( model.length() ) && ( ( model[ 0 ] == '*' ) || ( strstr( model.c_str(), ".bsp" ) ) ) )
		{
		setSolidType( SOLID_BSP );
		}
	else
		{
		setSolidType( SOLID_BBOX );
		}
	}

EXPORT_FROM_DLL void Entity::BecomeNonSolid
	(
	Event *ev
	)

	{
	setSolidType( SOLID_NOT );
	}

EXPORT_FROM_DLL void Entity::Ghost
	(
	Event *ev
	)

	{
   // Make not solid, but send still send over whether it is hidden or not
	setSolidType( SOLID_NOT );
   edict->svflags &= ~SVF_NOCLIENT;
	}

EXPORT_FROM_DLL void Entity::PlaySound
	(
	Event *ev
	)

	{
	char name[ 128 ];
	float volume;
   int channel;
   float attenuation;
   float pitch;
   float timeofs;
   float fadetime;
   int flags;
   int i;
	
   //
   // set defaults
   //
   name[0] = 0;
   volume = 1.0f;
   channel = CHAN_BODY;
   attenuation = ATTN_NORM;
   pitch = 1.0f;
   timeofs = 0;
   fadetime = 0;
   flags = SOUND_SYNCH;
   for ( i = 1 ; i <= ev->NumArgs() ; i++ )
      {
      switch (i-1)
         {
         case 0:
            strcpy( name, ev->GetString( i ) );
            break;
         case 1:
            volume = ev->GetFloat( i );
            break;
         case 2:
            channel = ev->GetInteger( i );
            break;
         case 3:
            attenuation = ev->GetFloat( i );
            break;
         case 4:
            pitch = ev->GetFloat( i );
            break;
         case 5:
            timeofs = ev->GetFloat( i );
            break;
         case 6:
            fadetime = ev->GetFloat( i );
            break;
         case 7:
            flags = ev->GetInteger( i );
            break;
         default:
            break;
         }
      }
   channel |= CHAN_NO_PHS_ADD;
	sound( name, volume, channel, attenuation, pitch, timeofs, fadetime, flags );
	}

EXPORT_FROM_DLL void Entity::StopSound
	(
	Event *ev
	)

	{
   if (ev->NumArgs() < 1)
      stopsound( CHAN_BODY );
   else
      stopsound( ev->GetInteger( 1 ) );
	}

EXPORT_FROM_DLL void Entity::SetGravityAxis
	(
	int axis
	)

	{
   Vector min;
   Vector max;

   if ( ( axis < 0 ) || ( axis > 5 ) )
      {
      axis = 0;
      }

   // don't do anything if the axis has been already set
   if ( axis == gravaxis )
      return;

	edict->s.effects &= ~( EF_GRAVITY_AXIS_0 | EF_GRAVITY_AXIS_1 | EF_GRAVITY_AXIS_2 );
	edict->s.effects |= GRAVITYAXIS_TO_EFFECTS( axis );
	gravaxis = EFFECTS_TO_GRAVITYAXIS( edict->s.effects );
	groundentity = NULL;

   const gravityaxis_t &grav = gravity_axis[ gravaxis ];

   min[ grav.x ] = mins[ 0 ];
   min[ grav.y ] = mins[ 1 ] * grav.sign;
   min[ grav.z ] = mins[ 2 ] * grav.sign;
   max[ grav.x ] = maxs[ 0 ];
   max[ grav.y ] = maxs[ 1 ] * grav.sign;
   max[ grav.z ] = maxs[ 2 ] * grav.sign;

   setSize( min, max );
	}

EXPORT_FROM_DLL void Entity::GravityAxisEvent
	(
	Event *ev
	)

	{
	SetGravityAxis( ev->GetInteger( 1 ) );
	}

EXPORT_FROM_DLL void Entity::BindEvent
	(
	Event *ev
	)

	{
	Entity *ent;

	ent = ev->GetEntity( 1 );
	if ( ent )
		{
		bind( ent );
		}
	}


EXPORT_FROM_DLL void Entity::JoinTeam
	(
	Event *ev
	)

	{
	Entity *ent;

	ent = ev->GetEntity( 1 );
	if ( ent )
		{
		joinTeam( ent );
		}
	}

EXPORT_FROM_DLL void Entity::SetLight
	(
	Event *ev
	)

	{
   edict->s.renderfx |= RF_DLIGHT;
   edict->s.color_r = ev->GetFloat( 1 );
   edict->s.color_g = ev->GetFloat( 2 );
   edict->s.color_b = ev->GetFloat( 3 );
   edict->s.radius  = ev->GetFloat( 4 );
   }

EXPORT_FROM_DLL void Entity::LightOn
	(
	Event *ev
	)

	{
   edict->s.renderfx |= RF_DLIGHT;
   }

EXPORT_FROM_DLL void Entity::LightOff
	(
	Event *ev
	)

	{
   edict->s.renderfx &= ~RF_DLIGHT;
   }

EXPORT_FROM_DLL void Entity::LightRed
	(
	Event *ev
	)

	{
   edict->s.renderfx |= RF_DLIGHT;
   edict->s.color_r = ev->GetFloat( 1 );
   }

EXPORT_FROM_DLL void Entity::LightGreen
	(
	Event *ev
	)

	{
   edict->s.renderfx |= RF_DLIGHT;
   edict->s.color_g = ev->GetFloat( 1 );
   }

EXPORT_FROM_DLL void Entity::LightBlue
	(
	Event *ev
	)

	{
   edict->s.renderfx |= RF_DLIGHT;
   edict->s.color_b = ev->GetFloat( 1 );
   }

EXPORT_FROM_DLL void Entity::LightRadius
	(
	Event *ev
	)

	{
   edict->s.renderfx |= RF_DLIGHT;
   edict->s.radius = ev->GetFloat( 1 );
   }

EXPORT_FROM_DLL void Entity::SetHealth
	(
	Event *ev
	)

	{
	health = ev->GetFloat( 1 );
   max_health = health;
	}

EXPORT_FROM_DLL void Entity::SetSize
	(
	Event *ev
	)

	{
	Vector min, max;

   min = ev->GetVector( 1 );
   max = ev->GetVector( 2 );
   setSize( min, max );
   }

EXPORT_FROM_DLL void Entity::SetScale
	(
	Event *ev
	)

	{
	setScale( ev->GetFloat( 1 ) );
   }

EXPORT_FROM_DLL void Entity::SetAlpha
	(
	Event *ev
	)

	{
	setAlpha( ev->GetFloat( 1 ) );
   }

EXPORT_FROM_DLL void Entity::SetOrigin
	(
	Event *ev
	)

	{
	setOrigin( ev->GetVector( 1 ) );
	}

EXPORT_FROM_DLL void Entity::SetTargetName
	(
	Event *ev
	)

	{
	SetTargetName( ev->GetString( 1 ) );
	}

EXPORT_FROM_DLL void Entity::SetTarget
	(
	Event *ev
	)

	{
	SetTarget( ev->GetString( 1 ) );
	}

EXPORT_FROM_DLL void Entity::SetKillTarget
	(
	Event *ev
	)

	{
	SetKillTarget( ev->GetString( 1 ) );
	}

EXPORT_FROM_DLL void Entity::SetAngles
	(
	Event *ev
	)

	{
	setAngles( ev->GetVector( 1 ) );	
	}

EXPORT_FROM_DLL void Entity::CourseAnglesEvent
	(
	Event *ev
	)

	{
	// Angles will be sent over the net as 8-bit values (default)
	edict->s.effects &= ~EF_SMOOTHANGLES;
	}

EXPORT_FROM_DLL void Entity::SmoothAnglesEvent
	(
	Event *ev
	)

	{
	// Angles will be sent over the net as 16-bit values for smoother rotation (or slow rotation)
	edict->s.effects |= EF_SMOOTHANGLES;
	}

EXPORT_FROM_DLL void Entity::RegisterAlias
	(
	Event *ev
	)

	{
   if ( ev->NumArgs() < 3 )
		{
      gi.Alias_Add( edict->s.modelindex, ev->GetString( 1 ), ev->GetString( 2 ),  1 );
		}
   else
		{
      gi.Alias_Add( edict->s.modelindex, ev->GetString( 1 ), ev->GetString( 2 ), ev->GetInteger( 3 ) ); 
		}
	}

EXPORT_FROM_DLL void Entity::RegisterAliasAndCache
	(
	Event *ev
	)

	{
   int length;
   str realname;
   const char * ptr;

   realname = ev->GetString( 2 );

   if ( ev->NumArgs() < 3 )
		{
      gi.Alias_Add( edict->s.modelindex, ev->GetString( 1 ), realname.c_str(),  1 );
		}
   else
		{
      gi.Alias_Add( edict->s.modelindex, ev->GetString( 1 ), realname.c_str(), ev->GetInteger( 3 ) ); 
		}

   length = realname.length();
   ptr = realname.c_str();
   ptr += length - 4;
   if ( ( length > 4 ) && ( !strcmpi( ptr, ".wav" ) ) )
		{
      gi.soundindex( realname.c_str() );
		}
   else if ( ( length > 4 ) && ( !strcmpi( ptr, ".def" ) ) )
		{
      gi.modelindex( realname.c_str() );
		}
	}

EXPORT_FROM_DLL void Entity::positioned_sound
	(
   Vector origin,
	str	soundname,
   float volume,
   int   channel,
   int   attenuation,
   float pitch,
   float timeofs,
   float fadetime,
   int   flags
	)

   {
	if ( soundname.length() )
		{
		gi.positioned_sound( worldorigin.vec3(), edict, channel, gi.soundindex( soundname.c_str() ), 
			volume, attenuation, timeofs, pitch, fadetime, flags );
		}
	else
		{
		warning( "sound", "Null sample pointer" );
		}
   }

EXPORT_FROM_DLL void Entity::RandomPositionedSound
	(
   Vector origin,
	str	soundname,
   float volume,
   int   channel,
   int   attenuation,
   float pitch,
   float timeofs,
   float fadetime,
   int   flags
	)

   {
   const char * name;

   name = gi.GlobalAlias_FindRandom( soundname.c_str() );
   if ( name )
		{
   	positioned_sound( worldorigin, name, volume, channel, attenuation, pitch, timeofs, fadetime, flags );
		}
   else
		{
		warning( "RandomPositionedSound", "Couldn't find alias for %s", soundname.c_str() );
		}
   }

EXPORT_FROM_DLL void Entity::sound
	(
	str	soundname,
   float volume,
   int   channel,
   int   attenuation,
   float pitch,
   float timeofs,
   float fadetime,
   int   flags
	)

   {
	if ( soundname.length() )
		{
		gi.sound( edict, channel, gi.soundindex( soundname.c_str() ), volume, 
			attenuation, timeofs, pitch, fadetime, flags );
		}
	else
		{
		warning( "sound", "Null sample pointer" );
		}
   }

EXPORT_FROM_DLL void Entity::RandomGlobalSound
	(
	str	soundname,
   float volume,
   int   channel,
   int   attenuation,
   float pitch,
   float timeofs,
   float fadetime,
   int   flags
	)

	{
   const char * name;

   name = gi.GlobalAlias_FindRandom( soundname.c_str() );
   if ( name )
		{
   	sound( name, volume, channel, attenuation, pitch, timeofs, fadetime, flags );
		}
   else
		{
		warning( "RandomGlobalSound", "Couldn't find alias for %s", soundname.c_str() );
		}
	}

EXPORT_FROM_DLL void Entity::RandomSound
	(
	str	soundname,
   float volume,
   int   channel,
   int   attenuation,
   float pitch,
   float timeofs,
   float fadetime,
   int   flags
	)

	{
	str realname;

   realname = GetRandomAlias( soundname );
   sound( realname, volume, channel, attenuation, pitch, timeofs, fadetime, flags );
	}

EXPORT_FROM_DLL void Entity::RandomSound
	(
	Event *ev
	)

	{
	str name;
	float volume;
   int   channel;
   float attenuation;
   float pitch;
   float timeofs;
   float fadetime;
   int flags;
   int i;
	
   //
   // set defaults
   //
   volume = 1.0f;
   channel = CHAN_BODY;
   attenuation = ATTN_NORM;
   pitch = 1.0f;
   timeofs = 0;
   fadetime = 0;
   flags = SOUND_SYNCH;
   for ( i = 1 ; i <= ev->NumArgs() ; i++ )
      {
      switch (i-1)
         {
         case 0:
            name = ev->GetString( i );
            break;
         case 1:
            volume = ev->GetFloat( i );
            break;
         case 2:
            channel = ev->GetInteger( i );
            break;
         case 3:
            attenuation = ev->GetFloat( i );
            break;
         case 4:
            pitch = ev->GetFloat( i );
            break;
         case 5:
            timeofs = ev->GetFloat( i );
            break;
         case 6:
            fadetime = ev->GetFloat( i );
            break;
         case 7:
            flags = ev->GetInteger( i );
            break;
         default:
            break;
         }
      }
   channel |= CHAN_NO_PHS_ADD;
	RandomSound( name, volume, channel, attenuation, pitch, timeofs, fadetime, flags );
	}

EXPORT_FROM_DLL void Entity::RandomPHSSound
	(
	Event *ev
	)

	{
	str name;
	float volume;
   int   channel;
   float attenuation;
   float pitch;
   float timeofs;
   float fadetime;
   int flags;
   int i;
	
   //
   // set defaults
   //
   volume = 1.0f;
   channel = CHAN_BODY;
   attenuation = ATTN_NORM;
   pitch = 1.0f;
   timeofs = 0;
   fadetime = 0;
   flags = SOUND_SYNCH;
   for ( i = 1 ; i <= ev->NumArgs() ; i++ )
      {
      switch (i-1)
         {
         case 0:
            name = ev->GetString( i );
            break;
         case 1:
            volume = ev->GetFloat( i );
            break;
         case 2:
            channel = ev->GetInteger( i );
            break;
         case 3:
            attenuation = ev->GetFloat( i );
            break;
         case 4:
            pitch = ev->GetFloat( i );
            break;
         case 5:
            timeofs = ev->GetFloat( i );
            break;
         case 6:
            fadetime = ev->GetFloat( i );
            break;
         case 7:
            flags = ev->GetInteger( i );
            break;
         default:
            break;
         }
      }
	RandomSound( name, volume, channel, attenuation, pitch, timeofs, fadetime, flags );
	}

EXPORT_FROM_DLL void Entity::PHSSound
	(
	Event *ev
	)

	{
	char name[ 128 ];
	float volume;
   int channel;
   float attenuation;
   float pitch;
   float timeofs;
   float fadetime;
   int flags;
   int i;
	
   //
   // set defaults
   //
   name[0] = 0;
   volume = 1.0f;
   channel = CHAN_BODY;
   attenuation = ATTN_NORM;
   pitch = 1.0f;
   timeofs = 0;
   fadetime = 0;
   flags = SOUND_SYNCH;
   for ( i = 1 ; i <= ev->NumArgs() ; i++ )
      {
      switch (i-1)
         {
         case 0:
            strcpy( name, ev->GetString( i ) );
            break;
         case 1:
            volume = ev->GetFloat( i );
            break;
         case 2:
            channel = ev->GetInteger( i );
            break;
         case 3:
            attenuation = ev->GetFloat( i );
            break;
         case 4:
            pitch = ev->GetFloat( i );
            break;
         case 5:
            timeofs = ev->GetFloat( i );
            break;
         case 6:
            fadetime = ev->GetFloat( i );
            break;
         case 7:
            flags = ev->GetInteger( i );
            break;
         default:
            break;
         }
      }
	sound( name, volume, channel, attenuation, pitch, timeofs, fadetime, flags );
	}


EXPORT_FROM_DLL void Entity::EntitySound
	(
	Event *ev
	)

	{
   edict->s.sound = gi.soundindex( ev->GetString( 1 ) );
   if ( ev->NumArgs() > 1 )
      {
      int attenuation;
      attenuation = ev->GetInteger( 2 );
      if (attenuation > 3) attenuation = 3;
      if (attenuation < 0) attenuation = 0;
      edict->s.sound |= attenuation<<14;
      }
   else
      {
      edict->s.sound |= ATTN_IDLE<<14;
      }
	}

EXPORT_FROM_DLL void Entity::StopEntitySound
	(
	Event *ev
	)

	{
   edict->s.sound = 0;
	}

EXPORT_FROM_DLL void Entity::RandomEntitySound
	(
	Event *ev
	)

	{
   const char * alias;
	const char * soundname;

   alias = ev->GetString( 1 );

   soundname = gi.Alias_FindRandom( edict->s.modelindex, alias );
   edict->s.sound = gi.soundindex( soundname );
   if ( ev->NumArgs() > 1 )
      {
      int attenuation;
      attenuation = ev->GetInteger( 2 );
      if (attenuation > 3) attenuation = 3;
      if (attenuation < 0) attenuation = 0;
      edict->s.sound |= attenuation<<14;
      }
   else
      {
      edict->s.sound |= ATTN_IDLE<<14;
      }
	}

EXPORT_FROM_DLL void Entity::RandomGlobalEntitySound
	(
	str soundname,
   int attenuation
	)

	{
   const char * name;

   name = gi.GlobalAlias_FindRandom( soundname.c_str() );
   if ( name )
		{
      edict->s.sound = gi.soundindex( name );

      bound( attenuation, 0, 3 );
      edict->s.sound |= attenuation<<14;
		}
   else
		{
		warning( "RandomGlobalEntitySound", "Couldn't find alias for %s", soundname.c_str() );
		}
	}

EXPORT_FROM_DLL void Entity::RandomGlobalEntitySoundEvent
	(
	Event *ev
	)

	{
   const char *alias;
   int attenuation;

   alias = ev->GetString( 1 );

   attenuation = ATTN_IDLE;
   if ( ev->NumArgs() > 1 )
      {
      attenuation = ev->GetInteger( 2 );
      }

   RandomGlobalEntitySound( alias, attenuation );
   }

EXPORT_FROM_DLL qboolean Entity::attach
	(
   int parent_entity_num,
   int group_num,
   int tri_num,
   Vector orient
	)

	{
   int i;
   Entity * parent;

   if ( entnum == parent_entity_num )
      {
      warning("attach","Trying to attach to oneself." );
      return false;
      }

   if (edict->s.parent)
      detach();
   //
   // get the parent
   //
   parent = ( Entity * )G_GetEntity( parent_entity_num );

   if (parent->numchildren < MAX_MODEL_CHILDREN)
      {
      //
      // find a free spot in the parent
      //
      for ( i=0; i < MAX_MODEL_CHILDREN; i++ )
         if (!parent->children[i])
            {
            break;
            }
      parent->children[i] = entnum;
      parent->numchildren++;
      edict->s.parent = parent_entity_num;
      edict->s.bone.group_num = group_num;
      edict->s.bone.tri_num = tri_num;
      VectorCopy( orient.vec3(), edict->s.bone.orientation );
      setOrigin( origin );
      return true;
      }
   return false;
	}

EXPORT_FROM_DLL void Entity::detach
	(
	void
	)

	{
   int i;
   int num;
   Entity * parent;

   if (!edict->s.parent)
      return;
   parent = ( Entity * )G_GetEntity( edict->s.parent );
   if (!parent)
      return;
   for ( i=0,num = parent->numchildren; i < MAX_MODEL_CHILDREN; i++ )
      {
      if (!parent->children[i])
         {
         continue;
         }
      if (parent->children[i] == entnum)
         {
         parent->children[i] = 0;
         parent->numchildren--;
         break;
         }
      num--;
      if (!num)
         break;
      }
   edict->s.parent = 0;

   //
   // i don't think we want to do this automatically, we might later, but for right now lets not
   //
   //setOrigin( edict->origin + parent->origin );
	}

void Entity::AnimEvent
	(
	Event *ev
	)

	{
	int num;

	num = gi.Anim_Random( edict->s.modelindex, ev->GetString( 1 ) );
	NextAnim( num );
   if ( !animating )
      {
      StartAnimating();
      }
	}

void Entity::StartAnimatingEvent
	(
	Event *ev
	)

	{
   StartAnimating();
   }

void Entity::StopAnimatingEvent
	(
	Event *ev
	)

	{
	StopAnimating();
	}

void Entity::EndAnimEvent
	(
	Event *ev
	)

	{
   PostEvent( EV_LastFrame, 0 );
	if ( animDoneEvent )
		{
	   PostEvent( animDoneEvent, 0 );
		animDoneEvent = NULL;
		}
   next_frame = 0;
	}

void Entity::NextAnimEvent
	(
	Event *ev
	)

	{
	int num;

	num = gi.Anim_Random( edict->s.modelindex, ev->GetString( 1 ) );
	NextAnim( num );
	}

void Entity::NextFrameEvent
	(
	Event *ev
	)

	{
   NextFrame( ev->GetInteger( 1 ) );
	}

void Entity::PrevFrameEvent
	(
	Event *ev
	)

	{
	edict->s.prevframe = ev->GetInteger( 1 );
	}

void Entity::SetFrameEvent
	( 
	Event *ev
	)

   {
	int framenum;

	framenum = ev->GetInteger( 1 );
	edict->s.frame = framenum;
	NextFrame( framenum + 1 );
   }

void Entity::Tesselate(Event *ev)
   {
   Vector origin, dir, temp;
   Entity * ent;
   int i, power, min_size, max_size, thickness;
   float percentage;

   // dir is 1
   // power is 2
   // minsize is 3
   // maxsize is 4
   // percentage is 5
   // thickness 6
   // entity is 7
   // origin 8

   //
   // initialize some variables
   //
   ent = this;
   min_size = TESS_DEFAULT_MIN_SIZE;
   max_size = TESS_DEFAULT_MIN_SIZE;
   thickness = min_size;
   percentage = TESS_DEFAULT_PERCENT;
   VectorCopy( vec3_origin, origin );
   VectorCopy( vec3_origin, dir );

   for ( i = 1; i <= ev->NumArgs(); i++ )
      {
      switch( i )
         {
         case 1:
            temp = ev->GetVector( i );
            temp.AngleVectors( &dir, NULL, NULL );
            break;
         case 2:
            power = ev->GetInteger( i );
            break;
         case 3:
            min_size = ev->GetInteger( i );
            break;
         case 4:
            max_size = ev->GetInteger( i );
            break;
         case 5:
            percentage = ev->GetFloat( i );
            break;
         case 6:
            thickness = ev->GetInteger( i );
            break;
         case 7:
            ent = ev->GetEntity( i );
            break;
         case 8:
            origin = ev->GetVector( i );
            break;
         }
      }
   TesselateModel
      (
      ent,
      min_size,
      max_size,
      dir,
      power,
      percentage,
      thickness,
      origin
      );
   }

void Entity::SetShatterMinSize( Event *ev )
   {
   tess_min_size = ev->GetInteger( 1 );
   }

void Entity::SetShatterMaxSize( Event *ev )
   {
   tess_max_size = ev->GetInteger( 1 );
   }

void Entity::SetShatterThickness( Event *ev )
   {
   tess_thickness = ev->GetInteger( 1 );
   }

void Entity::SetShatterPercentage( Event *ev )
   {
   tess_percentage = ev->GetFloat( 1 ) / 100.0f;;
   }

void Entity::Flags( Event *ev )
   {
   const char *flag;
   int mask;
   int action;
   int i;

#define FLAG_IGNORE  0
#define FLAG_SET     1
#define FLAG_CLEAR   2
#define FLAG_ADD     3

   for ( i = 1; i <= ev->NumArgs(); i++ )
      {
      action = FLAG_IGNORE;
      flag = ev->GetString( i );
      switch( flag[0] )
         {
         case '+':
            action = FLAG_ADD;
            flag++;
            break;
         case '-':
            action = FLAG_CLEAR;
            flag++;
            break;
         default:
            action = FLAG_SET;
            break;
         }

      if ( !stricmp( flag, "blood" ) )
         mask = FL_BLOOD;
      else if ( !stricmp( flag, "sparks" ) )
         mask = FL_SPARKS;
      else if ( !stricmp( flag, "shatter" ) )
         mask = FL_TESSELATE;
      else if ( !stricmp( flag, "blast" ) )
         mask = FL_BLASTMARK;
      else if ( !stricmp( flag, "die_shatter" ) )
         mask = FL_DIE_TESSELATE;
      else if ( !stricmp( flag, "explode" ) )
         mask = FL_DIE_EXPLODE;
      else if ( !stricmp( flag, "die_gibs" ) )
         mask = FL_DIE_GIBS;
      else if ( !stricmp( flag, "darken" ) )
         mask = FL_DARKEN;
      else if ( !stricmp( flag, "forcefield" ) )
         mask = FL_FORCEFIELD;
      else if ( !stricmp( flag, "stealth" ) )
         mask = FL_STEALTH;
      else
         {
         action = FLAG_IGNORE;
         ev->Error( "Unknown flag '%s'", flag );
         }
      switch (action)
         {
         case FLAG_SET:
            // preserver non-configurable bits
            flags &= (FL_BLOOD - 1);
         case FLAG_ADD:
            flags |= mask;
            break;
         case FLAG_CLEAR:
            flags &= ~mask;
            break;
         case FLAG_IGNORE:
            break;
         }
      }
   if ( parentmode->value )
      {
      if ( flags & (FL_BLOOD|FL_DIE_GIBS) ) 
         {
         flags &= ~FL_BLOOD;
         flags &= ~FL_DIE_GIBS;
         }
      }
   }

void Entity::Effects( Event *ev )
   {
   const char *flag;
   int mask=0;
   int action;
   int i;

#define FLAG_IGNORE  0
#define FLAG_SET     1
#define FLAG_CLEAR   2
#define FLAG_ADD     3
   for ( i = 1; i <= ev->NumArgs(); i++ )
      {
      action = 0;
      flag = ev->GetString( i );
      switch( flag[0] )
         {
         case '+':
            action = FLAG_ADD;
            flag++;
            break;
         case '-':
            action = FLAG_CLEAR;
            flag++;
            break;
         default:
            action = FLAG_SET;
            break;
         }

      if ( !stricmp( flag, "rotate" ) )
         mask = EF_ROTATE;
      else if ( !stricmp( flag, "rocket" ) )
         mask = EF_ROCKET;
      else if ( !stricmp( flag, "gib" ) )
         mask = EF_GIB;
      else if ( !stricmp( flag, "pulse" ) )
         mask = EF_PULSE;
      else if ( !stricmp( flag, "everyframe" ) )
         mask = EF_EVERYFRAME;
      else if ( !stricmp( flag, "autoanimate" ) )
         mask = EF_AUTO_ANIMATE;
      else 
         {
         action = FLAG_IGNORE;
         ev->Error( "Unknown token %s.", flag );
         }

      switch (action)
         {
         case FLAG_SET:
         case FLAG_ADD:
            edict->s.effects |= mask;
            break;
         case FLAG_CLEAR:
            edict->s.effects &= ~mask;
            break;
         case FLAG_IGNORE:
            break;
         }
      }
   }

void Entity::RenderEffects( Event *ev )
   {
   const char *flag;
   int mask=0;
   int action;
   int i;

#define FLAG_IGNORE  0
#define FLAG_SET     1
#define FLAG_CLEAR   2
#define FLAG_ADD     3

   for ( i = 1; i <= ev->NumArgs(); i++ )
      {
      action = 0;
      flag = ev->GetString( i );
      switch( flag[0] )
         {
         case '+':
            action = FLAG_ADD;
            flag++;
            break;
         case '-':
            action = FLAG_CLEAR;
            flag++;
            break;
         default:
            action = FLAG_SET;
            break;
         }

      if ( !stricmp( flag, "minlight" ) )
         mask = RF_MINLIGHT;
      else if ( !stricmp( flag, "fullbright" ) )
         mask = RF_FULLBRIGHT;
      else if ( !stricmp( flag, "envmapped" ) )
         mask = RF_ENVMAPPED;
      else if ( !stricmp( flag, "glow" ) )
         mask = RF_GLOW;
      else if ( !stricmp( flag, "dontdraw" ) )
         mask = RF_DONTDRAW;
      else 
         {
         action = FLAG_IGNORE;
         ev->Error( "Unknown token %s.", flag );
         }

      switch (action)
         {
         case FLAG_SET:
         case FLAG_ADD:
            edict->s.renderfx |= mask;
            break;
         case FLAG_CLEAR:
            edict->s.renderfx &= ~mask;
            break;
         case FLAG_IGNORE:
            break;
         }
      }
   }

void Entity::BroadcastSound
	(
	Event *soundevent,
	int   channel,
	Event &event,
	float radius
	)

	{
	Sentient *ent;
	Vector	delta;
	Event		*ev;
	str		name;
   float    r2;
   float    dist2;
	float		volume;
   float		attenuation;
   float		pitch;
   float		timeofs;
   float		fadetime;
   int		flags;
   int		i;
	int		n;
#if 0
	int		count;

	count = 0;
#endif

	if ( ( ( int )event != ( int )NullEvent ) && !( this->flags & FL_NOTARGET ) )
		{
      r2 = radius * radius;
	   n = SentientList.NumObjects();
	   for( i = 1; i <= n; i++ )
		   {
		   ent = SentientList.ObjectAt( i );
         if ( ent->deadflag || ( ent == this ) )
            {
            continue;
            }

         delta = centroid - ent->centroid;

         // dot product returns length squared
         dist2 = delta * delta;
			if ( 
               ( dist2 <= r2 ) &&
               (
                  ( edict->areanum == ent->edict->areanum ) ||
                  ( gi.AreasConnected( edict->areanum, ent->edict->areanum ) )
               )
            )

				{
				ev = new Event( event );
				ev->AddEntity( this );
				ev->AddVector( worldorigin );
				ent->PostEvent( ev, 0 );
#if 0
				count++;
#endif
				}
			}
		}

#if 0
	gi.dprintf( "Broadcast sound to %d entities\n", count );
#endif

	if ( !soundevent->NumArgs() )
		{
		return;
		}

   //
   // set defaults
   //
   volume = 1.0f;
   attenuation = ATTN_NORM;
   pitch = 1.0f;
   timeofs = 0;
   fadetime = 0;
   flags = 0;
   for ( i = 1 ; i <= soundevent->NumArgs() ; i++ )
      {
      switch (i-1)
         {
         case 0:
            name = soundevent->GetString( i );
            break;
         case 1:
            volume = soundevent->GetFloat( i );
            break;
         case 2:
            attenuation = soundevent->GetFloat( i );
            break;
         case 3:
            pitch = soundevent->GetFloat( i );
            break;
         case 4:
            timeofs = soundevent->GetFloat( i );
            break;
         case 5:
            fadetime = soundevent->GetFloat( i );
            break;
         case 6:
            flags = soundevent->GetInteger( i );
            break;
         default:
            break;
         }
      }

	RandomSound( name.c_str(), volume, channel, attenuation, pitch, timeofs, fadetime, flags );
	}

void Entity::WeaponSound
	(
	Event *ev
	)

	{
   BroadcastSound( ev, CHAN_WEAPON, EV_HeardWeapon, SOUND_WEAPON_RADIUS );
	}
	
void Entity::MovementSound
	(
	Event *ev
	)

	{
   static int movement_count = 0;
   //
   // movement sounds now happen very infrequently, unless this is a client
   //
   if ( isClient() || ( movement_count++ > 15 ) )
      {
	   BroadcastSound( ev, CHAN_BODY, EV_HeardMovement, SOUND_MOVEMENT_RADIUS );
      if ( movement_count > 15 )
         movement_count = 0;
      }
	}
	
void Entity::PainSound
	(
	Event *ev
	)

	{
   BroadcastSound( ev, CHAN_VOICE, EV_HeardPain, SOUND_PAIN_RADIUS );
	}
	
void Entity::DeathSound
	(
	Event *ev
	)

	{
   BroadcastSound( ev, CHAN_VOICE, EV_HeardDeath, SOUND_DEATH_RADIUS );
	}

void Entity::BreakingSound
	(
	Event *ev
	)

	{
   BroadcastSound( ev, CHAN_AUTO, EV_HeardBreaking, SOUND_BREAKING_RADIUS );
	}

void Entity::DoorSound
	(
	Event *ev
	)

	{
   BroadcastSound( ev, CHAN_AUTO, EV_HeardDoor, SOUND_DOOR_RADIUS );
	}
	
void Entity::MutantSound
	(
	Event *ev
	)

	{
	BroadcastSound( ev, CHAN_VOICE, EV_HeardMutant, SOUND_MUTANT_RADIUS );
	}
	
void Entity::VoiceSound
	(
	Event *ev
	)

	{
   BroadcastSound( ev, CHAN_VOICE, EV_HeardVoice, SOUND_VOICE_RADIUS );
	}
	
void Entity::MachineSound
	(
	Event *ev
	)

	{
	BroadcastSound( ev, CHAN_AUTO, EV_HeardMachine, SOUND_MACHINE_RADIUS );
	}
	
void Entity::RadioSound
	(
	Event *ev
	)

	{
	BroadcastSound( ev, CHAN_VOICE, EV_HeardRadio, SOUND_RADIO_RADIUS );
	}

void Entity::SpawnParticles
	(
	Event *ev
	)

   {
   int i;
   Vector norm;
   int count;
   int lightstyle;

   norm = orientation[0];
   count = 4;
   lightstyle = 122;
   for ( i = 1 ; i <= ev->NumArgs() ; i++ )
      {
      switch( i )
         {
         case 1:
      	   norm = ev->GetVector( 1 );
            break;
         case 2:
            count = ev->GetInteger( 2 );
            break;
         case 3:
            lightstyle = ev->GetInteger( 3 );
            break;
         case 4:
            flags = ev->GetInteger( 4 );
         default:
            break;
         }
      }
   Particles( worldorigin, norm, count, lightstyle, flags );
   }

void Entity::Prethink
	(
	void
	)

	{
	}

void Entity::Postthink
	(
	void
	)

	{
	}

void Entity::SetWaterType
   (
   void
   )

   {
   qboolean isinwater;

	watertype = gi.pointcontents( worldorigin.vec3() );
	isinwater = watertype & MASK_WATER;

	if ( isinwater )
		{
		waterlevel = 1;
		}
	else
		{
		waterlevel = 0;
		}
   }

void Entity::DamageSkin
   (
   trace_t * trace,
   float damage
   )

   {
   int group;

   group = trace->intersect.parentgroup;
   if ( !edict->s.groups[ group ] )
		{
      edict->s.groups[ group ]++;
		}
   }

void Entity::Kill
	(
	Event *ev
	)
	
	{
	health = 0;
	Damage( this, this, 10, worldorigin, vec_zero, vec_zero, 0, 0, MOD_SUICIDE, -1, -1, 1.0f );
	}


void Entity::GroupModelEvent
	(
	Event *ev
	)
	
	{
   const char * group_name;
   const char * token;
   int i, group_num, argnum, flags;
   int mask;
   int action;
   qboolean do_all;

#define FLAG_IGNORE  0
#define FLAG_SET     1
#define FLAG_CLEAR   2
#define FLAG_ADD     3

   do_all = false;
   // "group" is first
   group_name = ev->GetString( 1 );
   if ( str( group_name ) != str( "all" ) )
      {
      group_num = gi.Group_NameToNum( edict->s.modelindex, group_name );
      if (group_num < 0)
         {
		   ev->Error( "group %s not found.", group_name );
         }
      }
   else
      {
      group_num = 0;
      do_all = true;
      }
   flags = 0;
   argnum = 2;
   for ( i = argnum; i <= ev->NumArgs() ; i++ )
      {
      token = ev->GetString( i );
      action = 0;
      switch( token[0] )
         {
         case '+':
            action = FLAG_ADD;
            token++;
            break;
         case '-':
            action = FLAG_CLEAR;
            token++;
            break;
         default:
            action = FLAG_SET;
            break;
         }
      if (!strcmpi( token, "skin1"))
         {
         mask = MDL_GROUP_SKINOFFSET_BIT0;
         }
      else if (!strcmpi (token, "skin2"))
         {
         mask = MDL_GROUP_SKINOFFSET_BIT1;
         }
      else if (!strcmpi (token, "nodraw"))
         {
         mask = MDL_GROUP_NODRAW;
         }
      else if (!strcmpi (token, "envmapped"))
         {
         mask = MDL_GROUP_ENVMAPPED_SILVER;
         }
      else if (!strcmpi (token, "goldenvmapped"))
         {
         mask = MDL_GROUP_ENVMAPPED_GOLD;
         }
      else if (!strcmpi (token, "translucent33"))
         {
         mask = MDL_GROUP_TRANSLUCENT_33;
         }
      else if (!strcmpi (token, "translucent66"))
         {
         mask = MDL_GROUP_TRANSLUCENT_66;
         }
      else if (!strcmpi (token, "fullbright"))
         {
         mask = MDL_GROUP_FULLBRIGHT;
         }
      else
         {
         ev->Error( "Unknown token %s.", token );
         action = FLAG_IGNORE;
         }
      for( ; group_num < edict->s.numgroups ; group_num++ )
         {
         switch (action)
            {
            case FLAG_SET:
               // clear out group
               edict->s.groups[ group_num ] = 0;
            case FLAG_ADD:
               edict->s.groups[ group_num ] |= mask;
               break;
            case FLAG_CLEAR:
               edict->s.groups[ group_num ] &= ~mask;
               break;
            case FLAG_IGNORE:
               break;
            }
         if ( !do_all )
            break;
         }
      }
	}

void Entity::DialogEvent
	(
	Event * ev
	)

	{
	str name;
	float volume;
   int   channel;
   float attenuation;
   float pitch;
   float timeofs;
   float fadetime;
   int flags;
   int i;
   str icon_name;
   str dialog_text;
	
   if ( ( dialog->value == 1 ) || ( dialog->value == 3 ) )
      {
      icon_name = ev->GetString( 1 );
      dialog_text = ev->GetString( 2 );
      SendDialog( icon_name.c_str(), dialog_text.c_str() );
      }
	
   if ( ( dialog->value == 0 ) || ( dialog->value == 1 ) )
      return;

   //
   // set defaults
   //
   volume = 1.0f;
   channel = CHAN_DIALOG_SECONDARY | CHAN_NO_PHS_ADD;
   attenuation = ATTN_NORM;
   pitch = 1.0f;
   timeofs = 0;
   fadetime = 0;
   flags = SOUND_SYNCH;
   for ( i = 3 ; i <= ev->NumArgs() ; i++ )
      {
      switch (i-3)
         {
         case 0:
            name = ev->GetString( i );
            break;
         case 1:
            volume = ev->GetFloat( i );
            break;
         case 2:
            channel = ev->GetInteger( i );
            break;
         case 3:
            attenuation = ev->GetFloat( i );
            break;
         case 4:
            pitch = ev->GetFloat( i );
            break;
         case 5:
            timeofs = ev->GetFloat( i );
            break;
         case 6:
            fadetime = ev->GetFloat( i );
            break;
         case 7:
            flags = ev->GetInteger( i );
            break;
         default:
            break;
         }
      }
	sound( name, volume, channel, attenuation, pitch, timeofs, fadetime, flags );
	}

void Entity::AttachEvent
	(
	Event * ev
	)
	{
   Entity * parent;
   const char * bone;
   int groupindex;
   int tri_num;
   vec3_t orient;

	parent = ev->GetEntity( 1 );
   bone = ev->GetString( 2 );
   
   if ( !parent )
      return;

   if ( gi.GetBoneInfo( parent->edict->s.modelindex, bone, &groupindex, &tri_num, orient ) )
      {
      attach( parent->entnum, groupindex, tri_num, Vector(orient) );
      }
   else
      {
      warning( "AttachEvent", "GetBoneInfo failed for bone %s", bone );
      }
   }

void Entity::AttachModelEvent
	(
	Event * ev
	)
	{
   ThrowObject * tobj;
   const char * bone;
   int groupindex;
   int tri_num;
   vec3_t orient;
   str modelname;

   tobj = new ThrowObject;

	modelname = ev->GetString( 1 );
   bone = ev->GetString( 2 );
   if ( ev->NumArgs() > 2 )
		{
      tobj->SetTargetName( ev->GetString( 3 ) );
		}

   tobj->setModel( modelname );

   if ( gi.GetBoneInfo( edict->s.modelindex, bone, &groupindex, &tri_num, orient ) )
      {
      tobj->attach( this->entnum, groupindex, tri_num, Vector(orient) );
      }
   else
      {
      warning( "AttachModelEvent", "GetBoneInfo failed for bone %s", bone );
      }
   }

void Entity::DetachEvent
	(
	Event * ev
	)

	{
   float trans[ 3 ][ 3 ];
   Entity * ent;
   vec3_t p1, p2;

   if ( edict->s.parent <= 0 )
		{
      return;
		}

   ent = ( Entity * )G_GetEntity( edict->s.parent );

   if ( gi.GetBoneTransform( ent->edict->s.modelindex, edict->s.bone.group_num, edict->s.bone.tri_num, edict->s.bone.orientation,
      ent->edict->s.anim, ent->edict->s.frame, ent->edict->s.scale, trans, p1 ) )
      {
      VectorAdd( p1, origin.vec3(), p1 );
		MatrixTransformVector( p1, ent->orientation, p2 );
      VectorAdd( p2, ent->worldorigin, p2 );
      }

   detach();

   setOrigin( p2 );
	worldorigin.copyTo( edict->s.old_origin );
   }

void Entity::TakeDamageEvent
	(
	Event * ev
	)
	{
   takedamage = DAMAGE_YES;
   }

void Entity::NoDamageEvent
	(
	Event * ev
	)
	{
   takedamage = DAMAGE_NO;
   }

void Entity::SetSkinEvent
	(
	Event *ev
	)

	{
	int num;

	num = gi.Skin_NumForName( edict->s.modelindex, ev->GetString( 1 ) );
   if ( num >= 0 )
		{
      edict->s.skinnum = num;
		}
   else
		{
      ev->Error( "Could not find %s as a skin name.\n", ev->GetString( 1 ) );
		}
	}

void Entity::Lightoffset
   (
   Event *ev
   )

   {
   edict->s.lightofs = ev->GetFloat( 1 );

   if ( edict->s.lightofs != 0 )
      edict->s.renderfx |= RF_LIGHTOFFSET;
   else
      edict->s.renderfx &= ~RF_LIGHTOFFSET;
   }

void Entity::Gravity
   (
   Event *ev
   )

   {
   gravity = ev->GetFloat( 1 );
   }

void Entity::Minlight
   (
   Event *ev
   )

   {
   
   if ( ev->GetInteger( 1 ) )
      edict->s.renderfx |= RF_MINLIGHT;
   else
      edict->s.renderfx &= ~RF_MINLIGHT;
   }

void Entity::UseBoundingBoxEvent
   (
   Event *ev
   )
   {
	edict->svflags |= SVF_USEBBOX;
   }

void Entity::HurtEvent
   (
   Event *ev 
   )
   {
   Vector normal;
   float dmg;

   if ( ev->NumArgs() < 1 )
      {
      dmg = 50;
      }
   else
      {
      dmg = ev->GetFloat( 1 );
      }
   normal = Vector( orientation[ 0 ] );
   Damage( world, world, dmg, worldorigin, vec_zero, normal, dmg, 0, MOD_CRUSH, -1, -1, 1.0f );
   }

void Entity::IfSkillEvent
	(
	Event *ev
	)

	{
   float skilllevel;

	skilllevel = ev->GetFloat( 1 );

   if ( skill->value == skilllevel )
      {
	   int			argc;
	   int			numargs;
      Event       *event;
	   int			i;

	   numargs = ev->NumArgs();
	   argc = numargs - 2 + 1;

      event = new Event( ev->GetToken( 2 ) );

	   for( i = 1; i < argc; i++ )
		   {
         event->AddToken( ev->GetToken( 2 + i ) );
		   }
      ProcessEvent( event );
		}
	}

void Entity::Censor
	(
	Event *ev
	)

	{
   Vector delta;
   float oldsize;
   float newsize;

   if ( !parentmode->value )
      return;

   oldsize = size.length();
	setSolidType( SOLID_NOT );
   setModel( "censored.def" );
   gi.CalculateBounds( edict->s.modelindex, 1, mins.vec3(), maxs.vec3() );
   delta = maxs - mins;
   newsize = delta.length();
   edict->s.scale = oldsize / newsize;
   mins *= edict->s.scale;
   maxs *= edict->s.scale;
   setSize( mins, maxs );
   setOrigin( origin );
	}
