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