g_utils.c (11074B)
1 /* 2 Copyright (C) 1997-2001 Id Software, Inc. 3 4 This program is free software; you can redistribute it and/or 5 modify it under the terms of the GNU General Public License 6 as published by the Free Software Foundation; either version 2 7 of the License, or (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13 See the GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 19 */ 20 // g_utils.c -- misc utility functions for game module 21 22 #include "g_local.h" 23 24 25 void G_ProjectSource (vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result) 26 { 27 result[0] = point[0] + forward[0] * distance[0] + right[0] * distance[1]; 28 result[1] = point[1] + forward[1] * distance[0] + right[1] * distance[1]; 29 result[2] = point[2] + forward[2] * distance[0] + right[2] * distance[1] + distance[2]; 30 } 31 32 33 /* 34 ============= 35 G_Find 36 37 Searches all active entities for the next one that holds 38 the matching string at fieldofs (use the FOFS() macro) in the structure. 39 40 Searches beginning at the edict after from, or the beginning if NULL 41 NULL will be returned if the end of the list is reached. 42 43 ============= 44 */ 45 edict_t *G_Find (edict_t *from, int fieldofs, char *match) 46 { 47 char *s; 48 49 if (!from) 50 from = g_edicts; 51 else 52 from++; 53 54 for ( ; from < &g_edicts[globals.num_edicts] ; from++) 55 { 56 if (!from->inuse) 57 continue; 58 s = *(char **) ((byte *)from + fieldofs); 59 if (!s) 60 continue; 61 if (!Q_stricmp (s, match)) 62 return from; 63 } 64 65 return NULL; 66 } 67 68 69 /* 70 ================= 71 findradius 72 73 Returns entities that have origins within a spherical area 74 75 findradius (origin, radius) 76 ================= 77 */ 78 edict_t *findradius (edict_t *from, vec3_t org, float rad) 79 { 80 vec3_t eorg; 81 int j; 82 83 if (!from) 84 from = g_edicts; 85 else 86 from++; 87 for ( ; from < &g_edicts[globals.num_edicts]; from++) 88 { 89 if (!from->inuse) 90 continue; 91 if (from->solid == SOLID_NOT) 92 continue; 93 for (j=0 ; j<3 ; j++) 94 eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5); 95 if (VectorLength(eorg) > rad) 96 continue; 97 return from; 98 } 99 100 return NULL; 101 } 102 103 104 /* 105 ============= 106 G_PickTarget 107 108 Searches all active entities for the next one that holds 109 the matching string at fieldofs (use the FOFS() macro) in the structure. 110 111 Searches beginning at the edict after from, or the beginning if NULL 112 NULL will be returned if the end of the list is reached. 113 114 ============= 115 */ 116 #define MAXCHOICES 8 117 118 edict_t *G_PickTarget (char *targetname) 119 { 120 edict_t *ent = NULL; 121 int num_choices = 0; 122 edict_t *choice[MAXCHOICES]; 123 124 if (!targetname) 125 { 126 gi.dprintf("G_PickTarget called with NULL targetname\n"); 127 return NULL; 128 } 129 130 while(1) 131 { 132 ent = G_Find (ent, FOFS(targetname), targetname); 133 if (!ent) 134 break; 135 choice[num_choices++] = ent; 136 if (num_choices == MAXCHOICES) 137 break; 138 } 139 140 if (!num_choices) 141 { 142 gi.dprintf("G_PickTarget: target %s not found\n", targetname); 143 return NULL; 144 } 145 146 return choice[rand() % num_choices]; 147 } 148 149 150 151 void Think_Delay (edict_t *ent) 152 { 153 G_UseTargets (ent, ent->activator); 154 G_FreeEdict (ent); 155 } 156 157 /* 158 ============================== 159 G_UseTargets 160 161 the global "activator" should be set to the entity that initiated the firing. 162 163 If self.delay is set, a DelayedUse entity will be created that will actually 164 do the SUB_UseTargets after that many seconds have passed. 165 166 Centerprints any self.message to the activator. 167 168 Search for (string)targetname in all entities that 169 match (string)self.target and call their .use function 170 171 ============================== 172 */ 173 void G_UseTargets (edict_t *ent, edict_t *activator) 174 { 175 edict_t *t; 176 177 // 178 // check for a delay 179 // 180 if (ent->delay) 181 { 182 // create a temp object to fire at a later time 183 t = G_Spawn(); 184 t->classname = "DelayedUse"; 185 t->nextthink = level.time + ent->delay; 186 t->think = Think_Delay; 187 t->activator = activator; 188 if (!activator) 189 gi.dprintf ("Think_Delay with no activator\n"); 190 t->message = ent->message; 191 t->target = ent->target; 192 t->killtarget = ent->killtarget; 193 return; 194 } 195 196 197 // 198 // print the message 199 // 200 if ((ent->message) && !(activator->svflags & SVF_MONSTER)) 201 { 202 gi.centerprintf (activator, "%s", ent->message); 203 if (ent->noise_index) 204 gi.sound (activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0); 205 else 206 gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0); 207 } 208 209 // 210 // kill killtargets 211 // 212 if (ent->killtarget) 213 { 214 t = NULL; 215 while ((t = G_Find (t, FOFS(targetname), ent->killtarget))) 216 { 217 G_FreeEdict (t); 218 if (!ent->inuse) 219 { 220 gi.dprintf("entity was removed while using killtargets\n"); 221 return; 222 } 223 } 224 } 225 226 // 227 // fire targets 228 // 229 if (ent->target) 230 { 231 t = NULL; 232 while ((t = G_Find (t, FOFS(targetname), ent->target))) 233 { 234 // doors fire area portals in a specific way 235 if (!Q_stricmp(t->classname, "func_areaportal") && 236 (!Q_stricmp(ent->classname, "func_door") || !Q_stricmp(ent->classname, "func_door_rotating"))) 237 continue; 238 239 if (t == ent) 240 { 241 gi.dprintf ("WARNING: Entity used itself.\n"); 242 } 243 else 244 { 245 if (t->use) 246 t->use (t, ent, activator); 247 } 248 if (!ent->inuse) 249 { 250 gi.dprintf("entity was removed while using targets\n"); 251 return; 252 } 253 } 254 } 255 } 256 257 258 /* 259 ============= 260 TempVector 261 262 This is just a convenience function 263 for making temporary vectors for function calls 264 ============= 265 */ 266 float *tv (float x, float y, float z) 267 { 268 static int index; 269 static vec3_t vecs[8]; 270 float *v; 271 272 // use an array so that multiple tempvectors won't collide 273 // for a while 274 v = vecs[index]; 275 index = (index + 1)&7; 276 277 v[0] = x; 278 v[1] = y; 279 v[2] = z; 280 281 return v; 282 } 283 284 285 /* 286 ============= 287 VectorToString 288 289 This is just a convenience function 290 for printing vectors 291 ============= 292 */ 293 char *vtos (vec3_t v) 294 { 295 static int index; 296 static char str[8][32]; 297 char *s; 298 299 // use an array so that multiple vtos won't collide 300 s = str[index]; 301 index = (index + 1)&7; 302 303 Com_sprintf (s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]); 304 305 return s; 306 } 307 308 309 vec3_t VEC_UP = {0, -1, 0}; 310 vec3_t MOVEDIR_UP = {0, 0, 1}; 311 vec3_t VEC_DOWN = {0, -2, 0}; 312 vec3_t MOVEDIR_DOWN = {0, 0, -1}; 313 314 void G_SetMovedir (vec3_t angles, vec3_t movedir) 315 { 316 if (VectorCompare (angles, VEC_UP)) 317 { 318 VectorCopy (MOVEDIR_UP, movedir); 319 } 320 else if (VectorCompare (angles, VEC_DOWN)) 321 { 322 VectorCopy (MOVEDIR_DOWN, movedir); 323 } 324 else 325 { 326 AngleVectors (angles, movedir, NULL, NULL); 327 } 328 329 VectorClear (angles); 330 } 331 332 333 float vectoyaw (vec3_t vec) 334 { 335 float yaw; 336 337 if (/*vec[YAW] == 0 &&*/ vec[PITCH] == 0) 338 { 339 yaw = 0; 340 if (vec[YAW] > 0) 341 yaw = 90; 342 else if (vec[YAW] < 0) 343 yaw = -90; 344 } 345 else 346 { 347 yaw = (int) (atan2(vec[YAW], vec[PITCH]) * 180 / M_PI); 348 if (yaw < 0) 349 yaw += 360; 350 } 351 352 return yaw; 353 } 354 355 356 void vectoangles (vec3_t value1, vec3_t angles) 357 { 358 float forward; 359 float yaw, pitch; 360 361 if (value1[1] == 0 && value1[0] == 0) 362 { 363 yaw = 0; 364 if (value1[2] > 0) 365 pitch = 90; 366 else 367 pitch = 270; 368 } 369 else 370 { 371 if (value1[0]) 372 yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI); 373 else if (value1[1] > 0) 374 yaw = 90; 375 else 376 yaw = -90; 377 if (yaw < 0) 378 yaw += 360; 379 380 forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]); 381 pitch = (int) (atan2(value1[2], forward) * 180 / M_PI); 382 if (pitch < 0) 383 pitch += 360; 384 } 385 386 angles[PITCH] = -pitch; 387 angles[YAW] = yaw; 388 angles[ROLL] = 0; 389 } 390 391 char *G_CopyString (char *in) 392 { 393 char *out; 394 395 out = gi.TagMalloc (strlen(in)+1, TAG_LEVEL); 396 strcpy (out, in); 397 return out; 398 } 399 400 401 void G_InitEdict (edict_t *e) 402 { 403 e->inuse = true; 404 e->classname = "noclass"; 405 e->gravity = 1.0; 406 e->s.number = e - g_edicts; 407 } 408 409 /* 410 ================= 411 G_Spawn 412 413 Either finds a free edict, or allocates a new one. 414 Try to avoid reusing an entity that was recently freed, because it 415 can cause the client to think the entity morphed into something else 416 instead of being removed and recreated, which can cause interpolated 417 angles and bad trails. 418 ================= 419 */ 420 edict_t *G_Spawn (void) 421 { 422 int i; 423 edict_t *e; 424 425 e = &g_edicts[(int)maxclients->value+1]; 426 for ( i=maxclients->value+1 ; i<globals.num_edicts ; i++, e++) 427 { 428 // the first couple seconds of server time can involve a lot of 429 // freeing and allocating, so relax the replacement policy 430 if (!e->inuse && ( e->freetime < 2 || level.time - e->freetime > 0.5 ) ) 431 { 432 G_InitEdict (e); 433 return e; 434 } 435 } 436 437 if (i == game.maxentities) 438 gi.error ("ED_Alloc: no free edicts"); 439 440 globals.num_edicts++; 441 G_InitEdict (e); 442 return e; 443 } 444 445 /* 446 ================= 447 G_FreeEdict 448 449 Marks the edict as free 450 ================= 451 */ 452 void G_FreeEdict (edict_t *ed) 453 { 454 gi.unlinkentity (ed); // unlink from world 455 456 if ((ed - g_edicts) <= (maxclients->value + BODY_QUEUE_SIZE)) 457 { 458 // gi.dprintf("tried to free special edict\n"); 459 return; 460 } 461 462 memset (ed, 0, sizeof(*ed)); 463 ed->classname = "freed"; 464 ed->freetime = level.time; 465 ed->inuse = false; 466 } 467 468 469 /* 470 ============ 471 G_TouchTriggers 472 473 ============ 474 */ 475 void G_TouchTriggers (edict_t *ent) 476 { 477 int i, num; 478 edict_t *touch[MAX_EDICTS], *hit; 479 480 // dead things don't activate triggers! 481 if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0)) 482 return; 483 484 num = gi.BoxEdicts (ent->absmin, ent->absmax, touch 485 , MAX_EDICTS, AREA_TRIGGERS); 486 487 // be careful, it is possible to have an entity in this 488 // list removed before we get to it (killtriggered) 489 for (i=0 ; i<num ; i++) 490 { 491 hit = touch[i]; 492 if (!hit->inuse) 493 continue; 494 if (!hit->touch) 495 continue; 496 hit->touch (hit, ent, NULL, NULL); 497 } 498 } 499 500 /* 501 ============ 502 G_TouchSolids 503 504 Call after linking a new trigger in during gameplay 505 to force all entities it covers to immediately touch it 506 ============ 507 */ 508 void G_TouchSolids (edict_t *ent) 509 { 510 int i, num; 511 edict_t *touch[MAX_EDICTS], *hit; 512 513 num = gi.BoxEdicts (ent->absmin, ent->absmax, touch 514 , MAX_EDICTS, AREA_SOLID); 515 516 // be careful, it is possible to have an entity in this 517 // list removed before we get to it (killtriggered) 518 for (i=0 ; i<num ; i++) 519 { 520 hit = touch[i]; 521 if (!hit->inuse) 522 continue; 523 if (ent->touch) 524 ent->touch (hit, ent, NULL, NULL); 525 if (!ent->inuse) 526 break; 527 } 528 } 529 530 531 532 533 /* 534 ============================================================================== 535 536 Kill box 537 538 ============================================================================== 539 */ 540 541 /* 542 ================= 543 KillBox 544 545 Kills all entities that would touch the proposed new positioning 546 of ent. Ent should be unlinked before calling this! 547 ================= 548 */ 549 qboolean KillBox (edict_t *ent) 550 { 551 trace_t tr; 552 553 while (1) 554 { 555 tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID); 556 if (!tr.ent) 557 break; 558 559 // nail it 560 T_Damage (tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG); 561 562 // if we didn't kill it, fail 563 if (tr.ent->solid) 564 return false; 565 } 566 567 return true; // all clear 568 }