~ubuntu-branches/ubuntu/precise/openarena/precise

« back to all changes in this revision

Viewing changes to code/game/g_utils.c

  • Committer: Bazaar Package Importer
  • Author(s): Bruno "Fuddl" Kleinert
  • Date: 2007-01-20 12:28:09 UTC
  • Revision ID: james.westby@ubuntu.com-20070120122809-2yza5ojt7nqiyiam
Tags: upstream-0.6.0
ImportĀ upstreamĀ versionĀ 0.6.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
===========================================================================
 
3
Copyright (C) 1999-2005 Id Software, Inc.
 
4
 
 
5
This file is part of Quake III Arena source code.
 
6
 
 
7
Quake III Arena source code is free software; you can redistribute it
 
8
and/or modify it under the terms of the GNU General Public License as
 
9
published by the Free Software Foundation; either version 2 of the License,
 
10
or (at your option) any later version.
 
11
 
 
12
Quake III Arena source code is distributed in the hope that it will be
 
13
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
GNU General Public License for more details.
 
16
 
 
17
You should have received a copy of the GNU General Public License
 
18
along with Quake III Arena source code; if not, write to the Free Software
 
19
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
20
===========================================================================
 
21
*/
 
22
//
 
23
// g_utils.c -- misc utility functions for game module
 
24
 
 
25
#include "g_local.h"
 
26
 
 
27
typedef struct {
 
28
  char oldShader[MAX_QPATH];
 
29
  char newShader[MAX_QPATH];
 
30
  float timeOffset;
 
31
} shaderRemap_t;
 
32
 
 
33
#define MAX_SHADER_REMAPS 128
 
34
 
 
35
int remapCount = 0;
 
36
shaderRemap_t remappedShaders[MAX_SHADER_REMAPS];
 
37
 
 
38
void AddRemap(const char *oldShader, const char *newShader, float timeOffset) {
 
39
        int i;
 
40
 
 
41
        for (i = 0; i < remapCount; i++) {
 
42
                if (Q_stricmp(oldShader, remappedShaders[i].oldShader) == 0) {
 
43
                        // found it, just update this one
 
44
                        strcpy(remappedShaders[i].newShader,newShader);
 
45
                        remappedShaders[i].timeOffset = timeOffset;
 
46
                        return;
 
47
                }
 
48
        }
 
49
        if (remapCount < MAX_SHADER_REMAPS) {
 
50
                strcpy(remappedShaders[remapCount].newShader,newShader);
 
51
                strcpy(remappedShaders[remapCount].oldShader,oldShader);
 
52
                remappedShaders[remapCount].timeOffset = timeOffset;
 
53
                remapCount++;
 
54
        }
 
55
}
 
56
 
 
57
const char *BuildShaderStateConfig(void) {
 
58
        static char     buff[MAX_STRING_CHARS*4];
 
59
        char out[(MAX_QPATH * 2) + 5];
 
60
        int i;
 
61
  
 
62
        memset(buff, 0, MAX_STRING_CHARS);
 
63
        for (i = 0; i < remapCount; i++) {
 
64
                Com_sprintf(out, (MAX_QPATH * 2) + 5, "%s=%s:%5.2f@", remappedShaders[i].oldShader, remappedShaders[i].newShader, remappedShaders[i].timeOffset);
 
65
                Q_strcat( buff, sizeof( buff ), out);
 
66
        }
 
67
        return buff;
 
68
}
 
69
 
 
70
/*
 
71
=========================================================================
 
72
 
 
73
model / sound configstring indexes
 
74
 
 
75
=========================================================================
 
76
*/
 
77
 
 
78
/*
 
79
================
 
80
G_FindConfigstringIndex
 
81
 
 
82
================
 
83
*/
 
