Quake II RTX doxygen  1.0 dev
g_combat.c
Go to the documentation of this file.
1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (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. See the
12 GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18 // g_combat.c
19 
20 #include "g_local.h"
21 
22 /*
23 ============
24 CanDamage
25 
26 Returns qtrue if the inflictor can directly damage the target. Used for
27 explosions and melee attacks.
28 ============
29 */
30 qboolean CanDamage(edict_t *targ, edict_t *inflictor)
31 {
32  vec3_t dest;
33  trace_t trace;
34 
35 // bmodels need special checking because their origin is 0,0,0
36  if (targ->movetype == MOVETYPE_PUSH) {
37  VectorAdd(targ->absmin, targ->absmax, dest);
38  VectorScale(dest, 0.5, dest);
39  trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
40  if (trace.fraction == 1.0)
41  return qtrue;
42  if (trace.ent == targ)
43  return qtrue;
44  return qfalse;
45  }
46 
47  trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, targ->s.origin, inflictor, MASK_SOLID);
48  if (trace.fraction == 1.0)
49  return qtrue;
50 
51  VectorCopy(targ->s.origin, dest);
52  dest[0] += 15.0;
53  dest[1] += 15.0;
54  trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
55  if (trace.fraction == 1.0)
56  return qtrue;
57 
58  VectorCopy(targ->s.origin, dest);
59  dest[0] += 15.0;
60  dest[1] -= 15.0;
61  trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
62  if (trace.fraction == 1.0)
63  return qtrue;
64 
65  VectorCopy(targ->s.origin, dest);
66  dest[0] -= 15.0;
67  dest[1] += 15.0;
68  trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
69  if (trace.fraction == 1.0)
70  return qtrue;
71 
72  VectorCopy(targ->s.origin, dest);
73  dest[0] -= 15.0;
74  dest[1] -= 15.0;
75  trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
76  if (trace.fraction == 1.0)
77  return qtrue;
78 
79 
80  return qfalse;
81 }
82 
83 
84 /*
85 ============
86 Killed
87 ============
88 */
89 void Killed(edict_t *targ, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
90 {
91  if (targ->health < -999)
92  targ->health = -999;
93 
94  targ->enemy = attacker;
95 
96  if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD)) {
97 // targ->svflags |= SVF_DEADMONSTER; // now treat as a different content type
98  if (!(targ->monsterinfo.aiflags & AI_GOOD_GUY)) {
100  if (coop->value && attacker->client)
101  attacker->client->resp.score++;
102  // medics won't heal monsters that they kill themselves
103  if (strcmp(attacker->classname, "monster_medic") == 0)
104  targ->owner = attacker;
105  }
106  }
107 
108  if (targ->movetype == MOVETYPE_PUSH || targ->movetype == MOVETYPE_STOP || targ->movetype == MOVETYPE_NONE) {
109  // doors, triggers, etc
110  targ->die(targ, inflictor, attacker, damage, point);
111  return;
112  }
113 
114  if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD)) {
115  targ->touch = NULL;
116  monster_death_use(targ);
117  }
118 
119  targ->die(targ, inflictor, attacker, damage, point);
120 }
121 
122 
123 /*
124 ================
125 SpawnDamage
126 ================
127 */
128 void SpawnDamage(int type, vec3_t origin, vec3_t normal, int damage)
129 {
130  if (damage > 255)
131  damage = 255;
132  gi.WriteByte(svc_temp_entity);
133  gi.WriteByte(type);
134 // gi.WriteByte (damage);
135  gi.WritePosition(origin);
136  gi.WriteDir(normal);
137  gi.multicast(origin, MULTICAST_PVS);
138 }
139 
140 
141 /*
142 ============
143 T_Damage
144 
145 targ entity that is being damaged
146 inflictor entity that is causing the damage
147 attacker entity that caused the inflictor to damage targ
148  example: targ=monster, inflictor=rocket, attacker=player
149 
150 dir direction of the attack
151 point point at which the damage is being inflicted
152 normal normal vector from that point
153 damage amount of damage being inflicted
154 knockback force to be applied against targ as a result of the damage
155 
156 dflags these flags are used to control how T_Damage works
157  DAMAGE_RADIUS damage was indirect (from a nearby explosion)
158  DAMAGE_NO_ARMOR armor does not protect from this damage
159  DAMAGE_ENERGY damage is from an energy based weapon
160  DAMAGE_NO_KNOCKBACK do not affect velocity, just view angles
161  DAMAGE_BULLET damage is from a bullet (used for ricochets)
162  DAMAGE_NO_PROTECTION kills godmode, armor, everything
163 ============
164 */
165 static int CheckPowerArmor(edict_t *ent, vec3_t point, vec3_t normal, int damage, int dflags)
166 {
167  gclient_t *client;
168  int save;
169  int power_armor_type;
170  int index;
171  int damagePerCell;
172  int pa_te_type;
173  int power;
174  int power_used;
175 
176  if (!damage)
177  return 0;
178 
179  client = ent->client;
180 
181  if (dflags & DAMAGE_NO_ARMOR)
182  return 0;
183 
184  index = 0; // shut up gcc
185 
186  if (client) {
187  power_armor_type = PowerArmorType(ent);
188  if (power_armor_type != POWER_ARMOR_NONE) {
189  index = ITEM_INDEX(FindItem("Cells"));
190  power = client->pers.inventory[index];
191  }
192  } else if (ent->svflags & SVF_MONSTER) {
193  power_armor_type = ent->monsterinfo.power_armor_type;
194  power = ent->monsterinfo.power_armor_power;
195  } else
196  return 0;
197 
198  if (power_armor_type == POWER_ARMOR_NONE)
199  return 0;
200  if (!power)
201  return 0;
202 
203  if (power_armor_type == POWER_ARMOR_SCREEN) {
204  vec3_t vec;
205  float dot;
206  vec3_t forward;
207 
208  // only works if damage point is in front
209  AngleVectors(ent->s.angles, forward, NULL, NULL);
210  VectorSubtract(point, ent->s.origin, vec);
211  VectorNormalize(vec);
212  dot = DotProduct(vec, forward);
213  if (dot <= 0.3)
214  return 0;
215 
216  damagePerCell = 1;
217  pa_te_type = TE_SCREEN_SPARKS;
218  damage = damage / 3;
219  } else {
220  damagePerCell = 2;
221  pa_te_type = TE_SHIELD_SPARKS;
222  damage = (2 * damage) / 3;
223  }
224 
225  save = power * damagePerCell;
226  if (!save)
227  return 0;
228  if (save > damage)
229  save = damage;
230 
231  SpawnDamage(pa_te_type, point, normal, save);
232  ent->powerarmor_time = level.time + 0.2;
233 
234  power_used = save / damagePerCell;
235 
236  if (client)
237  client->pers.inventory[index] -= power_used;
238  else
239  ent->monsterinfo.power_armor_power -= power_used;
240  return save;
241 }
242 
243 static int CheckArmor(edict_t *ent, vec3_t point, vec3_t normal, int damage, int te_sparks, int dflags)
244 {
245  gclient_t *client;
246  int save;
247  int index;
248  gitem_t *armor;
249 
250  if (!damage)
251  return 0;
252 
253  client = ent->client;
254 
255  if (!client)
256  return 0;
257 
258  if (dflags & DAMAGE_NO_ARMOR)
259  return 0;
260 
261  index = ArmorIndex(ent);
262  if (!index)
263  return 0;
264 
265  armor = GetItemByIndex(index);
266 
267  if (dflags & DAMAGE_ENERGY)
268  save = ceil(((gitem_armor_t *)armor->info)->energy_protection * damage);
269  else
270  save = ceil(((gitem_armor_t *)armor->info)->normal_protection * damage);
271  if (save >= client->pers.inventory[index])
272  save = client->pers.inventory[index];
273 
274  if (!save)
275  return 0;
276 
277  client->pers.inventory[index] -= save;
278  SpawnDamage(te_sparks, point, normal, save);
279 
280  return save;
281 }
282 
283 void M_ReactToDamage(edict_t *targ, edict_t *attacker)
284 {
285  if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER))
286  return;
287 
288  if (attacker == targ || attacker == targ->enemy)
289  return;
290 
291  // if we are a good guy monster and our attacker is a player
292  // or another good guy, do not get mad at them
293  if (targ->monsterinfo.aiflags & AI_GOOD_GUY) {
294  if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY))
295  return;
296  }
297 
298  // we now know that we are not both good guys
299 
300  // if attacker is a client, get mad at them because he's good and we're not
301  if (attacker->client) {
302  targ->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
303 
304  // this can only happen in coop (both new and old enemies are clients)
305  // only switch if can't see the current enemy
306  if (targ->enemy && targ->enemy->client) {
307  if (visible(targ, targ->enemy)) {
308  targ->oldenemy = attacker;
309  return;
310  }
311  targ->oldenemy = targ->enemy;
312  }
313  targ->enemy = attacker;
314  if (!(targ->monsterinfo.aiflags & AI_DUCKED))
315  FoundTarget(targ);
316  return;
317  }
318 
319  // it's the same base (walk/swim/fly) type and a different classname and it's not a tank
320  // (they spray too much), get mad at them
321  if (((targ->flags & (FL_FLY | FL_SWIM)) == (attacker->flags & (FL_FLY | FL_SWIM))) &&
322  (strcmp(targ->classname, attacker->classname) != 0) &&
323  (strcmp(attacker->classname, "monster_tank") != 0) &&
324  (strcmp(attacker->classname, "monster_supertank") != 0) &&
325  (strcmp(attacker->classname, "monster_makron") != 0) &&
326  (strcmp(attacker->classname, "monster_jorg") != 0)) {
327  if (targ->enemy && targ->enemy->client)
328  targ->oldenemy = targ->enemy;
329  targ->enemy = attacker;
330  if (!(targ->monsterinfo.aiflags & AI_DUCKED))
331  FoundTarget(targ);
332  }
333  // if they *meant* to shoot us, then shoot back
334  else if (attacker->enemy == targ) {
335  if (targ->enemy && targ->enemy->client)
336  targ->oldenemy = targ->enemy;
337  targ->enemy = attacker;
338  if (!(targ->monsterinfo.aiflags & AI_DUCKED))
339  FoundTarget(targ);
340  }
341  // otherwise get mad at whoever they are mad at (help our buddy) unless it is us!
342  else if (attacker->enemy && attacker->enemy != targ) {
343  if (targ->enemy && targ->enemy->client)
344  targ->oldenemy = targ->enemy;
345  targ->enemy = attacker->enemy;
346  if (!(targ->monsterinfo.aiflags & AI_DUCKED))
347  FoundTarget(targ);
348  }
349 }
350 
351 qboolean CheckTeamDamage(edict_t *targ, edict_t *attacker)
352 {
353  //FIXME make the next line real and uncomment this block
354  // if ((ability to damage a teammate == OFF) && (targ's team == attacker's team))
355  return qfalse;
356 }
357 
358 void T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod)
359 {
360  gclient_t *client;
361  int take;
362  int save;
363  int asave;
364  int psave;
365  int te_sparks;
366 
367  if (!targ->takedamage)
368  return;
369 
370  // friendly fire avoidance
371  // if enabled you can't hurt teammates (but you can hurt yourself)
372  // knockback still occurs
373  if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value)) {
374  if (OnSameTeam(targ, attacker)) {
375  if ((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE)
376  damage = 0;
377  else
378  mod |= MOD_FRIENDLY_FIRE;
379  }
380  }
381  meansOfDeath = mod;
382 
383  // easy mode takes half damage
384  if (skill->value == 0 && deathmatch->value == 0 && targ->client) {
385  damage *= 0.5;
386  if (!damage)
387  damage = 1;
388  }
389 
390  client = targ->client;
391 
392  if (dflags & DAMAGE_BULLET)
393  te_sparks = TE_BULLET_SPARKS;
394  else
395  te_sparks = TE_SPARKS;
396 
397  VectorNormalize(dir);
398 
399 // bonus damage for suprising a monster
400  if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0))
401  damage *= 2;
402 
403  if (targ->flags & FL_NO_KNOCKBACK)
404  knockback = 0;
405 
406 // figure momentum add
407  if (!(dflags & DAMAGE_NO_KNOCKBACK)) {
408  if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP)) {
409  vec3_t kvel;
410  float mass;
411 
412  if (targ->mass < 50)
413  mass = 50;
414  else
415  mass = targ->mass;
416 
417  if (targ->client && attacker == targ)
418  VectorScale(dir, 1600.0 * (float)knockback / mass, kvel); // the rocket jump hack...
419  else
420  VectorScale(dir, 500.0 * (float)knockback / mass, kvel);
421 
422  VectorAdd(targ->velocity, kvel, targ->velocity);
423  }
424  }
425 
426  take = damage;
427  save = 0;
428 
429  // check for godmode
430  if ((targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION)) {
431  take = 0;
432  save = damage;
433  SpawnDamage(te_sparks, point, normal, save);
434  }
435 
436  // check for invincibility
437  if ((client && client->invincible_framenum > level.framenum) && !(dflags & DAMAGE_NO_PROTECTION)) {
438  if (targ->pain_debounce_time < level.time) {
439  gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0);
440  targ->pain_debounce_time = level.time + 2;
441  }
442  take = 0;
443  save = damage;
444  }
445 
446  psave = CheckPowerArmor(targ, point, normal, take, dflags);
447  take -= psave;
448 
449  asave = CheckArmor(targ, point, normal, take, te_sparks, dflags);
450  take -= asave;
451 
452  //treat cheat/powerup savings the same as armor
453  asave += save;
454 
455  // team damage avoidance
456  if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage(targ, attacker))
457  return;
458 
459 // do the damage
460  if (take) {
461  if ((targ->svflags & SVF_MONSTER) || (client))
462  {
463  // SpawnDamage(TE_BLOOD, point, normal, take);
464  SpawnDamage(TE_BLOOD, point, dir, take);
465  }
466  else
467  SpawnDamage(te_sparks, point, normal, take);
468 
469 
470  targ->health = targ->health - take;
471 
472  if (targ->health <= 0) {
473  if ((targ->svflags & SVF_MONSTER) || (client))
474  targ->flags |= FL_NO_KNOCKBACK;
475  Killed(targ, inflictor, attacker, take, point);
476  return;
477  }
478  }
479 
480  if (targ->svflags & SVF_MONSTER) {
481  M_ReactToDamage(targ, attacker);
482  if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take)) {
483  targ->pain(targ, attacker, knockback, take);
484  // nightmare mode monsters don't go into pain frames often
485  if (skill->value == 3)
486  targ->pain_debounce_time = level.time + 5;
487  }
488  } else if (client) {
489  if (!(targ->flags & FL_GODMODE) && (take))
490  targ->pain(targ, attacker, knockback, take);
491  } else if (take) {
492  if (targ->pain)
493  targ->pain(targ, attacker, knockback, take);
494  }
495 
496  // add to the damage inflicted on a player this frame
497  // the total will be turned into screen blends and view angle kicks
498  // at the end of the frame
499  if (client) {
500  client->damage_parmor += psave;
501  client->damage_armor += asave;
502  client->damage_blood += take;
503  client->damage_knockback += knockback;
504  VectorCopy(point, client->damage_from);
505  }
506 }
507 
508 
509 /*
510 ============
511 T_RadiusDamage
512 ============
513 */
514 void T_RadiusDamage(edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod)
515 {
516  float points;
517  edict_t *ent = NULL;
518  vec3_t v;
519  vec3_t dir;
520 
521  while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL) {
522  if (ent == ignore)
523  continue;
524  if (!ent->takedamage)
525  continue;
526 
527  VectorAdd(ent->mins, ent->maxs, v);
528  VectorMA(ent->s.origin, 0.5, v, v);
529  VectorSubtract(inflictor->s.origin, v, v);
530  points = damage - 0.5 * VectorLength(v);
531  if (ent == attacker)
532  points = points * 0.5;
533  if (points > 0) {
534  if (CanDamage(ent, inflictor)) {
535  VectorSubtract(ent->s.origin, inflictor->s.origin, dir);
536  T_Damage(ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
537  }
538  }
539  }
540 }
FindItem
gitem_t * FindItem(char *pickup_name)
Definition: g_items.c:98
gi
game_import_t gi
Definition: g_main.c:23
deathmatch
cvar_t * deathmatch
Definition: g_main.c:33
DEAD_DEAD
#define DEAD_DEAD
Definition: g_local.h:112
AI_GOOD_GUY
#define AI_GOOD_GUY
Definition: g_local.h:134
PowerArmorType
int PowerArmorType(edict_t *ent)
Definition: g_items.c:655
MOVETYPE_STOP
@ MOVETYPE_STOP
Definition: g_local.h:189
DAMAGE_ENERGY
#define DAMAGE_ENERGY
Definition: g_local.h:649
ITEM_INDEX
#define ITEM_INDEX(x)
Definition: g_local.h:600
monster_death_use
void monster_death_use(edict_t *self)
Definition: g_monster.c:470
DAMAGE_RADIUS
#define DAMAGE_RADIUS
Definition: g_local.h:647
MOVETYPE_PUSH
@ MOVETYPE_PUSH
Definition: g_local.h:188
FL_NO_KNOCKBACK
#define FL_NO_KNOCKBACK
Definition: g_local.h:70
meansOfDeath
int meansOfDeath
Definition: g_main.c:29
ArmorIndex
int ArmorIndex(edict_t *ent)
Definition: g_items.c:563
T_Damage
void T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod)
Definition: g_combat.c:358
DAMAGE_NO_KNOCKBACK
#define DAMAGE_NO_KNOCKBACK
Definition: g_local.h:650
FL_SWIM
#define FL_SWIM
Definition: g_local.h:60
POWER_ARMOR_SCREEN
#define POWER_ARMOR_SCREEN
Definition: g_local.h:157
vec3_origin
vec3_t vec3_origin
Definition: shared.c:21
DAMAGE_BULLET
#define DAMAGE_BULLET
Definition: g_local.h:651
DAMAGE_NO_PROTECTION
#define DAMAGE_NO_PROTECTION
Definition: g_local.h:652
findradius
edict_t * findradius(edict_t *from, vec3_t org, float rad)
Definition: g_utils.c:75
MOVETYPE_NONE
@ MOVETYPE_NONE
Definition: g_local.h:186
forward
static vec3_t forward
Definition: p_view.c:27
svc_temp_entity
#define svc_temp_entity
Definition: g_local.h:38
level_locals_t::killed_monsters
int killed_monsters
Definition: g_local.h:330
FL_GODMODE
#define FL_GODMODE
Definition: g_local.h:63
OnSameTeam
qboolean OnSameTeam(edict_t *ent1, edict_t *ent2)
Definition: g_cmds.c:46
gitem_armor_t
Definition: g_local.h:201
origin
static vec3_t origin
Definition: mesh.c:27
AngleVectors
void AngleVectors(vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
Definition: shared.c:23
CheckTeamDamage
qboolean CheckTeamDamage(edict_t *targ, edict_t *attacker)
Definition: g_combat.c:351
FoundTarget
void FoundTarget(edict_t *self)
Definition: g_ai.c:328
CheckArmor
static int CheckArmor(edict_t *ent, vec3_t point, vec3_t normal, int damage, int te_sparks, int dflags)
Definition: g_combat.c:243
CanDamage
qboolean CanDamage(edict_t *targ, edict_t *inflictor)
Definition: g_combat.c:30
skill
cvar_t * skill
Definition: g_main.c:36
level_locals_t::framenum
int framenum
Definition: g_local.h:298
level_locals_t::time
float time
Definition: g_local.h:299
coop
cvar_t * coop
Definition: g_main.c:34
AI_SOUND_TARGET
#define AI_SOUND_TARGET
Definition: g_local.h:128
visible
qboolean visible(edict_t *self, edict_t *other)
Definition: g_ai.c:268
M_ReactToDamage
void M_ReactToDamage(edict_t *targ, edict_t *attacker)
Definition: g_combat.c:283
Killed
void Killed(edict_t *targ, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
Definition: g_combat.c:89
SpawnDamage
void SpawnDamage(int type, vec3_t origin, vec3_t normal, int damage)
Definition: g_combat.c:128
level
level_locals_t level
Definition: g_main.c:22
GetItemByIndex
gitem_t * GetItemByIndex(int index)
Definition: g_items.c:61
FL_FLY
#define FL_FLY
Definition: g_local.h:59
MOVETYPE_BOUNCE
@ MOVETYPE_BOUNCE
Definition: g_local.h:196
dmflags
cvar_t * dmflags
Definition: g_main.c:35
CheckPowerArmor
static int CheckPowerArmor(edict_t *ent, vec3_t point, vec3_t normal, int damage, int dflags)
Definition: g_combat.c:165
DAMAGE_NO_ARMOR
#define DAMAGE_NO_ARMOR
Definition: g_local.h:648
AI_DUCKED
#define AI_DUCKED
Definition: g_local.h:137
VectorNormalize
vec_t VectorNormalize(vec3_t v)
Definition: shared.c:55
POWER_ARMOR_NONE
#define POWER_ARMOR_NONE
Definition: g_local.h:156
MOD_FRIENDLY_FIRE
#define MOD_FRIENDLY_FIRE
Definition: g_local.h:491
gitem_s::info
void * info
Definition: g_local.h:254
g_local.h
T_RadiusDamage
void T_RadiusDamage(edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod)
Definition: g_combat.c:514
gitem_s
Definition: g_local.h:232