1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 Copyright (C) 2000-2006 Tim Angus
5 
6 This file is part of Tremulous.
7 
8 Tremulous is free software; you can redistribute it
9 and/or modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the License,
11 or (at your option) any later version.
12 
13 Tremulous is distributed in the hope that it will be
14 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with Tremulous; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21 ===========================================================================
22 */
23 
24 #include "g_local.h"
25 
26 
27 /*QUAKED func_group (0 0 0) ?
28 Used to group brushes together just for editor convenience.  They are turned into normal brushes by the utilities.
29 */
30 
31 
32 /*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4)
33 Used as a positional target for calculations in the utilities (spotlights, etc), but removed during gameplay.
34 */
SP_info_null(gentity_t * self)35 void SP_info_null( gentity_t *self )
36 {
37   G_FreeEntity( self );
38 }
39 
40 
41 /*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
42 Used as a positional target for in-game calculation, like jumppad targets.
43 target_position does the same thing
44 */
SP_info_notnull(gentity_t * self)45 void SP_info_notnull( gentity_t *self )
46 {
47   G_SetOrigin( self, self->s.origin );
48 }
49 
50 
51 /*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) linear
52 Non-displayed light.
53 "light" overrides the default 300 intensity.
54 Linear checbox gives linear falloff instead of inverse square
55 Lights pointed at a target will be spotlights.
56 "radius" overrides the default 64 unit radius of a spotlight at the target point.
57 */
SP_light(gentity_t * self)58 void SP_light( gentity_t *self )
59 {
60   G_FreeEntity( self );
61 }
62 
63 
64 
65 /*
66 =================================================================================
67 
68 TELEPORTERS
69 
70 =================================================================================
71 */
72 
TeleportPlayer(gentity_t * player,vec3_t origin,vec3_t angles)73 void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles )
74 {
75   // unlink to make sure it can't possibly interfere with G_KillBox
76   trap_UnlinkEntity( player );
77 
78   VectorCopy( origin, player->client->ps.origin );
79   player->client->ps.origin[ 2 ] += 1;
80 
81   // spit the player out
82   AngleVectors( angles, player->client->ps.velocity, NULL, NULL );
83   VectorScale( player->client->ps.velocity, 400, player->client->ps.velocity );
84   player->client->ps.pm_time = 160;   // hold time
85   player->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
86 
87   // toggle the teleport bit so the client knows to not lerp
88   player->client->ps.eFlags ^= EF_TELEPORT_BIT;
89 
90   // set angles
91   SetClientViewAngle( player, angles );
92 
93   // kill anything at the destination
94   if( player->client->sess.sessionTeam != TEAM_SPECTATOR )
95     G_KillBox( player );
96 
97   // save results of pmove
98   BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue );
99 
100   // use the precise origin for linking
101   VectorCopy( player->client->ps.origin, player->r.currentOrigin );
102 
103   if( player->client->sess.sessionTeam != TEAM_SPECTATOR )
104     trap_LinkEntity (player);
105 }
106 
107 
108 /*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16)
109 Point teleporters at these.
110 Now that we don't have teleport destination pads, this is just
111 an info_notnull
112 */
SP_misc_teleporter_dest(gentity_t * ent)113 void SP_misc_teleporter_dest( gentity_t *ent )
114 {
115 }
116 
117 
118 //===========================================================
119 
120 /*QUAKED misc_model (1 0 0) (-16 -16 -16) (16 16 16)
121 "model"   arbitrary .md3 file to display
122 */
SP_misc_model(gentity_t * ent)123 void SP_misc_model( gentity_t *ent )
124 {
125 #if 0
126   ent->s.modelindex = G_ModelIndex( ent->model );
127   VectorSet (ent->mins, -16, -16, -16);
128   VectorSet (ent->maxs, 16, 16, 16);
129   trap_LinkEntity (ent);
130 
131   G_SetOrigin( ent, ent->s.origin );
132   VectorCopy( ent->s.angles, ent->s.apos.trBase );
133 #else
134   G_FreeEntity( ent );
135 #endif
136 }
137 
138 //===========================================================
139 
locateCamera(gentity_t * ent)140 void locateCamera( gentity_t *ent )
141 {
142   vec3_t    dir;
143   gentity_t *target;
144   gentity_t *owner;
145 
146   owner = G_PickTarget( ent->target );
147   if( !owner )
148   {
149     G_Printf( "Couldn't find target for misc_partal_surface\n" );
150     G_FreeEntity( ent );
151     return;
152   }
153   ent->r.ownerNum = owner->s.number;
154 
155   // frame holds the rotate speed
156   if( owner->spawnflags & 1 )
157     ent->s.frame = 25;
158   else if( owner->spawnflags & 2 )
159     ent->s.frame = 75;
160 
161   // swing camera ?
162   if( owner->spawnflags & 4 )
163   {
164     // set to 0 for no rotation at all
165     ent->s.powerups = 0;
166   }
167   else
168     ent->s.powerups = 1;
169 
170   // clientNum holds the rotate offset
171   ent->s.clientNum = owner->s.clientNum;
172 
173   VectorCopy( owner->s.origin, ent->s.origin2 );
174 
175   // see if the portal_camera has a target
176   target = G_PickTarget( owner->target );
177   if( target )
178   {
179     VectorSubtract( target->s.origin, owner->s.origin, dir );
180     VectorNormalize( dir );
181   }
182   else
183     G_SetMovedir( owner->s.angles, dir );
184 
185   ent->s.eventParm = DirToByte( dir );
186 }
187 
188 /*QUAKED misc_portal_surface (0 0 1) (-8 -8 -8) (8 8 8)
189 The portal surface nearest this entity will show a view from the targeted misc_portal_camera, or a mirror view if untargeted.
190 This must be within 64 world units of the surface!
191 */
SP_misc_portal_surface(gentity_t * ent)192 void SP_misc_portal_surface( gentity_t *ent )
193 {
194   VectorClear( ent->r.mins );
195   VectorClear( ent->r.maxs );
196   trap_LinkEntity( ent );
197 
198   ent->r.svFlags = SVF_PORTAL;
199   ent->s.eType = ET_PORTAL;
200 
201   if( !ent->target )
202   {
203     VectorCopy( ent->s.origin, ent->s.origin2 );
204   }
205   else
206   {
207     ent->think = locateCamera;
208     ent->nextthink = level.time + 100;
209   }
210 }
211 
212 /*QUAKED misc_portal_camera (0 0 1) (-8 -8 -8) (8 8 8) slowrotate fastrotate noswing
213 
214 The target for a misc_portal_director.  You can set either angles or target another entity to determine the direction of view.
215 "roll" an angle modifier to orient the camera around the target vector;
216 */
SP_misc_portal_camera(gentity_t * ent)217 void SP_misc_portal_camera( gentity_t *ent )
218 {
219   float roll;
220 
221   VectorClear( ent->r.mins );
222   VectorClear( ent->r.maxs );
223   trap_LinkEntity( ent );
224 
225   G_SpawnFloat( "roll", "0", &roll );
226 
227   ent->s.clientNum = roll / 360.0f * 256;
228 }
229 
230 /*
231 ======================================================================
232 
233   NEAT EFFECTS AND STUFF FOR TREMULOUS
234 
235 ======================================================================
236 */
237 
238 /*
239 ===============
240 SP_use_particle_system
241 
242 Use function for particle_system
243 ===============
244 */
SP_use_particle_system(gentity_t * self,gentity_t * other,gentity_t * activator)245 void SP_use_particle_system( gentity_t *self, gentity_t *other, gentity_t *activator )
246 {
247   //toggle EF_NODRAW
248   self->s.eFlags ^= EF_NODRAW;
249 }
250 
251 /*
252 ===============
253 SP_spawn_particle_system
254 
255 Spawn function for particle system
256 ===============
257 */
SP_misc_particle_system(gentity_t * self)258 void SP_misc_particle_system( gentity_t *self )
259 {
260   char  *s;
261 
262   G_SetOrigin( self, self->s.origin );
263 
264   G_SpawnString( "psName", "", &s );
265 
266   //add the particle system to the client precache list
267   self->s.modelindex = G_ParticleSystemIndex( s );
268 
269   if( self->spawnflags & 1 )
270     self->s.eFlags |= EF_NODRAW;
271 
272   self->use = SP_use_particle_system;
273   self->s.eType = ET_PARTICLE_SYSTEM;
274   trap_LinkEntity( self );
275 }
276 
277 /*
278 ===============
279 SP_use_anim_model
280 
281 Use function for anim model
282 ===============
283 */
SP_use_anim_model(gentity_t * self,gentity_t * other,gentity_t * activator)284 void SP_use_anim_model( gentity_t *self, gentity_t *other, gentity_t *activator )
285 {
286   if( self->spawnflags & 1 )
287   {
288     //if spawnflag 1 is set
289     //toggle EF_NODRAW
290     if( self->s.eFlags & EF_NODRAW )
291       self->s.eFlags &= ~EF_NODRAW;
292     else
293       self->s.eFlags |= EF_NODRAW;
294   }
295   else
296   {
297     //if the animation loops then toggle the animation
298     //toggle EF_MOVER_STOP
299     if( self->s.eFlags & EF_MOVER_STOP )
300       self->s.eFlags &= ~EF_MOVER_STOP;
301     else
302       self->s.eFlags |= EF_MOVER_STOP;
303   }
304 }
305 
306 /*
307 ===============
308 SP_misc_anim_model
309 
310 Spawn function for anim model
311 ===============
312 */
SP_misc_anim_model(gentity_t * self)313 void SP_misc_anim_model( gentity_t *self )
314 {
315   self->s.powerups  = (int)self->animation[ 0 ];
316   self->s.weapon    = (int)self->animation[ 1 ];
317   self->s.torsoAnim = (int)self->animation[ 2 ];
318   self->s.legsAnim  = (int)self->animation[ 3 ];
319 
320   self->s.angles2[ 0 ] = self->pos2[ 0 ];
321 
322   //add the model to the client precache list
323   self->s.modelindex = G_ModelIndex( self->model );
324 
325   self->use = SP_use_anim_model;
326 
327   self->s.eType = ET_ANIMMAPOBJ;
328 
329   trap_LinkEntity( self );
330 }
331 
332 /*
333 ===============
334 SP_use_light_flare
335 
336 Use function for light flare
337 ===============
338 */
SP_use_light_flare(gentity_t * self,gentity_t * other,gentity_t * activator)339 void SP_use_light_flare( gentity_t *self, gentity_t *other, gentity_t *activator )
340 {
341   self->s.eFlags ^= EF_NODRAW;
342 }
343 
344 /*
345 ===============
346 findEmptySpot
347 
348 Finds an empty spot radius units from origin
349 ==============
350 */
findEmptySpot(vec3_t origin,float radius,vec3_t spot)351 static void findEmptySpot( vec3_t origin, float radius, vec3_t spot )
352 {
353   int     i, j, k;
354   vec3_t  delta, test, total;
355   trace_t tr;
356 
357   VectorClear( total );
358 
359   //54(!) traces to test for empty spots
360   for( i = -1; i <= 1; i++ )
361   {
362     for( j = -1; j <= 1; j++ )
363     {
364       for( k = -1; k <= 1; k++ )
365       {
366         VectorSet( delta, ( i * radius ),
367                           ( j * radius ),
368                           ( k * radius ) );
369 
370         VectorAdd( origin, delta, test );
371 
372         trap_Trace( &tr, test, NULL, NULL, test, -1, MASK_SOLID );
373 
374         if( !tr.allsolid )
375         {
376           trap_Trace( &tr, test, NULL, NULL, origin, -1, MASK_SOLID );
377           VectorScale( delta, tr.fraction, delta );
378           VectorAdd( total, delta, total );
379         }
380       }
381     }
382   }
383 
384   VectorNormalize( total );
385   VectorScale( total, radius, total );
386   VectorAdd( origin, total, spot );
387 }
388 
389 /*
390 ===============
391 SP_misc_light_flare
392 
393 Spawn function for light flare
394 ===============
395 */
SP_misc_light_flare(gentity_t * self)396 void SP_misc_light_flare( gentity_t *self )
397 {
398   self->s.eType = ET_LIGHTFLARE;
399   self->s.modelindex = G_ShaderIndex( self->targetShaderName );
400   VectorCopy( self->pos2, self->s.origin2 );
401 
402   //try to find a spot near to the flare which is empty. This
403   //is used to facilitate visibility testing
404   findEmptySpot( self->s.origin, 8.0f, self->s.angles2 );
405 
406   self->use = SP_use_light_flare;
407 
408   G_SpawnFloat( "speed", "200", &self->speed );
409   self->s.time = self->speed;
410 
411   G_SpawnInt( "mindist", "0", &self->s.generic1 );
412 
413   if( self->spawnflags & 1 )
414     self->s.eFlags |= EF_NODRAW;
415 
416   trap_LinkEntity( self );
417 }
418