84
int G_FindConfigstringIndex( char *name, int start, int max, qboolean create ) {
 
85
        int             i;
 
86
        char    s[MAX_STRING_CHARS];
 
87
 
 
88
        if ( !name || !name[0] ) {
 
89
                return 0;
 
90
        }
 
91
 
 
92
        for ( i=1 ; i<max ; i++ ) {
 
93
                trap_GetConfigstring( start + i, s, sizeof( s ) );
 
94
                if ( !s[0] ) {
 
95
                        break;
 
96
                }
 
97
                if ( !strcmp( s, name ) ) {
 
98
                        return i;
 
99
                }
 
100
        }
 
101
 
 
102
        if ( !create ) {
 
103
                return 0;
 
104
        }
 
105
 
 
106
        if ( i == max ) {
 
107
                G_Error( "G_FindConfigstringIndex: overflow" );
 
108
        }
 
109
 
 
110
        trap_SetConfigstring( start + i, name );
 
111
 
 
112
        return i;
 
113
}
 
114
 
 
115
 
 
116
int G_ModelIndex( char *name ) {
 
117
        return G_FindConfigstringIndex (name, CS_MODELS, MAX_MODELS, qtrue);
 
118
}
 
119
 
 
120
int G_SoundIndex( char *name ) {
 
121
        return G_FindConfigstringIndex (name, CS_SOUNDS, MAX_SOUNDS, qtrue);
 
122
}
 
123
 
 
124
//=====================================================================
 
125
 
 
126
 
 
127
/*
 
128
================
 
129
G_TeamCommand
 
130
 
 
131
Broadcasts a command to only a specific team
 
132
================
 
133
*/
 
134
void G_TeamCommand( team_t team, char *cmd ) {
 
135
        int             i;
 
136
 
 
137
        for ( i = 0 ; i < level.maxclients ; i++ ) {
 
138
                if ( level.clients[i].pers.connected == CON_CONNECTED ) {
 
139
                        if ( level.clients[i].sess.sessionTeam == team ) {
 
140
                                trap_SendServerCommand( i, va("%s", cmd ));
 
141
                        }
 
142
                }
 
143
        }
 
144
}
 
145
 
 
146
 
 
147
/*
 
148
=============
 
149
G_Find
 
150
 
 
151
Searches all active entities for the next one that holds
 
152
the matching string at fieldofs (use the FOFS() macro) in the structure.
 
153
 
 
154
Searches beginning at the entity after from, or the beginning if NULL
 
155
NULL will be returned if the end of the list is reached.
 
156
 
 
157
=============
 
158
*/
 
159
gentity_t *G_Find (gentity_t *from, int fieldofs, const char *match)
 
160
{
 
161
        char    *s;
 
162
 
 
163
        if (!from)
 
164
                from = g_entities;
 
165
        else
 
166
                from++;
 
167
 
 
168
        for ( ; from < &g_entities[level.num_entities] ; from++)
 
169
        {
 
170
                if (!from->inuse)
 
171
                        continue;
 
172
                s = *(char **) ((byte *)from + fieldofs);
 
173
                if (!s)
 
174
                        continue;
 
175
                if (!Q_stricmp (s, match))
 
176
                        return from;
 
177
        }
 
178
 
 
179
        return NULL;
 
180
}
 
181
 
 
182
 
 
183
/*
 
184
=============
 
185
G_PickTarget
 
186
 
 
187
Selects a random entity from among the targets
 
188
=============
 
189
*/
 
190
#define MAXCHOICES      32
 
191
 
 
192
gentity_t *G_PickTarget (char *targetname)
 
193
{
 
194
        gentity_t       *ent = NULL;
 
195
        int             num_choices = 0;
 
196
        gentity_t       *choice[MAXCHOICES];
 
197
 
 
198
        if (!targetname)
 
199
        {
 
200
                G_Printf("G_PickTarget called with NULL targetname\n");
 
201
                return NULL;
 
202
        }
 
203
 
 
204
        while(1)
 
205
        {
 
206
                ent = G_Find (ent, FOFS(targetname), targetname);
 
207
                if (!ent)
 
208
                        break;
 
209
                choice[num_choices++] = ent;
 
210
                if (num_choices == MAXCHOICES)
 
211
                        break;
 
212
        }
 
213
 
 
214
        if (!num_choices)
 
215
        {
 
216
                G_Printf("G_PickTarget: target %s not found\n", targetname);
 
217
                return NULL;
 
218
        }
 
219
 
 
220
        return choice[rand() % num_choices];
 
221
}
 
