UFO: Alien Invasion
Loading...
Searching...
No Matches
g_health.cpp
Go to the documentation of this file.
1
4
5/*
6Copyright (C) 2002-2025 UFO: Alien Invasion.
7
8This program is free software; you can redistribute it and/or
9modify it under the terms of the GNU General Public License
10as published by the Free Software Foundation; either version 2
11of the License, or (at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
17See the GNU General Public License for more details.
18
19You should have received a copy of the GNU General Public License
20along with this program; if not, write to the Free Software
21Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23*/
24
25#include "g_health.h"
26#include "g_actor.h"
27#include "g_client.h"
28#include "g_combat.h"
29#include "g_edicts.h"
30#include "g_match.h"
31#include "g_utils.h"
32
33static byte G_GetImpactDirection(const Edict* const target, const vec3_t impact)
34{
35 vec3_t vec1, vec2;
36
37 VectorSubtract(impact, target->origin, vec1);
38 vec1[2] = 0;
39 VectorNormalize(vec1);
40 VectorCopy(dvecs[target->dir], vec2);
41 VectorNormalize(vec2);
42
43 return AngleToDir(VectorAngleBetween(vec2, vec1) * todeg);
44}
45
52void G_DamageActor (Edict* target, const int damage, const vec3_t impact)
53{
54 assert(target->chr.teamDef);
55
56 G_TakeDamage(target, damage);
57 if (damage > 0 && target->HP > 0) {
58 const teamDef_t* const teamDef = target->chr.teamDef;
59 if (impact) {
60 /* Direct hit */
61 const byte impactDirection = G_GetImpactDirection(target, impact);
62 const float impactHeight = impact[2] / (target->absBox.mins[2] + target->absBox.maxs[2]);
63 const int bodyPart = teamDef->bodyTemplate->getHitBodyPart(impactDirection, impactHeight);
64 target->chr.wounds.woundLevel[bodyPart] += damage;
65 } else {
66 /* No direct hit (splash damage) */
67 for (int bodyPart = 0; bodyPart < teamDef->bodyTemplate->numBodyParts(); ++bodyPart)
68 target->chr.wounds.woundLevel[bodyPart] += teamDef->bodyTemplate->getArea(bodyPart) * damage;
69 }
70 G_SendWoundStats(target);
71 }
72}
73
81void G_TreatActor (Actor* target, const fireDef_t* const fd, const int heal, const int healerTeam)
82{
83 assert(target->chr.teamDef);
84
85 /* Treat wounds */
86 if (fd->dmgweight == gi.csi->damNormal) {
87 int mostWounded = 0;
88 woundInfo_t* wounds = &target->chr.wounds;
89
90 /* Find the worst not treated wound */
91 for (int bodyPart = 0; bodyPart < target->chr.teamDef->bodyTemplate->numBodyParts(); ++bodyPart)
92 if (wounds->woundLevel[bodyPart] > wounds->woundLevel[mostWounded])
93 mostWounded = bodyPart;
94
95 if (wounds->woundLevel[mostWounded] > 0) {
96 const int woundsHealed = std::min(static_cast<int>(abs(heal) / target->chr.teamDef->bodyTemplate->bleedingFactor(mostWounded)),
97 wounds->woundLevel[mostWounded]);
98 G_TakeDamage(target, heal);
99 wounds->woundLevel[mostWounded] -= woundsHealed;
100 wounds->treatmentLevel[mostWounded] += woundsHealed;
101
102 /* Update stats here to get info on how many HP the target received. */
103 if (target->chr.scoreMission)
104 target->chr.scoreMission->heal += abs(heal);
105 }
106 }
107
108 /* Treat stunned actors */
109 if (fd->dmgweight == gi.csi->damStunElectro && target->isStunned()) {
110 if (CHRSH_IsTeamDefAlien(target->chr.teamDef) && target->getTeam() != healerTeam)
113 target->setStun(std::min(255, target->getStun() - heal));
114 else
115 target->setStun(std::max(0, target->getStun() + heal));
117 }
118
119 /* Increase morale */
120 if (fd->dmgweight == gi.csi->damShock)
121 target->setMorale(std::min(GET_MORALE(target->chr.score.skills[ABILITY_MIND]), target->morale - heal));
122
123 G_SendWoundStats(target);
124}
125
130void G_BleedWounds (const int team)
131{
132 Actor* actor = nullptr;
133
134 while ((actor = G_EdictsGetNextLivingActorOfTeam(actor, team))) {
135 if (CHRSH_IsTeamDefRobot(actor->chr.teamDef))
136 continue;
137 const teamDef_t* const teamDef = actor->chr.teamDef;
138 const woundInfo_t& wounds = actor->chr.wounds;
139 int damage = 0;
140 for (int bodyPart = 0; bodyPart < teamDef->bodyTemplate->numBodyParts(); ++bodyPart)
141 if (wounds.woundLevel[bodyPart] > actor->chr.maxHP * teamDef->bodyTemplate->woundThreshold(bodyPart))
142 damage += wounds.woundLevel[bodyPart] * teamDef->bodyTemplate->bleedingFactor(bodyPart);
143 if (damage > 0) {
144 G_PrintStats("%s is bleeding (damage: %i)", actor->chr.name, damage);
145 G_TakeDamage(actor, damage);
146 G_CheckDeathOrKnockout(actor, nullptr, nullptr, damage);
147 }
148 }
149 /* Maybe the last team member bled to death */
151}
152
157void G_SendWoundStats (Edict* const ent)
158{
159 for (int i = 0; i < ent->chr.teamDef->bodyTemplate->numBodyParts(); ++i) {
160 /* Sanity checks */
161 woundInfo_t& wounds = ent->chr.wounds;
162 wounds.woundLevel[i] = std::max(0, wounds.woundLevel[i]);
163 wounds.treatmentLevel[i] = std::max(0, wounds.treatmentLevel[i]);
164 wounds.woundLevel[i] = std::min(255, wounds.woundLevel[i]);
165 wounds.treatmentLevel[i] = std::min(255, wounds.treatmentLevel[i]);
166 if (wounds.woundLevel[i] + wounds.treatmentLevel[i] > 0)
167 G_EventActorWound(*ent, i);
168 }
169}
170
177float G_ActorGetInjuryPenalty (const Edict* const ent, const modifier_types_t type)
178{
179 float penalty = 0;
180
181 const teamDef_t* const teamDef = ent->chr.teamDef;
182 for (int bodyPart = 0; bodyPart < teamDef->bodyTemplate->numBodyParts(); ++bodyPart) {
183 const int threshold = ent->chr.maxHP * teamDef->bodyTemplate->woundThreshold(bodyPart);
184 const int injury = (ent->chr.wounds.woundLevel[bodyPart] + ent->chr.wounds.treatmentLevel[bodyPart] * 0.5);
185 if (injury > threshold)
186 penalty += 2 * teamDef->bodyTemplate->penalty(bodyPart, type) * injury / ent->chr.maxHP;
187 }
188
189 switch (type) {
192 break;
195 ++penalty;
196 break;
197 case MODIFIER_TU:
198 case MODIFIER_SIGHT:
199 penalty = 1 - penalty;
200 break;
202 penalty = ceil(penalty);
203 break;
204 default:
205 gi.DPrintf("G_ActorGetInjuryPenalty: Unknown modifier type %i\n", type);
206 penalty = 0;
207 break;
208 }
209
210 return penalty;
211}
212
213bool G_IsActorWounded (const Edict* ent, bool serious)
214{
215 if (ent == nullptr || !G_IsLivingActor(ent) || ent->chr.teamDef == nullptr)
216 return false;
217 const character_t& chr = ent->chr;
218 const BodyData* bodyTmp = chr.teamDef->bodyTemplate;
219 for (int i = 0; i < bodyTmp->numBodyParts(); ++i)
220 if (chr.wounds.woundLevel[i] > serious ? chr.maxHP * bodyTmp->woundThreshold(i) : 0)
221 return true;
222
223 return false;
224}
bool CHRSH_IsTeamDefRobot(const teamDef_t *const td)
Check if a team definition is a robot.
bool CHRSH_IsTeamDefAlien(const teamDef_t *const td)
Check if a team definition is alien.
@ ABILITY_MIND
Definition chr_shared.h:40
modifier_types_t
Definition chr_shared.h:255
@ MODIFIER_REACTION
Definition chr_shared.h:260
@ MODIFIER_ACCURACY
Definition chr_shared.h:256
@ MODIFIER_SIGHT
Definition chr_shared.h:259
@ MODIFIER_MOVEMENT
Definition chr_shared.h:258
@ MODIFIER_TU
Definition chr_shared.h:261
@ MODIFIER_SHOOTING
Definition chr_shared.h:257
vec3_t maxs
Definition aabb.h:258
vec3_t mins
Definition aabb.h:257
An Edict of type Actor.
Definition g_edict.h:348
bool isStunned() const
Definition g_edict.h:355
float getArea(const short bodyPart) const
float woundThreshold(const short bodyPart) const
short getHitBodyPart(const byte direction, const float height) const
short numBodyParts(void) const
float penalty(const short bodyPart, const modifier_types_t type) const
float bleedingFactor(const short bodyPart) const
character_t chr
Definition g_edict.h:116
void setMorale(int mor)
Definition g_edict.h:311
vec3_t origin
Definition g_edict.h:53
int HP
Definition g_edict.h:89
byte dir
Definition g_edict.h:86
int getTeam() const
Definition g_edict.h:269
AABB absBox
Definition g_edict.h:61
void setStun(int stu)
Definition g_edict.h:302
int getStun() const
Definition g_edict.h:308
int morale
Definition g_edict.h:91
void G_ActorCheckRevitalise(Actor *actor)
Definition g_actor.cpp:386
bool G_IsLivingActor(const Edict *ent)
Checks whether the given edict is a living actor.
Definition g_actor.cpp:43
Interface for g_client.cpp.
void G_CheckDeathOrKnockout(Actor *target, Actor *attacker, const fireDef_t *fd, int damage)
Definition g_combat.cpp:499
All parts of the main game logic that are combat related.
Actor * G_EdictsGetNextLivingActorOfTeam(Actor *lastEnt, const int team)
Iterate through the living actor entities of the given team.
Definition g_edicts.cpp:216
functions to handle the storage and lifecycle of all edicts in the game module.
void G_EventActorWound(const Edict &ent, const int bodyPart)
Send info about an actor's wounds to the client.
Definition g_events.cpp:399
static byte G_GetImpactDirection(const Edict *const target, const vec3_t impact)
Definition g_health.cpp:33
void G_BleedWounds(const int team)
Deal damage to each wounded team member.
Definition g_health.cpp:130
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
void G_SendWoundStats(Edict *const ent)
Send wound stats to network buffer.
Definition g_health.cpp:157
bool G_IsActorWounded(const Edict *ent, bool serious)
Definition g_health.cpp:213
game_import_t gi
Definition g_main.cpp:39
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_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
void G_PrintStats(const char *format,...)
Prints stats to game console and stats log file.
Definition g_utils.cpp:304
Misc utility functions for game module.
const vec4_t dvecs[PATHFINDING_DIRECTIONS]
Definition mathlib.cpp:58
float VectorAngleBetween(const vec3_t vec1, const vec3_t vec2)
Calculates the angle (in radians) between the two given vectors.
Definition mathlib.cpp:484
vec_t VectorNormalize(vec3_t v)
Calculate unit vector for a given vec3_t.
Definition mathlib.cpp:745
int AngleToDir(int angle)
Returns the index of array directionAngles[DIRECTIONS] whose value is the closest to angle.
Definition mathlib.cpp:130
#define todeg
Definition mathlib.h:51
#define GET_MORALE(ab)
Definition q_shared.h:290
QGL_EXTERN GLint i
Definition r_gl.h:113
QGL_EXTERN GLint GLenum type
Definition r_gl.h:94
Describes a character with all its attributes.
Definition chr_shared.h:388
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
woundInfo_t wounds
Definition chr_shared.h:402
char name[MAX_VAR]
Definition chr_shared.h:390
int skills[SKILL_NUM_TYPES]
Definition chr_shared.h:122
this is a fire definition for our weapons/ammo
Definition inv_shared.h:110
byte dmgweight
Definition inv_shared.h:141
const BodyData * bodyTemplate
Definition chr_shared.h:350
Info on a wound.
Definition chr_shared.h:361
int woundLevel[BODYPART_MAXTYPE]
Definition chr_shared.h:362
int treatmentLevel[BODYPART_MAXTYPE]
Definition chr_shared.h:363
vec_t vec3_t[3]
Definition ufotypes.h:39
#define VectorSubtract(a, b, dest)
Definition vector.h:45
#define VectorCopy(src, dest)
Definition vector.h:51