UFO: Alien Invasion
Loading...
Searching...
No Matches
g_combat.cpp
Go to the documentation of this file.
1
5
6/*
7Copyright (C) 2002-2025 UFO: Alien Invasion.
8
9This program is free software; you can redistribute it and/or
10modify it under the terms of the GNU General Public License
11as published by the Free Software Foundation; either version 2
12of the License, or (at your option) any later version.
13
14This program is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18See the GNU General Public License for more details.
19
20You should have received a copy of the GNU General Public License
21along with this program; if not, write to the Free Software
22Foundation, 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
45
52static 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
98static 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]))
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
171static 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
224static 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
257static 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:
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:
280 break;
281 case TEAM_PHALANX:
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
304static 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:
318 break;
319 case TEAM_ALIEN:
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
362int 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
389static 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
499void 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
536static 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
558static 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
638static 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 {
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
671void 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
707static 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 */
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
875static 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
894static 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
930static 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);
1061 VectorMA(tr.endpos, MAX_WALL_THICKNESS_FOR_SHOOTING_THROUGH, dir, tracefrom);
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
1132static 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
1183bool 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);
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);
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 */
1429
1430 /* check for Reaction fire against the shooter */
1431 if (allowReaction)
1433 } else {
1434 actor->dir = prevDir;
1435 assert(actor->dir < CORE_DIRECTIONS);
1436 }
1437 return true;
1438}
#define AABB_STRING
Definition aabb.h:40
bool CHRSH_IsTeamDefRobot(const teamDef_t *const td)
Check if a team definition is a robot.
@ ABILITY_MIND
Definition chr_shared.h:40
@ ABILITY_ACCURACY
Definition chr_shared.h:39
killtypes_t
Definition chr_shared.h:27
@ KILLED_TEAM
Definition chr_shared.h:30
@ KILLED_ENEMIES
Definition chr_shared.h:28
@ KILLED_CIVILIANS
Definition chr_shared.h:29
@ KILLED_NUM_TYPES
Definition chr_shared.h:32
@ SND_HURT
Definition chr_shared.h:220
@ MODIFIER_ACCURACY
Definition chr_shared.h:256
#define _(String)
Definition cl_shared.h:44
#define INVDEF(containerID)
Definition cl_shared.h:48
void asIntString(char *str, size_t len)
Prints a representation of the box.
Definition aabb.h:167
void getCenter(vec3_t center) const
Calculates the center of the bounding box.
Definition aabb.h:155
An Edict of type Actor.
Definition g_edict.h:348
bool isDead() const
Definition g_edict.h:362
void removeReaction()
Definition g_edict.h:378
bool isStunned() const
Definition g_edict.h:355
bool isCrouched() const
Definition g_edict.h:361
void setDazed()
Definition g_edict.h:371
teammask_t visflags
Definition g_edict.h:82
bool isSameTeamAs(const Edict *other) const
Definition g_edict.h:278
character_t chr
Definition g_edict.h:116
Item * getArmour() const
Definition g_edict.h:246
bool isSameAs(const Edict *other) const
Definition g_edict.h:275
void addStun(int stu)
Definition g_edict.h:305
void setMorale(int mor)
Definition g_edict.h:311
const char * classname
Definition g_edict.h:67
Item * getRightHandItem() const
Definition g_edict.h:249
vec3_t origin
Definition g_edict.h:53
pos3_t pos
Definition g_edict.h:55
actorSizeEnum_t fieldSize
Definition g_edict.h:141
int HP
Definition g_edict.h:89
byte dir
Definition g_edict.h:86
Item * getLeftHandItem() const
Definition g_edict.h:252
bool inuse
Definition g_edict.h:47
int getTeam() const
Definition g_edict.h:269
bool(* destroy)(Edict *self)
Definition g_edict.h:155
AABB absBox
Definition g_edict.h:61
bool isSamePosAs(const pos3_t cmpPos)
Check whether the edict is on the given position.
Definition g_edict.h:286
entity_type_t type
Definition g_edict.h:81
int getStun() const
Definition g_edict.h:308
int getTus() const
Definition g_edict.h:318
int morale
Definition g_edict.h:91
const char * model
Definition g_edict.h:74
actorHands_t getHand() const
Definition chr_shared.h:165
const objDef_t * getWeapon() const
Definition chr_shared.h:161
int getFmIdx() const
Definition chr_shared.h:157
Item * getHeadgear() const
item instance data, with linked list capability
Definition inv_shared.h:402
const objDef_t * ammoDef(void) const
Definition inv_shared.h:460
int getAmmoLeft() const
Definition inv_shared.h:466
const objDef_t * def(void) const
Definition inv_shared.h:469
const fireDef_t * getFiredefs() const
Returns the firedefinitions for a given weapon/ammo.
void setAmmoLeft(int value)
Definition inv_shared.h:441
Definition line.h:31
void Com_DPrintf(int level, const char *fmt,...)
A Com_Printf that only shows up if the "developer" cvar is set.
Definition common.cpp:440
static transfer_t tr
#define MASK_SHOT
Definition defines.h:275
#define PRINT_HUD
Definition defines.h:107
#define MAX_TEAMS
Definition defines.h:98
#define SURF_BURN
Definition defines.h:264
#define UNIT_SIZE
Definition defines.h:121
#define CONTENTS_SOLID
Definition defines.h:223
#define DEBUG_GAME
Definition defines.h:61
#define GROUND_DELTA
Definition defines.h:115
void G_ActorReserveTUs(Edict *ent, int resReaction, int resShot, int resCrouch)
Reserves TUs for different actor actions.
Definition g_actor.cpp:136
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
void G_ActorSetTU(Edict *ent, int tus)
Definition g_actor.cpp:267
int G_ActorGetModifiedTimeForFiredef(const Edict *const ent, const fireDef_t *const fd, const bool reaction)
Definition g_actor.cpp:764
bool G_IsLivingActor(const Edict *ent)
Checks whether the given edict is a living actor.
Definition g_actor.cpp:43
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
#define G_IsCrouched(ent)
Definition g_actor.h:32
#define G_IsDead(ent)
Definition g_actor.h:34
playermask_t G_TeamToPM(int team)
Generates the player bit mask for a given team.
Definition g_client.cpp:144
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
static chrScoreMission_t scoreMission[MAX_EDICTS]
Definition g_client.cpp:51
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
void G_ClientPrintf(const Player &player, int printLevel, const char *fmt,...)
Definition g_client.cpp:206
bool G_ActionCheckForReaction(const Player &player, Actor *actor, int TU)
Checks whether the requested action is possible.
Definition g_client.cpp:403
teammask_t G_PMToVis(playermask_t playerMask)
Converts player mask to vis mask.
Definition g_client.cpp:165
playermask_t G_VisToPM(teammask_t teamMask)
Converts vis mask to player mask.
Definition g_client.cpp:186
Interface for g_client.cpp.
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
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
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
#define MAX_WALL_THICKNESS_FOR_SHOOTING_THROUGH
Definition g_combat.cpp:38
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
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
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
#define GRENADE_DT
Definition g_combat.cpp:690
static bool G_TeamPointVis(int team, const vec3_t point)
Test if point is "visible" from team.
Definition g_combat.cpp:52
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
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 G_ApplyProtection(const Edict *target, const byte dmgWeight, int damage)
Reduces damage by armour and natural protection.
Definition g_combat.cpp:362
#define GRENADE_STOPSPEED
Definition g_combat.cpp:691
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
void G_CheckDeathOrKnockout(Actor *target, Actor *attacker, const fireDef_t *fd, int damage)
Definition g_combat.cpp:499
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.
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
morale_modifiers
Definition g_combat.cpp:40
@ ML_SHOOT
Definition g_combat.cpp:41
@ ML_WOUND
Definition g_combat.cpp:42
@ ML_DEATH
Definition g_combat.cpp:43
static bool G_FireAffectedSurface(const cBspSurface_t *surface, const fireDef_t *fd)
Checks surface vulnerability for firedefinition damagetype.
Definition g_combat.cpp:536
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
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.
All parts of the main game logic that are combat related.
Actor * makeActor(Edict *ent)
Convert an Edict pointer into an Actor pointer.
Definition g_edicts.cpp:327
Actor * G_EdictsGetNextLivingActorOfTeam(Actor *lastEnt, const int team)
Iterate through the living actor entities of the given team.
Definition g_edicts.cpp:216
Actor * G_EdictsGetNextLivingActor(Actor *lastEnt)
Iterate through the living actor entities.
Definition g_edicts.cpp:196
Edict * G_EdictsGetNext(Edict *lastEnt)
Iterate through the list of entities.
Definition g_edicts.cpp:107
Edict * G_EdictsGetNextInUse(Edict *lastEnt)
Iterate through the entities that are in use.
Definition g_edicts.cpp:166
Actor * G_EdictsGetLivingActorFromPos(const pos3_t pos)
Searches an actor at the given grid location.
Definition g_edicts.cpp:270
Edict * G_EdictsGetByNum(const int num)
Get an entity by it's number.
Definition g_edicts.cpp:83
functions to handle the storage and lifecycle of all edicts in the game module.
void G_EventEnd(void)
Definition g_events.cpp:711
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
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
void G_EventActorTurn(const Edict &ent)
Send the turn event for the given entity.
Definition g_events.cpp:77
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
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
void G_EventEndShoot(const Edict &ent, teammask_t teamMask)
Ends the shooting event.
Definition g_events.cpp:217
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
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
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
void G_EventSendState(playermask_t playerMask, const Edict &ent)
Definition g_events.cpp:466
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
void G_DamageActor(Edict *target, const int damage, const vec3_t impact)
Deals damage and causes wounds.
Definition g_health.cpp:52
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
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
bool G_IsActorWounded(const Edict *ent, bool serious)
Definition g_health.cpp:213
Edict * G_GetFloorItems(Edict *ent)
Prepares a list of items on the floor at given entity position.
Edict * G_GetFloorItemFromPos(const pos3_t pos)
Callback to G_GetEdictFromPos() for given position, used to get items from position.
cvar_t * mor_pain
Definition g_main.cpp:84
cvar_t * mor_attacker
Definition g_main.cpp:96
#define MORALE_RANDOM(mod)
Definition g_local.h:248
game_locals_t game
Definition g_main.cpp:37
cvar_t * sv_shot_origin
Definition g_main.cpp:63
level_locals_t level
Definition g_main.cpp:38
cvar_t * mor_panic
Definition g_main.cpp:103
cvar_t * mob_shoot
Definition g_main.cpp:79
#define G_IsActor(ent)
Definition g_local.h:127
cvar_t * mor_default
Definition g_main.cpp:87
#define G_IsAI(ent)
Definition g_local.h:141
cvar_t * mof_enemy
Definition g_main.cpp:83
#define G_IsBreakable(ent)
Definition g_local.h:137
#define G_IsVisibleForTeam(ent, team)
Definition g_local.h:144
cvar_t * mon_teamfactor
Definition g_main.cpp:99
#define G_TeamToVisMask(team)
Definition g_local.h:143
void G_SendStats(Edict &ent)
Send stats to network buffer.
Definition g_stats.cpp:34
game_import_t gi
Definition g_main.cpp:39
cvar_t * mor_distance
Definition g_main.cpp:90
#define G_IsCivilian(ent)
Definition g_local.h:148
#define G_IsAIPlayer(player)
Definition g_local.h:142
cvar_t * mor_victim
Definition g_main.cpp:93
#define G_IsSinglePlayer()
Definition g_local.h:146
#define G_IsAlien(ent)
Definition g_local.h:149
cvar_t * mof_teamkill
Definition g_main.cpp:81
cvar_t * mof_civilian
Definition g_main.cpp:82
#define G_IsBrushModel(ent)
Definition g_local.h:138
cvar_t * g_difficulty
Definition g_main.cpp:125
cvar_t * mob_wound
Definition g_main.cpp:78
cvar_t * mof_watching
Definition g_main.cpp:80
cvar_t * g_nodamage
Definition g_main.cpp:116
cvar_t * mob_death
Definition g_main.cpp:77
#define G_IsSmoke(ent)
Definition g_local.h:131
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
Match related functions.
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 ...
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.
void G_ReactionFirePostShot(Actor *target)
Called after 'target' has fired, this might trigger more reaction fire or resolve outstanding reactio...
Reaction fire system.
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
void G_SpawnFireField(const vec3_t vec, const char *particle, int rounds, int damage, vec_t radius)
Definition g_spawn.cpp:521
Edict * G_SpawnFloor(const pos3_t pos)
Spawns a new entity at the floor.
Definition g_spawn.cpp:535
void G_SpawnStunSmokeField(const vec3_t vec, const char *particle, int rounds, int damage, vec_t radius)
Definition g_spawn.cpp:526
Edict * G_SpawnParticle(const vec3_t origin, int spawnflags, const char *particle)
Definition g_spawn.cpp:551
Brings new objects into the world.
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 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 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
void G_FreeEdict(Edict *ent)
Marks the edict as free.
Definition g_utils.cpp:41
int G_TouchTriggers(Edict *ent, const entity_type_t type)
Check the world against triggers for the current entity.
Definition g_utils.cpp:547
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
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
Misc utility functions for game module.
void G_VisFlagsAdd(Edict &ent, teammask_t teamMask)
Definition g_vis.cpp:433
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
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 G_VisFlagsReset(Edict &ent)
Definition g_vis.cpp:438
bool G_SmokeVis(const vec3_t from, const Edict *check)
tests for smoke interference
Definition g_vis.cpp:65
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
float G_ActorVis(const Edict *ent, const Edict *check, bool full)
calculate how much check is "visible" by ent
Definition g_vis.cpp:100
int32_t fireDefIndex_t
Definition inv_shared.h:78
#define CID_HEADGEAR
Definition inv_shared.h:50
int32_t containerIndex_t
Definition inv_shared.h:46
#define WEAPON_BALANCE
Definition inv_shared.h:43
#define FIRESH_IsMedikit(firedef)
Definition inv_shared.h:661
#define SKILL_BALANCE
Definition inv_shared.h:44
#define CID_FLOOR
Definition inv_shared.h:55
#define CID_LEFT
Definition inv_shared.h:48
#define CID_RIGHT
Definition inv_shared.h:47
voidpf uLong int origin
Definition ioapi.h:45
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
void gaussrand(float *gauss1, float *gauss2)
generate two gaussian distributed random numbers with median at 0 and stdev of 1
Definition mathlib.cpp:529
vec_t VectorLength(const vec3_t v)
Calculate the length of a vector.
Definition mathlib.cpp:434
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
float crand(void)
Return random values between -1 and 1.
Definition mathlib.cpp:517
bool RayIntersectAABB(const vec3_t start, const vec3_t end, const AABB &aabb)
Definition mathlib.cpp:1110
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 AngleToDir(int angle)
Returns the index of array directionAngles[DIRECTIONS] whose value is the closest to angle.
Definition mathlib.cpp:130
void VecToAngles(const vec3_t value1, vec3_t angles)
Converts a vector to an angle vector.
Definition mathlib.cpp:934
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
#define CORE_DIRECTIONS
Definition mathlib.h:88
#define YAW
Definition mathlib.h:55
#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 PITCH
Definition mathlib.h:54
#define todeg
Definition mathlib.h:51
#define TEAM_PHALANX
Definition q_shared.h:62
#define SF_BODY
Definition q_shared.h:249
#define TEAM_ALIEN
Definition q_shared.h:63
#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
#define SF_BOUNCED
Definition q_shared.h:251
#define IS_SHOT_HEADGEAR(x)
Determine whether the selected shoot type is for the item in the headgear slot.
Definition q_shared.h:245
#define GRAVITY
Definition q_shared.h:276
entity_type_t
Definition q_shared.h:145
@ ET_ACTOR
Definition q_shared.h:148
@ ET_SMOKE
Definition q_shared.h:168
@ ET_FIRE
Definition q_shared.h:169
@ ET_SMOKESTUN
Definition q_shared.h:170
#define SF_IMPACT
Definition q_shared.h:248
#define GET_MORALE(ab)
Definition q_shared.h:290
#define ST_NUM_SHOOT_TYPES
Amount of shoottypes available.
Definition q_shared.h:236
#define SF_BOUNCING
Definition q_shared.h:250
int32_t shoot_types_t
Available shoot types - also see the ST_ constants.
Definition q_shared.h:206
#define TEAM_CIVILIAN
Definition q_shared.h:61
#define GET_ACC(ab, sk, pn)
Definition q_shared.h:289
QGL_EXTERN GLint i
Definition r_gl.h:113
QGL_EXTERN GLint GLenum type
Definition r_gl.h:94
#define lengthof(x)
Definition shared.h:105
uint32_t surfaceFlags
Definition typedefs.h:39
chrReservations_t reservedTus
Definition chr_shared.h:415
const teamDef_t * teamDef
Definition chr_shared.h:413
chrScoreMission_t * scoreMission
Definition chr_shared.h:407
chrScoreGlobal_t score
Definition chr_shared.h:406
FiremodeSettings RFmode
Definition chr_shared.h:416
Inventory inv
Definition chr_shared.h:411
Structure of all stats collected for an actor over time.
Definition chr_shared.h:119
int stuns[KILLED_NUM_TYPES]
Definition chr_shared.h:127
int kills[KILLED_NUM_TYPES]
Definition chr_shared.h:126
int skills[SKILL_NUM_TYPES]
Definition chr_shared.h:122
Structure of all stats collected in a mission.
Definition chr_shared.h:75
int hitsSplashDamage[SKILL_NUM_TYPES][KILLED_NUM_TYPES]
Definition chr_shared.h:96
int fired[SKILL_NUM_TYPES]
Definition chr_shared.h:86
int firedSplash[SKILL_NUM_TYPES]
Definition chr_shared.h:92
bool firedHit[KILLED_NUM_TYPES]
Definition chr_shared.h:88
int hitsSplash[SKILL_NUM_TYPES][KILLED_NUM_TYPES]
Definition chr_shared.h:95
int hits[SKILL_NUM_TYPES][KILLED_NUM_TYPES]
Definition chr_shared.h:89
int firedTUs[SKILL_NUM_TYPES]
Definition chr_shared.h:87
bool firedSplashHit[KILLED_NUM_TYPES]
Definition chr_shared.h:94
int firedSplashTUs[SKILL_NUM_TYPES]
Definition chr_shared.h:93
this is a fire definition for our weapons/ammo
Definition inv_shared.h:110
float crouch
Definition inv_shared.h:151
byte dmgweight
Definition inv_shared.h:141
const struct objDef_s * obj
Definition inv_shared.h:125
bool irgoggles
Definition inv_shared.h:140
vec2_t damage
Definition inv_shared.h:158
int weaponSkill
Definition inv_shared.h:162
float splrad
Definition inv_shared.h:161
vec2_t spldmg
Definition inv_shared.h:160
int throughWall
Definition inv_shared.h:142
vec2_t spread
Definition inv_shared.h:146
float bounceFac
Definition inv_shared.h:150
void getShotOrigin(const vec3_t from, const vec3_t dir, bool crouching, vec3_t shotOrigin) const
float range
Definition inv_shared.h:152
bool rolled
Definition inv_shared.h:138
bool launched
Definition inv_shared.h:137
bool gravity
Definition inv_shared.h:136
inventory definition for our menus
Definition inv_shared.h:371
containerIndex_t id
Definition inv_shared.h:373
bool single
Definition inv_shared.h:375
Defines all attributes of objects used in the inventory.
Definition inv_shared.h:264
bool deplete
Definition inv_shared.h:301
bool oneshot
Definition inv_shared.h:299
byte dmgtype
Definition inv_shared.h:325
short protection[MAX_DAMAGETYPES]
Definition inv_shared.h:322
bool fireTwoHanded
Definition inv_shared.h:279
const char * id
Definition inv_shared.h:268
bool thrown
Definition inv_shared.h:281
used in shot probability calculations (pseudo shots)
Definition g_combat.h:31
int friendCount
Definition g_combat.h:33
bool allow_self
Definition g_combat.h:37
int civilian
Definition g_combat.h:34
int enemyCount
Definition g_combat.h:32
short resistance[MAX_DAMAGETYPES]
Definition chr_shared.h:345
const char * getActorSound(int gender, actorSound_t soundType) const
pos_t pos3_t[3]
Definition ufotypes.h:58
vec_t vec3_t[3]
Definition ufotypes.h:39
vec_t vec2_t[2]
Definition ufotypes.h:38
static int oldPos
#define VectorDist(a, b)
Definition vector.h:69
#define VectorSubtract(a, b, dest)
Definition vector.h:45
#define VectorCopy(src, dest)
Definition vector.h:51
#define VectorCompare(a, b)
Definition vector.h:63
#define VectorDistSqr(a, b)
Definition vector.h:68
#define VectorAdd(a, b, dest)
Definition vector.h:47
#define DotProduct(x, y)
Returns the distance between two 3-dimensional vectors.
Definition vector.h:44
#define VectorScale(in, scale, out)
Definition vector.h:79
#define VectorLengthSqr(a)
Definition vector.h:71