UFO: Alien Invasion
Doxygen documentation generating
chr_shared.cpp
Go to the documentation of this file.
1 
6 /*
7 Copyright (C) 2002-2023 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 "q_shared.h"
27 #include "chr_shared.h"
28 
29 character_s::character_s ()
30 {
31  init();
32 }
33 
34 void character_s::init ()
35 {
36  name[0] = path[0] = body[0] = head[0] = '\0';
37  ucn = bodySkin = headSkin = HP = minHP = maxHP = STUN = morale = state = gender = 0;
38  fieldSize = ACTOR_SIZE_INVALID;
39  scoreMission = nullptr;
40  teamDef = nullptr;
41  inv.init();
42  RFmode.set(ACTOR_HAND_NOT_SET, 0, nullptr);
43  wounds = woundInfo_t();
44  score = chrScoreGlobal_t();
45  reservedTus = chrReservations_t();
46  for (int i = 0; i < MAX_CHARACTER_IMPLANTS; i++)
47  implants[i] = implant_t();
48 }
49 
55 const char* teamDef_s::getActorSound (int gender, actorSound_t soundType) const
56 {
57  if (gender < 0 || gender >= NAME_LAST) {
58  Com_DPrintf(DEBUG_SOUND|DEBUG_CLIENT, "getActorSound: invalid gender: %i\n", gender);
59  return nullptr;
60  }
61  if (numSounds[soundType][gender] <= 0) {
62  Com_DPrintf(DEBUG_SOUND|DEBUG_CLIENT, "getActorSound: no sound defined for sound type: %i, teamID: '%s', gender: %i\n", soundType, id, gender);
63  return nullptr;
64  }
65 
66  // Can't use LIST_GetRandom() or LIST_GetByIdx() in the game module
67  const int random = rand() % numSounds[soundType][gender];
68  const linkedList_t* list = sounds[soundType][gender];
69  for (int j = 0; j < random; j++) {
70  assert(list);
71  list = list->next;
72  }
73 
74  assert(list);
75  assert(list->data);
76  return (const char*)list->data;
77 }
78 
83 bool CHRSH_IsTeamDefAlien (const teamDef_t* const td)
84 {
85  return td->team == TEAM_ALIEN;
86 }
87 
88 bool CHRSH_IsArmourUseableForTeam (const objDef_t* od, const teamDef_t* teamDef)
89 {
90  assert(teamDef);
91  assert(od->isArmour());
92 
93  if (!teamDef->armour)
94  return false;
95 
96  return od->useable == teamDef->team;
97 }
98 
103 bool CHRSH_IsTeamDefRobot (const teamDef_t* const td)
104 {
105  return td->robot;
106 }
107 
108 const chrTemplate_t* CHRSH_GetTemplateByID (const teamDef_t* teamDef, const char* templateId)
109 {
110  if (!Q_strnull(templateId))
111  for (int i = 0; i < teamDef->numTemplates; i++)
112  if (Q_streq(teamDef->characterTemplates[i]->id, templateId))
113  return teamDef->characterTemplates[i];
114 
115  return nullptr;
116 }
117 
122 {
123  chrScoreGlobal_t& s = chr.score;
124  if (fabs(e.accuracy) > EQUAL_EPSILON)
125  s.skills[ABILITY_ACCURACY] *= 1.0f + e.accuracy;
126  if (fabs(e.mind) > EQUAL_EPSILON)
127  s.skills[ABILITY_MIND] *= 1.0f + e.mind;
128  if (fabs(e.power) > EQUAL_EPSILON)
129  s.skills[ABILITY_POWER] *= 1.0f + e.power;
130  if (fabs(e.TUs) > EQUAL_EPSILON)
131  s.skills[ABILITY_SPEED] *= 1.0f + e.TUs;
132 
133  if (fabs(e.morale) > EQUAL_EPSILON)
134  chr.morale *= 1.0f + e.morale;
135 }
136 
141 {
142  for (int i = 0; i < lengthof(chr.implants); i++) {
143  implant_t& implant = chr.implants[i];
144  const implantDef_t* def = implant.def;
145  /* empty slot */
146  if (def == nullptr || def->item == nullptr)
147  continue;
148  const objDef_t& od = *def->item;
149  const itemEffect_t* e = od.strengthenEffect;
150 
151  if (implant.installedTime > 0) {
152  implant.installedTime--;
153  if (implant.installedTime == 0 && e != nullptr && e->isPermanent) {
155  }
156  }
157 
158  if (implant.removedTime > 0) {
159  implant.removedTime--;
160  if (implant.removedTime == 0) {
161  implant.def = nullptr;
162  continue;
163  }
164  }
165  if (e == nullptr || e->period <= 0)
166  continue;
167 
168  implant.trigger--;
169  if (implant.trigger <= 0)
170  continue;
171 
173  implant.trigger = e->period;
174  }
175 }
176 
181 {
182  const objDef_t& od = *def.item;
183 
184  if (!od.implant) {
185  Com_Printf("object '%s' is no implant\n", od.id);
186  return nullptr;
187  }
188 
189  const itemEffect_t* e = od.strengthenEffect;
190  if (e != nullptr && e->period <= 0 && !e->isPermanent) {
191  Com_Printf("object '%s' is not permanent\n", od.id);
192  return nullptr;
193  }
194 
195  for (int i = 0; i < lengthof(chr.implants); i++) {
196  implant_t& implant = chr.implants[i];
197  /* already filled slot */
198  if (implant.def != nullptr)
199  continue;
200 
201  implant = implant_t();
202  implant.def = &def;
203  if (e != nullptr && !e->isPermanent)
204  implant.trigger = e->period;
205  implant.installedTime = def.installationTime;
206 
207  return &chr.implants[i];
208  }
209  Com_Printf("no free implant slot\n");
210  return nullptr;
211 }
212 
221 void CHRSH_CharGenAbilitySkills (character_t* chr, bool multiplayer, const char* templateId)
222 {
223  const chrTemplate_t* chrTemplate;
224  const teamDef_t* teamDef = chr->teamDef;
225 
226  if (multiplayer && teamDef->team == TEAM_PHALANX)
227  /* @todo Hard coded template id, remove when possible */
228  templateId = "soldier_mp";
229 
230  if (!Q_strnull(templateId)) {
231  chrTemplate = CHRSH_GetTemplateByID(teamDef, templateId);
232  if (!chrTemplate)
233  Sys_Error("CHRSH_CharGenAbilitySkills: Character template not found (%s) in %s", templateId, teamDef->id);
234  } else if (teamDef->characterTemplates[0]) {
235  if (teamDef->numTemplates > 1) {
236  float sumRate = 0.0f;
237  for (int i = 0; i < teamDef->numTemplates; i++) {
238  chrTemplate = teamDef->characterTemplates[i];
239  sumRate += chrTemplate->rate;
240  }
241  if (sumRate > 0.0f) {
242  const float soldierRoll = frand();
243  float curRate = 0.0f;
244  for (chrTemplate = teamDef->characterTemplates[0]; chrTemplate->id; chrTemplate++) {
245  curRate += chrTemplate->rate;
246  if (curRate && soldierRoll <= (curRate / sumRate))
247  break;
248  }
249  } else {
250  /* No rates or all set to 0 default to first template */
251  chrTemplate = teamDef->characterTemplates[0];
252  }
253  } else {
254  /* Only one template */
255  chrTemplate = teamDef->characterTemplates[0];
256  }
257  } else {
258  Sys_Error("CHRSH_CharGenAbilitySkills: No character template for team %s!", teamDef->id);
259  }
260 
261  assert(chrTemplate);
262  const int (*skillsTemplate)[2] = chrTemplate->skills;
263 
264  /* Abilities and skills -- random within the range */
265  for (int i = 0; i < SKILL_NUM_TYPES; i++) {
266  const int abilityWindow = skillsTemplate[i][1] - skillsTemplate[i][0];
267  /* Reminder: In case if abilityWindow==0 the ability will be set to the lower limit. */
268  const int temp = (frand() * abilityWindow) + skillsTemplate[i][0];
269  chr->score.skills[i] = temp;
270  chr->score.initialSkills[i] = temp;
271  }
272 
273  /* Health. */
274  const int abilityWindow = skillsTemplate[SKILL_NUM_TYPES][1] - skillsTemplate[SKILL_NUM_TYPES][0];
275  const int temp = (frand() * abilityWindow) + skillsTemplate[SKILL_NUM_TYPES][0];
276  chr->score.initialSkills[SKILL_NUM_TYPES] = temp;
277  chr->maxHP = temp;
278  chr->HP = temp;
279 
280  /* Morale */
281  chr->morale = GET_MORALE(chr->score.skills[ABILITY_MIND]);
282  if (chr->morale >= MAX_SKILL)
283  chr->morale = MAX_SKILL;
284 
285  /* Initialize the experience values */
286  for (int i = 0; i <= SKILL_NUM_TYPES; i++) {
287  chr->score.experience[i] = 0;
288  }
289 }
290 
297 const char* CHRSH_CharGetBody (const character_t* const chr)
298 {
299  static char returnModel[MAX_VAR];
300 
301  /* models of UGVs don't change - because they are already armoured */
302  if (chr->inv.getArmour() && !CHRSH_IsTeamDefRobot(chr->teamDef)) {
303  const objDef_t* od = chr->inv.getArmour()->def();
304  const char* id = od->armourPath;
305  if (!od->isArmour())
306  Sys_Error("CHRSH_CharGetBody: Item is no armour");
307 
308  Com_sprintf(returnModel, sizeof(returnModel), "%s%s/%s", chr->path, id, chr->body);
309  } else
310  Com_sprintf(returnModel, sizeof(returnModel), "%s/%s", chr->path, chr->body);
311  return returnModel;
312 }
313 
319 const char* CHRSH_CharGetHead (const character_t* const chr)
320 {
321  static char returnModel[MAX_VAR];
322 
323  /* models of UGVs don't change - because they are already armoured */
324  if (chr->inv.getArmour() && !chr->teamDef->robot) {
325  const objDef_t* od = chr->inv.getArmour()->def();
326  const char* id = od->armourPath;
327  if (!od->isArmour())
328  Sys_Error("CHRSH_CharGetBody: Item is no armour");
329 
330  Com_sprintf(returnModel, sizeof(returnModel), "%s%s/%s", chr->path, id, chr->head);
331  } else
332  Com_sprintf(returnModel, sizeof(returnModel), "%s/%s", chr->path, chr->head);
333  return returnModel;
334 }
335 
337  _totalBodyArea(0.0f), _numBodyParts(0)
338 {
339 }
340 
341 short BodyData::getRandomBodyPart (void) const
342 {
343  const float rnd = frand() * _totalBodyArea;
344  float currentArea = 0.0f;
345  short bodyPart;
346 
347  for (bodyPart = 0; bodyPart < _numBodyParts; ++bodyPart) {
348  currentArea += getArea(bodyPart);
349  if (rnd <= currentArea)
350  break;
351  }
352  if (bodyPart >= _numBodyParts) {
353  bodyPart = 0;
354  Com_DPrintf(DEBUG_SHARED, "Warning: No bodypart hit, defaulting to %s!\n", name(bodyPart));
355  }
356  return bodyPart;
357 }
358 
359 const char* BodyData::id (void) const
360 {
361  return _id;
362 }
363 
364 const char* BodyData::id (const short bodyPart) const
365 {
366  return _bodyParts[bodyPart].id;
367 }
368 
369 const char* BodyData::name (const short bodyPart) const
370 {
371  return _bodyParts[bodyPart].name;
372 }
373 
374 float BodyData::penalty (const short bodyPart, const modifier_types_t type) const
375 {
376  return _bodyParts[bodyPart].penalties[type] * 0.01f;
377 }
378 
379 float BodyData::bleedingFactor (const short bodyPart) const
380 {
381  return _bodyParts[bodyPart].bleedingFactor * 0.01f;
382 }
383 
384 float BodyData::woundThreshold (const short bodyPart) const
385 {
386  return _bodyParts[bodyPart].woundThreshold * 0.01f;
387 }
388 
389 short BodyData::numBodyParts (void) const
390 {
391  return _numBodyParts;
392 }
393 
394 void BodyData::setId (const char* id)
395 {
396  Q_strncpyz(_id, id, sizeof(_id));
397 }
398 
399 void BodyData::addBodyPart (const BodyPartData& bodyPart)
400 {
401  _bodyParts[_numBodyParts] = bodyPart;
403 }
404 
405 short BodyData::getHitBodyPart (const byte direction, const float height) const
406 {
407  const float rnd = frand();
408  short bodyPart;
409  float curRand = 0;
410 
411  for (bodyPart = 0; bodyPart < _numBodyParts; ++bodyPart) {
412  vec4_t shape;
413  Vector4Copy(_bodyParts[bodyPart].shape, shape);
414  if (height <= shape[3] || height > shape[2] + shape[3])
415  continue;
416  curRand += (direction < 2 ? shape[0] : (direction < 4 ? shape[1] : (shape[0] + shape[1]) * 0.5f));
417  if (rnd <= curRand)
418  break;
419  }
420  if (bodyPart >= _numBodyParts) {
421  bodyPart = 0;
422  Com_DPrintf(DEBUG_SHARED, "Warning: No bodypart hit, defaulting to %s!\n", name(bodyPart));
423  }
424  return bodyPart;
425 }
426 
427 float BodyData::getArea(const short bodyPart) const
428 {
429  return (_bodyParts[bodyPart].shape[0] + _bodyParts[bodyPart].shape[1]) * 0.5f * _bodyParts[bodyPart].shape[2];
430 }
bool Q_strnull(const char *string)
Definition: shared.h:138
int removedTime
Definition: chr_shared.h:375
char id[MAX_VAR]
Definition: chr_shared.h:57
short getHitBodyPart(const byte direction, const float height) const
Definition: chr_shared.cpp:405
const objDef_t * def(void) const
Definition: inv_shared.h:469
bool armour
Definition: chr_shared.h:334
static void CHRSH_UpdateCharacterWithEffect(character_t &chr, const itemEffect_t &e)
Assign the effect values to the character.
Definition: chr_shared.cpp:121
float woundThreshold(const short bodyPart) const
Definition: chr_shared.cpp:384
void Sys_Error(const char *error,...)
Definition: g_main.cpp:421
BodyData(void)
Definition: chr_shared.cpp:336
int penalties[MODIFIER_MAX]
Definition: chr_shared.h:272
QGL_EXTERN GLint GLenum type
Definition: r_gl.h:94
#define TEAM_PHALANX
Definition: q_shared.h:62
void * data
Definition: list.h:31
implant_t implants[MAX_CHARACTER_IMPLANTS]
Definition: chr_shared.h:418
#define TEAM_ALIEN
Definition: q_shared.h:63
const char * id(void) const
Definition: chr_shared.cpp:359
Common header file.
void addBodyPart(const BodyPartData &bodyPart)
Definition: chr_shared.cpp:399
static chrScoreMission_t scoreMission[MAX_EDICTS]
Definition: g_client.cpp:51
char body[MAX_VAR]
Definition: chr_shared.h:392
int numTemplates
Definition: chr_shared.h:348
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition: shared.cpp:494
const char * armourPath
Definition: inv_shared.h:272
const implantDef_t * def
Definition: chr_shared.h:373
Describes a character with all its attributes.
Definition: chr_shared.h:388
const implant_t * CHRSH_ApplyImplant(character_t &chr, const implantDef_t &def)
Add a new implant to a character.
Definition: chr_shared.cpp:180
Structure of all stats collected for an actor over time.
Definition: chr_shared.h:119
short _numBodyParts
Definition: chr_shared.h:283
char id[MAX_TEXPATH]
Definition: chr_shared.h:270
bool CHRSH_IsTeamDefAlien(const teamDef_t *const td)
Check if a team definition is alien.
Definition: chr_shared.cpp:83
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
const teamDef_t * teamDef
Definition: chr_shared.h:413
const char * CHRSH_CharGetBody(const character_t *const chr)
Returns the body model for the soldiers for armoured and non armoured soldiers.
Definition: chr_shared.cpp:297
itemEffect_t * strengthenEffect
Definition: inv_shared.h:283
char _id[MAX_TEXPATH]
Definition: chr_shared.h:280
char head[MAX_VAR]
Definition: chr_shared.h:393
char name[MAX_TEXPATH]
Definition: chr_shared.h:271
void CHRSH_UpdateImplants(character_t &chr)
Updates the characters permanent implants. Called every day.
Definition: chr_shared.cpp:140
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
void CHRSH_CharGenAbilitySkills(character_t *chr, bool multiplayer, const char *templateId)
Generates a skill and ability set for any character.
Definition: chr_shared.cpp:221
int experience[SKILL_NUM_TYPES+1]
Definition: chr_shared.h:120
#define DEBUG_CLIENT
Definition: defines.h:59
BodyPartData _bodyParts[BODYPART_MAXTYPE]
Definition: chr_shared.h:281
int skills[SKILL_NUM_TYPES+1][2]
Definition: chr_shared.h:59
char id[MAX_VAR]
Definition: chr_shared.h:309
float bleedingFactor(const short bodyPart) const
Definition: chr_shared.cpp:379
bool isPermanent
Definition: inv_shared.h:89
#define MAX_VAR
Definition: shared.h:36
float power
Definition: inv_shared.h:96
int useable
Definition: inv_shared.h:304
const chrTemplate_t * CHRSH_GetTemplateByID(const teamDef_t *teamDef, const char *templateId)
Definition: chr_shared.cpp:108
Info on a wound.
Definition: chr_shared.h:361
bool CHRSH_IsTeamDefRobot(const teamDef_t *const td)
Check if a team definition is a robot.
Definition: chr_shared.cpp:103
linkedList_t * next
Definition: list.h:32
#define MAX_CHARACTER_IMPLANTS
Definition: chr_shared.h:371
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
char path[MAX_VAR]
Definition: chr_shared.h:391
short getRandomBodyPart(void) const
Definition: chr_shared.cpp:341
bool isArmour() const
Definition: inv_shared.h:346
const char * CHRSH_CharGetHead(const character_t *const chr)
Returns the head model for the soldiers for armoured and non armoured soldiers.
Definition: chr_shared.cpp:319
#define EQUAL_EPSILON
Definition: mathlib.h:40
const struct objDef_s * item
Definition: inv_shared.h:104
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
const chrTemplate_t * characterTemplates[MAX_TEMPLATES_PER_TEAM]
Definition: chr_shared.h:347
float mind
Definition: inv_shared.h:97
int woundThreshold
Definition: chr_shared.h:275
int installationTime
Definition: inv_shared.h:105
const char * name(const short bodyPart) const
Definition: chr_shared.cpp:369
const char * id
Definition: inv_shared.h:268
Item * getArmour() const
Definition: inv_shared.cpp:990
float frand(void)
Return random values between 0 and 1.
Definition: mathlib.cpp:506
float penalty(const short bodyPart, const modifier_types_t type) const
Definition: chr_shared.cpp:374
QGL_EXTERN GLint i
Definition: r_gl.h:113
modifier_types_t
Definition: chr_shared.h:255
bool implant
Definition: inv_shared.h:282
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition: r_gl.h:110
chrScoreGlobal_t score
Definition: chr_shared.h:406
#define MAX_SKILL
Definition: q_shared.h:278
int trigger
Definition: chr_shared.h:376
Defines all attributes of objects used in the inventory.
Definition: inv_shared.h:264
int bleedingFactor
Definition: chr_shared.h:274
vec4_t shape
Definition: chr_shared.h:273
int initialSkills[SKILL_NUM_TYPES+1]
Definition: chr_shared.h:123
float getArea(const short bodyPart) const
Definition: chr_shared.cpp:427
void setId(const char *id)
Definition: chr_shared.cpp:394
#define lengthof(x)
Definition: shared.h:105
#define DEBUG_SOUND
Definition: defines.h:63
actorSound_t
Types of actor sounds being issued by CL_ActorPlaySound().
Definition: chr_shared.h:218
int skills[SKILL_NUM_TYPES]
Definition: chr_shared.h:122
#define GET_MORALE(ab)
Definition: q_shared.h:290
#define Q_streq(a, b)
Definition: shared.h:136
#define DEBUG_SHARED
Definition: defines.h:55
float _totalBodyArea
Definition: chr_shared.h:282
int installedTime
Definition: chr_shared.h:374
uint8_t byte
Definition: ufotypes.h:34
#define ACTOR_SIZE_INVALID
Definition: defines.h:301
How many TUs (and of what type) did a player reserve for a unit?
Definition: chr_shared.h:186
#define Vector4Copy(src, dest)
Definition: vector.h:53
short numBodyParts(void) const
Definition: chr_shared.cpp:389
float accuracy
Definition: inv_shared.h:94
bool CHRSH_IsArmourUseableForTeam(const objDef_t *od, const teamDef_t *teamDef)
Definition: chr_shared.cpp:88
float morale
Definition: inv_shared.h:98
bool robot
Definition: chr_shared.h:333
Inventory inv
Definition: chr_shared.h:411
vec_t vec4_t[4]
Definition: ufotypes.h:40