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