222
 
 
223
 
 
224
/*
 
225
==============================
 
226
G_UseTargets
 
227
 
 
228
"activator" should be set to the entity that initiated the firing.
 
229
 
 
230
Search for (string)targetname in all entities that
 
231
match (string)self.target and call their .use function
 
232
 
 
233
==============================
 
234
*/
 
235
void G_UseTargets( gentity_t *ent, gentity_t *activator ) {
 
236
        gentity_t               *t;
 
237
        
 
238
        if ( !ent ) {
 
239
                return;
 
240
        }
 
241
 
 
242
        if (ent->targetShaderName && ent->targetShaderNewName) {
 
243
                float f = level.time * 0.001;
 
244
                AddRemap(ent->targetShaderName, ent->targetShaderNewName, f);
 
245
                trap_SetConfigstring(CS_SHADERSTATE, BuildShaderStateConfig());
 
246
        }
 
247
 
 
248
        if ( !ent->target ) {
 
249
                return;
 
250
        }
 
251
 
 
252
        t = NULL;
 
253
        while ( (t = G_Find (t, FOFS(targetname), ent->target)) != NULL ) {
 
254
                if ( t == ent ) {
 
255
                        G_Printf ("WARNING: Entity used itself.\n");
 
256
                } else {
 
257
                        if ( t->use ) {
 
258
                                t->use (t, ent, activator);
 
259
                        }
 
260
                }
 
261
                if ( !ent->inuse ) {
 
262
                        G_Printf("entity was removed while using targets\n");
 
263
                        return;
 
264
                }
 
265
        }
 
266
}
 
267
 
 
268
 
 
269
/*
 
270
=============
 
271
TempVector
 
272
 
 
273
This is just a convenience function
 
274
for making temporary vectors for function calls
 
275
=============
 
276
*/
 
277
float   *tv( float x, float y, float z ) {
 
278
        static  int             index;
 
279
        static  vec3_t  vecs[8];
 
280
        float   *v;
 
281
 
 
282
        // use an array so that multiple tempvectors won't collide
 
283
        // for a while
 
284
        v = vecs[index];
 
285
        index = (index + 1)&7;
 
286
 
 
287
        v[0] = x;
 
288
        v[1] = y;
 
289
        v[2] = z;
 
290
 
 
291
        return v;
 
292
}
 
293
 
 
294
 
 
295
/*
 
296
=============
 
297
VectorToString
 
298
 
 
299
This is just a convenience function
 
300
for printing vectors
 
301
=============
 
302
*/
 
303
char    *vtos( const vec3_t v ) {
 
304
        static  int             index;
 
305
        static  char    str[8][32];
 
306
        char    *s;
 
307
 
 
308
        // use an array so that multiple vtos won't collide
 
309
        s = str[index];
 
310
        index = (index + 1)&7;
 
311
 
 
312
        Com_sprintf (s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]);
 
313
 
 
314
        return s;
 
315
}
 
316
 
 
317
 
 
318
/*
 
319
===============
 
320
G_SetMovedir
 
321
 
 
322
The editor only specifies a single value for angles (yaw),
 
323
but we have special constants to generate an up or down direction.
 
324
Angles will be cleared, because it is being used to represent a direction
 
325
instead of an orientation.
 
326
===============
 
327
*/
 
328
void G_SetMovedir( vec3_t angles, vec3_t movedir ) {
 
329
        static vec3_t VEC_UP            = {0, -1, 0};
 
330
        static vec3_t MOVEDIR_UP        = {0, 0, 1};
 
331
        static vec3_t VEC_DOWN          = {0, -2, 0};
 
332
        static vec3_t MOVEDIR_DOWN      = {0, 0, -1};
 
333
 
 
334
        if ( VectorCompare (angles, VEC_UP) ) {
 
335
                VectorCopy (MOVEDIR_UP, movedir);
 
336
        } else if ( VectorCompare (angles, VEC_DOWN) ) {
 
337
                VectorCopy (MOVEDIR_DOWN, movedir);
 
338
        } else {
 
339
                AngleVectors (angles, movedir, NULL, NULL);
 
340
        }
 
341
        VectorClear( angles );
 
342
}
 
