1 /**
2  * @file
3  * @brief Menu related console command callbacks
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.
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 "../../cl_shared.h"
27 #include "../../ui/ui_dataids.h"
28 #include "cp_campaign.h"
29 #include "cp_installation_callbacks.h"
30 #include "cp_installation.h"
31 #include "cp_geoscape.h"
32 #include "cp_popup.h"
33 #include "cp_ufo.h"
34 #include "cp_uforecovery_callbacks.h"
35 
36  /**
37  * @brief Sets the title of the installation to a cvar to prepare the rename menu.
38  * @note it also assigns description text
39  */
INS_SetInstallationTitle(installationType_t type)40 static void INS_SetInstallationTitle (installationType_t type)
41 {
42 	const installationTemplate_t* insTemp = INS_GetInstallationTemplateByType(type);
43 	cgi->Cvar_Set("mn_installation_title", "%s #%i", (insTemp) ? _(insTemp->name) : _("Installation"), ccs.campaignStats.installationsBuilt + 1);
44 	if (!insTemp || !Q_strvalid(insTemp->description))
45 		cgi->UI_ResetData(TEXT_BUILDING_INFO);
46 	else
47 		cgi->UI_RegisterText(TEXT_BUILDING_INFO, _(insTemp->description));
48 }
49 
50 /**
51  * @brief Select an installation when clicking on it on geoscape
52  * @param[in] installation The installation to select
53  * @note This is (and should be) the only place where installationCurrent is set
54  * to a value that is not @c nullptr
55  */
INS_SelectInstallation(installation_t * installation)56 void INS_SelectInstallation (installation_t* installation)
57 {
58 	const int timetobuild = std::max(0, installation->installationTemplate->buildTime - (ccs.date.day - installation->buildStart));
59 
60 	Com_DPrintf(DEBUG_CLIENT, "INS_SelectInstallation: select installation with id %i\n", installation->idx);
61 	ccs.mapAction = MA_NONE;
62 	if (installation->installationStatus == INSTALLATION_WORKING) {
63 		cgi->Cvar_Set("mn_installation_timetobuild", "-");
64 	} else {
65 		cgi->Cvar_Set("mn_installation_timetobuild", ngettext("%d day", "%d days", timetobuild), timetobuild);
66 	}
67 	INS_SetCurrentSelectedInstallation(installation);
68 
69 	switch (installation->installationTemplate->type) {
70 	case INSTALLATION_UFOYARD:
71 		cgi->UI_PushWindow("popup_ufoyards");
72 		break;
73 	case INSTALLATION_DEFENCE:
74 		cgi->UI_PushWindow("basedefence");
75 		break;
76 	default:
77 		cgi->UI_PushWindow("popup_installationstatus");
78 		break;
79 	}
80 }
81 
82 /**
83  * @brief Constructs a new installation.
84  */
INS_BuildInstallation_f(void)85 static void INS_BuildInstallation_f (void)
86 {
87 	const installationTemplate_t* installationTemplate;
88 
89 	if (cgi->Cmd_Argc() < 1) {
90 		Com_Printf("Usage: %s <installationType>\n", cgi->Cmd_Argv(0));
91 		return;
92 	}
93 
94 	/* We shouldn't build more installations than the actual limit */
95 	if (B_GetInstallationLimit() <= INS_GetCount())
96 		return;
97 
98 	installationTemplate = INS_GetInstallationTemplateByID(cgi->Cmd_Argv(1));
99 	if (!installationTemplate) {
100 		Com_Printf("The installation type %s passed for %s is not valid.\n", cgi->Cmd_Argv(1), cgi->Cmd_Argv(0));
101 		return;
102 	}
103 
104 	assert(installationTemplate->cost >= 0);
105 
106 	if (ccs.credits - installationTemplate->cost > 0) {
107 		/* set up the installation */
108 		installation_t* installation = INS_Build(installationTemplate, ccs.newBasePos, cgi->Cvar_GetString("mn_installation_title"));
109 
110 		CP_UpdateCredits(ccs.credits - installationTemplate->cost);
111 		/* this cvar is used for disabling the installation build button on geoscape if MAX_INSTALLATIONS was reached */
112 		cgi->Cvar_SetValue("mn_installation_count", INS_GetCount());
113 
114 		const nation_t* nation = GEO_GetNation(installation->pos);
115 		if (nation)
116 			Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("A new installation has been built: %s (nation: %s)"), installation->name, _(nation->name));
117 		else
118 			Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("A new installation has been built: %s"), installation->name);
119 		MSO_CheckAddNewMessage(NT_INSTALLATION_BUILDSTART, _("Installation building"), cp_messageBuffer, MSG_CONSTRUCTION);
120 	} else {
121 		if (installationTemplate->type == INSTALLATION_RADAR) {
122 			if (GEO_IsRadarOverlayActivated())
123 					GEO_SetOverlay("radar");
124 		}
125 		if (ccs.mapAction == MA_NEWINSTALLATION)
126 			ccs.mapAction = MA_NONE;
127 
128 		CP_Popup(_("Notice"), _("Not enough credits to set up a new installation."));
129 	}
130 	ccs.mapAction = MA_NONE;
131 }
132 
133 /**
134  * @brief Called when an installation is opened or a new installation is created on geoscape.
135  * For a new installation the installationID is -1.
136  */
INS_SelectInstallation_f(void)137 static void INS_SelectInstallation_f (void)
138 {
139 	int installationID;
140 	installation_t* installation;
141 
142 	if (cgi->Cmd_Argc() < 2) {
143 		Com_Printf("Usage: %s <installationID>\n", cgi->Cmd_Argv(0));
144 		return;
145 	}
146 	installationID = atoi(cgi->Cmd_Argv(1));
147 
148 	installation = INS_GetByIDX(installationID);
149 	if (installation != nullptr)
150 		INS_SelectInstallation(installation);
151 }
152 
153 /**
154  * @brief Creates console command to change the name of a installation.
155  * Copies the value of the cvar mn_installation_title over as the name of the
156  * current selected installation
157  */
INS_ChangeInstallationName_f(void)158 static void INS_ChangeInstallationName_f (void)
159 {
160 	installation_t* installation = INS_GetCurrentSelectedInstallation();
161 
162 	/* maybe called without installation initialized or active */
163 	if (!installation)
164 		return;
165 
166 	Q_strncpyz(installation->name, cgi->Cvar_GetString("mn_installation_title"), sizeof(installation->name));
167 }
168 
169 /**
170  * @brief console function for destroying an installation
171  * @sa INS_DestroyInstallation
172  */
INS_DestroyInstallation_f(void)173 static void INS_DestroyInstallation_f (void)
174 {
175 	installation_t* installation;
176 
177 	if (cgi->Cmd_Argc() < 2 || atoi(cgi->Cmd_Argv(1)) < 0) {
178 		installation = INS_GetCurrentSelectedInstallation();
179 	} else {
180 		installation = INS_GetByIDX(atoi(cgi->Cmd_Argv(1)));
181 		if (!installation) {
182 			Com_DPrintf(DEBUG_CLIENT, "Installation not founded (idx %i)\n", atoi(cgi->Cmd_Argv(1)));
183 			return;
184 		}
185 	}
186 
187 	/* Ask 'Are you sure?' by default */
188 	if (cgi->Cmd_Argc() < 3 || !atoi(cgi->Cmd_Argv(2))) {
189 		char command[MAX_VAR];
190 
191 		Com_sprintf(command, sizeof(command), "mn_installation_destroy %d 1; ui_pop;", installation->idx);
192 		cgi->UI_PopupButton(_("Destroy Installation"), _("Do you really want to destroy this installation?"),
193 			command, _("Destroy"), _("Destroy installation"),
194 			"ui_pop;", _("Cancel"), _("Forget it"),
195 			nullptr, nullptr, nullptr);
196 		return;
197 	}
198 	INS_DestroyInstallation(installation);
199 }
200 
201 /**
202  * @brief updates the installation limit cvar for menus
203  */
INS_UpdateInstallationLimit_f(void)204 static void INS_UpdateInstallationLimit_f (void)
205 {
206 	cgi->Cvar_SetValue("mn_installation_max", B_GetInstallationLimit());
207 }
208 
209 /**
210  * @brief Fills the UI with ufo yard data
211  */
INS_FillUFOYardData_f(void)212 static void INS_FillUFOYardData_f (void)
213 {
214 	installation_t* ins;
215 
216 	cgi->UI_ExecuteConfunc("ufolist_clear");
217 	if (cgi->Cmd_Argc() < 2 || atoi(cgi->Cmd_Argv(1)) < 0) {
218 		ins = INS_GetCurrentSelectedInstallation();
219 		if (!ins || ins->installationTemplate->type != INSTALLATION_UFOYARD)
220 			ins = INS_GetFirstUFOYard(false);
221 	} else {
222 		ins = INS_GetByIDX(atoi(cgi->Cmd_Argv(1)));
223 		if (!ins)
224 			Com_DPrintf(DEBUG_CLIENT, "Installation not founded (idx %i)\n", atoi(cgi->Cmd_Argv(1)));
225 	}
226 
227 	if (ins) {
228 		const nation_t* nat = GEO_GetNation(ins->pos);
229 		const int timeToBuild = std::max(0, ins->installationTemplate->buildTime - (ccs.date.day - ins->buildStart));
230 		const char* buildTime = (timeToBuild > 0 && ins->installationStatus == INSTALLATION_UNDER_CONSTRUCTION) ? va(ngettext("%d day", "%d days", timeToBuild), timeToBuild) : "-";
231 		const int freeCap = std::max(0, ins->ufoCapacity.max - ins->ufoCapacity.cur);
232 		const char* nationName = nat ? _(nat->name) : "";
233 
234 		cgi->UI_ExecuteConfunc("ufolist_addufoyard %d \"%s\" \"%s\" %d %d \"%s\"", ins->idx, ins->name, nationName, ins->ufoCapacity.max, freeCap, buildTime);
235 
236 		US_Foreach(ufo) {
237 			if (ufo->installation != ins)
238 				continue;
239 
240 			const char* ufoName = UFO_GetName(ufo->ufoTemplate);
241 			const char* condition = va(_("Condition: %3.0f%%"), ufo->condition * 100);
242 			const char* status = US_StoredUFOStatus(ufo);
243 			cgi->UI_ExecuteConfunc("ufolist_addufo %d \"%s\" \"%s\" \"%s\" \"%s\"", ufo->idx, ufoName, condition, ufo->ufoTemplate->model, status);
244 		}
245 	}
246 }
247 
248 /**
249  * @brief Fills create installation / installation type selection popup
250  */
INS_FillTypes_f(void)251 static void INS_FillTypes_f (void)
252 {
253 	cgi->UI_ExecuteConfunc("installationtype_clear");
254 	if (INS_GetCount() < B_GetInstallationLimit()) {
255 		int i;
256 		for (i = 0; i < ccs.numInstallationTemplates; i++) {
257 			const installationTemplate_t* tpl = &ccs.installationTemplates[i];
258 			if (tpl->once && INS_HasType(tpl->type, INSTALLATION_NOT_USED))
259 				continue;
260 			if (tpl->tech == nullptr || RS_IsResearched_ptr(tpl->tech)) {
261 				cgi->UI_ExecuteConfunc("installationtype_add \"%s\" \"%s\" \"%s\" \"%d c\"", tpl->id, _(tpl->name),
262 					(tpl->buildTime > 0) ? va("%d %s", tpl->buildTime, ngettext("day", "days", tpl->buildTime)) : "-", tpl->cost);
263 			}
264 		}
265 	}
266 
267 	/** @todo Move this out from installations code */
268 	if (B_GetCount() < MAX_BASES)
269 		cgi->UI_ExecuteConfunc("installationtype_add base \"%s\" - \"%d c\"", _("Base"), ccs.curCampaign->basecost);
270 }
271 
272 /**
273  * @brief Selects installation type to build
274  */
INS_SelectType_f(void)275 static void INS_SelectType_f (void)
276 {
277 	if (cgi->Cmd_Argc() < 2)
278 		return;
279 
280 	const char* id = cgi->Cmd_Argv(1);
281 
282 	if (ccs.mapAction == MA_NEWINSTALLATION) {
283 		GEO_ResetAction();
284 		return;
285 	}
286 
287 	const installationTemplate_t* tpl = INS_GetInstallationTemplateByID(id);
288 	if (!tpl) {
289 		Com_Printf("Invalid installation template\n");
290 		return;
291 	}
292 
293 	if (INS_GetCount() >= B_GetInstallationLimit()) {
294 		Com_Printf("Maximum number of installations reached\n");
295 		return;
296 	}
297 
298 	if (tpl->tech != nullptr && !RS_IsResearched_ptr(tpl->tech)) {
299 		Com_Printf("This type of installation is not yet researched\n");
300 		return;
301 	}
302 
303 	if (tpl->once && INS_HasType(tpl->type, INSTALLATION_NOT_USED)) {
304 		Com_Printf("Cannot build more of this installation\n");
305 		return;
306 	}
307 
308 	ccs.mapAction = MA_NEWINSTALLATION;
309 
310 	/* show radar overlay (if not already displayed) */
311 	if (tpl->type == INSTALLATION_RADAR && !GEO_IsRadarOverlayActivated())
312 		GEO_SetOverlay("radar");
313 
314 	INS_SetInstallationTitle(tpl->type);
315 	cgi->Cvar_Set("mn_installation_type", "%s", tpl->id);
316 }
317 
INS_InitCallbacks(void)318 void INS_InitCallbacks (void)
319 {
320 	cgi->Cmd_AddCommand("mn_installation_select", INS_SelectInstallation_f, "Parameter is the installation index. -1 will build a new one.");
321 	cgi->Cmd_AddCommand("mn_installation_build", INS_BuildInstallation_f, nullptr);
322 	cgi->Cmd_AddCommand("mn_installation_changename", INS_ChangeInstallationName_f, "Called after editing the cvar installation name");
323 	cgi->Cmd_AddCommand("mn_installation_destroy", INS_DestroyInstallation_f, "Destroys an installation");
324 	cgi->Cmd_AddCommand("mn_installation_update_max_count", INS_UpdateInstallationLimit_f, "Updates the installation count limit");
325 
326 	cgi->Cmd_AddCommand("ui_fill_installationtypes", INS_FillTypes_f, "Fills create installation / installation type selection popup");
327 	cgi->Cmd_AddCommand("ui_build_installationtype", INS_SelectType_f, "Selects installation type to build");
328 	cgi->Cmd_AddCommand("ui_fillufoyards", INS_FillUFOYardData_f, "Fills UFOYard UI with data");
329 
330 	cgi->Cvar_SetValue("mn_installation_count", INS_GetCount());
331 	cgi->Cvar_Set("mn_installation_title", "");
332 	cgi->Cvar_Set("mn_installation_type", "");
333 	cgi->Cvar_Set("mn_installation_max", "");
334 }
335 
INS_ShutdownCallbacks(void)336 void INS_ShutdownCallbacks (void)
337 {
338 	cgi->Cmd_RemoveCommand("ui_build_installationtype");
339 	cgi->Cmd_RemoveCommand("ui_fill_installationtypes");
340 	cgi->Cmd_RemoveCommand("ui_fillufoyards");
341 
342 	cgi->Cmd_RemoveCommand("mn_installation_select");
343 	cgi->Cmd_RemoveCommand("mn_installation_build");
344 	cgi->Cmd_RemoveCommand("mn_installation_changename");
345 	cgi->Cmd_RemoveCommand("mn_installation_destroy");
346 	cgi->Cmd_RemoveCommand("mn_installation_update_max_count");
347 	cgi->Cvar_Delete("mn_installation_count");
348 	cgi->Cvar_Delete("mn_installation_title");
349 	cgi->Cvar_Delete("mn_installation_max");
350 	cgi->Cvar_Delete("mn_installation_type");
351 }
352