UFO: Alien Invasion
Loading...
Searching...
No Matches
cp_character.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#include "../../cl_shared.h"
26#include "cp_character.h"
27#include "cp_campaign.h"
28
42
56{
57 switch (skill) {
58 case ABILITY_POWER:
59 return 125;
60 case ABILITY_SPEED:
61 return 91;
63 return 450;
64 case ABILITY_MIND:
65 return 450;
66 case SKILL_CLOSE:
67 return 680;
68 case SKILL_HEAVY:
69 return 680;
70 case SKILL_ASSAULT:
71 return 680;
72 case SKILL_SNIPER:
73 return 680;
74 case SKILL_EXPLOSIVE:
75 return 680;
76 case SKILL_NUM_TYPES: /* This is health. */
77 return 360;
78 case SKILL_PILOTING:
79 case SKILL_TARGETING:
80 case SKILL_EVADING:
81 return 0;
82 default:
83 cgi->Com_Error(ERR_DROP, "G_GetMaxExperiencePerMission: invalid skill type");
84 return -1;
85 }
86}
87
93{
94 for (int i = 0; i < SKILL_NUM_TYPES; ++i)
95 chr->score.skills[i] = std::min(MAX_SKILL, chr->score.initialSkills[i] +
96 static_cast<int>(pow(static_cast<float>(chr->score.experience[i]) / 10, 0.6f)));
97
98 chr->maxHP = std::min(MAX_MAXHP, chr->score.initialSkills[SKILL_NUM_TYPES] +
99 static_cast<int>(pow(static_cast<float>(chr->score.experience[SKILL_NUM_TYPES]) / 10, 0.6f)));
100}
101
106void CHAR_UpdateData (linkedList_t* updateCharacters)
107{
108 LIST_Foreach(updateCharacters, updateCharacter_t, c) {
109 Employee* employee = E_GetEmployeeFromChrUCN(c->ucn);
110
111 if (!employee) {
112 cgi->Com_Printf("Warning: Could not get character with ucn: %i.\n", c->ucn);
113 continue;
114 }
115
116 character_t* chr = &employee->chr;
117 const bool fullHP = c->HP >= chr->maxHP;
118 chr->STUN = c->STUN;
119 chr->morale = c->morale;
120
121 memcpy(chr->wounds.treatmentLevel, c->wounds.treatmentLevel, sizeof(chr->wounds.treatmentLevel));
122 memcpy(chr->score.kills, c->chrscore.kills, sizeof(chr->score.kills));
123 memcpy(chr->score.stuns, c->chrscore.stuns, sizeof(chr->score.stuns));
124 chr->score.assignedMissions = c->chrscore.assignedMissions;
125
126 for (int i = ABILITY_POWER; i <= SKILL_NUM_TYPES; ++i) {
127 const int maxXP = CHAR_GetMaxExperiencePerMission(static_cast<abilityskills_t>(i));
128 const int gainedXP = std::min(maxXP, c->chrscore.experience[i] - chr->score.experience[i]);
129 chr->score.experience[i] += gainedXP;
130 cgi->Com_DPrintf(DEBUG_CLIENT, "CP_UpdateCharacterData: Soldier %s earned %d experience points in skill #%d (total experience: %d)\n",
131 chr->name, gainedXP, i, chr->score.experience[SKILL_NUM_TYPES]);
132 }
133
135 /* If character returned unscratched and maxHP just went up due to experience
136 * don't send him/her to the hospital */
137 chr->HP = (fullHP ? chr->maxHP : std::min(c->HP, chr->maxHP));
138 }
139}
140
151void CHAR_ParseData (dbuffer* msg, linkedList_t** updateCharacters)
152{
153 const int num = cgi->NET_ReadByte(msg);
154
155 if (num < 0)
156 cgi->Com_Error(ERR_DROP, "CP_ParseCharacterData: invalid character number found in stream (%i)\n", num);
157
158 for (int i = 0; i < num; i++) {
160 c.ucn = cgi->NET_ReadShort(msg);
161 c.HP = cgi->NET_ReadShort(msg);
162 c.STUN = cgi->NET_ReadByte(msg);
163 c.morale = cgi->NET_ReadByte(msg);
164
165 int j;
166 for (j = 0; j < BODYPART_MAXTYPE; ++j)
167 c.wounds.treatmentLevel[j] = cgi->NET_ReadByte(msg);
168
169 for (j = 0; j < SKILL_NUM_TYPES + 1; j++)
170 c.chrscore.experience[j] = cgi->NET_ReadLong(msg);
171 for (j = 0; j < KILLED_NUM_TYPES; j++)
172 c.chrscore.kills[j] = cgi->NET_ReadShort(msg);
173 for (j = 0; j < KILLED_NUM_TYPES; j++)
174 c.chrscore.stuns[j] = cgi->NET_ReadShort(msg);
175 c.chrscore.assignedMissions = cgi->NET_ReadShort(msg);
176 LIST_Add(updateCharacters, c);
177 }
178}
179
186static bool CHAR_ShouldUpdateSoldierRank (const rank_t* rank, const character_t* chr)
187{
188 if (rank->type != EMPL_SOLDIER)
189 return false;
190
191 /* mind is not yet enough */
192 if (chr->score.skills[ABILITY_MIND] < rank->mind)
193 return false;
194
195 /* not enough killed enemies yet */
196 if (chr->score.kills[KILLED_ENEMIES] < rank->killedEnemies)
197 return false;
198
199 /* too many civilians and team kills */
201 return false;
202
203 return true;
204}
205
212void CHAR_UpdateStats (const base_t* base, const aircraft_t* aircraft)
213{
214 assert(aircraft);
215
216 /* only soldiers have stats and ranks, ugvs not */
217 E_Foreach(EMPL_SOLDIER, employee) {
218 if (!employee->isHiredInBase(aircraft->homebase))
219 continue;
220 if (!AIR_IsEmployeeInAircraft(employee, aircraft))
221 continue;
222 character_t* chr = &employee->chr;
223
224 /* Remember the number of assigned mission for this character. */
225 chr->score.assignedMissions++;
226
230
231 /* Check if the soldier meets the requirements for a higher rank
232 * and do a promotion. */
233 if (ccs.numRanks < 2)
234 continue;
235
236 for (int j = ccs.numRanks - 1; j > chr->score.rank; j--) {
237 const rank_t* rank = CL_GetRankByIdx(j);
238 if (!CHAR_ShouldUpdateSoldierRank(rank, chr))
239 continue;
240
241 chr->score.rank = j;
242 if (chr->HP > 0)
243 Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("%s has been promoted to %s.\n"), chr->name, _(rank->name));
244 else
245 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));
246 MS_AddNewMessage(_("Soldier promoted"), cp_messageBuffer, MSG_PROMOTION);
247 break;
248 }
249 }
250 cgi->Com_DPrintf(DEBUG_CLIENT, "CHAR_UpdateStats: Done\n");
251}
252
253
254#ifdef DEBUG
258static void CHAR_DebugChangeStats_f (void)
259{
260 if (cgi->Cmd_Argc() < 2) {
261 cgi->Com_Printf("Usage: %s <baseIDX>\n", cgi->Cmd_Argv(0));
262 return;
263 }
264 base_t* base = B_GetBaseByIDX(atoi(cgi->Cmd_Argv(1)));
265 if (!base) {
266 cgi->Com_Printf("Invalid base idx\n");
267 return;
268 }
269
270 E_Foreach(EMPL_SOLDIER, employee) {
271 if (!employee->isHiredInBase(base))
272 continue;
273
274 character_t* chr = &(employee->chr);
275 assert(chr);
276
277 for (int j = 0; j < KILLED_NUM_TYPES; j++)
278 chr->score.kills[j]++;
279 }
280 if (base->aircraftCurrent)
282}
283#endif /* DEBUG */
284
289{
290#ifdef DEBUG
291 cgi->Cmd_AddCommand("debug_statsupdate", CHAR_DebugChangeStats_f, "Debug function to increase the kills and test the ranks");
292#endif
293}
294
298void CHAR_Shutdown (void)
299{
300#ifdef DEBUG
301 cgi->Cmd_RemoveCommand("debug_statsupdate");
302#endif
303}
abilityskills_t
Definition chr_shared.h:36
@ SKILL_PILOTING
Definition chr_shared.h:48
@ SKILL_HEAVY
Definition chr_shared.h:43
@ ABILITY_POWER
Definition chr_shared.h:37
@ ABILITY_SPEED
Definition chr_shared.h:38
@ SKILL_NUM_TYPES
Definition chr_shared.h:51
@ SKILL_EVADING
Definition chr_shared.h:50
@ SKILL_SNIPER
Definition chr_shared.h:45
@ ABILITY_MIND
Definition chr_shared.h:40
@ SKILL_CLOSE
Definition chr_shared.h:42
@ SKILL_EXPLOSIVE
Definition chr_shared.h:46
@ SKILL_TARGETING
Definition chr_shared.h:49
@ ABILITY_ACCURACY
Definition chr_shared.h:39
@ SKILL_ASSAULT
Definition chr_shared.h:44
@ KILLED_TEAM
Definition chr_shared.h:30
@ KILLED_ENEMIES
Definition chr_shared.h:28
@ KILLED_CIVILIANS
Definition chr_shared.h:29
@ KILLED_NUM_TYPES
Definition chr_shared.h:32
#define BODYPART_MAXTYPE
Definition chr_shared.h:266
CGAME_HARD_LINKED_FUNCTIONS linkedList_t * LIST_Add(linkedList_t **listDest, void const *data, size_t length)
Share stuff between the different cgame implementations.
#define _(String)
Definition cl_shared.h:44
character_t chr
#define ERR_DROP
Definition common.h:211
const aircraft_t * AIR_IsEmployeeInAircraft(const Employee *employee, const aircraft_t *aircraft)
Tells you if an employee is assigned to an aircraft.
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:313
ccs_t ccs
Header file for single player campaign control.
const cgame_import_t * cgi
int CHAR_GetMaxExperiencePerMission(const abilityskills_t skill)
Determines the maximum amount of XP per skill that can be gained from any one mission.
void CHAR_InitStartup(void)
Campaign initialization actions for the character module.
void CHAR_UpdateData(linkedList_t *updateCharacters)
Transforms the battlescape values to the character.
void CHAR_ParseData(dbuffer *msg, linkedList_t **updateCharacters)
Parses the character data which was send by G_MatchSendResults using G_SendCharacterData.
void CHAR_Shutdown(void)
Campaign closing actions for the character module.
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.
void CHAR_UpdateStats(const base_t *base, const aircraft_t *aircraft)
Update employees stats after mission.
Header file for character (soldier, alien) related campaign functions.
Employee * E_GetEmployeeFromChrUCN(int uniqueCharacterNumber)
Searches all employee for the ucn (character id).
@ EMPL_SOLDIER
Definition cp_employee.h:31
#define E_Foreach(employeeType, var)
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.
char cp_messageBuffer[MAX_MESSAGE_TEXT]
@ MSG_PROMOTION
Definition cp_messages.h:45
rank_t * CL_GetRankByIdx(const int index)
Returns a rank at an index.
Definition cp_rank.cpp:50
#define DEBUG_CLIENT
Definition defines.h:59
#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
#define MAX_MAXHP
Definition q_shared.h:279
#define MAX_SKILL
Definition q_shared.h:278
QGL_EXTERN GLint i
Definition r_gl.h:113
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition shared.cpp:494
An aircraft with all it's data.
struct base_s * homebase
A base with all it's data.
Definition cp_base.h:84
aircraft_t * aircraftCurrent
Definition cp_base.h:100
Describes a character with all its attributes.
Definition chr_shared.h:388
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
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 stuns[KILLED_NUM_TYPES]
Definition chr_shared.h:127
int kills[KILLED_NUM_TYPES]
Definition chr_shared.h:126
int skills[SKILL_NUM_TYPES]
Definition chr_shared.h:122
int experience[SKILL_NUM_TYPES+1]
Definition chr_shared.h:120
Describes a rank that a recruit can gain.
Definition cp_rank.h:29
int mind
Definition cp_rank.h:35
const char * name
Definition cp_rank.h:31
int killedOthers
Definition cp_rank.h:37
int type
Definition cp_rank.h:34
int killedEnemies
Definition cp_rank.h:36
chrScoreGlobal_t chrscore
Info on a wound.
Definition chr_shared.h:361
int treatmentLevel[BODYPART_MAXTYPE]
Definition chr_shared.h:363