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