1 /**
2 * @file
3 * @brief Team management, name generation and parsing.
4 */
5
6 /*
7 Copyright (C) 2002-2013 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.m
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 "client.h"
27 #include "cl_team.h"
28 #include "cl_inventory.h"
29 #include "cgame/cl_game.h"
30 #include "battlescape/cl_localentity.h"
31 #include "battlescape/cl_actor.h"
32 #include "battlescape/events/e_parse.h"
33 #include "battlescape/events/e_main.h"
34 #include "ui/ui_data.h"
35 #include "ui/ui_nodes.h"
36
37 /** @brief List of currently displayed or equipable characters. */
38 linkedList_t* chrDisplayList;
39
40 /**
41 * @brief Allocate a skin from the cls structure
42 * @return A actorskin structure
43 */
CL_AllocateActorSkin(const char * name)44 actorSkin_t* CL_AllocateActorSkin (const char* name)
45 {
46 int index;
47 actorSkin_t* skin;
48
49 if (cls.numActorSkins >= lengthof(cls.actorSkins))
50 Sys_Error("CL_AllocateActorSkin: Max actorskin hit");
51
52 index = R_ModAllocateActorSkin(name);
53 skin = &cls.actorSkins[index];
54 OBJZERO(*skin);
55 skin->idx = index;
56
57 skin->id = Mem_PoolStrDup(name, com_genericPool, 0);
58
59 cls.numActorSkins++;
60 return skin;
61 }
62
63 /**
64 * @brief Get number of registered actorskins
65 * @return Number of registered actorskins
66 */
CL_GetActorSkinCount(void)67 unsigned int CL_GetActorSkinCount (void)
68 {
69 return cls.numActorSkins;
70 }
71
72 /**
73 * @brief Get a actorskin from idx
74 * @return A actorskin, else nullptr
75 */
CL_GetActorSkinByIDS(unsigned int idx)76 static const actorSkin_t* CL_GetActorSkinByIDS (unsigned int idx)
77 {
78 if (idx >= cls.numActorSkins)
79 return nullptr;
80 return &cls.actorSkins[idx];
81 }
82
CL_CharacterSkillAndScoreCvars(const character_t * chr)83 static void CL_CharacterSkillAndScoreCvars (const character_t* chr)
84 {
85 const chrScoreGlobal_t* score = &chr->score;
86 Cvar_ForceSet("mn_name", chr->name);
87 Cvar_ForceSet("mn_body", CHRSH_CharGetBody(chr));
88 Cvar_ForceSet("mn_head", CHRSH_CharGetHead(chr));
89 Cvar_ForceSet("mn_body_skin", va("%i", chr->bodySkin));
90 Cvar_ForceSet("mn_head_skin", va("%i", chr->headSkin));
91
92 Cvar_SetValue("mn_vpwr", score->skills[ABILITY_POWER]);
93 Cvar_SetValue("mn_vspd", score->skills[ABILITY_SPEED]);
94 Cvar_SetValue("mn_vacc", score->skills[ABILITY_ACCURACY]);
95 Cvar_SetValue("mn_vmnd", score->skills[ABILITY_MIND]);
96 Cvar_SetValue("mn_vcls", score->skills[SKILL_CLOSE]);
97 Cvar_SetValue("mn_vhvy", score->skills[SKILL_HEAVY]);
98 Cvar_SetValue("mn_vass", score->skills[SKILL_ASSAULT]);
99 Cvar_SetValue("mn_vsnp", score->skills[SKILL_SNIPER]);
100 Cvar_SetValue("mn_vexp", score->skills[SKILL_EXPLOSIVE]);
101 Cvar_SetValue("mn_vpil", score->skills[SKILL_PILOTING]);
102 Cvar_SetValue("mn_vtar", score->skills[SKILL_TARGETING]);
103 Cvar_SetValue("mn_vevad", score->skills[SKILL_EVADING]);
104 Cvar_SetValue("mn_vpwri", score->initialSkills[ABILITY_POWER]);
105 Cvar_SetValue("mn_vspdi", score->initialSkills[ABILITY_SPEED]);
106 Cvar_SetValue("mn_vacci", score->initialSkills[ABILITY_ACCURACY]);
107 Cvar_SetValue("mn_vmndi", score->initialSkills[ABILITY_MIND]);
108 Cvar_SetValue("mn_vclsi", score->initialSkills[SKILL_CLOSE]);
109 Cvar_SetValue("mn_vhvyi", score->initialSkills[SKILL_HEAVY]);
110 Cvar_SetValue("mn_vassi", score->initialSkills[SKILL_ASSAULT]);
111 Cvar_SetValue("mn_vsnpi", score->initialSkills[SKILL_SNIPER]);
112 Cvar_SetValue("mn_vexpi", score->initialSkills[SKILL_EXPLOSIVE]);
113 Cvar_SetValue("mn_vpili", score->initialSkills[SKILL_PILOTING]);
114 Cvar_SetValue("mn_vtari", score->initialSkills[SKILL_TARGETING]);
115 Cvar_SetValue("mn_vevadi", score->initialSkills[SKILL_EVADING]);
116 Cvar_SetValue("mn_vhp", chr->HP);
117 Cvar_SetValue("mn_vhpmax", chr->maxHP);
118
119 Cvar_Set("mn_tpwr", "%s (%i)", CL_ActorGetSkillString(score->skills[ABILITY_POWER]), score->skills[ABILITY_POWER]);
120 Cvar_Set("mn_tspd", "%s (%i)", CL_ActorGetSkillString(score->skills[ABILITY_SPEED]), score->skills[ABILITY_SPEED]);
121 Cvar_Set("mn_tacc", "%s (%i)", CL_ActorGetSkillString(score->skills[ABILITY_ACCURACY]), score->skills[ABILITY_ACCURACY]);
122 Cvar_Set("mn_tmnd", "%s (%i)", CL_ActorGetSkillString(score->skills[ABILITY_MIND]), score->skills[ABILITY_MIND]);
123 Cvar_Set("mn_tcls", "%s (%i)", CL_ActorGetSkillString(score->skills[SKILL_CLOSE]), score->skills[SKILL_CLOSE]);
124 Cvar_Set("mn_thvy", "%s (%i)", CL_ActorGetSkillString(score->skills[SKILL_HEAVY]), score->skills[SKILL_HEAVY]);
125 Cvar_Set("mn_tass", "%s (%i)", CL_ActorGetSkillString(score->skills[SKILL_ASSAULT]), score->skills[SKILL_ASSAULT]);
126 Cvar_Set("mn_tsnp", "%s (%i)", CL_ActorGetSkillString(score->skills[SKILL_SNIPER]), score->skills[SKILL_SNIPER]);
127 Cvar_Set("mn_texp", "%s (%i)", CL_ActorGetSkillString(score->skills[SKILL_EXPLOSIVE]), score->skills[SKILL_EXPLOSIVE]);
128 Cvar_Set("mn_tpil", "%s (%i)", CL_ActorGetSkillString(score->skills[SKILL_PILOTING]), score->skills[SKILL_PILOTING]);
129 Cvar_Set("mn_ttar", "%s (%i)", CL_ActorGetSkillString(score->skills[SKILL_TARGETING]), score->skills[SKILL_TARGETING]);
130 Cvar_Set("mn_tevad", "%s (%i)", CL_ActorGetSkillString(score->skills[SKILL_EVADING]), score->skills[SKILL_EVADING]);
131 Cvar_Set("mn_thp", "%i (%i)", chr->HP, chr->maxHP);
132 }
133
134 /**
135 * @brief Updates the character cvars for the given character.
136 *
137 * The models and stats that are displayed in the menu are stored in cvars.
138 * These cvars are updated here when you select another character.
139 *
140 * @param[in] chr Pointer to character_t (may not be null)
141 * @sa CL_UGVCvars
142 * @sa CL_ActorSelect
143 */
CL_ActorCvars(const character_t * chr)144 static void CL_ActorCvars (const character_t* chr)
145 {
146 Item* weapon;
147 assert(chr);
148
149 /* visible equipment */
150 weapon = chr->inv.getRightHandContainer();
151 if (weapon)
152 Cvar_Set("mn_rweapon", "%s", weapon->def()->model);
153 else
154 Cvar_Set("mn_rweapon", "");
155 weapon = chr->inv.getLeftHandContainer();
156 if (weapon)
157 Cvar_Set("mn_lweapon", "%s", weapon->def()->model);
158 else
159 Cvar_Set("mn_lweapon", "");
160 }
161
162 /**
163 * @brief Return the skill string for the given skill level
164 * @return skill string
165 * @param[in] skill a skill value between 0 and MAX_SKILL
166 */
CL_ActorGetSkillString(const int skill)167 const char* CL_ActorGetSkillString (const int skill)
168 {
169 const int skillLevel = skill * 10 / MAX_SKILL;
170 #ifdef DEBUG
171 if (skill > MAX_SKILL) {
172 Com_Printf("CL_GetSkillString: Skill is bigger than max allowed skill value (%i/%i).\n", skill, MAX_SKILL);
173 }
174 #endif
175 switch (skillLevel) {
176 case 0:
177 return _("Poor");
178 case 1:
179 return _("Mediocre");
180 case 2:
181 return _("Average");
182 case 3:
183 return _("Competent");
184 case 4:
185 return _("Proficient");
186 case 5:
187 return _("Very Good");
188 case 6:
189 return _("Highly Proficient");
190 case 7:
191 return _("Excellent");
192 case 8:
193 return _("Outstanding");
194 case 9:
195 return _("Impressive");
196 case 10:
197 return _("Superhuman");
198 default:
199 Com_Printf("CL_ActorGetSkillString: Unknown skill: %i (index: %i).\n", skill, skillLevel);
200 return "";
201 }
202 }
203
204 /**
205 * @brief Updates the UGV cvars for the given "character".
206 * The models and stats that are displayed in the menu are stored in cvars.
207 * These cvars are updated here when you select another character.
208 * @param[in] chr Pointer to character_t (may not be null)
209 * @sa CL_ActorCvars
210 * @sa CL_ActorSelect
211 */
CL_UGVCvars(const character_t * chr)212 static void CL_UGVCvars (const character_t* chr)
213 {
214 Cvar_Set("mn_lweapon", "");
215 Cvar_Set("mn_rweapon", "");
216 Cvar_Set("mn_vmnd", "0");
217 Cvar_Set("mn_tmnd", "%s (0)", CL_ActorGetSkillString(chr->score.skills[ABILITY_MIND]));
218 }
219
CL_UpdateCharacterValues(const character_t * chr)220 void CL_UpdateCharacterValues (const character_t* chr)
221 {
222 CL_CharacterSkillAndScoreCvars(chr);
223
224 if (chr->teamDef->robot)
225 CL_UGVCvars(chr);
226 else
227 CL_ActorCvars(chr);
228
229 GAME_CharacterCvars(chr);
230 }
231
232 /**
233 * @brief Generates the skills and inventory for a character and for a 2x2 unit
234 * @param[in] chr The employee to create character data for.
235 * @param[in] teamDefName Which team to use for creation.
236 */
CL_GenerateCharacter(character_t * chr,const char * teamDefName)237 void CL_GenerateCharacter (character_t* chr, const char* teamDefName)
238 {
239 chr->init();
240
241 /* link inventory */
242 cls.i.destroyInventory(&chr->inv);
243
244 /* get ucn */
245 chr->ucn = cls.nextUniqueCharacterNumber++;
246
247 chr->reservedTus.shotSettings.set(ACTOR_HAND_NOT_SET, -1, nullptr);
248
249 Com_GetCharacterValues(teamDefName, chr);
250 /* Create attributes. */
251 CHRSH_CharGenAbilitySkills(chr, GAME_IsMultiplayer());
252 }
253
254 /**
255 * @brief Init skins into the GUI
256 */
CL_InitSkin_f(void)257 static void CL_InitSkin_f (void)
258 {
259 /* create option for singleplayer skins */
260 if (UI_GetOption(OPTION_SINGLEPLAYER_SKINS) == nullptr) {
261 uiNode_t* skins = nullptr;
262 int idx = 0;
263 const actorSkin_t* skin;
264 while ((skin = CL_GetActorSkinByIDS(idx++))) {
265 if (!skin->singleplayer)
266 continue;
267 UI_AddOption(&skins, skin->id, skin->name, va("%d", skin->idx));
268 }
269 UI_RegisterOption(OPTION_SINGLEPLAYER_SKINS, skins);
270 }
271
272 /* create option for multiplayer skins */
273 if (UI_GetOption(OPTION_MULTIPLAYER_SKINS) == nullptr) {
274 uiNode_t* skins = nullptr;
275 int idx = 0;
276 const actorSkin_t* skin;
277 while ((skin = CL_GetActorSkinByIDS(idx++))) {
278 if (!skin->multiplayer)
279 continue;
280 UI_AddOption(&skins, skin->id, skin->name, va("%d", skin->idx));
281 }
282 UI_RegisterOption(OPTION_MULTIPLAYER_SKINS, skins);
283 }
284 }
285
286 /**
287 * @brief Fix actorskin idx according to game mode
288 */
CL_FixActorSkinIDX(int idx)289 static int CL_FixActorSkinIDX (int idx)
290 {
291 const actorSkin_t* skin = CL_GetActorSkinByIDS(idx);
292
293 /** @todo we should check somewhere there is at least 1 skin */
294 if (skin == nullptr) {
295 idx = 0;
296 } else {
297 if (GAME_IsSingleplayer() && !skin->singleplayer)
298 idx = 0;
299 else if (GAME_IsMultiplayer() && !skin->multiplayer)
300 idx = 0;
301 }
302 return idx;
303 }
304
305 /**
306 * @brief Change the skin of the selected actor.
307 */
CL_ChangeSkin_f(void)308 static void CL_ChangeSkin_f (void)
309 {
310 const int sel = cl_selected->integer;
311 character_t* chr = (character_t*)LIST_GetByIdx(chrDisplayList, sel);
312 if (chr == nullptr) {
313 return;
314 }
315 const int newSkin = CL_FixActorSkinIDX(Cvar_GetInteger("mn_body_skin"));
316 Cvar_SetValue("mn_body_skin", newSkin);
317 /** @todo Get the skin id from the model by using the actorskin id */
318 /** @todo Or remove skins from models and convert character_t->skin to string */
319 chr->bodySkin = newSkin;
320 }
321
322 /**
323 * @brief Use current skin for all team members onboard.
324 */
CL_ChangeSkinForWholeTeam_f(void)325 static void CL_ChangeSkinForWholeTeam_f (void)
326 {
327 /* Get selected skin and fall back to default skin if it is not valid. */
328 const int newSkin = CL_FixActorSkinIDX(Cvar_GetInteger("mn_body_skin"));
329 /* Apply new skin to all (shown/displayed) team-members. */
330 /** @todo What happens if a model of a team member does not have the selected skin? */
331 LIST_Foreach(chrDisplayList, character_t, chr) {
332 /** @todo Get the skin id from the model by using the actorskin id */
333 /** @todo Or remove skins from models and convert character_t->skin to string */
334 chr->bodySkin = newSkin;
335 }
336 }
337
TEAM_InitStartup(void)338 void TEAM_InitStartup (void)
339 {
340 Cmd_AddCommand("team_initskin", CL_InitSkin_f, "Init skin according to the game mode");
341 Cmd_AddCommand("team_changeskin", CL_ChangeSkin_f, "Change the skin of the soldier");
342 Cmd_AddCommand("team_changeskinteam", CL_ChangeSkinForWholeTeam_f, "Change the skin for the whole current team");
343 }
344