UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
cp_character.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 #include "../../cl_shared.h"
26 #include "cp_character.h"
27 #include "cp_campaign.h"
28 
29 typedef struct {
30  int ucn;
31  int HP;
32  int STUN;
33  int morale;
35 
38 
52 {
53  switch (skill) {
54  case ABILITY_POWER:
55  return 125;
56  case ABILITY_SPEED:
57  return 91;
58  case ABILITY_ACCURACY:
59  return 450;
60  case ABILITY_MIND:
61  return 450;
62  case SKILL_CLOSE:
63  return 680;
64  case SKILL_HEAVY:
65  return 680;
66  case SKILL_ASSAULT:
67  return 680;
68  case SKILL_SNIPER:
69  return 680;
70  case SKILL_EXPLOSIVE:
71  return 680;
72  case SKILL_NUM_TYPES: /* This is health. */
73  return 360;
74  case SKILL_PILOTING:
75  case SKILL_TARGETING:
76  case SKILL_EVADING:
77  return 0;
78  default:
79  cgi->Com_Error(ERR_DROP, "G_GetMaxExperiencePerMission: invalid skill type");
80  return -1;
81  }
82 }
83 
89 {
90  for (int i = 0; i < SKILL_NUM_TYPES; ++i)
91  chr->score.skills[i] = std::min(MAX_SKILL, chr->score.initialSkills[i] +
92  static_cast<int>(pow(static_cast<float>(chr->score.experience[i]) / 10, 0.6f)));
93 
94  chr->maxHP = std::min(MAX_MAXHP, chr->score.initialSkills[SKILL_NUM_TYPES] +
95  static_cast<int>(pow(static_cast<float>(chr->score.experience[SKILL_NUM_TYPES]) / 10, 0.6f)));
96 }
97 
102 void CHAR_UpdateData (linkedList_t* updateCharacters)
103 {
104  LIST_Foreach(updateCharacters, updateCharacter_t, c) {
105  Employee* employee = E_GetEmployeeFromChrUCN(c->ucn);
106 
107  if (!employee) {
108  cgi->Com_Printf("Warning: Could not get character with ucn: %i.\n", c->ucn);
109  continue;
110  }
111 
112  character_t* chr = &employee->chr;
113  const bool fullHP = c->HP >= chr->maxHP;
114  chr->STUN = c->STUN;
115  chr->morale = c->morale;
116 
117  memcpy(chr->wounds.treatmentLevel, c->wounds.treatmentLevel, sizeof(chr->wounds.treatmentLevel));
118  memcpy(chr->score.kills, c->chrscore.kills, sizeof(chr->score.kills));
119  memcpy(chr->score.stuns, c->chrscore.stuns, sizeof(chr->score.stuns));
120  chr->score.assignedMissions = c->chrscore.assignedMissions;
121 
122  for (int i = ABILITY_POWER; i <= SKILL_NUM_TYPES; ++i) {
123  const int maxXP = CHAR_GetMaxExperiencePerMission(static_cast<abilityskills_t>(i));
124  const int gainedXP = std::min(maxXP, c->chrscore.experience[i] - chr->score.experience[i]);
125  chr->score.experience[i] += gainedXP;
126  cgi->Com_DPrintf(DEBUG_CLIENT, "CP_UpdateCharacterData: Soldier %s earned %d experience points in skill #%d (total experience: %d)\n",
127  chr->name, gainedXP, i, chr->score.experience[SKILL_NUM_TYPES]);
128  }
129 
130  CHAR_UpdateSkills(chr);
131  /* If character returned unscratched and maxHP just went up due to experience
132  * don't send him/her to the hospital */
133  chr->HP = (fullHP ? chr->maxHP : std::min(c->HP, chr->maxHP));
134  }
135 }
136 
147 void CHAR_ParseData (dbuffer* msg, linkedList_t** updateCharacters)
148 {
149  const int num = cgi->NET_ReadByte(msg);
150 
151  if (num < 0)
152  cgi->Com_Error(ERR_DROP, "CP_ParseCharacterData: invalid character number found in stream (%i)\n", num);
153 
154  for (int i = 0; i < num; i++) {
156  OBJZERO(c);
157  c.ucn = cgi->NET_ReadShort(msg);
158  c.HP = cgi->NET_ReadShort(msg);
159  c.STUN = cgi->NET_ReadByte(msg);
160  c.morale = cgi->NET_ReadByte(msg);
161 
162  int j;
163  for (j = 0; j < BODYPART_MAXTYPE; ++j)
164  c.wounds.treatmentLevel[j] = cgi->NET_ReadByte(msg);
165 
166  for (j = 0; j < SKILL_NUM_TYPES + 1; j++)
167  c.chrscore.experience[j] = cgi->NET_ReadLong(msg);
168  for (j = 0; j < KILLED_NUM_TYPES; j++)
169  c.chrscore.kills[j] = cgi->NET_ReadShort(msg);
170  for (j = 0; j < KILLED_NUM_TYPES; j++)
171  c.chrscore.stuns[j] = cgi->NET_ReadShort(msg);
172  c.chrscore.assignedMissions = cgi->NET_ReadShort(msg);
173  LIST_Add(updateCharacters, c);
174  }
175 }
176 
183 static bool CHAR_ShouldUpdateSoldierRank (const rank_t* rank, const character_t* chr)
184 {
185  if (rank->type != EMPL_SOLDIER)
186  return false;
187 
188  /* mind is not yet enough */
189  if (chr->score.skills[ABILITY_MIND] < rank->mind)
190  return false;
191 
192  /* not enough killed enemies yet */
193  if (chr->score.kills[KILLED_ENEMIES] < rank->killedEnemies)
194  return false;
195 
196  /* too many civilians and team kills */
197  if (chr->score.kills[KILLED_CIVILIANS] + chr->score.kills[KILLED_TEAM] > rank->killedOthers)
198  return false;
199 
200  return true;
201 }
202 
209 void CHAR_UpdateStats (const base_t* base, const aircraft_t* aircraft)
210 {
211  assert(aircraft);
212 
213  /* only soldiers have stats and ranks, ugvs not */
214  E_Foreach(EMPL_SOLDIER, employee) {
215  if (!employee->isHiredInBase(aircraft->homebase))
216  continue;
217  if (!AIR_IsEmployeeInAircraft(employee, aircraft))
218  continue;
219  character_t* chr = &employee->chr;
220 
221  /* Remember the number of assigned mission for this character. */
222  chr->score.assignedMissions++;
223 
228  /* Check if the soldier meets the requirements for a higher rank
229  * and do a promotion. */
230  if (ccs.numRanks < 2)
231  continue;
232 
233  for (int j = ccs.numRanks - 1; j > chr->score.rank; j--) {
234  const rank_t* rank = CL_GetRankByIdx(j);
235  if (!CHAR_ShouldUpdateSoldierRank(rank, chr))
236  continue;
237 
238  chr->score.rank = j;
239  if (chr->HP > 0)
240  Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("%s has been promoted to %s.\n"), chr->name, _(rank->name));
241  else
242  Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("%s has been awarded the posthumous rank of %s\nfor inspirational gallantry in the face of overwhelming odds.\n"), chr->name, _(rank->name));
243  MS_AddNewMessage(_("Soldier promoted"), cp_messageBuffer, MSG_PROMOTION);
244  break;
245  }
246  }
247  cgi->Com_DPrintf(DEBUG_CLIENT, "CHAR_UpdateStats: Done\n");
248 }
249 
250 
251 #ifdef DEBUG
252 
255 static void CHAR_DebugChangeStats_f (void)
256 {
257  if (cgi->Cmd_Argc() < 2) {
258  cgi->Com_Printf("Usage: %s <baseIDX>\n", cgi->Cmd_Argv(0));
259  return;
260  }
261  base_t* base = B_GetBaseByIDX(atoi(cgi->Cmd_Argv(1)));
262  if (!base) {
263  cgi->Com_Printf("Invalid base idx\n");
264  return;
265  }
266 
267  E_Foreach(EMPL_SOLDIER, employee) {
268  if (!employee->isHiredInBase(base))
269  continue;
270 
271  character_t* chr = &(employee->chr);
272  assert(chr);
273 
274  for (int j = 0; j < KILLED_NUM_TYPES; j++)
275  chr->score.kills[j]++;
276  }
277  if (base->aircraftCurrent)
278  CHAR_UpdateStats(base, base->aircraftCurrent);
279 }
280 #endif /* DEBUG */
281 
285 void CHAR_InitStartup (void)
286 {
287 #ifdef DEBUG
288  cgi->Cmd_AddCommand("debug_statsupdate", CHAR_DebugChangeStats_f, "Debug function to increase the kills and test the ranks");
289 #endif
290 }
291 
295 void CHAR_Shutdown (void)
296 {
297 #ifdef DEBUG
298  cgi->Cmd_RemoveCommand("debug_statsupdate");
299 #endif
300 }
void CHAR_InitStartup(void)
Campaign initialization actions for the character module.
#define BODYPART_MAXTYPE
Definition: chr_shared.h:255
void CHAR_Shutdown(void)
Campaign closing actions for the character module.
abilityskills_t
Definition: chr_shared.h:36
int CHAR_GetMaxExperiencePerMission(const abilityskills_t skill)
Determines the maximum amount of XP per skill that can be gained from any one mission.
int killedEnemies
Definition: cp_rank.h:36
chrScoreGlobal_t score
Definition: chr_shared.h:387
Header file for character (soldier, alien) related campaign functions.
Describes a rank that a recruit can gain.
Definition: cp_rank.h:29
#define E_Foreach(employeeType, var)
Definition: cp_employee.h:122
const aircraft_t * AIR_IsEmployeeInAircraft(const Employee *employee, const aircraft_t *aircraft)
Tells you if an employee is assigned to an aircraft.
int experience[SKILL_NUM_TYPES+1]
Definition: chr_shared.h:120
#define _(String)
Definition: cl_shared.h:43
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition: shared.cpp:494
int numRanks
Definition: cp_campaign.h:369
void CHAR_UpdateData(linkedList_t *updateCharacters)
Transforms the battlescape values to the character.
character_t chr
Definition: cp_employee.h:119
int stuns[KILLED_NUM_TYPES]
Definition: chr_shared.h:127
int killedOthers
Definition: cp_rank.h:37
rank_t * CL_GetRankByIdx(const int index)
Returns a rank at an index.
Definition: cp_rank.cpp:50
uiMessageListNodeMessage_t * MS_AddNewMessage(const char *title, const char *text, messageType_t type, technology_t *pedia, bool popup, bool playSound)
Adds a new message to message stack.
Definition: cp_messages.cpp:61
A base with all it's data.
Definition: cp_base.h:84
woundInfo_t wounds
aircraft_t * aircraftCurrent
Definition: cp_base.h:100
#define ERR_DROP
Definition: common.h:211
int treatmentLevel[BODYPART_MAXTYPE]
Definition: chr_shared.h:352
#define DEBUG_CLIENT
Definition: defines.h:59
#define OBJZERO(obj)
Definition: shared.h:178
int kills[KILLED_NUM_TYPES]
Definition: chr_shared.h:126
void CHAR_UpdateSkills(character_t *chr)
Updates the character skills after a mission.
static bool CHAR_ShouldUpdateSoldierRank(const rank_t *rank, const character_t *chr)
Checks whether a soldier should be promoted.
const cgame_import_t * cgi
void CHAR_ParseData(dbuffer *msg, linkedList_t **updateCharacters)
Parses the character data which was send by G_MatchSendResults using G_SendCharacterData.
int initialSkills[SKILL_NUM_TYPES+1]
Definition: chr_shared.h:123
ccs_t ccs
Definition: cp_campaign.cpp:62
int type
Definition: cp_rank.h:34
base_t * B_GetBaseByIDX(int baseIdx)
Array bound check for the base index. Will also return unfounded bases as long as the index is in the...
Definition: cp_base.cpp:312
Info on a wound.
Definition: chr_shared.h:350
chrScoreGlobal_t chrscore
const char * name
Definition: cp_rank.h:31
char cp_messageBuffer[MAX_MESSAGE_TEXT]
Definition: cp_messages.cpp:31
CGAME_HARD_LINKED_FUNCTIONS linkedList_t * LIST_Add(linkedList_t **listDest, void const *data, size_t length)
woundInfo_t wounds
Definition: chr_shared.h:383
struct base_s * homebase
Definition: cp_aircraft.h:149
#define MAX_MAXHP
Definition: q_shared.h:279
Structure of all stats collected for an actor over time.
Definition: chr_shared.h:119
QGL_EXTERN GLint i
Definition: r_gl.h:113
#define MAX_SKILL
Definition: q_shared.h:278
Employee * E_GetEmployeeFromChrUCN(int uniqueCharacterNumber)
Searches all employee for the ucn (character id)
int mind
Definition: cp_rank.h:35
#define LIST_Foreach(list, type, var)
Iterates over a linked list, it's safe to delete the returned entry from the list while looping over ...
Definition: list.h:41
Header file for single player campaign control.
char name[MAX_VAR]
Definition: chr_shared.h:371
An aircraft with all it's data.
Definition: cp_aircraft.h:114
void CHAR_UpdateStats(const base_t *base, const aircraft_t *aircraft)
Update employees stats after mission.
int skills[SKILL_NUM_TYPES]
Definition: chr_shared.h:122
const char *IMPORT * Cmd_Argv(int n)
Describes a character with all its attributes.
Definition: chr_shared.h:369