343
 
 
344
 
 
345
float vectoyaw( const vec3_t vec ) {
 
346
        float   yaw;
 
347
        
 
348
        if (vec[YAW] == 0 && vec[PITCH] == 0) {
 
349
                yaw = 0;
 
350
        } else {
 
351
                if (vec[PITCH]) {
 
352
                        yaw = ( atan2( vec[YAW], vec[PITCH]) * 180 / M_PI );
 
353
                } else if (vec[YAW] > 0) {
 
354
                        yaw = 90;
 
355
                } else {
 
356
                        yaw = 270;
 
357
                }
 
358
                if (yaw < 0) {
 
359
                        yaw += 360;
 
360
                }
 
361
        }
 
362
 
 
363
        return yaw;
 
364
}
 
365
 
 
366
 
 
367
void G_InitGentity( gentity_t *e ) {
 
368
        e->inuse = qtrue;
 
369
        e->classname = "noclass";
 
370
        e->s.number = e - g_entities;
 
371
        e->r.ownerNum = ENTITYNUM_NONE;
 
372
}
 
373
 
 
374
/*
 
375
=================
 
376
G_Spawn
 
377
 
 
378
Either finds a free entity, or allocates a new one.
 
379
 
 
380
  The slots from 0 to MAX_CLIENTS-1 are always reserved for clients, and will
 
381
never be used by anything else.
 
382
 
 
383
Try to avoid reusing an entity that was recently freed, because it
 
384
can cause the client to think the entity morphed into something else
 
385
instead of being removed and recreated, which can cause interpolated
 
386
angles and bad trails.
 
387
=================
 
388
*/
 
389
gentity_t *G_Spawn( void ) {
 
390
        int                     i, force;
 
391
        gentity_t       *e;
 
392
 
 
393
        e = NULL;       // shut up warning
 
394
        i = 0;          // shut up warning
 
395
        for ( force = 0 ; force < 2 ; force++ ) {
 
396
                // if we go through all entities and can't find one to free,
 
397
                // override the normal minimum times before use
 
398
                e = &g_entities[MAX_CLIENTS];
 
399
                for ( i = MAX_CLIENTS ; i<level.num_entities ; i++, e++) {
 
400
                        if ( e->inuse ) {
 
401
                                continue;
 
402
                        }
 
403
 
 
404
                        // the first couple seconds of server time can involve a lot of
 
405
                        // freeing and allocating, so relax the replacement policy
 
406
                        if ( !force && e->freetime > level.startTime + 2000 && level.time - e->freetime < 1000 ) {
 
407
                                continue;
 
408
                        }
 
409
 
 
410
                        // reuse this slot
 
411
                        G_InitGentity( e );
 
412
                        return e;
 
413
                }
 
414
                if ( i != MAX_GENTITIES ) {
 
415
                        break;
 
416
                }
 
417
        }
 
418
        if ( i == ENTITYNUM_MAX_NORMAL ) {
 
419
                for (i = 0; i < MAX_GENTITIES; i++) {
 
420
                        G_Printf("%4i: %s\n", i, g_entities[i].classname);
 
421
                }
 
422
                G_Error( "G_Spawn: no free entities" );
 
423
        }
 
424
        
 
425
        // open up a new slot
 
426
        level.num_entities++;
 
427
 
 
428
        // let the server system know that there are more entities
 
429
        trap_LocateGameData( level.gentities, level.num_entities, sizeof( gentity_t ), 
 
430
                &level.clients[0].ps, sizeof( level.clients[0] ) );
 
431
 
 
432
        G_InitGentity( e );
 
433
        return e;
 
434
}
 
435
 
 
436
/*
 
437
=================
 
438
G_EntitiesFree
 
439
=================
 
440
*/
 
