1 /*
2  * Copyright 2010-2014 OpenXcom Developers.
3  *
4  * This file is part of OpenXcom.
5  *
6  * OpenXcom is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * OpenXcom is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with OpenXcom.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 #define _USE_MATH_DEFINES
20 #include "MonthlyReportState.h"
21 #include <sstream>
22 #include <cmath>
23 #include "../Engine/Game.h"
24 #include "../Resource/ResourcePack.h"
25 #include "../Engine/Language.h"
26 #include "../Engine/Palette.h"
27 #include "../Interface/TextButton.h"
28 #include "../Interface/Window.h"
29 #include "../Interface/Text.h"
30 #include "../Savegame/SavedGame.h"
31 #include "../Savegame/GameTime.h"
32 #include "PsiTrainingState.h"
33 #include "../Savegame/Region.h"
34 #include "../Savegame/Country.h"
35 #include "../Ruleset/RuleCountry.h"
36 #include "DefeatState.h"
37 #include "Globe.h"
38 #include "../Savegame/AlienBase.h"
39 #include "../Engine/Options.h"
40 #include "../Menu/SaveGameState.h"
41 
42 namespace OpenXcom
43 {
44 
45 /**
46  * Initializes all the elements in the Monthly Report screen.
47  * @param game Pointer to the core game.
48  * @param psi Show psi training afterwards?
49  * @param globe Pointer to the globe.
50  */
MonthlyReportState(Game * game,bool psi,Globe * globe)51 MonthlyReportState::MonthlyReportState(Game *game, bool psi, Globe *globe) : State(game), _psi(psi), _gameOver(false), _ratingTotal(0), _fundingDiff(0), _lastMonthsRating(0), _happyList(0), _sadList(0), _pactList(0)
52 {
53 	_globe = globe;
54 	// Create objects
55 	_window = new Window(this, 320, 200, 0, 0);
56 	_btnOk = new TextButton(50, 12, 135, 180);
57 	_btnBigOk = new TextButton(120, 18, 100, 174);
58 	_txtTitle = new Text(300, 17, 16, 8);
59 	_txtMonth = new Text(110, 9, 16, 24);
60 	_txtRating = new Text(180, 9, 125, 24);
61 	_txtChange = new Text(300, 9, 16, 32);
62 	_txtDesc = new Text(280, 140, 16, 40);
63 	_txtFailure = new Text(290, 160, 15, 10);
64 
65 	// Set palette
66 	setPalette("PAL_GEOSCAPE", 3);
67 
68 	add(_window);
69 	add(_btnOk);
70 	add(_btnBigOk);
71 	add(_txtTitle);
72 	add(_txtMonth);
73 	add(_txtRating);
74 	add(_txtChange);
75 	add(_txtDesc);
76 	add(_txtFailure);
77 
78 	centerAllSurfaces();
79 
80 	// Set up objects
81 	_window->setColor(Palette::blockOffset(15)-1);
82 	_window->setBackground(_game->getResourcePack()->getSurface("BACK13.SCR"));
83 
84 	_btnOk->setColor(Palette::blockOffset(8)+10);
85 	_btnOk->setText(tr("STR_OK"));
86 	_btnOk->onMouseClick((ActionHandler)&MonthlyReportState::btnOkClick);
87 	_btnOk->onKeyboardPress((ActionHandler)&MonthlyReportState::btnOkClick, Options::keyOk);
88 	_btnOk->onKeyboardPress((ActionHandler)&MonthlyReportState::btnOkClick, Options::keyCancel);
89 
90 	_btnBigOk->setColor(Palette::blockOffset(8)+10);
91 	_btnBigOk->setText(tr("STR_OK"));
92 	_btnBigOk->onMouseClick((ActionHandler)&MonthlyReportState::btnOkClick);
93 	_btnBigOk->onKeyboardPress((ActionHandler)&MonthlyReportState::btnOkClick, Options::keyOk);
94 	_btnBigOk->onKeyboardPress((ActionHandler)&MonthlyReportState::btnOkClick, Options::keyCancel);
95 	_btnBigOk->setVisible(false);
96 
97 	_txtTitle->setColor(Palette::blockOffset(15)-1);
98 	_txtTitle->setBig();
99 	_txtTitle->setText(tr("STR_XCOM_PROJECT_MONTHLY_REPORT"));
100 
101 	_txtFailure->setColor(Palette::blockOffset(8)+10);
102 	_txtFailure->setBig();
103 	_txtFailure->setAlign(ALIGN_CENTER);
104 	_txtFailure->setVerticalAlign(ALIGN_MIDDLE);
105 	_txtFailure->setWordWrap(true);
106 	_txtFailure->setText(tr("STR_YOU_HAVE_FAILED"));
107 	_txtFailure->setVisible(false);
108 
109 	calculateChanges();
110 
111 	int month = _game->getSavedGame()->getTime()->getMonth() - 1, year = _game->getSavedGame()->getTime()->getYear();
112 	if (month == 0)
113 	{
114 		month = 12;
115 		year--;
116 	}
117 	std::string m;
118 	switch (month)
119 	{
120 	case 1: m = "STR_JAN"; break;
121 	case 2: m = "STR_FEB"; break;
122 	case 3: m = "STR_MAR"; break;
123 	case 4: m = "STR_APR"; break;
124 	case 5: m = "STR_MAY"; break;
125 	case 6: m = "STR_JUN"; break;
126 	case 7: m = "STR_JUL"; break;
127 	case 8: m = "STR_AUG"; break;
128 	case 9: m = "STR_SEP"; break;
129 	case 10: m = "STR_OCT"; break;
130 	case 11: m = "STR_NOV"; break;
131 	case 12: m = "STR_DEC"; break;
132 	default: m = "";
133 	}
134 	int difficulty_threshold = 100*((int)(_game->getSavedGame()->getDifficulty())-9);
135 
136 	_txtMonth->setColor(Palette::blockOffset(15)-1);
137 	_txtMonth->setSecondaryColor(Palette::blockOffset(8)+10);
138 	_txtMonth->setText(tr("STR_MONTH").arg(tr(m)).arg(year));
139 
140 	// Calculate rating
141 	std::wstring rating = tr("STR_RATING_TERRIBLE");
142 	if (_ratingTotal > difficulty_threshold-300)
143 	{
144 		rating = tr("STR_RATING_POOR");
145 	}
146 	if (_ratingTotal > difficulty_threshold)
147 	{
148 		rating = tr("STR_RATING_OK");
149 	}
150 	if (_ratingTotal > 0)
151 	{
152 		rating = tr("STR_RATING_GOOD");
153 	}
154 	if (_ratingTotal > 500)
155 	{
156 		rating = tr("STR_RATING_EXCELLENT");
157 	}
158 
159 	_txtRating->setColor(Palette::blockOffset(15)-1);
160 	_txtRating->setSecondaryColor(Palette::blockOffset(8)+10);
161 	_txtRating->setText(tr("STR_MONTHLY_RATING").arg(_ratingTotal).arg(rating));
162 
163 	std::wostringstream ss3;
164 	if (_fundingDiff > 0)
165 		ss3 << '+';
166 	ss3 << Text::formatFunding(_fundingDiff);
167 
168 	_txtChange->setColor(Palette::blockOffset(15)-1);
169 	_txtChange->setSecondaryColor(Palette::blockOffset(8)+10);
170 	_txtChange->setText(tr("STR_FUNDING_CHANGE").arg(ss3.str()));
171 
172 	_txtDesc->setColor(Palette::blockOffset(8)+10);
173 	_txtDesc->setWordWrap(true);
174 
175 	// calculate satisfaction
176 	std::wostringstream ss4;
177 	std::wstring satisFactionString = tr("STR_COUNCIL_IS_DISSATISFIED");
178 	bool resetWarning = true;
179 	if (_ratingTotal > difficulty_threshold)
180 	{
181 		satisFactionString = tr("STR_COUNCIL_IS_GENERALLY_SATISFIED");
182 	}
183 	if (_ratingTotal > 500)
184 	{
185 		satisFactionString = tr("STR_COUNCIL_IS_VERY_PLEASED");
186 	}
187 	if (_lastMonthsRating <= difficulty_threshold && _ratingTotal <= difficulty_threshold)
188 	{
189 		satisFactionString = tr("STR_YOU_HAVE_NOT_SUCCEEDED");
190 		_pactList.erase(_pactList.begin(), _pactList.end());
191 		_happyList.erase(_happyList.begin(), _happyList.end());
192 		_sadList.erase(_sadList.begin(), _sadList.end());
193 		_gameOver = true;
194 	}
195 	ss4 << satisFactionString;
196 
197 	if (!_gameOver)
198 	{
199 		if (_game->getSavedGame()->getFunds() <= -1000000)
200 		{
201 			if (_game->getSavedGame()->getWarned())
202 			{
203 				ss4.str(L"");
204 				ss4 << tr("STR_YOU_HAVE_NOT_SUCCEEDED");
205 				_pactList.erase(_pactList.begin(), _pactList.end());
206 				_happyList.erase(_happyList.begin(), _happyList.end());
207 				_sadList.erase(_sadList.begin(), _sadList.end());
208 				_gameOver = true;
209 			}
210 			else
211 			{
212 				ss4 << "\n\n" << tr("STR_COUNCIL_REDUCE_DEBTS");
213 				_game->getSavedGame()->setWarned(true);
214 				resetWarning = false;
215 			}
216 		}
217 	}
218 
219 	if (resetWarning && _game->getSavedGame()->getWarned())
220 		_game->getSavedGame()->setWarned(false);
221 
222 	ss4 << countryList(_happyList, "STR_COUNTRY_IS_PARTICULARLY_PLEASED", "STR_COUNTRIES_ARE_PARTICULARLY_HAPPY");
223 	ss4 << countryList(_sadList, "STR_COUNTRY_IS_UNHAPPY_WITH_YOUR_ABILITY", "STR_COUNTRIES_ARE_UNHAPPY_WITH_YOUR_ABILITY");
224 	ss4 << countryList(_pactList, "STR_COUNTRY_HAS_SIGNED_A_SECRET_PACT", "STR_COUNTRIES_HAVE_SIGNED_A_SECRET_PACT");
225 
226 	_txtDesc->setText(ss4.str());
227 }
228 
229 
230 /**
231  *
232  */
~MonthlyReportState()233 MonthlyReportState::~MonthlyReportState()
234 {
235 
236 }
237 
238 /**
239  * Returns to the previous screen.
240  * @param action Pointer to an action.
241  */
btnOkClick(Action *)242 void MonthlyReportState::btnOkClick(Action *)
243 {
244 	if (!_gameOver)
245 	{
246 		_game->popState();
247 		if (_psi)
248 		{
249 			_game->pushState(new PsiTrainingState(_game));
250 		}
251 		// Autosave
252 		if (_game->getSavedGame()->isIronman())
253 		{
254 			_game->pushState(new SaveGameState(_game, OPT_GEOSCAPE, SAVE_IRONMAN));
255 		}
256 		else if (Options::autosave)
257 		{
258 			_game->pushState(new SaveGameState(_game, OPT_GEOSCAPE, SAVE_AUTO_GEOSCAPE));
259 		}
260 	}
261 	else
262 	{
263 		if (_txtFailure->getVisible())
264 		{
265 			_game->popState();
266 			_game->pushState(new DefeatState(_game));
267 		}
268 		else
269 		{
270 			_window->setColor(Palette::blockOffset(8)+10);
271 			_txtTitle->setVisible(false);
272 			_txtMonth->setVisible(false);
273 			_txtRating->setVisible(false);
274 			_txtChange->setVisible(false);
275 			_txtDesc->setVisible(false);
276 			_btnOk->setVisible(false);
277 			_btnBigOk->setVisible(true);
278 			_txtFailure->setVisible(true);
279 			_game->getResourcePack()->playMusic("GMLOSE");
280 		}
281 	}
282 }
283 
284 /**
285  * Update all our activity counters, gather all our scores,
286  * get our countries to make sign pacts, adjust their fundings,
287  * assess their satisfaction, and finally calculate our overall
288  * total score, with thanks to Volutar for the formulas.
289  */
calculateChanges()290 void MonthlyReportState::calculateChanges()
291 {
292 	// initialize all our variables.
293 	_lastMonthsRating = 0;
294 	int xcomSubTotal = 0;
295 	int xcomTotal = 0;
296 	int alienTotal = 0;
297 	int monthOffset = _game->getSavedGame()->getFundsList().size() - 2;
298 	int lastMonthOffset = _game->getSavedGame()->getFundsList().size() - 3;
299 	if (lastMonthOffset < 0)
300 		lastMonthOffset += 2;
301 
302 	// update activity meters, calculate a total score based on regional activity
303 	// and gather last month's score
304 	for (std::vector<Region*>::iterator k = _game->getSavedGame()->getRegions()->begin(); k != _game->getSavedGame()->getRegions()->end(); ++k)
305 	{
306 		(*k)->newMonth();
307 		if ((*k)->getActivityXcom().size() > 2)
308 			_lastMonthsRating += (*k)->getActivityXcom().at(lastMonthOffset)-(*k)->getActivityAlien().at(lastMonthOffset);
309 		xcomSubTotal += (*k)->getActivityXcom().at(monthOffset);
310 		alienTotal += (*k)->getActivityAlien().at(monthOffset);
311 	}
312 
313 	// apply research bonus AFTER calculating our total, because this bonus applies to the council ONLY,
314 	// and shouldn't influence each country's decision.
315 
316 	// the council is more lenient after the first month
317 	if (_game->getSavedGame()->getMonthsPassed() > 1)
318 		_game->getSavedGame()->getResearchScores().at(monthOffset) += 400;
319 
320 	xcomTotal = _game->getSavedGame()->getResearchScores().at(monthOffset) + xcomSubTotal;
321 
322 
323 	if (_game->getSavedGame()->getResearchScores().size() > 2)
324 		_lastMonthsRating += _game->getSavedGame()->getResearchScores().at(lastMonthOffset);
325 
326 
327 	// now that we have our totals we can send the relevant info to the countries
328 	// and have them make their decisions weighted on the council's perspective.
329 	for (std::vector<Country*>::iterator k = _game->getSavedGame()->getCountries()->begin(); k != _game->getSavedGame()->getCountries()->end(); ++k)
330 	{
331 		// add them to the list of new pact members
332 		// this is done BEFORE initiating a new month
333 		// because the _newPact flag will be reset in the
334 		// process
335 		if ((*k)->getNewPact())
336 		{
337 			_pactList.push_back((*k)->getRules()->getType());
338 		}
339 
340 		// determine satisfaction level, sign pacts, adjust funding
341 		// and update activity meters,
342 		(*k)->newMonth(xcomTotal, alienTotal);
343 
344 		// and after they've made their decisions, calculate the difference, and add
345 		// them to the appropriate lists.
346 		_fundingDiff += (*k)->getFunding().back()-(*k)->getFunding().at((*k)->getFunding().size()-2);
347 		switch((*k)->getSatisfaction())
348 		{
349 		case 1:
350 			_sadList.push_back((*k)->getRules()->getType());
351 			break;
352 		case 3:
353 			_happyList.push_back((*k)->getRules()->getType());
354 			break;
355 		default:
356 			break;
357 		}
358 	}
359 
360 	//calculate total.
361 	_ratingTotal = xcomTotal - alienTotal;
362 }
363 
364 /**
365  * Builds a sentence from a list of countries, adding the appropriate
366  * separators and pluralization.
367  * @param countries List of country string IDs.
368  * @param singular String ID to append at the end if the list is singular.
369  * @param plural String ID to append at the end if the list is plural.
370  */
countryList(const std::vector<std::string> & countries,const std::string & singular,const std::string & plural)371 std::wstring MonthlyReportState::countryList(const std::vector<std::string> &countries, const std::string &singular, const std::string &plural)
372 {
373 	std::wostringstream ss;
374 	if (!countries.empty())
375 	{
376 		ss << "\n\n";
377 		if (countries.size() == 1)
378 		{
379 			ss << tr(singular).arg(tr(countries.front()));
380 		}
381 		else
382 		{
383 			LocalizedText list = tr(countries.front());
384 			std::vector<std::string>::const_iterator i;
385 			for (i = countries.begin() + 1; i < countries.end() - 1; ++i)
386 			{
387 				list = tr("STR_COUNTRIES_COMMA").arg(list).arg(tr(*i));
388 			}
389 			list = tr("STR_COUNTRIES_AND").arg(list).arg(tr(*i));
390 			ss << tr(plural).arg(list);
391 		}
392 	}
393 	return ss.str();
394 }
395 
396 }
397