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