UFO: Alien Invasion
Loading...
Searching...
No Matches
chr_shared.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 "q_shared.h"
27#include "chr_shared.h"
28
29character_s::character_s ()
30{
31 init();
32}
33
34void 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
55const 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
83bool CHRSH_IsTeamDefAlien (const teamDef_t* const td)
84{
85 return td->team == TEAM_ALIEN;
86}
87
88bool 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
103bool CHRSH_IsTeamDefRobot (const teamDef_t* const td)
104{
105 return td->robot;
106}
107
108const 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
221void 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];
277 chr->maxHP = temp;
278 chr->HP = temp;
279
280 /* Morale */
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
297const 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
319const 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
338{
339}
340
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
359const char* BodyData::id (void) const
360{
361 return _id;
362}
363
364const char* BodyData::id (const short bodyPart) const
365{
366 return _bodyParts[bodyPart].id;
367}
368
369const char* BodyData::name (const short bodyPart) const
370{
371 return _bodyParts[bodyPart].name;
372}
373
374float BodyData::penalty (const short bodyPart, const modifier_types_t type) const
375{
376 return _bodyParts[bodyPart].penalties[type] * 0.01f;
377}
378
379float BodyData::bleedingFactor (const short bodyPart) const
380{
381 return _bodyParts[bodyPart].bleedingFactor * 0.01f;
382}
383
384float BodyData::woundThreshold (const short bodyPart) const
385{
386 return _bodyParts[bodyPart].woundThreshold * 0.01f;
387}
388
389short BodyData::numBodyParts (void) const
390{
391 return _numBodyParts;
392}
393
394void BodyData::setId (const char* id)
395{
396 Q_strncpyz(_id, id, sizeof(_id));
397}
398
400{
401 _bodyParts[_numBodyParts] = bodyPart;
403}
404
405short 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
427float BodyData::getArea(const short bodyPart) const
428{
429 return (_bodyParts[bodyPart].shape[0] + _bodyParts[bodyPart].shape[1]) * 0.5f * _bodyParts[bodyPart].shape[2];
430}
const implant_t * CHRSH_ApplyImplant(character_t &chr, const implantDef_t &def)
Add a new implant to a character.
void CHRSH_CharGenAbilitySkills(character_t *chr, bool multiplayer, const char *templateId)
Generates a skill and ability set for any character.
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.
const char * CHRSH_CharGetBody(const character_t *const chr)
Returns the body model for the soldiers for armoured and non armoured soldiers.
void CHRSH_UpdateImplants(character_t &chr)
Updates the characters permanent implants. Called every day.
bool CHRSH_IsArmourUseableForTeam(const objDef_t *od, const teamDef_t *teamDef)
const char * CHRSH_CharGetHead(const character_t *const chr)
Returns the head model for the soldiers for armoured and non armoured soldiers.
const chrTemplate_t * CHRSH_GetTemplateByID(const teamDef_t *teamDef, const char *templateId)
static void CHRSH_UpdateCharacterWithEffect(character_t &chr, const itemEffect_t &e)
Assign the effect values to the character.
#define MAX_CHARACTER_IMPLANTS
Definition chr_shared.h:371
@ ABILITY_POWER
Definition chr_shared.h:37
@ ABILITY_SPEED
Definition chr_shared.h:38
@ SKILL_NUM_TYPES
Definition chr_shared.h:51
@ ABILITY_MIND
Definition chr_shared.h:40
@ ABILITY_ACCURACY
Definition chr_shared.h:39
@ NAME_LAST
Definition chr_shared.h:237
actorSound_t
Types of actor sounds being issued by CL_ActorPlaySound().
Definition chr_shared.h:218
modifier_types_t
Definition chr_shared.h:255
float getArea(const short bodyPart) const
void setId(const char *id)
void addBodyPart(const BodyPartData &bodyPart)
BodyData(void)
BodyPartData _bodyParts[BODYPART_MAXTYPE]
Definition chr_shared.h:281
float woundThreshold(const short bodyPart) const
const char * name(const short bodyPart) const
short getHitBodyPart(const byte direction, const float height) const
short _numBodyParts
Definition chr_shared.h:283
char _id[MAX_TEXPATH]
Definition chr_shared.h:280
short numBodyParts(void) const
float _totalBodyArea
Definition chr_shared.h:282
float penalty(const short bodyPart, const modifier_types_t type) const
const char * id(void) const
float bleedingFactor(const short bodyPart) const
short getRandomBodyPart(void) const
Item * getArmour() const
const objDef_t * def(void) const
Definition inv_shared.h:469
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
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
#define DEBUG_CLIENT
Definition defines.h:59
#define DEBUG_SOUND
Definition defines.h:63
#define DEBUG_SHARED
Definition defines.h:55
#define ACTOR_SIZE_INVALID
Definition defines.h:301
static chrScoreMission_t scoreMission[MAX_EDICTS]
Definition g_client.cpp:51
void Sys_Error(const char *error,...)
Definition g_main.cpp:421
@ ACTOR_HAND_NOT_SET
Definition inv_shared.h:627
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
float frand(void)
Return random values between 0 and 1.
Definition mathlib.cpp:506
#define EQUAL_EPSILON
Definition mathlib.h:40
Common header file.
#define TEAM_PHALANX
Definition q_shared.h:62
#define TEAM_ALIEN
Definition q_shared.h:63
#define GET_MORALE(ab)
Definition q_shared.h:290
#define MAX_SKILL
Definition q_shared.h:278
QGL_EXTERN GLint i
Definition r_gl.h:113
QGL_EXTERN GLint GLenum type
Definition r_gl.h:94
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition r_gl.h:110
#define Q_streq(a, b)
Definition shared.h:136
bool Q_strnull(const char *string)
Definition shared.h:138
#define MAX_VAR
Definition shared.h:36
#define lengthof(x)
Definition shared.h:105
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition shared.cpp:457
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition shared.cpp:494
Describes a character with all its attributes.
Definition chr_shared.h:388
const teamDef_t * teamDef
Definition chr_shared.h:413
char head[MAX_VAR]
Definition chr_shared.h:393
char path[MAX_VAR]
Definition chr_shared.h:391
chrScoreGlobal_t score
Definition chr_shared.h:406
char body[MAX_VAR]
Definition chr_shared.h:392
Inventory inv
Definition chr_shared.h:411
implant_t implants[MAX_CHARACTER_IMPLANTS]
Definition chr_shared.h:418
How many TUs (and of what type) did a player reserve for a unit?
Definition chr_shared.h:186
Structure of all stats collected for an actor over time.
Definition chr_shared.h:119
int initialSkills[SKILL_NUM_TYPES+1]
Definition chr_shared.h:123
int skills[SKILL_NUM_TYPES]
Definition chr_shared.h:122
int experience[SKILL_NUM_TYPES+1]
Definition chr_shared.h:120
int skills[SKILL_NUM_TYPES+1][2]
Definition chr_shared.h:59
char id[MAX_VAR]
Definition chr_shared.h:57
int removedTime
Definition chr_shared.h:375
const implantDef_t * def
Definition chr_shared.h:373
int installedTime
Definition chr_shared.h:374
const struct objDef_s * item
Definition inv_shared.h:104
int installationTime
Definition inv_shared.h:105
float accuracy
Definition inv_shared.h:94
bool isPermanent
Definition inv_shared.h:89
void * data
Definition list.h:31
linkedList_t * next
Definition list.h:32
Defines all attributes of objects used in the inventory.
Definition inv_shared.h:264
const char * armourPath
Definition inv_shared.h:272
itemEffect_t * strengthenEffect
Definition inv_shared.h:283
bool implant
Definition inv_shared.h:282
const char * id
Definition inv_shared.h:268
bool isArmour() const
Definition inv_shared.h:346
int useable
Definition inv_shared.h:304
const chrTemplate_t * characterTemplates[MAX_TEMPLATES_PER_TEAM]
Definition chr_shared.h:347
char id[MAX_VAR]
Definition chr_shared.h:309
bool armour
Definition chr_shared.h:334
int numTemplates
Definition chr_shared.h:348
Info on a wound.
Definition chr_shared.h:361
vec_t vec4_t[4]
Definition ufotypes.h:40
#define Vector4Copy(src, dest)
Definition vector.h:53