441
qboolean G_EntitiesFree( void ) {
 
442
        int                     i;
 
443
        gentity_t       *e;
 
444
 
 
445
        e = &g_entities[MAX_CLIENTS];
 
446
        for ( i = MAX_CLIENTS; i < level.num_entities; i++, e++) {
 
447
                if ( e->inuse ) {
 
448
                        continue;
 
449
                }
 
450
                // slot available
 
451
                return qtrue;
 
452
        }
 
453
        return qfalse;
 
454
}
 
455
 
 
456
 
 
457
/*
 
458
=================
 
459
G_FreeEntity
 
460
 
 
461
Marks the entity as free
 
462
=================
 
463
*/
 
464
void G_FreeEntity( gentity_t *ed ) {
 
465
        trap_UnlinkEntity (ed);         // unlink from world
 
466
 
 
467
        if ( ed->neverFree ) {
 
468
                return;
 
469
        }
 
470
 
 
471
        memset (ed, 0, sizeof(*ed));
 
472
        ed->classname = "freed";
 
473
        ed->freetime = level.time;
 
474
        ed->inuse = qfalse;
 
475
}
 
476
 
 
477
/*
 
478
=================
 
479
G_TempEntity
 
480
 
 
481
Spawns an event entity that will be auto-removed
 
482
The origin will be snapped to save net bandwidth, so care
 
483
must be taken if the origin is right on a surface (snap towards start vector first)
 
484
=================
 
485
*/
 
486
gentity_t *G_TempEntity( vec3_t origin, int event ) {
 
487
        gentity_t               *e;
 
488
        vec3_t          snapped;
 
489
 
 
490
        e = G_Spawn();
 
491
        e->s.eType = ET_EVENTS + event;
 
492
 
 
493
        e->classname = "tempEntity";
 
494
        e->eventTime = level.time;
 
495
        e->freeAfterEvent = qtrue;
 
496
 
 
497
        VectorCopy( origin, snapped );
 
498
        SnapVector( snapped );          // save network bandwidth
 
499
        G_SetOrigin( e, snapped );
 
500
 
 
501
        // find cluster for PVS
 
502
        trap_LinkEntity( e );
 
503
 
 
504
        return e;
 
505
}
 
506
 
 
507
 
 
508
 
 
509
/*
 
510
==============================================================================
 
511
 
 
512
Kill box
 
513
 
 
514
==============================================================================
 
515
*/
 
516
 
 
517
/*
 
518
=================
 
519
G_KillBox
 
520
 
 
521
Kills all entities that would touch the proposed new positioning
 
522
of ent.  Ent should be unlinked before calling this!
 
523
=================
 
524
*/
 
525
void G_KillBox (gentity_t *ent) {
 
526
        int                     i, num;
 
527
        int                     touch[MAX_GENTITIES];
 
528
        gentity_t       *hit;
 
529
        vec3_t          mins, maxs;
 
530
 
 
531
        VectorAdd( ent->client->ps.origin, ent->r.mins, mins );
 
532
        VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs );
 
533
        num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
 
534
 
 
535
        for (i=0 ; i<num ; i++) {
 
536
                hit = &g_entities[touch[i]];
 
537
                if ( !hit->client ) {
 
538
                        continue;
 
539
                }
 
540
 
 
541
                // nail it
 
542
                G_Damage ( hit, ent, ent, NULL, NULL,
 
543
                        100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
 
544
        }
 
545
 
 
546
}
 
547
 
 
548
//==============================================================================
 
549
 
 
550
/*
 
551
===============
 
552
G_AddPredictableEvent
 
553
 
 
554
Use for non-pmove events that would also be predicted on the
 
555
client side: jumppads and item pickups
 
556
Adds an event+parm and twiddles the event counter
 
557
===============
 
558
*/
 
559
void G_AddPredictableEvent( gentity_t *ent, int event, int eventParm ) {
 
560
        if ( !ent->client ) {
 
561
                return;
 
562
        }
 
563
        BG_AddPredictableEventToPlayerstate( event, eventParm, &ent->client->ps );
 
564
}
 
