UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
g_combat.cpp
Go to the documentation of this file.
1 
6 /*
7 Copyright (C) 2002-2020 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24 */
25 
26 #include "g_combat.h"
27 #include "g_actor.h"
28 #include "g_client.h"
29 #include "g_edicts.h"
30 #include "g_health.h"
31 #include "g_inventory.h"
32 #include "g_match.h"
33 #include "g_reaction.h"
34 #include "g_spawn.h"
35 #include "g_utils.h"
36 #include "g_vis.h"
37 
38 #define MAX_WALL_THICKNESS_FOR_SHOOTING_THROUGH 8
39 
40 typedef enum {
45 
52 static bool G_TeamPointVis (int team, const vec3_t point)
53 {
54  /* test if point is visible from team */
55  Actor* from = nullptr;
56  while ((from = G_EdictsGetNextLivingActorOfTeam(from, team))) {
57  if (!G_FrustumVis(from, point))
58  continue;
59  /* get viewers eye height */
60  vec3_t eye;
61  G_ActorGetEyeVector(from, eye);
62 
63  /* line of sight */
64  if (G_TestLine(eye, point))
65  continue;
66  const float distance = VectorDist(from->origin, point);
67  bool blocked = false;
68  /* check visibility in the smoke */
69  if (distance >= UNIT_SIZE) {
70  Edict* e = nullptr;
71  while ((e = G_EdictsGetNextInUse(e))) {
72  if (!G_IsSmoke(e))
73  continue;
74  if (!RayIntersectAABB(eye, point, e->absBox))
75  continue;
76 
77  blocked = true;
78  break;
79  }
80  }
81  if (!blocked)
82  return true;
83  }
84 
85  /* not visible */
86  return false;
87 }
88 
98 static void G_Morale (morale_modifiers type, const Edict* victim, const Edict* attacker, int param)
99 {
100  Actor* actor = nullptr;
101  while ((actor = G_EdictsGetNextLivingActor(actor))) {
102  /* this only applies to ET_ACTOR but not ET_ACTOR2x2 */
103  if (actor->type != ET_ACTOR)
104  continue;
105  if (G_IsCivilian(actor))
106  continue;
107 
108  /* morale damage depends on the damage */
109  float mod = mob_wound->value * param;
110  if (type == ML_SHOOT)
111  mod *= mob_shoot->value;
112  /* death hurts morale even more than just damage */
113  if (type == ML_DEATH)
114  mod += mob_death->value;
115  /* seeing how someone gets shot increases the morale change */
116  if (actor == victim || (G_FrustumVis(actor, victim->origin) && G_ActorVis(actor, victim, false)))
117  mod *= mof_watching->value;
118  if (attacker != nullptr && actor->isSameTeamAs(attacker)) {
119  /* teamkills are considered to be bad form, but won't cause an increased morale boost for the enemy */
120  /* morale boost isn't equal to morale loss (it's lower, but morale gets regenerated) */
121  if (victim->isSameTeamAs(attacker))
122  mod *= mof_teamkill->value;
123  else
124  mod *= mof_enemy->value;
125  }
126  /* seeing a civilian die is more "acceptable" */
127  if (G_IsCivilian(victim))
128  mod *= mof_civilian->value;
129  /* if an ally (or in singleplayermode, as human, a civilian) got shot, lower the morale, don't heighten it. */
130  if (victim->isSameTeamAs(actor) || (G_IsCivilian(victim) && !G_IsAlien(actor) && G_IsSinglePlayer()))
131  mod *= -1;
132  if (attacker != nullptr) {
133  /* if you stand near to the attacker or the victim, the morale change is higher. */
134  mod *= mor_default->value + pow(0.5f, VectorDist(actor->origin, victim->origin) / mor_distance->value)
135  * mor_victim->value + pow(0.5f, VectorDist(actor->origin, attacker->origin) / mor_distance->value)
136  * mor_attacker->value;
137  } else {
138  mod *= mor_default->value + pow(0.5f, VectorDist(actor->origin, victim->origin) / mor_distance->value)
139  * mor_victim->value;
140  }
141  /* morale damage depends on the number of living allies */
142  mod *= (1 - mon_teamfactor->value)
143  + mon_teamfactor->value * (level.num_spawned[victim->getTeam()] + 1)
144  / (level.num_alive[victim->getTeam()] + 1);
145  /* being hit isn't fun */
146  if (actor == victim)
147  mod *= mor_pain->value;
148  /* clamp new morale */
149  /*+0.9 to allow weapons like flamethrowers to inflict panic (typecast rounding) */
150  const int newMorale = actor->morale + (int) (MORALE_RANDOM(mod) + 0.9);
151  if (newMorale > GET_MORALE(actor->chr.score.skills[ABILITY_MIND]))
152  actor->setMorale(GET_MORALE(actor->chr.score.skills[ABILITY_MIND]));
153  else if (newMorale < 0)
154  actor->setMorale(0);
155  else
156  actor->setMorale(newMorale);
157 
158  /* send phys data */
159  G_SendStats(*actor);
160  }
161 }
162 
171 static void G_ShotMorale (const Actor* shooter, const fireDef_t* fd, const vec3_t from, const Item* weapon, const vec3_t impact)
172 {
173  /* Skip not detectable shoots */
174  if (weapon->def()->dmgtype == gi.csi->damLaser || fd->irgoggles)
175  return;
176 
177  Actor* check = nullptr;
178  const float minDist = UNIT_SIZE * 1.5f;
179  while ((check = G_EdictsGetNextLivingActor(check))) {
180  /* Skip yourself */
181  if (check == shooter)
182  continue;
183  pos3_t target;
184  VecToPos(impact, target);
185  /* Skip the hit actor -- morale was already handled */
186  if (check->isSamePosAs(target))
187  continue;
188  vec3_t dir1, dir2;
189  VectorSubtract(check->origin, from, dir1);
190  VectorSubtract(impact, from, dir2);
191  const float len1 = VectorLength(dir1);
192  const float len2 = VectorLength(dir2);
193  const float dot = DotProduct(dir1, dir2);
194  if (dot / (len1 * len2) < 0.7f)
195  continue;
196  /* Skip if shooting next or over an ally */
197  if (check->isSameTeamAs(shooter) && VectorDistSqr(check->origin, shooter->origin)
198  <= minDist * minDist)
199  continue;
200  vec3_t vec1;
201  if (len1 > len2) {
202  VectorSubtract(dir2, dir1, vec1);
203  } else {
204  VectorScale(dir2, dot / (len2 * len2), vec1);
205  VectorSubtract(dir1, vec1, vec1);
206  }
207  const float morDist = (check->isSamePosAs(target) ? UNIT_SIZE * 0.5f : minDist);
208  if (VectorLengthSqr(vec1) <= morDist * morDist) {
209  /* @todo Add a visibility check here? */
210  G_Morale(ML_SHOOT, check, shooter, fd->damage[0]);
211  }
212  }
213 }
214 
224 static void G_UpdateShotMock (shot_mock_t* mock, const Edict* shooter, const Edict* struck, int damage)
225 {
226  assert(!struck->isSameAs(shooter) || mock->allow_self);
227 
228  if (damage <= 0)
229  return;
230 
231  if (!struck->inuse || G_IsDead(struck))
232  return;
233 
234  if (!G_IsAI(shooter) && !G_IsVisibleForTeam(struck, shooter->getTeam()))
235  return;
236 
237  if (G_IsCivilian(struck))
238  mock->civilian += 1;
239  else if (struck->isSameTeamAs(shooter))
240  mock->friendCount += 1;
241  else if (G_IsActor(struck))
242  mock->enemyCount += 1;
243  else
244  return;
245 
246  mock->damage += damage;
247 }
248 
257 static void G_UpdateCharacterBodycount (Edict* attacker, const fireDef_t* fd, const Actor* target)
258 {
259  if (!attacker || !target)
260  return;
261 
262  chrScoreGlobal_t* scoreGlobal = &attacker->chr.score;
264  /* only phalanx soldiers have this */
265  if (!scoreMission)
266  return;
267 
269  switch (target->getTeam()) {
270  case TEAM_ALIEN:
271  type = KILLED_ENEMIES;
272  if (fd) {
273  assert(fd->weaponSkill >= 0);
274  assert(fd->weaponSkill < lengthof(scoreMission->skillKills));
275  scoreMission->skillKills[fd->weaponSkill]++;
276  }
277  break;
278  case TEAM_CIVILIAN:
279  type = KILLED_CIVILIANS;
280  break;
281  case TEAM_PHALANX:
282  type = KILLED_TEAM;
283  break;
284  default:
285  return;
286  }
287 
288  if (target->isStunned()) {
289  scoreMission->stuns[type]++;
290  scoreGlobal->stuns[type]++;
291  } else if (target->isDead()) {
292  scoreMission->kills[type]++;
293  scoreGlobal->kills[type]++;
294  }
295 }
296 
304 static void G_UpdateHitScore (Edict* attacker, const Edict* target, const fireDef_t* fd, const int splashDamage)
305 {
306  if (!attacker || !target || !fd)
307  return;
308 
309  chrScoreMission_t* score = attacker->chr.scoreMission;
310  /* Abort if no player team. */
311  if (!score)
312  return;
313 
315  switch (target->getTeam()) {
316  case TEAM_CIVILIAN:
317  type = KILLED_CIVILIANS;
318  break;
319  case TEAM_ALIEN:
320  type = KILLED_ENEMIES;
321  break;
322  default:
323  return;
324  }
325 
326  if (splashDamage) {
327  if (attacker->isSameTeamAs(target)) {
328  /* Increase friendly fire counter. */
329  score->hitsSplashDamage[fd->weaponSkill][KILLED_TEAM] += splashDamage;
330  if (!score->firedSplashHit[KILLED_TEAM]) {
331  score->hitsSplash[fd->weaponSkill][KILLED_TEAM]++;
332  score->firedSplashHit[KILLED_TEAM] = true;
333  }
334  }
335 
336  score->hitsSplashDamage[fd->weaponSkill][type] += splashDamage;
337  if (!score->firedSplashHit[type]) {
338  score->hitsSplash[fd->weaponSkill][type]++;
339  score->firedSplashHit[type] = true;
340  }
341  } else {
342  if (attacker->isSameTeamAs(target) && !score->firedHit[KILLED_TEAM]) {
343  /* Increase friendly fire counter. */
344  score->hits[fd->weaponSkill][KILLED_TEAM]++;
345  score->firedHit[KILLED_TEAM] = true;
346  }
347 
348  if (!score->firedHit[type]) {
349  score->hits[fd->weaponSkill][type]++;
350  score->firedHit[type] = true;
351  }
352  }
353 }
354 
362 int G_ApplyProtection (const Edict* target, const byte dmgWeight, int damage)
363 {
364  const int naturalProtection = target->chr.teamDef->resistance[dmgWeight];
365  if (target->getArmour()) {
366  const objDef_t* armourDef = target->getArmour()->def();
367  const short armourProtection = armourDef->protection[dmgWeight];
368  const short totalProtection = armourProtection + naturalProtection;
369  damage = std::min(std::max(0, damage - armourProtection), std::max(1, damage - totalProtection));
370  } else {
371  damage = std::max(1, damage - naturalProtection);
372  }
373  return damage;
374 }
375 
389 static bool G_Damage (Edict* target, const fireDef_t* fd, int damage, Actor* attacker, shot_mock_t* mock, const vec3_t impact)
390 {
391  assert(target);
392 
393  const bool stunEl = (fd->obj->dmgtype == gi.csi->damStunElectro);
394  const bool stunGas = (fd->obj->dmgtype == gi.csi->damStunGas);
395  const bool shock = (fd->obj->dmgtype == gi.csi->damShock);
396  const bool smoke = (fd->obj->dmgtype == gi.csi->damSmoke);
397 
398  /* Breakables */
399  if (G_IsBrushModel(target) && G_IsBreakable(target)) {
400  /* Breakables are immune to stun & shock damage. */
401  if (stunEl || stunGas || shock || mock || smoke)
402  return false;
403 
404  if (damage >= target->HP) {
405  /* don't reset the HP value here, this value is used to distinguish
406  * between triggered destroy and a shoot */
407  assert(target->destroy);
408  target->destroy(target);
409 
410  /* maybe the attacker is seeing something new? */
411  G_CheckVisTeamAll(attacker->getTeam(), 0, attacker);
412 
413  /* check if attacker appears/perishes for any other team */
414  G_CheckVis(attacker);
415  } else {
416  G_TakeDamage(target, damage);
417  }
418  return true;
419  }
420 
421  /* Actors don't die again. */
422  if (!G_IsLivingActor(target))
423  return false;
424  /* Now we know that the target is an actor */
425  Actor* victim = makeActor(target);
426 
427  /* only actors after this point - and they must have a teamdef */
428  assert(victim->chr.teamDef);
429  const bool isRobot = CHRSH_IsTeamDefRobot(victim->chr.teamDef);
430 
431  /* Apply armour effects. */
432  if (damage > 0) {
433  damage = G_ApplyProtection(victim, fd->dmgweight, damage);
434  } else if (damage < 0) {
435  /* Robots can't be healed. */
436  if (isRobot)
437  return false;
438  }
439  Com_DPrintf(DEBUG_GAME, " Total damage: %d\n", damage);
440 
441  /* Apply difficulty settings. */
442  if (G_IsSinglePlayer()) {
443  if (G_IsAlien(attacker) && !G_IsAlien(victim))
444  damage *= pow(1.18f, g_difficulty->value);
445  else if (!G_IsAlien(attacker) && G_IsAlien(victim))
446  damage *= pow(1.18f, -g_difficulty->value);
447  }
448 
449  assert(attacker->getTeam() >= 0 && attacker->getTeam() < MAX_TEAMS);
450  assert(victim->getTeam() >= 0 && victim->getTeam() < MAX_TEAMS);
451 
452  if ((g_nodamage != nullptr && !g_nodamage->integer) || mock) {
453  /* hit */
454  if (mock) {
455  G_UpdateShotMock(mock, attacker, victim, damage);
456  } else if (stunEl) {
457  victim->addStun(damage);
458  } else if (stunGas) {
459  if (!isRobot) /* Can't stun robots with gas */
460  victim->addStun(damage);
461  } else if (shock) {
462  /* Only do this if it's not one from our own team ... they should have known that there was a flashbang coming. */
463  if (!isRobot && !victim->isSameTeamAs(attacker)) {
465  /* dazed entity wont reaction fire */
466  victim->removeReaction();
467  G_ActorReserveTUs(victim, 0, victim->chr.reservedTus.shot, victim->chr.reservedTus.crouch);
468  /* flashbangs kill TUs */
469  G_ActorSetTU(victim, 0);
470  G_SendStats(*victim);
471  /* entity is dazed */
472  victim->setDazed();
473  G_EventSendState(G_VisToPM(victim->visflags), *victim);
474  return !mock;
475  } else {
476  return false;
477  }
478  } else {
479  if (damage < 0) {
480  /* The 'attacker' is healing the victim. */
481  G_TreatActor(victim, fd, damage, attacker->getTeam());
482  } else {
483  /* Real damage was dealt. */
484  G_DamageActor(victim, damage, impact);
485  /* Update overall splash damage for stats/score. */
486  if (!mock && damage > 0 && fd->splrad)
487  G_UpdateHitScore(attacker, victim, fd, damage);
488  }
489  }
490  }
491 
492  if (mock)
493  return false;
494 
495  G_CheckDeathOrKnockout(victim, attacker, fd, damage);
496  return true;
497 }
498 
499 void G_CheckDeathOrKnockout (Actor* target, Actor* attacker, const fireDef_t* fd, int damage)
500 {
501  /* Sanity check */
502  target->HP = std::min(std::max(target->HP, 0), target->chr.maxHP);
503  /* Check death/knockout. */
504  if (target->HP == 0 || target->HP <= target->getStun()) {
505  G_SendStats(*target);
506 
507  if (G_ActorDieOrStun(target, attacker)) {
508  G_PrintActorStats(target, attacker, fd);
509 
510  /* apply morale changes */
511  if (mor_panic->integer)
512  G_Morale(ML_DEATH, target, attacker, damage);
513 
514  /* Update number of killed/stunned actors for this attacker. */
515  G_UpdateCharacterBodycount(attacker, fd, target);
516  }
517  } else {
518  target->chr.minHP = std::min(target->chr.minHP, target->HP);
519  if (damage > 0) {
520  if (mor_panic->integer)
521  G_Morale(ML_WOUND, target, attacker, damage);
522  }
523  G_SendStats(*target);
524  }
525 }
526 
536 static inline bool G_FireAffectedSurface (const cBspSurface_t* surface, const fireDef_t* fd)
537 {
538  if (!surface)
539  return false;
540 
541  if (!(surface->surfaceFlags & SURF_BURN))
542  return false;
543 
544  if (fd->obj->dmgtype == gi.csi->damIncendiary || fd->obj->dmgtype == gi.csi->damFire || fd->obj->dmgtype == gi.csi->damBlast)
545  return true;
546 
547  return false;
548 }
549 
558 static void G_SplashDamage (Actor* ent, const fireDef_t* fd, vec3_t impact, shot_mock_t* mock, const trace_t* tr)
559 {
560  assert(fd->splrad > 0.0f);
561 
562  const bool shock = (fd->obj->dmgtype == gi.csi->damShock);
563 
564  Edict* check = nullptr;
565  while ((check = G_EdictsGetNextInUse(check))) {
566  /* If we use a blinding weapon we skip the target if it's looking
567  * away from the impact location. */
568  if (shock && !G_FrustumVis(check, impact))
569  continue;
570 
571  const bool isActor = G_IsLivingActor(check);
572  vec3_t center;
573  if (G_IsBrushModel(check) && G_IsBreakable(check))
574  check->absBox.getCenter(center);
575  else if (isActor || G_IsBreakable(check))
576  VectorCopy(check->origin, center);
577  else
578  continue;
579 
580  /* check for distance */
581  float dist = VectorDist(impact, center);
582  dist = dist > UNIT_SIZE / 2 ? dist - UNIT_SIZE / 2 : 0.0f;
583  if (dist > fd->splrad)
584  continue;
585 
586  if (fd->irgoggles) {
587  if (isActor) {
588  /* check whether this actor (check) is in the field of view of the 'shooter' (ent) */
589  if (G_FrustumVis(ent, check->origin)) {
590  if (!mock) {
591  vec3_t eyeEnt;
592  G_ActorGetEyeVector(ent, eyeEnt);
593  if (!G_SmokeVis(eyeEnt, check)) {
594  const unsigned int playerMask = G_TeamToPM(ent->getTeam()) ^ G_VisToPM(check->visflags);
595  G_AppearPerishEvent(playerMask, true, *check, ent);
596  G_VisFlagsAdd(*check, G_PMToVis(playerMask));
597  }
598  }
599  }
600  }
601  continue;
602  }
603 
604  /* check for walls */
605  if (isActor && G_TestLine(impact, check->origin))
606  continue;
607 
608  /* do damage */
609  const int damage = shock ? 0 : fd->spldmg[0] * (1.0f - dist / fd->splrad);
610 
611  if (mock)
612  mock->allow_self = true;
613  /* Send hurt sounds for actors, but only if they'll recieve damage from this attack */
614  if (G_Damage(check, fd, damage, ent, mock, nullptr) && isActor
615  && (G_ApplyProtection(check, fd->dmgweight, damage) > 0) && !shock) {
616  const teamDef_t* teamDef = check->chr.teamDef;
617  const int gender = check->chr.gender;
618  const char* sound = teamDef->getActorSound(gender, SND_HURT);
619  G_EventSpawnSound(G_VisToPM(check->visflags), *check, nullptr, sound);
620  }
621  if (mock)
622  mock->allow_self = false;
623  }
624 
626  if (tr && G_FireAffectedSurface(tr->surface, fd)) {
627  /* move a little away from the impact vector */
628  VectorMA(impact, 1, tr->plane.normal, impact);
629  G_SpawnParticle(impact, tr->contentFlags >> 8, "burning");
630  }
631 }
632 
638 static void G_SpawnItemOnFloor (const pos3_t pos, const Item* item)
639 {
640  Edict* floor = G_GetFloorItemFromPos(pos);
641  if (floor == nullptr) {
642  floor = G_SpawnFloor(pos);
643 
644  if (!game.invi.tryAddToInventory(&floor->chr.inv, item, INVDEF(CID_FLOOR))) {
645  G_FreeEdict(floor);
646  } else {
647  Actor* actor = G_EdictsGetLivingActorFromPos(pos);
648 
649  /* send the inventory */
650  G_CheckVis(floor);
651 
652  if (actor != nullptr)
653  G_GetFloorItems(actor);
654  }
655  } else {
656  if (game.invi.tryAddToInventory(&floor->chr.inv, item, INVDEF(CID_FLOOR))) {
657  /* make it invisible to send the inventory in the below vis check */
658  G_EventPerish(*floor);
659  G_VisFlagsReset(*floor);
660  G_CheckVis(floor, true);
661  }
662  }
663 }
664 
671 void G_CalcEffectiveSpread (const Actor* shooter, const fireDef_t* fd, vec2_t effSpread)
672 {
673  /* Get accuracy value for this attacker. */
674  const float acc = GET_ACC(shooter->chr.score.skills[ABILITY_ACCURACY],
676 
677  /* Base spread multiplier comes from the firedef's spread values. Soldier skills further modify the spread.
678  * A good soldier will tighten the spread, a bad one will widen it, for skillBalanceMinimum values between 0 and 1.*/
679  const float commonfactor = std::max(0.0f, WEAPON_BALANCE + SKILL_BALANCE * acc);
680  effSpread[PITCH] = fd->spread[0] * commonfactor;
681  effSpread[YAW] = fd->spread[1] * commonfactor;
682 
683  /* If the attacker is crouched this modifier is included as well. */
684  if (shooter->isCrouched() && fd->crouch > 0.0f) {
685  effSpread[PITCH] *= fd->crouch;
686  effSpread[YAW] *= fd->crouch;
687  }
688 }
689 
690 #define GRENADE_DT 0.1f
691 #define GRENADE_STOPSPEED 60.0f
692 
707 static void G_ShootGrenade (const Player& player, Actor* shooter, const fireDef_t* fd,
708  const vec3_t from, const pos3_t at, int mask, const Item* weapon, shot_mock_t* mock, int z_align, vec3_t impact)
709 {
710  /* Check if the shooter is still alive (me may fire with area-damage ammo and have just hit the near ground). */
711  if (shooter->isDead())
712  return;
713 
714  /* get positional data */
715  vec3_t last;
716  VectorCopy(from, last);
717  vec3_t target;
718  gi.GridPosToVec(shooter->fieldSize, at, target);
719  /* first apply z_align value */
720  target[2] -= z_align;
721 
722  /* prefer to aim grenades at the ground */
723  target[2] -= GROUND_DELTA;
724 
725  /* calculate parabola */
726  vec3_t startV;
727  float dt = gi.GrenadeTarget(last, target, fd->range, fd->launched, fd->rolled, startV);
728  if (!dt) {
729  if (!mock)
730  G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - impossible throw!"));
731  return;
732  }
733 
734  /* cap start speed */
735  const float speed = std::min(VectorLength(startV), fd->range);
736 
737  /* add random effects and get new dir */
738  vec3_t angles;
739  VecToAngles(startV, angles);
740 
741  /* Get 2 gaussian distributed random values */
742  float gauss1;
743  float gauss2;
744  gaussrand(&gauss1, &gauss2);
745 
746  vec2_t effSpread;
747  G_CalcEffectiveSpread(shooter, fd, effSpread);
748 
749  angles[PITCH] += gauss1 * effSpread[0];
750  angles[YAW] += gauss2 * effSpread[1];
751 
752  AngleVectors(angles, startV, nullptr, nullptr);
753  VectorScale(startV, speed, startV);
754 
755  /* move */
756  vec3_t oldPos;
757  VectorCopy(last, oldPos);
758  vec3_t curV;
759  VectorCopy(startV, curV);
760  float time = 0.0f;
761  dt = 0.0f;
762  int bounce = 0;
763  byte flags = SF_BOUNCING;
764  vec3_t newPos;
765 
766  VectorMA(oldPos, GRENADE_DT, curV, newPos);
767  newPos[2] -= 0.5f * GRAVITY * GRENADE_DT * GRENADE_DT;
768  trace_t tr = G_Trace(Line(oldPos, newPos), shooter, MASK_SHOT);
769  if (tr.fraction < 1.0f) {
770  const Edict* trEnt = G_EdictsGetByNum(tr.entNum);
771  if (trEnt && (trEnt->isSameTeamAs(shooter) || G_IsCivilian(trEnt)) && G_IsCrouched(trEnt)) {
772  dt += GRENADE_DT;
773  VectorCopy(newPos, oldPos);
774  VectorCopy(newPos, impact);
775  }
776  }
777 
778  for (;;) {
779  /* kinematics */
780  VectorMA(oldPos, GRENADE_DT, curV, newPos);
781  newPos[2] -= 0.5f * GRAVITY * GRENADE_DT * GRENADE_DT;
782  curV[2] -= GRAVITY * GRENADE_DT;
783 
784  /* trace */
785  tr = G_Trace(Line(oldPos, newPos), shooter, MASK_SHOT);
786  if (tr.fraction < 1.0f || time + dt > 4.0f) {
787  /* the ent possibly hit by the trace */
788  const Edict* trEnt = G_EdictsGetByNum(tr.entNum);
789  const float bounceFraction = tr.surface ? gi.GetBounceFraction(tr.surface->name) : 1.0f;
790 
791  /* advance time */
792  dt += tr.fraction * GRENADE_DT;
793  time += dt;
794  bounce++;
795 
796  if (tr.fraction < 1.0f)
797  VectorCopy(tr.endpos, newPos);
798 
799  /* calculate additional visibility */
800  if (!mock) {
801  for (int i = 0; i < MAX_TEAMS; i++)
802  if (player.getTeam() != level.activeTeam && G_TeamPointVis(i, newPos))
803  mask |= 1 << i;
804  }
805 
806  /* enough bouncing around or we have hit an actor */
807  if (VectorLength(curV) < GRENADE_STOPSPEED || time > 4.0f || bounce > fd->bounce
808  || (!fd->delay && trEnt && G_IsActor(trEnt))) {
809  if (!mock) {
810  /* explode */
811  byte impactFlags = flags;
812  if (trEnt && G_IsActor(trEnt))
813  impactFlags |= SF_BODY;
814  else
815  impactFlags |= SF_IMPACT;
816  G_EventThrow(mask, fd, dt, impactFlags, last, startV);
817  }
818 
819  /* Grenade flew out of map! */
820  if (tr.fraction > 1.0f)
821  return;
822 
823  tr.endpos[2] += 10.0f;
824  VectorCopy(tr.endpos, impact);
825 
826  /* check if this is a stone, ammo clip or grenade */
827  if (fd->splrad > 0.0f) {
828  G_SplashDamage(shooter, fd, impact, mock, &tr);
829  } else if (!mock) {
830  /* spawn the stone on the floor */
831  if (fd->ammo && !fd->splrad && weapon->def()->thrown) {
832  pos3_t drop;
833  VecToPos(impact, drop);
834  G_SpawnItemOnFloor(drop, weapon);
835  }
836  }
837  return;
838  }
839 
840  /* send */
841  if (!mock) {
842  G_EventThrow(mask, fd, dt, flags, last, startV);
843  /* send shot sound to the others */
844  G_EventShootHidden(mask, fd, false, impact, flags, nullptr);
845  }
846 
847  flags |= SF_BOUNCED;
848 
849  /* bounce */
850  VectorScale(curV, fd->bounceFac * bounceFraction, curV);
851  vec3_t temp;
852  VectorScale(tr.plane.normal, -DotProduct(tr.plane.normal, curV), temp);
853  VectorAdd(temp, curV, startV);
854  VectorAdd(temp, startV, curV);
855 
856  /* prepare next move */
857  VectorCopy(tr.endpos, last);
858  VectorCopy(tr.endpos, oldPos);
859  VectorCopy(curV, startV);
860  dt = 0.0f;
861  } else {
862  dt += GRENADE_DT;
863  VectorCopy(newPos, oldPos);
864  VectorCopy(newPos, impact);
865  }
866  }
867 }
868 
869 #ifdef DEBUG
870 
875 static void DumpTrace (vec3_t start, const trace_t& tr)
876 {
877  Com_DPrintf(DEBUG_GAME, "start (%i, %i, %i) end (%i, %i, %i)\n",
878  (int)start[0], (int)start[1], (int)start[2],
879  (int)tr.endpos[0], (int)tr.endpos[1], (int)tr.endpos[2]);
880  Com_DPrintf(DEBUG_GAME, "allsolid:%s startsolid:%s fraction:%f contentFlags:%X\n",
881  tr.allsolid ? "true" : "false",
882  tr.startsolid ? "true" : "false",
883  tr.fraction, tr.contentFlags);
884  Edict* trEnt = G_EdictsGetByNum(tr.entNum); /* the ent possibly hit by the trace */
885  Com_DPrintf(DEBUG_GAME, "is entity:%s %s %i\n",
886  trEnt ? "yes" : "no",
887  trEnt ? trEnt->classname : "",
888  trEnt ? trEnt->HP : 0);
889 }
890 
894 static void DumpAllEntities (void)
895 {
896  int i = 0;
897  Edict* check = nullptr;
898 
899  while ((check = G_EdictsGetNext(check))) {
900  char absBoxStr[AABB_STRING];
901  char entBoxStr[AABB_STRING];
902  check->absBox.asIntString(absBoxStr, sizeof(absBoxStr));
903  check->absBox.asIntString(entBoxStr, sizeof(entBoxStr));
904  Com_DPrintf(DEBUG_GAME, "%i %s %s %s %s %s\n", i,
905  check->inuse ? "in use" : "unused",
906  check->classname,
907  check->model,
908  absBoxStr,
909  entBoxStr);
910  i++;
911  }
912 }
913 #endif
914 
930 static void G_ShootSingle (Actor* ent, const fireDef_t* fd, const vec3_t from, const pos3_t at,
931  int mask, const Item* weapon, shot_mock_t* mock, int z_align, int i, shoot_types_t shootType, vec3_t impact)
932 {
933  /* Check if the shooter is still alive (me may fire with area-damage ammo and have just hit the near ground). */
934  if (ent->isDead()) {
935  Com_DPrintf(DEBUG_GAME, "G_ShootSingle: Shooter is dead, shot not possible.\n");
936  return;
937  }
938  /* Calc direction of the shot. */
939  gi.GridPosToVec(ent->fieldSize, at, impact); /* Get the position of the targeted grid-cell. ('impact' is used only temporary here)*/
940  const bool pointTrace = VectorCompare(impact, from);
941  if (!pointTrace)
942  impact[2] -= z_align;
943  vec3_t cur_loc;
944  VectorCopy(from, cur_loc); /* Set current location of the projectile to the starting (muzzle) location. */
945  vec3_t dir;
946  VectorSubtract(impact, cur_loc, dir); /* Calculate the vector from current location to the target. */
947 
948  if (!pointTrace) {
949  VectorNormalizeFast(dir); /* Normalize the vector i.e. make length 1.0 */
950 
951  vec3_t angles;
952  VecToAngles(dir, angles); /* Get the angles of the direction vector. */
953 
954  /* Get 2 gaussian distributed random values */
955  float gauss1;
956  float gauss2;
957  gaussrand(&gauss1, &gauss2);
958 
959  vec2_t effSpread;
960  G_CalcEffectiveSpread(ent, fd, effSpread);
961 
962  /* Modify the angles with the accuracy modifier as a randomizer-range. */
963  angles[PITCH] += gauss1 * effSpread[0];
964  angles[YAW] += gauss2 * effSpread[1];
965 
966  /* Convert changed angles into new direction. */
967  AngleVectors(angles, dir, nullptr, nullptr);
968  }
969 
970  /* shoot and bounce */
971  int throughWall = fd->throughWall;
972  float range = fd->range;
973  int bounce = 0;
974  byte flags = 0;
975 
976  int damage;
977  /* Are we healing? */
978  if (FIRESH_IsMedikit(fd))
979  damage = fd->damage[0] + (fd->damage[1] * crand());
980  else
981  damage = std::max(0.0f, fd->damage[0] + (fd->damage[1] * crand()));
982 
983  /* Check if we are shooting over an adjacent crouching friendly unit */
984  VectorMA(cur_loc, UNIT_SIZE * 1.4f, dir, impact);
985  const Edict* passEnt = ent;
986  trace_t tr = G_Trace(Line(cur_loc, impact), passEnt, MASK_SHOT);
987  Edict* trEnt = G_EdictsGetByNum(tr.entNum); /* the ent possibly hit by the trace */
988  if (trEnt && (trEnt->isSameTeamAs(ent) || G_IsCivilian(trEnt)) && G_IsCrouched(trEnt) && !FIRESH_IsMedikit(fd)) {
989  VectorMA(cur_loc, UNIT_SIZE * 1.4f, dir, cur_loc);
990  passEnt = trEnt;
991  }
992 
993  vec3_t tracefrom; /* sum */
994  VectorCopy(cur_loc, tracefrom);
995 
996  vec3_t temp;
997  for (;;) {
998  /* Calc 'impact' vector that is located at the end of the range
999  * defined by the fireDef_t. This is not really the impact location,
1000  * but rather the 'endofrange' location, see below for another use.*/
1001  VectorMA(cur_loc, range, dir, impact);
1002 
1003  /* Do the trace from current position of the projectile
1004  * to the end_of_range location.*/
1005  tr = G_Trace(Line(tracefrom, impact), passEnt, MASK_SHOT);
1006  trEnt = G_EdictsGetByNum(tr.entNum); /* the ent possibly hit by the trace */
1007 
1008 #ifdef DEBUG
1009  DumpAllEntities();
1010  DumpTrace(tracefrom, tr);
1011 #endif
1012 
1013  /* maybe we start the trace from within a brush (e.g. in case of throughWall) */
1014  if (tr.startsolid)
1015  break;
1016 
1017  /* _Now_ we copy the correct impact location. */
1018  VectorCopy(tr.endpos, impact);
1019 
1020  /* set flags when trace hit something */
1021  if (tr.fraction < 1.0f) {
1022  if (trEnt && G_IsActor(trEnt)
1023  /* check if we differentiate between body and wall */
1024  && !fd->delay)
1025  flags |= SF_BODY;
1026  else if (bounce < fd->bounce)
1027  flags |= SF_BOUNCING;
1028  else
1029  flags |= SF_IMPACT;
1030  }
1031 
1032  /* victims see shots */
1033  if (trEnt && G_IsActor(trEnt))
1034  mask |= G_TeamToVisMask(trEnt->getTeam());
1035 
1036  if (!mock) {
1037  /* send shot */
1038  const bool firstShot = (i == 0);
1039  G_EventShoot(*ent, mask, fd, firstShot, shootType, flags, &tr, tracefrom, impact);
1040 
1041  /* send shot sound to the others */
1042  G_EventShootHidden(mask, fd, false, impact, flags, G_EdictsGetByNum(tr.entNum));
1043 
1044  if (firstShot && G_FireAffectedSurface(tr.surface, fd)) {
1045  vec3_t origin;
1046  /* sent particle to all players */
1047  VectorMA(impact, 1, tr.plane.normal, origin);
1048  G_SpawnParticle(origin, tr.contentFlags >> 8, "fire");
1049  }
1050  }
1051 
1052  if (tr.fraction < 1.0f && !fd->bounce) {
1053  /* check for shooting through wall */
1054  if (throughWall && (tr.contentFlags & CONTENTS_SOLID)) {
1055  throughWall--;
1056  Com_DPrintf(DEBUG_GAME, "Shot through wall, %i walls left.\n", throughWall);
1057  /* reduce damage */
1060  damage /= sqrt(fd->throughWall - throughWall + 1.0f);
1062  continue;
1063  }
1064 
1065  /* do splash damage */
1066  if (fd->splrad > 0.0f) {
1067  VectorMA(impact, sv_shot_origin->value, tr.plane.normal, impact);
1068  G_SplashDamage(ent, fd, impact, mock, &tr);
1069  }
1070  }
1071 
1072  const bool hitEnt = trEnt && (G_IsActor(trEnt) || (G_IsBreakable(trEnt) && damage > 0));
1073  /* bounce is checked here to see if the rubber rocket hit walls enough times to wear out*/
1074  bounce++;
1075  /* stop, we hit something or have bounced enough */
1076  if (hitEnt || bounce > fd->bounce || tr.fraction >= 1.0f) {
1077  if (!mock) {
1078  /* spawn the throwable item on the floor but only if it is not depletable */
1079  if (fd->ammo && !fd->splrad && weapon->def()->thrown && !weapon->def()->deplete) {
1080  pos3_t drop;
1081 
1082  if (ent->isSamePosAs(at)) { /* throw under his own feet */
1083  VectorCopy(at, drop);
1084  } else {
1085  impact[2] -= 20.0f; /* a hack: no-gravity items are flying high */
1086  VecToPos(impact, drop);
1087  }
1088 
1089  G_SpawnItemOnFloor(drop, weapon);
1090  }
1091  }
1092 
1093  /* do damage if the trace hit an entity */
1094  if (hitEnt) {
1095  VectorCopy(tr.endpos, impact);
1096  G_Damage(trEnt, fd, damage, ent, mock, impact);
1097 
1098  if (!mock) { /* check for firedHit is done in G_UpdateHitScore */
1099  /* Count this as a hit of this firemode. */
1100  G_UpdateHitScore(ent, trEnt, fd, 0);
1101  }
1102  }
1103 
1104  /* do splash damage if we haven't yet */
1105  if (tr.fraction >= 1.0f && fd->splrad > 0.0f) {
1106  VectorMA(impact, sv_shot_origin->value, tr.plane.normal, impact);
1107  G_SplashDamage(ent, fd, impact, mock, &tr);
1108  }
1109  break;
1110  }
1111 
1112  range -= tr.fraction * range;
1113  VectorCopy(impact, cur_loc);
1114  VectorScale(tr.plane.normal, -DotProduct(tr.plane.normal, dir), temp);
1115  VectorAdd(temp, dir, dir);
1116  VectorAdd(temp, dir, dir);
1117  flags |= SF_BOUNCED;
1118  }
1119 }
1120 
1132 static bool G_PrepareShot (Edict* ent, shoot_types_t shootType, fireDefIndex_t firemode, Item** weapon, containerIndex_t* container, const fireDef_t** fd)
1133 {
1134  if (shootType >= ST_NUM_SHOOT_TYPES)
1135  gi.Error("G_GetShotFromType: unknown shoot type %i.\n", shootType);
1136 
1137  Item* item;
1138  if (IS_SHOT_HEADGEAR(shootType)) {
1139  item = ent->chr.inv.getHeadgear();
1140  if (!item)
1141  return false;
1142  *container = CID_HEADGEAR;
1143  } else if (IS_SHOT_RIGHT(shootType)) {
1144  item = ent->getRightHandItem();
1145  if (!item)
1146  return false;
1147  *container = CID_RIGHT;
1148  } else {
1149  item = ent->getLeftHandItem();
1150  if (!item)
1151  return false;
1152  *container = CID_LEFT;
1153  }
1154 
1155  /* Get firedef from the weapon entry instead */
1156  const fireDef_t* fdArray = item->getFiredefs();
1157  if (fdArray == nullptr)
1158  return false;
1159 
1160  *weapon = item;
1161 
1162  assert(firemode >= 0);
1163  *fd = &fdArray[firemode];
1164 
1165  return true;
1166 }
1167 
1183 bool G_ClientShoot (const Player& player, Actor* actor, const pos3_t at, shoot_types_t shootType,
1184  fireDefIndex_t firemode, shot_mock_t* mock, bool allowReaction, int z_align)
1185 {
1186  /* just in 'test-whether-it's-possible'-mode or the player is an
1187  * ai - no readable feedback needed */
1188  const bool quiet = (mock != nullptr) || G_IsAIPlayer(&player);
1189 
1190  Item* weapon = nullptr;
1191  const fireDef_t* fd = nullptr;
1192  containerIndex_t container = 0;
1193  if (!G_PrepareShot(actor, shootType, firemode, &weapon, &container, &fd)) {
1194  if (!weapon && !quiet)
1195  G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - object not activatable!"));
1196  return false;
1197  }
1198 
1199 
1200  /* check if action is possible
1201  * if allowReaction is false, it is a shot from reaction fire, so don't check the active team */
1202  const int tusNeeded = G_ActorGetModifiedTimeForFiredef(actor, fd, false);
1203  if (allowReaction) {
1204  if (!G_ActionCheckForCurrentTeam(player, actor, tusNeeded))
1205  return false;
1206  } else {
1207  if (!G_ActionCheckForReaction(player, actor, tusNeeded))
1208  return false;
1209  }
1210 
1211  /* Don't allow to shoot yourself */
1212  if (!fd->irgoggles && actor->isSamePosAs(at))
1213  return false;
1214  const Actor* targetEnt = nullptr;
1215  if (FIRESH_IsMedikit(fd)) {
1216  targetEnt = G_EdictsGetLivingActorFromPos(at);
1217  if (!targetEnt)
1218  return false;
1219  else if (fd->dmgweight == gi.csi->damNormal && !G_IsActorWounded(targetEnt)) {
1220  if (!quiet)
1221  G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - target is not wounded!"));
1222  return false;
1223  }
1224  }
1225 
1226  /* check that we're not firing a twohanded weapon with one hand! */
1227  if (weapon->def()->fireTwoHanded && actor->getLeftHandItem()) {
1228  if (!quiet)
1229  G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - weapon cannot be fired one handed!"));
1230  return false;
1231  }
1232 
1233  /* check we're not out of ammo */
1234  int ammo = weapon->getAmmoLeft();
1235  if (!ammo && fd->ammo && !weapon->def()->thrown) {
1236  if (!quiet)
1237  G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - no ammo!"));
1238  return false;
1239  }
1240 
1241  /* check target is not out of range */
1242  vec3_t target;
1243  gi.GridPosToVec(actor->fieldSize, at, target);
1244  if (fd->range < VectorDist(actor->origin, target)) {
1245  if (!quiet)
1246  G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - target out of range!"));
1247  return false;
1248  }
1249 
1250  /* Count for stats if it's no mock-shot and it's a Phalanx soldier (aliens do not have this info yet). */
1251  if (!mock && actor->chr.scoreMission) {
1252  /* Count this start of the shooting for stats/score. */
1254  if (fd->splrad > 0.0) {
1255  /* Splash damage */
1256  actor->chr.scoreMission->firedSplashTUs[fd->weaponSkill] += tusNeeded;
1257  actor->chr.scoreMission->firedSplash[fd->weaponSkill]++;
1258  for (int i = 0; i < KILLED_NUM_TYPES; i++) {
1260  actor->chr.scoreMission->firedSplashHit[i] = false;
1261  }
1262  } else {
1263  /* Direct hits */
1264  actor->chr.scoreMission->firedTUs[fd->weaponSkill] += tusNeeded;
1265  actor->chr.scoreMission->fired[fd->weaponSkill]++;
1266  for (int i = 0; i < KILLED_NUM_TYPES; i++) {
1268  actor->chr.scoreMission->firedHit[i] = false;
1269  }
1270  }
1271  }
1272 
1273  /* fire shots */
1274  int shots = fd->shots;
1275  if (fd->ammo && !weapon->def()->thrown) {
1284  if (ammo < fd->ammo) {
1285  shots = fd->shots * ammo / fd->ammo;
1286  ammo = 0;
1287  } else {
1288  ammo -= fd->ammo;
1289  }
1290  if (shots < 1) {
1291  if (!quiet)
1292  G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - not enough ammo!"));
1293  return false;
1294  }
1295  }
1296 
1297  /* rotate the player */
1298  const int prevDir = mock ? actor->dir : 0;
1299  vec3_t dir;
1300 
1301  if (!actor->isSamePosAs(at)) {
1302  VectorSubtract(at, actor->pos, dir);
1303  actor->dir = AngleToDir((int) (atan2(dir[1], dir[0]) * todeg));
1304  assert(actor->dir < CORE_DIRECTIONS);
1305 
1306  if (!mock) {
1307  G_CheckVisTeamAll(actor->getTeam(), 0, actor);
1308  G_EventActorTurn(*actor);
1309  }
1310  }
1311 
1312  /* calculate visibility */
1313  target[2] -= z_align;
1314  VectorSubtract(target, actor->origin, dir);
1315  vec3_t center;
1316  VectorMA(actor->origin, 0.5, dir, center);
1317  int mask = 0;
1318  for (int i = 0; i < MAX_TEAMS; i++)
1319  if (G_IsVisibleForTeam(actor, i) || G_TeamPointVis(i, target) || G_TeamPointVis(i, center))
1320  mask |= G_TeamToVisMask(i);
1321 
1322  if (!mock) {
1323  /* check whether this has forced any reaction fire */
1324  if (allowReaction) {
1325  G_ReactionFirePreShot(actor, tusNeeded); /* if commented out, this disables the 'draw' situation */
1326  if (actor->isDead())
1327  /* dead men can't shoot */
1328  return false;
1329  }
1330  /* Check we aren't trying to heal a dead actor */
1331  if (targetEnt != nullptr && (targetEnt->isDead() && !targetEnt->isStunned()))
1332  return false;
1333 
1334  /* start shoot */
1335  G_EventStartShoot(*actor, mask, shootType, at);
1336 
1337  /* send shot sound to the others */
1338  G_EventShootHidden(mask, fd, true, actor->origin, 0, nullptr);
1339 
1340  /* State info so we can check if an item was already removed. */
1341  bool itemAlreadyRemoved = false;
1342  /* ammo... */
1343  if (fd->ammo) {
1344  if (ammo > 0 || !weapon->def()->thrown) {
1345  G_EventInventoryAmmo(*actor, weapon->ammoDef(), ammo, shootType);
1346  weapon->setAmmoLeft(ammo);
1347  } else { /* delete the knife or the rifle without ammo */
1348  const invDef_t* invDef = INVDEF(container);
1349  assert(invDef->single);
1350  itemAlreadyRemoved = true; /* for assert only */
1351  game.invi.emptyContainer(&actor->chr.inv, invDef->id);
1352  G_ReactionFireSettingsUpdate(actor, actor->chr.RFmode.getFmIdx(), actor->chr.RFmode.getHand(),
1353  actor->chr.RFmode.getWeapon());
1354  G_EventInventoryDelete(*actor, G_VisToPM(actor->visflags), invDef->id, 0, 0);
1355  }
1356  }
1357 
1358  /* remove throwable oneshot && deplete weapon from inventory */
1359  if (weapon->def()->thrown && weapon->def()->oneshot && weapon->def()->deplete) {
1360  if (itemAlreadyRemoved)
1361  gi.Error("Item %s is already removed", weapon->def()->id);
1362  const invDef_t* invDef = INVDEF(container);
1363  assert(invDef->single);
1364  game.invi.emptyContainer(&actor->chr.inv, invDef->id);
1365  G_ReactionFireSettingsUpdate(actor, actor->chr.RFmode.getFmIdx(), actor->chr.RFmode.getHand(),
1366  actor->chr.RFmode.getWeapon());
1367  G_EventInventoryDelete(*actor, G_VisToPM(actor->visflags), invDef->id, 0, 0);
1368  }
1369  }
1370 
1371  vec3_t shotOrigin;
1372  fd->getShotOrigin(actor->origin, dir, actor->isCrouched(), shotOrigin);
1373 
1374  /* Fire all shots. */
1375  vec3_t impact;
1376  for (int i = 0; i < shots; ++i) {
1377  if (fd->gravity) {
1378  G_ShootGrenade(player, actor, fd, shotOrigin, at, mask, weapon, mock, z_align, impact);
1379  } else {
1380  G_ShootSingle(actor, fd, shotOrigin, at, mask, weapon, mock, z_align, i, shootType, impact);
1381  }
1382  if (!mock && mor_panic->integer)
1383  G_ShotMorale(actor, fd, shotOrigin, weapon, impact);
1384  }
1385 
1386  if (!mock) {
1387  const bool smoke = fd->obj->dmgtype == gi.csi->damSmoke;
1388  const bool incendiary = fd->obj->dmgtype == gi.csi->damIncendiary;
1389  const bool stunGas = fd->obj->dmgtype == gi.csi->damStunGas;
1390  /* Ignore off-map impacts when spawning fire, smoke, etc fields */
1391  if (gi.isOnMap(impact)) {
1392  if (smoke) {
1393  const int damage = std::max(0.0f, fd->damage[0] + fd->damage[1] * crand());
1394  const int rounds = std::max(2, fd->rounds);
1395  G_SpawnSmokeField(impact, "smokefield", rounds, damage, fd->splrad);
1396  } else if (incendiary) {
1397  const int damage = std::max(0.0f, fd->damage[0] + fd->damage[1] * crand());
1398  const int rounds = std::max(2, fd->rounds);
1399  G_SpawnFireField(impact, "firefield", rounds, damage, fd->splrad);
1400  } else if (stunGas) {
1401  const int damage = std::max(0.0f, fd->damage[0] + fd->damage[1] * crand());
1402  const int rounds = std::max(2, fd->rounds);
1403  G_SpawnStunSmokeField(impact, "gasfield", rounds, damage, fd->splrad);
1404  }
1405  }
1406 
1407  /* check whether this caused a touch event for close actors */
1408  if (smoke || incendiary || stunGas) {
1409  const entity_type_t type = smoke ? ET_SMOKE : incendiary ? ET_FIRE : ET_SMOKESTUN;
1410  Edict* closeActor = nullptr;
1411  while ((closeActor = G_FindRadius(closeActor, impact, fd->splrad, ET_ACTOR))) {
1412  G_TouchTriggers(closeActor, type);
1413  }
1414  }
1415 
1416  /* send TUs if actor still alive */
1417  if (actor->inuse && !actor->isDead()) {
1418  G_ActorSetTU(actor, actor->getTus() - tusNeeded);
1419  G_SendStats(*actor);
1420  }
1421 
1422  G_EventEndShoot(*actor, mask);
1423 
1424  /* end events */
1425  G_EventEnd();
1426 
1427  /* check for win/draw conditions */
1428  G_MatchEndCheck();
1429 
1430  /* check for Reaction fire against the shooter */
1431  if (allowReaction)
1432  G_ReactionFirePostShot(actor);
1433  } else {
1434  actor->dir = prevDir;
1435  assert(actor->dir < CORE_DIRECTIONS);
1436  }
1437  return true;
1438 }
void G_EventEnd(void)
Definition: g_events.cpp:711
Edict * G_SpawnFloor(const pos3_t pos)
Spawns a new entity at the floor.
Definition: g_spawn.cpp:535
vec_t VectorLength(const vec3_t v)
Calculate the length of a vector.
Definition: mathlib.cpp:434
void G_ClientPrintf(const Player &player, int printLevel, const char *fmt,...)
Definition: g_client.cpp:206
Edict * G_EdictsGetNextInUse(Edict *lastEnt)
Iterate through the entities that are in use.
Definition: g_edicts.cpp:166
int G_TouchTriggers(Edict *ent, const entity_type_t type)
Check the world against triggers for the current entity.
Definition: g_utils.cpp:547
Reaction fire system.
#define VectorCopy(src, dest)
Definition: vector.h:51
void removeReaction()
Definition: g_edict.h:378
cvar_t * sv_shot_origin
Definition: g_main.cpp:63
const objDef_t * getWeapon() const
Definition: chr_shared.h:154
int damStunElectro
Definition: q_shared.h:535
bool tryAddToInventory(Inventory *const inv, const Item *const item, const invDef_t *container)
Tries to add an item to a container (in the inventory inv).
Definition: inventory.cpp:470
#define MORALE_RANDOM(mod)
Definition: g_local.h:248
bool isSameTeamAs(const Edict *other) const
Definition: g_edict.h:278
trace_t G_Trace(const Line &trLine, const Edict *passent, int contentmask)
collision detection - this version is more accurate and includes entity tests
Definition: g_utils.cpp:265
void setAmmoLeft(int value)
Definition: inv_shared.h:441
chrScoreGlobal_t score
Definition: chr_shared.h:387
Actor * G_EdictsGetNextLivingActor(Actor *lastEnt)
Iterate through the living actor entities.
Definition: g_edicts.cpp:196
void G_EventInventoryAmmo(const Edict &ent, const objDef_t *ammo, int amount, shoot_types_t shootType)
Change the amount of available ammo for the given entity.
Definition: g_events.cpp:181
static void G_ShootSingle(Actor *ent, const fireDef_t *fd, const vec3_t from, const pos3_t at, int mask, const Item *weapon, shot_mock_t *mock, int z_align, int i, shoot_types_t shootType, vec3_t impact)
Fires straight shots.
Definition: g_combat.cpp:930
float bounceFac
Definition: inv_shared.h:150
QGL_EXTERN GLint GLenum type
Definition: r_gl.h:94
void getShotOrigin(const vec3_t from, const vec3_t dir, bool crouching, vec3_t shotOrigin) const
#define TEAM_PHALANX
Definition: q_shared.h:62
void VectorMA(const vec3_t veca, const float scale, const vec3_t vecb, vec3_t outVector)
Sets vector_out (vc) to vevtor1 (va) + scale * vector2 (vb)
Definition: mathlib.cpp:261
#define MAX_TEAMS
Definition: defines.h:98
#define SF_BODY
Definition: q_shared.h:249
int getStun() const
Definition: g_edict.h:308
this is a fire definition for our weapons/ammo
Definition: inv_shared.h:110
bool G_SmokeVis(const vec3_t from, const Edict *check)
tests for smoke interference
Definition: g_vis.cpp:65
#define TEAM_ALIEN
Definition: q_shared.h:63
void G_CheckVis(Edict *check, const vischeckflags_t visFlags)
Check if the edict appears/perishes for the other teams. If they appear for other teams...
Definition: g_vis.cpp:409
int getAmmoLeft() const
Definition: inv_shared.h:466
void G_EventActorTurn(const Edict &ent)
Send the turn event for the given entity.
Definition: g_events.cpp:77
const teamDef_t * teamDef
Definition: chr_shared.h:394
int damStunGas
Definition: q_shared.h:533
vec2_t spread
Definition: inv_shared.h:146
int civilian
Definition: g_combat.h:34
#define _(String)
Definition: cl_shared.h:43
Misc utility functions for game module.
static chrScoreMission_t scoreMission[MAX_EDICTS]
Definition: g_client.cpp:51
#define CONTENTS_SOLID
Definition: defines.h:223
static void G_SpawnItemOnFloor(const pos3_t pos, const Item *item)
Spawn an item on the floor. A new ET_ITEM edict is created if needed.
Definition: g_combat.cpp:638
teammask_t G_PMToVis(playermask_t playerMask)
Converts player mask to vis mask.
Definition: g_client.cpp:165
voidpf uLong int origin
Definition: ioapi.h:45
static bool G_TeamPointVis(int team, const vec3_t point)
Test if point is "visible" from team.
Definition: g_combat.cpp:52
Actor * G_EdictsGetLivingActorFromPos(const pos3_t pos)
Searches an actor at the given grid location.
Definition: g_edicts.cpp:270
vec2_t spldmg
Definition: inv_shared.h:160
vec3_t endpos
Definition: tracing.h:59
static void G_SplashDamage(Actor *ent, const fireDef_t *fd, vec3_t impact, shot_mock_t *mock, const trace_t *tr)
Deals splash damage to a target and its surroundings.
Definition: g_combat.cpp:558
int activeTeam
Definition: g_local.h:101
uint32_t surfaceFlags
Definition: typedefs.h:39
int hitsSplash[SKILL_NUM_TYPES][KILLED_NUM_TYPES]
Definition: chr_shared.h:95
bool isDead() const
Definition: g_edict.h:362
character_t chr
Definition: g_edict.h:116
const fireDef_t * getFiredefs() const
Returns the firedefinitions for a given weapon/ammo.
Definition: inv_shared.cpp:576
int damShock
Definition: q_shared.h:529
TR_PLANE_TYPE plane
Definition: tracing.h:60
int enemyCount
Definition: g_combat.h:32
bool inuse
Definition: g_edict.h:47
Structure of all stats collected in a mission.
Definition: chr_shared.h:75
bool isStunned() const
Definition: g_edict.h:355
float value
Definition: cvar.h:80
AABB absBox
Definition: g_edict.h:61
const objDef_t * def(void) const
Definition: inv_shared.h:469
void G_ActorReserveTUs(Edict *ent, int resReaction, int resShot, int resCrouch)
Reserves TUs for different actor actions.
Definition: g_actor.cpp:136
#define VectorDist(a, b)
Definition: vector.h:69
static int oldPos
InventoryInterface invi
Definition: g_local.h:76
int stuns[KILLED_NUM_TYPES]
Definition: chr_shared.h:127
#define TEAM_CIVILIAN
Definition: q_shared.h:61
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
int getFmIdx() const
Definition: chr_shared.h:150
Item * getHeadgear() const
Definition: inv_shared.cpp:965
cvar_t * mob_wound
Definition: g_main.cpp:78
#define MAX_WALL_THICKNESS_FOR_SHOOTING_THROUGH
Definition: g_combat.cpp:38
#define G_IsCrouched(ent)
Definition: g_actor.h:32
#define G_IsBrushModel(ent)
Definition: g_local.h:138
void VectorNormalizeFast(vec3_t v)
fast vector normalize routine that does not check to make sure that length != 0, nor does it return l...
Definition: mathlib.cpp:762
cvar_t * mob_shoot
Definition: g_main.cpp:79
byte dir
Definition: g_edict.h:86
#define G_IsActor(ent)
Definition: g_local.h:127
Defines all attributes of objects used in the inventory.
Definition: inv_shared.h:264
#define SF_BOUNCING
Definition: q_shared.h:250
byte dmgtype
Definition: inv_shared.h:325
void G_AppearPerishEvent(playermask_t playerMask, bool appear, Edict &check, const Edict *ent)
Send the appear or perish event to the affected clients.
Definition: g_client.cpp:245
int integer
Definition: cvar.h:81
#define VectorScale(in, scale, out)
Definition: vector.h:79
void G_ReactionFireSettingsUpdate(Actor *actor, fireDefIndex_t fmIdx, actorHands_t hand, const objDef_t *od)
Updates the reaction fire settings in case something was moved into a hand or from a hand that would ...
Definition: g_reaction.cpp:555
chrScoreMission_t * scoreMission
Definition: chr_shared.h:388
morale_modifiers
Definition: g_combat.cpp:40
void G_CheckDeathOrKnockout(Actor *target, Actor *attacker, const fireDef_t *fd, int damage)
Definition: g_combat.cpp:499
bool G_IsLivingActor(const Edict *ent)
Checks whether the given edict is a living actor.
Definition: g_actor.cpp:43
int hits[SKILL_NUM_TYPES][KILLED_NUM_TYPES]
Definition: chr_shared.h:89
float G_ActorVis(const Edict *ent, const Edict *check, bool full)
calculate how much check is "visible" by ent
Definition: g_vis.cpp:100
Item * getArmour() const
Definition: g_edict.h:246
Actor * makeActor(Edict *ent)
Convert an Edict pointer into an Actor pointer.
Definition: g_edicts.cpp:327
const csi_t * csi
Definition: game.h:176
static bool G_Damage(Edict *target, const fireDef_t *fd, int damage, Actor *attacker, shot_mock_t *mock, const vec3_t impact)
Deals damage of a give type and amount to a target.
Definition: g_combat.cpp:389
int damIncendiary
Definition: q_shared.h:537
float fraction
Definition: tracing.h:58
const struct objDef_s * obj
Definition: inv_shared.h:125
short protection[MAX_DAMAGETYPES]
Definition: inv_shared.h:322
#define YAW
Definition: mathlib.h:55
Item * getRightHandItem() const
Definition: g_edict.h:249
int AngleToDir(int angle)
Returns the index of array directionAngles[DIRECTIONS] whose value is the closest to angle...
Definition: mathlib.cpp:130
game_locals_t game
Definition: g_main.cpp:37
#define SURF_BURN
Definition: defines.h:264
cvar_t * mob_death
Definition: g_main.cpp:77
void G_EventSendState(playermask_t playerMask, const Edict &ent)
Definition: g_events.cpp:466
#define IS_SHOT_RIGHT(x)
Determine whether the selected shoot type is for the item in the right hand, either shooting or react...
Definition: q_shared.h:243
int getTeam() const
Definition: g_edict.h:269
#define CID_LEFT
Definition: inv_shared.h:48
bool fireTwoHanded
Definition: inv_shared.h:279
item instance data, with linked list capability
Definition: inv_shared.h:402
#define todeg
Definition: mathlib.h:51
Match related functions.
int damFire
Definition: q_shared.h:528
int hitsSplashDamage[SKILL_NUM_TYPES][KILLED_NUM_TYPES]
Definition: chr_shared.h:96
int kills[KILLED_NUM_TYPES]
Definition: chr_shared.h:82
void AngleVectors(const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
Create the rotation matrix in order to rotate something.
Definition: mathlib.cpp:631
int firedSplash[SKILL_NUM_TYPES]
Definition: chr_shared.h:92
used in shot probability calculations (pseudo shots)
Definition: g_combat.h:31
cvar_t * mof_teamkill
Definition: g_main.cpp:81
bool deplete
Definition: inv_shared.h:301
#define CID_HEADGEAR
Definition: inv_shared.h:50
#define DotProduct(x, y)
Returns the distance between two 3-dimensional vectors.
Definition: vector.h:44
bool launched
Definition: inv_shared.h:137
#define CID_RIGHT
Definition: inv_shared.h:47
int damage
Definition: g_combat.h:36
cvar_t * g_difficulty
Definition: g_main.cpp:125
bool gravity
Definition: inv_shared.h:136
int friendCount
Definition: g_combat.h:33
game_import_t gi
Definition: g_main.cpp:39
bool thrown
Definition: inv_shared.h:281
#define GRENADE_DT
Definition: g_combat.cpp:690
int32_t shoot_types_t
Available shoot types - also see the ST_ constants.
Definition: q_shared.h:206
bool single
Definition: inv_shared.h:375
#define G_IsCivilian(ent)
Definition: g_local.h:148
#define IS_SHOT_HEADGEAR(x)
Determine whether the selected shoot type is for the item in the headgear slot.
Definition: q_shared.h:245
void G_SpawnFireField(const vec3_t vec, const char *particle, int rounds, int damage, vec_t radius)
Definition: g_spawn.cpp:521
static void G_UpdateShotMock(shot_mock_t *mock, const Edict *shooter, const Edict *struck, int damage)
Function to calculate possible damages for mock pseudoaction.
Definition: g_combat.cpp:224
Edict * G_FindRadius(Edict *from, const vec3_t org, float rad, entity_type_t type)
Returns entities that have origins within a spherical area.
Definition: g_utils.cpp:408
int kills[KILLED_NUM_TYPES]
Definition: chr_shared.h:126
teammask_t visflags
Definition: g_edict.h:82
All parts of the main game logic that are combat related.
int32_t fireDefIndex_t
Definition: inv_shared.h:78
void VecToAngles(const vec3_t value1, vec3_t angles)
Converts a vector to an angle vector.
Definition: mathlib.cpp:934
bool isSamePosAs(const pos3_t cmpPos)
Check whether the edict is on the given position.
Definition: g_edict.h:286
#define GET_ACC(ab, sk, pn)
Definition: q_shared.h:289
int damBlast
Definition: q_shared.h:528
int G_ActorGetModifiedTimeForFiredef(const Edict *const ent, const fireDef_t *const fd, const bool reaction)
Definition: g_actor.cpp:764
#define VecToPos(v, p)
Map boundary is +/- MAX_WORLD_WIDTH - to get into the positive area we add the possible max negative ...
Definition: mathlib.h:100
#define FIRESH_IsMedikit(firedef)
Definition: inv_shared.h:661
pos3_t pos
Definition: g_edict.h:55
void G_PrintActorStats(const Edict *victim, const Edict *attacker, const fireDef_t *fd)
Prints stats about who killed who with what and how.
Definition: g_utils.cpp:336
cvar_t * mor_panic
Definition: g_main.cpp:103
short resistance[MAX_DAMAGETYPES]
Definition: chr_shared.h:334
void G_EventShootHidden(teammask_t teamMask, const fireDef_t *fd, bool firstShoot, const vec3_t impact, int flags, const Edict *targetEdict)
Start the shooting event for hidden actors.
Definition: g_events.cpp:232
#define PITCH
Definition: mathlib.h:54
An Edict of type Actor.
Definition: g_edict.h:348
#define CID_FLOOR
Definition: inv_shared.h:55
#define WEAPON_BALANCE
Definition: inv_shared.h:43
Edict * G_SpawnParticle(const vec3_t origin, int spawnflags, const char *particle)
Definition: g_spawn.cpp:551
byte num_alive[MAX_TEAMS]
Definition: g_local.h:115
bool G_TestLine(const vec3_t start, const vec3_t end)
fast version of a line trace but without including entities
Definition: g_utils.cpp:253
cvar_t * mor_attacker
Definition: g_main.cpp:96
bool oneshot
Definition: inv_shared.h:299
#define SF_IMPACT
Definition: q_shared.h:248
bool CHRSH_IsTeamDefRobot(const teamDef_t *const td)
Check if a team definition is a robot.
Definition: chr_shared.cpp:102
static transfer_t tr
Edict * G_GetFloorItemFromPos(const pos3_t pos)
Callback to G_GetEdictFromPos() for given position, used to get items from position.
Definition: g_inventory.cpp:48
Definition: line.h:31
playermask_t G_VisToPM(teammask_t teamMask)
Converts vis mask to player mask.
Definition: g_client.cpp:186
void Com_DPrintf(int level, const char *fmt,...)
A Com_Printf that only shows up if the "developer" cvar is set.
Definition: common.cpp:398
static void G_UpdateCharacterBodycount(Edict *attacker, const fireDef_t *fd, const Actor *target)
Update character stats for this mission after successful shoot.
Definition: g_combat.cpp:257
int damSmoke
Definition: q_shared.h:536
void G_DamageActor(Edict *target, const int damage, const vec3_t impact)
Deals damage and causes wounds.
Definition: g_health.cpp:52
#define GRAVITY
Definition: q_shared.h:276
#define INVDEF(containerID)
Definition: cl_shared.h:47
int throughWall
Definition: inv_shared.h:142
static bool G_FireAffectedSurface(const cBspSurface_t *surface, const fireDef_t *fd)
Checks surface vulnerability for firedefinition damagetype.
Definition: g_combat.cpp:536
pos_t pos3_t[3]
Definition: ufotypes.h:58
byte dmgweight
Definition: inv_shared.h:141
vec2_t damage
Definition: inv_shared.h:158
void getCenter(vec3_t center) const
Calculates the center of the bounding box.
Definition: aabb.h:155
void G_SpawnSmokeField(const vec3_t vec, const char *particle, int rounds, int damage, vec_t radius)
Spawns a smoke field that is available for some rounds.
Definition: g_spawn.cpp:516
static void G_ShootGrenade(const Player &player, Actor *shooter, const fireDef_t *fd, const vec3_t from, const pos3_t at, int mask, const Item *weapon, shot_mock_t *mock, int z_align, vec3_t impact)
A parabola-type shoot (grenade, throw).
Definition: g_combat.cpp:707
actorSizeEnum_t fieldSize
Definition: g_edict.h:141
#define UNIT_SIZE
Definition: defines.h:121
#define MASK_SHOT
Definition: defines.h:275
void G_TakeDamage(Edict *ent, int damage)
Applies the given damage value to an edict that is either an actor or has the FL_DESTROYABLE flag set...
Definition: g_utils.cpp:215
bool firedHit[KILLED_NUM_TYPES]
Definition: chr_shared.h:88
#define VectorCompare(a, b)
Definition: vector.h:63
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
cvar_t * mof_watching
Definition: g_main.cpp:80
#define VectorLengthSqr(a)
Definition: vector.h:71
#define G_TeamToVisMask(team)
Definition: g_local.h:143
int32_t containerIndex_t
Definition: inv_shared.h:46
void G_EventSpawnSound(playermask_t playerMask, const Edict &ent, const vec3_t origin, const char *sound)
Spawns a sound (that will be spatialized on the client side)
Definition: g_events.cpp:40
Structure of all stats collected for an actor over time.
Definition: chr_shared.h:119
bool firedSplashHit[KILLED_NUM_TYPES]
Definition: chr_shared.h:94
int G_CheckVisTeamAll(const int team, const vischeckflags_t visFlags, const Edict *ent)
Do G_CheckVisTeam for all entities ent is the one that is looking at the others.
Definition: g_vis.cpp:376
void asIntString(char *str, size_t len)
Prints a representation of the box.
Definition: aabb.h:167
int damNormal
Definition: q_shared.h:528
static void G_UpdateHitScore(Edict *attacker, const Edict *target, const fireDef_t *fd, const int splashDamage)
Increases the 'hit' score by one for all affected teams/skills by one (except splash damage...
Definition: g_combat.cpp:304
void gaussrand(float *gauss1, float *gauss2)
generate two gaussian distributed random numbers with median at 0 and stdev of 1
Definition: mathlib.cpp:529
#define VectorAdd(a, b, dest)
Definition: vector.h:47
void setMorale(int mor)
Definition: g_edict.h:311
void G_ReactionFirePreShot(const Actor *target, const int fdTime)
Called when 'target' is about to shoot, this forces a 'draw' to decide who gets the first shot...
Definition: g_reaction.cpp:994
int skillKills[SKILL_NUM_TYPES]
Definition: chr_shared.h:99
#define G_IsSinglePlayer()
Definition: g_local.h:146
static void G_Morale(morale_modifiers type, const Edict *victim, const Edict *attacker, int param)
Applies morale changes to actors around a wounded or killed actor.
Definition: g_combat.cpp:98
bool rolled
Definition: inv_shared.h:138
float range
Definition: inv_shared.h:152
FiremodeSettings RFmode
Definition: chr_shared.h:397
char name[MAX_QPATH]
Definition: typedefs.h:38
#define G_IsAI(ent)
Definition: g_local.h:141
bool G_IsActorWounded(const Edict *ent, bool serious)
Definition: g_health.cpp:213
bool G_ActionCheckForCurrentTeam(const Player &player, Actor *ent, int TU)
Checks whether the requested action is possible for the current active team.
Definition: g_client.cpp:380
bool G_ClientShoot(const Player &player, Actor *actor, const pos3_t at, shoot_types_t shootType, fireDefIndex_t firemode, shot_mock_t *mock, bool allowReaction, int z_align)
Setup for shooting, either real or mock.
Definition: g_combat.cpp:1183
Actor * G_EdictsGetNextLivingActorOfTeam(Actor *lastEnt, const int team)
Iterate through the living actor entities of the given team.
Definition: g_edicts.cpp:216
void G_VisFlagsReset(Edict &ent)
Definition: g_vis.cpp:438
QGL_EXTERN GLint i
Definition: r_gl.h:113
int getTus() const
Definition: g_edict.h:318
#define G_IsDead(ent)
Definition: g_actor.h:34
void G_EventInventoryDelete(const Edict &ent, playermask_t playerMask, const containerIndex_t containerId, int x, int y)
Tell the client to remove the item from the container.
Definition: g_events.cpp:131
bool isCrouched() const
Definition: g_edict.h:361
#define G_IsAlien(ent)
Definition: g_local.h:149
void addStun(int stu)
Definition: g_edict.h:305
#define SKILL_BALANCE
Definition: inv_shared.h:44
Brings new objects into the world.
entity_type_t type
Definition: g_edict.h:81
bool allow_self
Definition: g_combat.h:37
void G_ActorSetTU(Edict *ent, int tus)
Definition: g_actor.cpp:267
#define GROUND_DELTA
Definition: defines.h:115
#define CORE_DIRECTIONS
Definition: mathlib.h:88
cvar_t * mor_victim
Definition: g_main.cpp:93
void G_EventShoot(const Edict &ent, teammask_t teamMask, const fireDef_t *fd, bool firstShoot, shoot_types_t shootType, int flags, const trace_t *trace, const vec3_t from, const vec3_t impact)
Do the shooting.
Definition: g_events.cpp:260
int rounds
Definition: inv_shared.h:163
bool G_FrustumVis(const Edict *from, const vec3_t point)
Checks whether a point is "visible" from the edicts position.
Definition: g_vis.cpp:38
void G_EventPerish(const Edict &ent)
Send an event to all clients that are seeing the given edict, that it just has disappeared.
Definition: g_events.cpp:158
functions to handle the storage and lifecycle of all edicts in the game module.
unsigned num_spawned[MAX_TEAMS]
Definition: g_local.h:112
int firedTUs[SKILL_NUM_TYPES]
Definition: chr_shared.h:87
vec3_t origin
Definition: g_edict.h:53
#define GRENADE_STOPSPEED
Definition: g_combat.cpp:691
bool isSameAs(const Edict *other) const
Definition: g_edict.h:275
bool(* destroy)(Edict *self)
Definition: g_edict.h:155
float crand(void)
Return random values between -1 and 1.
Definition: mathlib.cpp:517
bool startsolid
Definition: tracing.h:57
const objDef_t * ammoDef(void) const
Definition: inv_shared.h:460
const char * getActorSound(int gender, actorSound_t soundType) const
Returns the actor sounds for a given category.
Definition: chr_shared.cpp:54
int weaponSkill
Definition: inv_shared.h:162
vec_t vec3_t[3]
Definition: ufotypes.h:39
inventory definition for our menus
Definition: inv_shared.h:371
bool RayIntersectAABB(const vec3_t start, const vec3_t end, const AABB &aabb)
Definition: mathlib.cpp:1110
vec_t vec2_t[2]
Definition: ufotypes.h:38
entity_type_t
Definition: q_shared.h:145
float splrad
Definition: inv_shared.h:161
void G_ActorGetEyeVector(const Edict *actor, vec3_t eye)
Fills a vector with the eye position of a given actor.
Definition: g_actor.cpp:357
void G_SpawnStunSmokeField(const vec3_t vec, const char *particle, int rounds, int damage, vec_t radius)
Definition: g_spawn.cpp:526
Inventory inv
Definition: chr_shared.h:392
bool G_ActorDieOrStun(Actor *actor, Edict *attacker)
Reports and handles death or stun of an actor. If the HP of an actor is zero the actor will die...
Definition: g_actor.cpp:435
uint32_t contentFlags
Definition: tracing.h:63
containerIndex_t id
Definition: inv_shared.h:373
int firedSplashTUs[SKILL_NUM_TYPES]
Definition: chr_shared.h:93
int morale
Definition: g_edict.h:91
void G_MatchEndCheck(void)
Checks whether there are still actors to fight with left. If none are the match end will be triggered...
Definition: g_match.cpp:280
#define lengthof(x)
Definition: shared.h:105
#define G_IsVisibleForTeam(ent, team)
Definition: g_local.h:144
void G_EventThrow(teammask_t teamMask, const fireDef_t *fd, float dt, byte flags, const vec3_t position, const vec3_t velocity)
Definition: g_events.cpp:440
Edict * G_GetFloorItems(Edict *ent)
Prepares a list of items on the floor at given entity position.
Definition: g_inventory.cpp:59
const char * classname
Definition: g_edict.h:67
#define GET_MORALE(ab)
Definition: q_shared.h:290
Edict * G_EdictsGetByNum(const int num)
Get an entity by it's number.
Definition: g_edicts.cpp:83
cvar_t * mor_pain
Definition: g_main.cpp:84
float crouch
Definition: inv_shared.h:151
actorHands_t getHand() const
Definition: chr_shared.h:158
int fired[SKILL_NUM_TYPES]
Definition: chr_shared.h:86
void G_ReactionFirePostShot(Actor *target)
Called after 'target' has fired, this might trigger more reaction fire or resolve outstanding reactio...
void emptyContainer(Inventory *const inv, const containerIndex_t container)
Clears the linked list of a container - removes all items from this container.
Definition: inventory.cpp:501
#define AABB_STRING
Definition: aabb.h:40
void G_SendStats(Edict &ent)
Send stats to network buffer.
Definition: g_stats.cpp:34
void G_VisFlagsAdd(Edict &ent, teammask_t teamMask)
Definition: g_vis.cpp:433
static void G_ShotMorale(const Actor *shooter, const fireDef_t *fd, const vec3_t from, const Item *weapon, const vec3_t impact)
Applies morale changes to actors who find themselves in the general direction of a shot...
Definition: g_combat.cpp:171
int bounce
Definition: inv_shared.h:149
void G_CalcEffectiveSpread(const Actor *shooter, const fireDef_t *fd, vec2_t effSpread)
Calculate the effective spread for the given actor and firemode.
Definition: g_combat.cpp:671
#define ST_NUM_SHOOT_TYPES
Amount of shoottypes available.
Definition: q_shared.h:236
Definition: g_edict.h:45
cvar_t * mof_civilian
Definition: g_main.cpp:82
cBspSurface_t * surface
Definition: tracing.h:61
void G_EventStartShoot(const Edict &ent, teammask_t teamMask, shoot_types_t shootType, const pos3_t at)
Start the shooting event.
Definition: g_events.cpp:203
bool allsolid
Definition: tracing.h:56
#define G_IsSmoke(ent)
Definition: g_local.h:131
void G_EventEndShoot(const Edict &ent, teammask_t teamMask)
Ends the shooting event.
Definition: g_events.cpp:217
Item * getLeftHandItem() const
Definition: g_edict.h:252
uint8_t byte
Definition: ufotypes.h:34
int stuns[KILLED_NUM_TYPES]
Definition: chr_shared.h:83
int damLaser
Definition: q_shared.h:532
#define G_IsAIPlayer(player)
Definition: g_local.h:142
#define VectorDistSqr(a, b)
Definition: vector.h:68
cvar_t * mof_enemy
Definition: g_main.cpp:83
float G_ActorGetInjuryPenalty(const Edict *const ent, const modifier_types_t type)
Returns the penalty to the given stat caused by the actor wounds.
Definition: g_health.cpp:177
int skills[SKILL_NUM_TYPES]
Definition: chr_shared.h:122
void G_FreeEdict(Edict *ent)
Marks the edict as free.
Definition: g_utils.cpp:41
#define SF_BOUNCED
Definition: q_shared.h:251
cvar_t * mor_default
Definition: g_main.cpp:87
const char * model
Definition: g_edict.h:74
cvar_t * mon_teamfactor
Definition: g_main.cpp:99
void setDazed()
Definition: g_edict.h:371
bool G_ActionCheckForReaction(const Player &player, Actor *actor, int TU)
Checks whether the requested action is possible.
Definition: g_client.cpp:403
#define VectorSubtract(a, b, dest)
Definition: vector.h:45
const char * id
Definition: inv_shared.h:268
cvar_t * mor_distance
Definition: g_main.cpp:90
void G_TreatActor(Actor *target, const fireDef_t *const fd, const int heal, const int healerTeam)
Heals a target and treats wounds.
Definition: g_health.cpp:81
Interface for g_client.cpp.
Edict * G_EdictsGetNext(Edict *lastEnt)
Iterate through the list of entities.
Definition: g_edicts.cpp:107
playermask_t G_TeamToPM(int team)
Generates the player bit mask for a given team.
Definition: g_client.cpp:144
#define DEBUG_GAME
Definition: defines.h:61
int entNum
Definition: tracing.h:67
#define G_IsBreakable(ent)
Definition: g_local.h:137
level_locals_t level
Definition: g_main.cpp:38
int HP
Definition: g_edict.h:89
int G_ApplyProtection(const Edict *target, const byte dmgWeight, int damage)
Reduces damage by armour and natural protection.
Definition: g_combat.cpp:362
#define PRINT_HUD
Definition: defines.h:107
static bool G_PrepareShot(Edict *ent, shoot_types_t shootType, fireDefIndex_t firemode, Item **weapon, containerIndex_t *container, const fireDef_t **fd)
Prepares weapon, firemode and container used for shoot.
Definition: g_combat.cpp:1132
chrReservations_t reservedTus
Definition: chr_shared.h:396
cvar_t * g_nodamage
Definition: g_main.cpp:116
bool irgoggles
Definition: inv_shared.h:140
killtypes_t
Definition: chr_shared.h:27