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 #include "ResearchInfoState.h"
20 #include "../Engine/Action.h"
21 #include "../Engine/Game.h"
22 #include "../Resource/ResourcePack.h"
23 #include "../Engine/Language.h"
24 #include "../Engine/Palette.h"
25 #include "../Engine/Options.h"
26 #include "../Interface/TextButton.h"
27 #include "../Interface/Window.h"
28 #include "../Interface/Text.h"
29 #include "../Savegame/Base.h"
30 #include "../Ruleset/RuleResearch.h"
31 #include "../Ruleset/Ruleset.h"
32 #include "../Savegame/ItemContainer.h"
33 #include "../Savegame/ResearchProject.h"
34 #include "../Interface/ArrowButton.h"
35 #include "../Engine/Timer.h"
36 #include "../Engine/RNG.h"
37 #include <sstream>
38 #include <limits>
39 
40 namespace OpenXcom
41 {
42 
43 /**
44  * Initializes all the elements in the ResearchProject screen.
45  * @param game Pointer to the core game.
46  * @param base Pointer to the base to get info from.
47  * @param rule A RuleResearch which will be used to create a new ResearchProject
48  */
ResearchInfoState(Game * game,Base * base,RuleResearch * rule)49 ResearchInfoState::ResearchInfoState(Game *game, Base *base, RuleResearch * rule) : State(game), _base(base), _project(new ResearchProject(rule, int(rule->getCost() * OpenXcom::RNG::generate(50, 150)/100))), _rule(rule)
50 {
51 	buildUi();
52 }
53 
54 /**
55  * Initializes all the elements in the ResearchProject screen.
56  * @param game Pointer to the core game.
57  * @param base Pointer to the base to get info from.
58  * @param project A ResearchProject to modify
59  */
ResearchInfoState(Game * game,Base * base,ResearchProject * project)60 ResearchInfoState::ResearchInfoState(Game *game, Base *base, ResearchProject * project) : State(game), _base(base), _project(project), _rule(0)
61 {
62 	buildUi();
63 }
64 
65 /**
66  * Builds dialog.
67  */
buildUi()68 void ResearchInfoState::buildUi()
69 {
70 	_screen = false;
71 
72 	_window = new Window(this, 230, 140, 45, 30);
73 	_txtTitle = new Text(210, 17, 61, 40);
74 
75 	_txtAvailableScientist = new Text(210, 9, 61, 60);
76 	_txtAvailableSpace = new Text(210, 9, 61, 70);
77 	_txtAllocatedScientist = new Text(210, 17, 61, 80);
78 	_txtMore = new Text(110, 17, 85, 100);
79 	_txtLess = new Text(110, 17, 85, 120);
80 	_btnCancel = new TextButton(90, 16, 61, 145);
81 	_btnOk = new TextButton(83, 16, 169, 145);
82 
83 	_btnMore = new ArrowButton(ARROW_BIG_UP, 13, 14, 195, 100);
84 	_btnLess = new ArrowButton(ARROW_BIG_DOWN, 13, 14, 195, 120);
85 
86 	_surfaceScientists = new InteractiveSurface(230, 140, 45, 30);
87 	_surfaceScientists->onMouseClick((ActionHandler)&ResearchInfoState::handleWheel, 0);
88 
89 	// Set palette
90 	setPalette("PAL_BASESCAPE", 1);
91 
92 	add(_surfaceScientists);
93 	add(_window);
94 	add(_btnOk);
95 	add(_btnCancel);
96 	add(_txtTitle);
97 	add(_txtAvailableScientist);
98 	add(_txtAvailableSpace);
99 	add(_txtAllocatedScientist);
100 	add(_txtMore);
101 	add(_txtLess);
102 	add(_btnMore);
103 	add(_btnLess);
104 
105 	centerAllSurfaces();
106 
107 	// Set up objects
108 	_window->setColor(Palette::blockOffset(13)+5);
109 	_window->setBackground(_game->getResourcePack()->getSurface("BACK05.SCR"));
110 	_txtTitle->setColor(Palette::blockOffset(13)+5);
111 	_txtTitle->setBig();
112 
113 	_txtTitle->setText(_rule ? tr(_rule->getName()) : tr(_project->getRules ()->getName()));
114 
115 	_txtAvailableScientist->setColor(Palette::blockOffset(13)+5);
116 	_txtAvailableScientist->setSecondaryColor(Palette::blockOffset(13));
117 
118 	_txtAvailableSpace->setColor(Palette::blockOffset(13)+5);
119 	_txtAvailableSpace->setSecondaryColor(Palette::blockOffset(13));
120 
121 	_txtAllocatedScientist->setColor(Palette::blockOffset(13)+5);
122 	_txtAllocatedScientist->setSecondaryColor(Palette::blockOffset(13));
123 	_txtAllocatedScientist->setBig();
124 
125 	_txtMore->setText(tr("STR_INCREASE"));
126 	_txtLess->setText(tr("STR_DECREASE"));
127 
128 	_txtMore->setColor(Palette::blockOffset(13)+5);
129 	_txtLess->setColor(Palette::blockOffset(13)+5);
130 
131 	_txtMore->setBig();
132 	_txtLess->setBig();
133 
134 	if (_rule)
135 	{
136 		_base->addResearch(_project);
137 		if (_rule->needItem() &&
138 				(_game->getRuleset()->getUnit(_rule->getName()) ||
139 				 Options::spendResearchedItems))
140 		{
141 			_base->getItems()->removeItem(_rule->getName(), 1);
142 		}
143 	}
144 	setAssignedScientist();
145 	_btnMore->setColor(Palette::blockOffset(13)+5);
146 	_btnLess->setColor(Palette::blockOffset(13)+5);
147 	_btnMore->onMousePress((ActionHandler)&ResearchInfoState::morePress);
148 	_btnMore->onMouseRelease((ActionHandler)&ResearchInfoState::moreRelease);
149 	_btnMore->onMouseClick((ActionHandler)&ResearchInfoState::moreClick, 0);
150 	_btnLess->onMousePress((ActionHandler)&ResearchInfoState::lessPress);
151 	_btnLess->onMouseRelease((ActionHandler)&ResearchInfoState::lessRelease);
152 	_btnLess->onMouseClick((ActionHandler)&ResearchInfoState::lessClick, 0);
153 
154 	_timerMore = new Timer(250);
155 	_timerMore->onTimer((StateHandler)&ResearchInfoState::more);
156 	_timerLess = new Timer(250);
157 	_timerLess->onTimer((StateHandler)&ResearchInfoState::less);
158 
159 	_btnOk->setColor(Palette::blockOffset(13)+10);
160 	_btnOk->onMouseClick((ActionHandler)&ResearchInfoState::btnOkClick);
161 	_btnOk->onKeyboardPress((ActionHandler)&ResearchInfoState::btnOkClick, Options::keyOk);
162 	_btnCancel->setColor(Palette::blockOffset(13)+10);
163 	if (_rule)
164 	{
165 		_btnOk->setText(tr("STR_START_PROJECT"));
166 		_btnCancel->setText(tr("STR_CANCEL_UC"));
167 		_btnCancel->onKeyboardPress((ActionHandler)&ResearchInfoState::btnCancelClick, Options::keyCancel);
168 	}
169 	else
170 	{
171 		_btnOk->setText(tr("STR_OK"));
172 		_btnCancel->setText(tr("STR_CANCEL_PROJECT"));
173 		_btnOk->onKeyboardPress((ActionHandler)&ResearchInfoState::btnOkClick, Options::keyCancel);
174 	}
175 	_btnCancel->onMouseClick((ActionHandler)&ResearchInfoState::btnCancelClick);
176 }
177 
178 /**
179  * Returns to the previous screen.
180  * @param action Pointer to an action.
181  */
btnOkClick(Action *)182 void ResearchInfoState::btnOkClick(Action *)
183 {
184 	_game->popState();
185 }
186 
187 /**
188  * Returns to the previous screen, removing the current project from the active
189  * research list.
190  * @param action Pointer to an action.
191  */
btnCancelClick(Action *)192 void ResearchInfoState::btnCancelClick(Action *)
193 {
194 	const RuleResearch *ruleResearch = _rule ? _rule : _project->getRules();
195 	if (ruleResearch->needItem() &&
196 			(_game->getRuleset()->getUnit(ruleResearch->getName()) ||
197 			 Options::spendResearchedItems))
198 	{
199 		_base->getItems()->addItem(ruleResearch->getName(), 1);
200 	}
201 	_base->removeResearch(_project);
202 	_game->popState();
203 }
204 
205 /**
206  * Updates count of assigned/free scientists and available lab space.
207  */
setAssignedScientist()208 void ResearchInfoState::setAssignedScientist()
209 {
210 	_txtAvailableScientist->setText(tr("STR_SCIENTISTS_AVAILABLE_UC").arg(_base->getAvailableScientists()));
211 	_txtAvailableSpace->setText(tr("STR_LABORATORY_SPACE_AVAILABLE_UC").arg(_base->getFreeLaboratories()));
212 	_txtAllocatedScientist->setText(tr("STR_SCIENTISTS_ALLOCATED").arg(_project->getAssigned()));
213 }
214 
215 /**
216  * Increases or decreases the scientists according the mouse-wheel used.
217  * @param action Pointer to an Action.
218  */
handleWheel(Action * action)219 void ResearchInfoState::handleWheel(Action *action)
220 {
221 	if (action->getDetails()->button.button == SDL_BUTTON_WHEELUP) moreByValue(Options::changeValueByMouseWheel);
222 	else if (action->getDetails()->button.button == SDL_BUTTON_WHEELDOWN) lessByValue(Options::changeValueByMouseWheel);
223 }
224 
225 /**
226  * Starts the timeMore timer.
227  * @param action Pointer to an Action.
228  */
morePress(Action * action)229 void ResearchInfoState::morePress(Action *action)
230 {
231 	if (action->getDetails()->button.button == SDL_BUTTON_LEFT) _timerMore->start();
232 }
233 
234 /**
235  * Stops the timeMore timer.
236  * @param action Pointer to an Action.
237  */
moreRelease(Action * action)238 void ResearchInfoState::moreRelease(Action *action)
239 {
240 	if (action->getDetails()->button.button == SDL_BUTTON_LEFT)
241 	{
242 		_timerMore->setInterval(250);
243 		_timerMore->stop();
244 	}
245 }
246 
247 /**
248  * Allocates scientists to the current project;
249  * one scientist on left-click, all scientists on right-click.
250  * @param action Pointer to an Action.
251  */
moreClick(Action * action)252 void ResearchInfoState::moreClick(Action *action)
253 {
254 	if (action->getDetails()->button.button == SDL_BUTTON_RIGHT)
255 		moreByValue(std::numeric_limits<int>::max());
256 	if (action->getDetails()->button.button == SDL_BUTTON_LEFT)
257 		moreByValue(1);
258 }
259 
260 /**
261  * Starts the timeLess timer.
262  * @param action Pointer to an Action.
263  */
lessPress(Action * action)264 void ResearchInfoState::lessPress(Action *action)
265 {
266 	if (action->getDetails()->button.button == SDL_BUTTON_LEFT) _timerLess->start ();
267 }
268 
269 /**
270  * Stops the timeLess timer.
271  * @param action Pointer to an Action.
272  */
lessRelease(Action * action)273 void ResearchInfoState::lessRelease(Action *action)
274 {
275 	if (action->getDetails()->button.button == SDL_BUTTON_LEFT)
276 	{
277 		_timerLess->setInterval(250);
278 		_timerLess->stop ();
279 	}
280 }
281 
282 /**
283  * Removes scientists from the current project;
284  * one scientist on left-click, all scientists on right-click.
285  * @param action Pointer to an Action.
286  */
lessClick(Action * action)287 void ResearchInfoState::lessClick(Action *action)
288 {
289 	if (action->getDetails()->button.button == SDL_BUTTON_RIGHT)
290 		lessByValue(std::numeric_limits<int>::max());
291 	if (action->getDetails()->button.button == SDL_BUTTON_LEFT)
292 		lessByValue(1);
293 }
294 
295 /**
296  * Adds one scientist to the project if possible.
297  */
more()298 void ResearchInfoState::more()
299 {
300 	_timerMore->setInterval(50);
301 	moreByValue(1);
302 }
303 
304 /**
305  * Adds the given number of scientists to the project if possible.
306  * @param change Number of scientists to add.
307  */
moreByValue(int change)308 void ResearchInfoState::moreByValue(int change)
309 {
310 	if (0 >= change) return;
311 	int freeScientist = _base->getAvailableScientists();
312 	int freeSpaceLab = _base->getFreeLaboratories();
313 	if (freeScientist > 0 && freeSpaceLab > 0)
314 	{
315 		change = std::min(std::min(freeScientist, freeSpaceLab), change);
316 		_project->setAssigned(_project->getAssigned()+change);
317 		_base->setScientists(_base->getScientists()-change);
318 		setAssignedScientist();
319 	}
320 }
321 
322 /**
323  * Removes one scientist from the project if possible.
324  */
less()325 void ResearchInfoState::less()
326 {
327 	_timerLess->setInterval(50);
328 	lessByValue(1);
329 }
330 
331 /**
332  * Removes the given number of scientists from the project if possible.
333  * @param change Number of scientists to subtract.
334  */
lessByValue(int change)335 void ResearchInfoState::lessByValue(int change)
336 {
337 	if (0 >= change) return;
338 	int assigned = _project->getAssigned();
339 	if (assigned > 0)
340 	{
341 		change = std::min(assigned, change);
342 		_project->setAssigned(assigned-change);
343 		_base->setScientists(_base->getScientists()+change);
344 		setAssignedScientist();
345 	}
346 }
347 
348 /**
349  * Runs state functionality every cycle (used to update the timer).
350  */
think()351 void ResearchInfoState::think()
352 {
353 	State::think();
354 
355 	_timerLess->think (this, 0);
356 	_timerMore->think (this, 0);
357 }
358 }
359