565
 
 
566
 
 
567
/*
 
568
===============
 
569
G_AddEvent
 
570
 
 
571
Adds an event+parm and twiddles the event counter
 
572
===============
 
573
*/
 
574
void G_AddEvent( gentity_t *ent, int event, int eventParm ) {
 
575
        int             bits;
 
576
 
 
577
        if ( !event ) {
 
578
                G_Printf( "G_AddEvent: zero event added for entity %i\n", ent->s.number );
 
579
                return;
 
580
        }
 
581
 
 
582
        // clients need to add the event in playerState_t instead of entityState_t
 
583
        if ( ent->client ) {
 
584
                bits = ent->client->ps.externalEvent & EV_EVENT_BITS;
 
585
                bits = ( bits + EV_EVENT_BIT1 ) & EV_EVENT_BITS;
 
586
                ent->client->ps.externalEvent = event | bits;
 
587
                ent->client->ps.externalEventParm = eventParm;
 
588
                ent->client->ps.externalEventTime = level.time;
 
589
        } else {
 
590
                bits = ent->s.event & EV_EVENT_BITS;
 
591
                bits = ( bits + EV_EVENT_BIT1 ) & EV_EVENT_BITS;
 
592
                ent->s.event = event | bits;
 
593
                ent->s.eventParm = eventParm;
 
594
        }
 
595
        ent->eventTime = level.time;
 
596
}
 
597
 
 
598
 
 
599
/*
 
600
=============
 
601
G_Sound
 
602
=============
 
603
*/
 
604
void G_Sound( gentity_t *ent, int channel, int soundIndex ) {
 
605
        gentity_t       *te;
 
606
 
 
607
        te = G_TempEntity( ent->r.currentOrigin, EV_GENERAL_SOUND );
 
608
        te->s.eventParm = soundIndex;
 
609
}
 
610
 
 
611
 
 
612
//==============================================================================
 
613
 
 
614
 
 
615
/*
 
616
================
 
617
G_SetOrigin
 
618
 
 
619
Sets the pos trajectory for a fixed position
 
620
================
 
621
*/
 
622
void G_SetOrigin( gentity_t *ent, vec3_t origin ) {
 
623
        VectorCopy( origin, ent->s.pos.trBase );
 
624
        ent->s.pos.trType = TR_STATIONARY;
 
625
        ent->s.pos.trTime = 0;
 
626
        ent->s.pos.trDuration = 0;
 
627
        VectorClear( ent->s.pos.trDelta );
 
628
 
 
629
        VectorCopy( origin, ent->r.currentOrigin );
 
630
}
 
631
 
 
632
/*
 
633
================
 
634
DebugLine
 
635
 
 
636
  debug polygons only work when running a local game
 
637
  with r_debugSurface set to 2
 
638
================
 
639
*/
 
640
int DebugLine(vec3_t start, vec3_t end, int color) {
 
641
        vec3_t points[4], dir, cross, up = {0, 0, 1};
 
642
        float dot;
 
643
 
 
644
        VectorCopy(start, points[0]);
 
645
        VectorCopy(start, points[1]);
 
646
        //points[1][2] -= 2;
 
647
        VectorCopy(end, points[2]);
 
648
        //points[2][2] -= 2;
 
649
        VectorCopy(end, points[3]);
 
650
 
 
651
 
 
652
        VectorSubtract(end, start, dir);
 
653
        VectorNormalize(dir);
 
654
        dot = DotProduct(dir, up);
 
655
        if (dot > 0.99 || dot < -0.99) VectorSet(cross, 1, 0, 0);
 
656
        else CrossProduct(dir, up, cross);
 
657
 
 
658
        VectorNormalize(cross);
 
659
 
 
660
        VectorMA(points[0], 2, cross, points[0]);
 
661
        VectorMA(points[1], -2, cross, points[1]);
 
662
        VectorMA(points[2], -2, cross, points[2]);
 
663
        VectorMA(points[3], 2, cross, points[3]);
 
664
 
 
665
        return trap_DebugPolygonCreate(color, 4, points);
 
666
}