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 "SellState.h"
20 #include <sstream>
21 #include <climits>
22 #include <cmath>
23 #include <iomanip>
24 #include "../Engine/Action.h"
25 #include "../Engine/Game.h"
26 #include "../Resource/ResourcePack.h"
27 #include "../Engine/Language.h"
28 #include "../Engine/Palette.h"
29 #include "../Interface/TextButton.h"
30 #include "../Interface/Window.h"
31 #include "../Interface/Text.h"
32 #include "../Interface/TextList.h"
33 #include "../Savegame/BaseFacility.h"
34 #include "../Savegame/SavedGame.h"
35 #include "../Savegame/Base.h"
36 #include "../Savegame/Soldier.h"
37 #include "../Savegame/Craft.h"
38 #include "../Savegame/ItemContainer.h"
39 #include "../Ruleset/Ruleset.h"
40 #include "../Ruleset/RuleItem.h"
41 #include "../Ruleset/Armor.h"
42 #include "../Ruleset/RuleCraft.h"
43 #include "../Savegame/CraftWeapon.h"
44 #include "../Savegame/Transfer.h"
45 #include "../Ruleset/RuleCraftWeapon.h"
46 #include "../Engine/Timer.h"
47 #include "../Engine/Options.h"
48 
49 namespace OpenXcom
50 {
51 
52 /**
53  * Initializes all the elements in the Sell/Sack screen.
54  * @param game Pointer to the core game.
55  * @param base Pointer to the base to get info from.
56  * @param origin Game section that originated this state.
57  */
SellState(Game * game,Base * base,OptionsOrigin origin)58 SellState::SellState(Game *game, Base *base, OptionsOrigin origin) : State(game), _base(base), _qtys(), _soldiers(), _crafts(), _items(), _sel(0), _itemOffset(0), _total(0), _hasSci(0), _hasEng(0), _spaceChange(0), _origin(origin)
59 {
60 	bool overfull = Options::storageLimitsEnforced && _base->storesOverfull();
61 
62 	// Create objects
63 	_window = new Window(this, 320, 200, 0, 0);
64 	_btnOk = new TextButton(overfull? 288:148, 16, overfull? 16:8, 176);
65 	_btnCancel = new TextButton(148, 16, 164, 176);
66 	_txtTitle = new Text(310, 17, 5, 8);
67 	_txtSales = new Text(150, 9, 10, 24);
68 	_txtFunds = new Text(150, 9, 160, 24);
69 	_txtSpaceUsed = new Text(150, 9, 160, 34);
70 	_txtItem = new Text(130, 9, 10, Options::storageLimitsEnforced? 44:33);
71 	_txtQuantity = new Text(54, 9, 126, Options::storageLimitsEnforced? 44:33);
72 	_txtSell = new Text(96, 9, 180, Options::storageLimitsEnforced? 44:33);
73 	_txtValue = new Text(40, 9, 260, Options::storageLimitsEnforced? 44:33);
74 	_lstItems = new TextList(287, Options::storageLimitsEnforced? 112:120, 8, Options::storageLimitsEnforced? 55:44);
75 
76 	// Set palette
77 	if (origin == OPT_BATTLESCAPE)
78 	{
79 		setPalette("PAL_GEOSCAPE", 0);
80 		_color  = Palette::blockOffset(15)-1;
81 		_color2 = Palette::blockOffset(8)+10;
82 		_color3 = Palette::blockOffset(8)+5;
83 		_colorAmmo = Palette::blockOffset(15)+6;
84 	}
85 	else
86 	{
87 		setPalette("PAL_BASESCAPE", 0);
88 		_color  = Palette::blockOffset(13)+10;
89 		_color2 = Palette::blockOffset(13);
90 		_color3 = Palette::blockOffset(13)+5;
91 		_colorAmmo = Palette::blockOffset(15)+6;
92 	}
93 
94 	add(_window);
95 	add(_btnOk);
96 	add(_btnCancel);
97 	add(_txtTitle);
98 	add(_txtSales);
99 	add(_txtFunds);
100 	add(_txtSpaceUsed);
101 	add(_txtItem);
102 	add(_txtQuantity);
103 	add(_txtSell);
104 	add(_txtValue);
105 	add(_lstItems);
106 
107 	centerAllSurfaces();
108 
109 	// Set up objects
110 	_window->setColor(_color);
111 	_window->setBackground(_game->getResourcePack()->getSurface("BACK13.SCR"));
112 
113 	_btnOk->setColor(_color);
114 	_btnOk->setText(tr("STR_SELL_SACK"));
115 	_btnOk->onMouseClick((ActionHandler)&SellState::btnOkClick);
116 	_btnOk->onKeyboardPress((ActionHandler)&SellState::btnOkClick, Options::keyOk);
117 
118 	_btnCancel->setColor(_color);
119 	_btnCancel->setText(tr("STR_CANCEL"));
120 	_btnCancel->onMouseClick((ActionHandler)&SellState::btnCancelClick);
121 	_btnCancel->onKeyboardPress((ActionHandler)&SellState::btnCancelClick, Options::keyCancel);
122 
123 	if (overfull)
124 	{
125 		_btnCancel->setVisible(false);
126 		_btnOk->setVisible(false);
127 	}
128 
129 	_txtTitle->setColor(_color);
130 	_txtTitle->setBig();
131 	_txtTitle->setAlign(ALIGN_CENTER);
132 	_txtTitle->setText(tr("STR_SELL_ITEMS_SACK_PERSONNEL"));
133 
134 	_txtSales->setColor(_color);
135 	_txtSales->setSecondaryColor(_color2);
136 	_txtSales->setText(tr("STR_VALUE_OF_SALES").arg(Text::formatFunding(_total)));
137 
138 	_txtFunds->setColor(_color);
139 	_txtFunds->setSecondaryColor(_color2);
140 	_txtFunds->setText(tr("STR_FUNDS").arg(Text::formatFunding(_game->getSavedGame()->getFunds())));
141 
142 	_txtSpaceUsed->setColor(_color);
143 	_txtSpaceUsed->setSecondaryColor(_color2);
144 	_txtSpaceUsed->setVisible(Options::storageLimitsEnforced);
145 
146 	std::wostringstream ss5;
147 	ss5 << _base->getUsedStores() << ":" << _base->getAvailableStores();
148 	_txtSpaceUsed->setText(ss5.str());
149 	_txtSpaceUsed->setText(tr("STR_SPACE_USED").arg(ss5.str()));
150 
151 	_txtItem->setColor(_color);
152 	_txtItem->setText(tr("STR_ITEM"));
153 
154 	_txtQuantity->setColor(_color);
155 	_txtQuantity->setText(tr("STR_QUANTITY_UC"));
156 
157 	_txtSell->setColor(_color);
158 	_txtSell->setText(tr("STR_SELL_SACK"));
159 
160 	_txtValue->setColor(_color);
161 	_txtValue->setText(tr("STR_VALUE"));
162 
163 	_lstItems->setColor(_color);
164 	_lstItems->setArrowColumn(182, ARROW_VERTICAL);
165 	_lstItems->setColumns(4, 156, 54, 24, 53);
166 	_lstItems->setSelectable(true);
167 	_lstItems->setBackground(_window);
168 	_lstItems->setMargin(2);
169 	_lstItems->onLeftArrowPress((ActionHandler)&SellState::lstItemsLeftArrowPress);
170 	_lstItems->onLeftArrowRelease((ActionHandler)&SellState::lstItemsLeftArrowRelease);
171 	_lstItems->onLeftArrowClick((ActionHandler)&SellState::lstItemsLeftArrowClick);
172 	_lstItems->onRightArrowPress((ActionHandler)&SellState::lstItemsRightArrowPress);
173 	_lstItems->onRightArrowRelease((ActionHandler)&SellState::lstItemsRightArrowRelease);
174 	_lstItems->onRightArrowClick((ActionHandler)&SellState::lstItemsRightArrowClick);
175 	_lstItems->onMousePress((ActionHandler)&SellState::lstItemsMousePress);
176 
177 	for (std::vector<Soldier*>::iterator i = _base->getSoldiers()->begin(); i != _base->getSoldiers()->end(); ++i)
178 	{
179 		if ((*i)->getCraft() == 0)
180 		{
181 			_qtys.push_back(0);
182 			_soldiers.push_back(*i);
183 			_lstItems->addRow(4, (*i)->getName(true).c_str(), L"1", L"0", Text::formatFunding(0).c_str());
184 			++_itemOffset;
185 		}
186 	}
187 	for (std::vector<Craft*>::iterator i = _base->getCrafts()->begin(); i != _base->getCrafts()->end(); ++i)
188 	{
189 		if ((*i)->getStatus() != "STR_OUT")
190 		{
191 			_qtys.push_back(0);
192 			_crafts.push_back(*i);
193 			_lstItems->addRow(4, (*i)->getName(_game->getLanguage()).c_str(), L"1", L"0", Text::formatFunding((*i)->getRules()->getSellCost()).c_str());
194 			++_itemOffset;
195 		}
196 	}
197 	if (_base->getAvailableScientists() > 0)
198 	{
199 		_qtys.push_back(0);
200 		_hasSci = 1;
201 		std::wostringstream ss;
202 		ss << _base->getAvailableScientists();
203 		_lstItems->addRow(4, tr("STR_SCIENTIST").c_str(), ss.str().c_str(), L"0", Text::formatFunding(0).c_str());
204 		++_itemOffset;
205 	}
206 	if (_base->getAvailableEngineers() > 0)
207 	{
208 		_qtys.push_back(0);
209 		_hasEng = 1;
210 		std::wostringstream ss;
211 		ss << _base->getAvailableEngineers();
212 		_lstItems->addRow(4, tr("STR_ENGINEER").c_str(), ss.str().c_str(), L"0", Text::formatFunding(0).c_str());
213 		++_itemOffset;
214 	}
215 	const std::vector<std::string> &items = _game->getRuleset()->getItemsList();
216 	for (std::vector<std::string>::const_iterator i = items.begin(); i != items.end(); ++i)
217 	{
218 		int qty = _base->getItems()->getItem(*i);
219 		if (Options::storageLimitsEnforced && origin == OPT_BATTLESCAPE)
220 		{
221 			for (std::vector<Transfer*>::iterator j = _base->getTransfers()->begin(); j != _base->getTransfers()->end(); ++j)
222 			{
223 				if ((*j)->getItems() == *i)
224 				{
225 					qty += (*j)->getQuantity();
226 				}
227 			}
228 			for (std::vector<Craft*>::iterator j = _base->getCrafts()->begin(); j != _base->getCrafts()->end(); ++j)
229 			{
230 				qty += (*j)->getItems()->getItem(*i);
231 			}
232 		}
233 		if (qty > 0 && (Options::canSellLiveAliens || !_game->getRuleset()->getItem(*i)->getAlien()))
234 		{
235 			_qtys.push_back(0);
236 			_items.push_back(*i);
237 			RuleItem *rule = _game->getRuleset()->getItem(*i);
238 			std::wostringstream ss;
239 			ss << qty;
240 			std::wstring item = tr(*i);
241 			if (rule->getBattleType() == BT_AMMO || (rule->getBattleType() == BT_NONE && rule->getClipSize() > 0))
242 			{
243 				item.insert(0, L"  ");
244 				_lstItems->addRow(4, item.c_str(), ss.str().c_str(), L"0", Text::formatFunding(rule->getSellCost()).c_str());
245 				_lstItems->setRowColor(_qtys.size() - 1, _colorAmmo);
246 			}
247 			else
248 			{
249 				_lstItems->addRow(4, item.c_str(), ss.str().c_str(), L"0", Text::formatFunding(rule->getSellCost()).c_str());
250 			}
251 		}
252 	}
253 
254 	_timerInc = new Timer(250);
255 	_timerInc->onTimer((StateHandler)&SellState::increase);
256 	_timerDec = new Timer(250);
257 	_timerDec->onTimer((StateHandler)&SellState::decrease);
258 }
259 
260 /**
261  *
262  */
~SellState()263 SellState::~SellState()
264 {
265 	delete _timerInc;
266 	delete _timerDec;
267 }
268 
269 /**
270  * Runs the arrow timers.
271  */
think()272 void SellState::think()
273 {
274 	State::think();
275 
276 	_timerInc->think(this, 0);
277 	_timerDec->think(this, 0);
278 }
279 
280 /**
281  * Gets the index of selected craft.
282  * @param selected Selected craft.
283  * @return Index of the selected craft.
284  */
getCraftIndex(size_t selected) const285 size_t SellState::getCraftIndex(size_t selected) const
286 {
287 	return selected - _soldiers.size();
288 }
289 
290 /**
291  * Sells the selected items.
292  * @param action Pointer to an action.
293  */
btnOkClick(Action *)294 void SellState::btnOkClick(Action *)
295 {
296 	_game->getSavedGame()->setFunds(_game->getSavedGame()->getFunds() + _total);
297 	for (size_t i = 0; i < _qtys.size(); ++i)
298 	{
299 		if (_qtys[i] > 0)
300 		{
301 			switch (getType(i))
302 			{
303 			case SELL_SOLDIER:
304 				for (std::vector<Soldier*>::iterator s = _base->getSoldiers()->begin(); s != _base->getSoldiers()->end(); ++s)
305 				{
306 					if (*s == _soldiers[i])
307 					{
308 						if((*s)->getArmor()->getStoreItem() != "STR_NONE")
309 						{
310 							_base->getItems()->addItem((*s)->getArmor()->getStoreItem());
311 						}
312 						_base->getSoldiers()->erase(s);
313 						break;
314 					}
315 				}
316 				delete _soldiers[i];
317 				break;
318 
319 			case SELL_CRAFT:
320 				{
321 				Craft *craft = _crafts[getCraftIndex(i)];
322 
323 				// Remove weapons from craft
324 				for (std::vector<CraftWeapon*>::iterator w = craft->getWeapons()->begin(); w != craft->getWeapons()->end(); ++w)
325 				{
326 					if ((*w) != 0)
327 					{
328 						_base->getItems()->addItem((*w)->getRules()->getLauncherItem());
329 						_base->getItems()->addItem((*w)->getRules()->getClipItem(), (*w)->getClipsLoaded(_game->getRuleset()));
330 					}
331 				}
332 
333 				// Remove items from craft
334 				for (std::map<std::string, int>::iterator it = craft->getItems()->getContents()->begin(); it != craft->getItems()->getContents()->end(); ++it)
335 				{
336 					_base->getItems()->addItem(it->first, it->second);
337 				}
338 
339 				// Remove soldiers from craft
340 				for (std::vector<Soldier*>::iterator s = _base->getSoldiers()->begin(); s != _base->getSoldiers()->end(); ++s)
341 				{
342 					if ((*s)->getCraft() == craft)
343 					{
344 						(*s)->setCraft(0);
345 					}
346 				}
347 
348 				// Clear Hangar
349 				for (std::vector<BaseFacility*>::iterator f = _base->getFacilities()->begin(); f != _base->getFacilities()->end(); ++f)
350 				{
351 					if ((*f)->getCraft() == craft)
352 					{
353 						(*f)->setCraft(0);
354 						break;
355 					}
356 				}
357 
358 				// Remove craft
359 				for (std::vector<Craft*>::iterator c = _base->getCrafts()->begin(); c != _base->getCrafts()->end(); ++c)
360 				{
361 					if (*c == craft)
362 					{
363 						_base->getCrafts()->erase(c);
364 						break;
365 					}
366 				}
367 				delete craft;
368 				}
369 				break;
370 
371 			case SELL_SCIENTIST:
372 				_base->setScientists(_base->getScientists() - _qtys[i]);
373 				break;
374 
375 			case SELL_ENGINEER:
376 				_base->setEngineers(_base->getEngineers() - _qtys[i]);
377 				break;
378 
379 			case SELL_ITEM:
380 				if (_base->getItems()->getItem(_items[getItemIndex(i)]) < _qtys[i])
381 				{
382 					const std::string itemName = _items[getItemIndex(i)];
383 					int toRemove = _qtys[i] - _base->getItems()->getItem(itemName);
384 
385 					// remove all of said items from base
386 					_base->getItems()->removeItem(itemName, INT_MAX);
387 
388 					// if we still need to remove any, remove them from the crafts first, and keep a running tally
389 					for (std::vector<Craft*>::iterator j = _base->getCrafts()->begin(); j != _base->getCrafts()->end() && toRemove; ++j)
390 					{
391 						if ((*j)->getItems()->getItem(itemName) < toRemove)
392 						{
393 							toRemove -= (*j)->getItems()->getItem(itemName);
394 							(*j)->getItems()->removeItem(itemName, INT_MAX);
395 						}
396 						else
397 						{
398 							(*j)->getItems()->removeItem(itemName, toRemove);
399 							toRemove = 0;
400 						}
401 					}
402 
403 					// if there are STILL any left to remove, take them from the transfers, and if necessary, delete it.
404 					for (std::vector<Transfer*>::iterator j = _base->getTransfers()->begin(); j != _base->getTransfers()->end() && toRemove;)
405 					{
406 						if ((*j)->getItems() == itemName)
407 						{
408 							if ((*j)->getQuantity() <= toRemove)
409 							{
410 								toRemove -= (*j)->getQuantity();
411 								delete *j;
412 								j = _base->getTransfers()->erase(j);
413 							}
414 							else
415 							{
416 								(*j)->setItems((*j)->getItems(), (*j)->getQuantity() - toRemove);
417 								toRemove = 0;
418 							}
419 						}
420 						else
421 						{
422 							++j;
423 						}
424 					}
425 				}
426 				else
427 				{
428 					_base->getItems()->removeItem(_items[getItemIndex(i)], _qtys[i]);
429 				}
430 			}
431 		}
432 	}
433 	_game->popState();
434 }
435 
436 /**
437  * Returns to the previous screen.
438  * @param action Pointer to an action.
439  */
btnCancelClick(Action *)440 void SellState::btnCancelClick(Action *)
441 {
442 	_game->popState();
443 }
444 
445 /**
446  * Starts increasing the item.
447  * @param action Pointer to an action.
448  */
lstItemsLeftArrowPress(Action * action)449 void SellState::lstItemsLeftArrowPress(Action *action)
450 {
451 	_sel = _lstItems->getSelectedRow();
452 	if (action->getDetails()->button.button == SDL_BUTTON_LEFT && !_timerInc->isRunning()) _timerInc->start();
453 }
454 
455 /**
456  * Stops increasing the item.
457  * @param action Pointer to an action.
458  */
lstItemsLeftArrowRelease(Action * action)459 void SellState::lstItemsLeftArrowRelease(Action *action)
460 {
461 	if (action->getDetails()->button.button == SDL_BUTTON_LEFT)
462 	{
463 		_timerInc->stop();
464 	}
465 }
466 
467 /**
468  * Increases the selected item;
469  * by one on left-click, to max on right-click.
470  * @param action Pointer to an action.
471  */
lstItemsLeftArrowClick(Action * action)472 void SellState::lstItemsLeftArrowClick(Action *action)
473 {
474 	if (action->getDetails()->button.button == SDL_BUTTON_RIGHT) changeByValue(INT_MAX, 1);
475 	if (action->getDetails()->button.button == SDL_BUTTON_LEFT)
476 	{
477 		changeByValue(1,1);
478 		_timerInc->setInterval(250);
479 		_timerDec->setInterval(250);
480 	}
481 }
482 
483 /**
484  * Starts decreasing the item.
485  * @param action Pointer to an action.
486  */
lstItemsRightArrowPress(Action * action)487 void SellState::lstItemsRightArrowPress(Action *action)
488 {
489 	_sel = _lstItems->getSelectedRow();
490 	if (action->getDetails()->button.button == SDL_BUTTON_LEFT && !_timerDec->isRunning()) _timerDec->start();
491 }
492 
493 /**
494  * Stops decreasing the item.
495  * @param action Pointer to an action.
496  */
lstItemsRightArrowRelease(Action * action)497 void SellState::lstItemsRightArrowRelease(Action *action)
498 {
499 	if (action->getDetails()->button.button == SDL_BUTTON_LEFT)
500 	{
501 		_timerDec->stop();
502 	}
503 }
504 
505 /**
506  * Decreases the selected item;
507  * by one on left-click, to 0 on right-click.
508  * @param action Pointer to an action.
509  */
lstItemsRightArrowClick(Action * action)510 void SellState::lstItemsRightArrowClick(Action *action)
511 {
512 	if (action->getDetails()->button.button == SDL_BUTTON_RIGHT) changeByValue(INT_MAX, -1);
513 	if (action->getDetails()->button.button == SDL_BUTTON_LEFT)
514 	{
515 		changeByValue(1,-1);
516 		_timerInc->setInterval(250);
517 		_timerDec->setInterval(250);
518 	}
519 }
520 
521 /**
522  * Handles the mouse-wheels on the arrow-buttons.
523  * @param action Pointer to an action.
524  */
lstItemsMousePress(Action * action)525 void SellState::lstItemsMousePress(Action *action)
526 {
527 	_sel = _lstItems->getSelectedRow();
528 	if (action->getDetails()->button.button == SDL_BUTTON_WHEELUP)
529 	{
530 		_timerInc->stop();
531 		_timerDec->stop();
532 		if (action->getAbsoluteXMouse() >= _lstItems->getArrowsLeftEdge() &&
533 			action->getAbsoluteXMouse() <= _lstItems->getArrowsRightEdge())
534 		{
535 			changeByValue(Options::changeValueByMouseWheel, 1);
536 		}
537 	}
538 	else if (action->getDetails()->button.button == SDL_BUTTON_WHEELDOWN)
539 	{
540 		_timerInc->stop();
541 		_timerDec->stop();
542 		if (action->getAbsoluteXMouse() >= _lstItems->getArrowsLeftEdge() &&
543 			action->getAbsoluteXMouse() <= _lstItems->getArrowsRightEdge())
544 		{
545 			changeByValue(Options::changeValueByMouseWheel, -1);
546 		}
547 	}
548 }
549 
550 /**
551  * Gets the price of the currently selected item.
552  * @return Price of the selected item.
553  */
getPrice()554 int SellState::getPrice()
555 {
556 	// Personnel/craft aren't worth anything
557 	switch(getType(_sel))
558 	{
559 	case SELL_SOLDIER:
560 	case SELL_ENGINEER:
561 	case SELL_SCIENTIST:
562 		return 0;
563 	case SELL_ITEM:
564 		return _game->getRuleset()->getItem(_items[getItemIndex(_sel)])->getSellCost();
565 	case SELL_CRAFT:
566 		Craft *craft =  _crafts[getCraftIndex(_sel)];
567 		return craft->getRules()->getSellCost();
568 	}
569     return 0;
570 }
571 
572 /**
573  * Gets the quantity of the currently selected item
574  * on the base.
575  * @return Quantity of selected item on the base.
576  */
getQuantity()577 int SellState::getQuantity()
578 {
579 	int qty = 0;
580 	// Soldiers/crafts are individual
581 	switch(getType(_sel))
582 	{
583 	case SELL_SOLDIER:
584 	case SELL_CRAFT:
585 		return 1;
586 	case SELL_SCIENTIST:
587 		return _base->getAvailableScientists();
588 	case SELL_ENGINEER:
589 		return _base->getAvailableEngineers();
590 	case SELL_ITEM:
591 		qty = _base->getItems()->getItem(_items[getItemIndex(_sel)]);
592 		if (Options::storageLimitsEnforced && _origin == OPT_BATTLESCAPE)
593 		{
594 			for (std::vector<Transfer*>::iterator j = _base->getTransfers()->begin(); j != _base->getTransfers()->end(); ++j)
595 			{
596 				if ((*j)->getItems() == _items[getItemIndex(_sel)])
597 				{
598 					qty += (*j)->getQuantity();
599 				}
600 			}
601 			for (std::vector<Craft*>::iterator j = _base->getCrafts()->begin(); j != _base->getCrafts()->end(); ++j)
602 			{
603 				qty += (*j)->getItems()->getItem(_items[getItemIndex(_sel)]);
604 			}
605 		}
606 		return qty;
607 	}
608 
609 	return 0;
610 }
611 
612 /**
613  * Increases the quantity of the selected item to sell by one.
614  */
increase()615 void SellState::increase()
616 {
617 	_timerDec->setInterval(50);
618 	_timerInc->setInterval(50);
619 	changeByValue(1,1);
620 }
621 
622 /**
623  * Increases or decreases the quantity of the selected item to sell.
624  * @param change How much we want to add or remove.
625  * @param dir Direction to change, +1 to increase or -1 to decrease.
626  */
changeByValue(int change,int dir)627 void SellState::changeByValue(int change, int dir)
628 {
629 	if (dir > 0)
630 	{
631 		if (0 >= change || getQuantity() <=_qtys[_sel]) return;
632 		change = std::min(getQuantity() - _qtys[_sel], change);
633 	}
634 	else
635 	{
636 		if (0 >= change || 0 >= _qtys[_sel]) return;
637 		change = std::min(_qtys[_sel], change);
638 	}
639 	_qtys[_sel] += dir * change;
640 	_total += dir * getPrice() * change;
641 
642 	// Calculate the change in storage space.
643 	Craft *craft;
644 	RuleItem *armor, *item, *weapon, *ammo;
645 	double total = 0.0;
646 	switch (getType(_sel))
647 	{
648 	case SELL_SOLDIER:
649 		if (_soldiers[_sel]->getArmor()->getStoreItem() != "STR_NONE")
650 		{
651 			armor = _game->getRuleset()->getItem(_soldiers[_sel]->getArmor()->getStoreItem());
652 			_spaceChange += dir * armor->getSize();
653 		}
654 		break;
655 	case SELL_CRAFT:
656 		craft = _crafts[getCraftIndex(_sel)];
657 		for (std::vector<CraftWeapon*>::iterator w = craft->getWeapons()->begin(); w != craft->getWeapons()->end(); ++w)
658 		{
659 			if (*w)
660 			{
661 				weapon = _game->getRuleset()->getItem((*w)->getRules()->getLauncherItem());
662 				total += weapon->getSize();
663 				ammo = _game->getRuleset()->getItem((*w)->getRules()->getClipItem());
664 				if (ammo)
665 					total += ammo->getSize() * (*w)->getClipsLoaded(_game->getRuleset());
666 			}
667 		}
668 		_spaceChange += dir * total;
669 		break;
670 	case SELL_ITEM:
671 		item = _game->getRuleset()->getItem(_items[getItemIndex(_sel)]);
672 		_spaceChange -= dir * change * item->getSize();
673 		break;
674 	default:
675 		break;
676 	}
677 
678 	updateItemStrings();
679 }
680 
681 /**
682  * Decreases the quantity of the selected item to sell by one.
683  */
decrease()684 void SellState::decrease()
685 {
686 	_timerInc->setInterval(50);
687 	_timerDec->setInterval(50);
688 	changeByValue(1,-1);
689 }
690 
691 /**
692  * Updates the quantity-strings of the selected item.
693  */
updateItemStrings()694 void SellState::updateItemStrings()
695 {
696 	std::wostringstream ss, ss2, ss5;
697 	ss << _qtys[_sel];
698 	_lstItems->setCellText(_sel, 2, ss.str());
699 	ss2 << getQuantity() - _qtys[_sel];
700 	_lstItems->setCellText(_sel, 1, ss2.str());
701 	_txtSales->setText(tr("STR_VALUE_OF_SALES").arg(Text::formatFunding(_total)));
702 
703 	if (_qtys[_sel] > 0)
704 	{
705 		_lstItems->setRowColor(_sel, _color2);
706 	}
707 	else
708 	{
709 		_lstItems->setRowColor(_sel, _color);
710 		if (_sel > _itemOffset)
711 		{
712 			RuleItem *rule = _game->getRuleset()->getItem(_items[_sel - _itemOffset]);
713 			if (rule->getBattleType() == BT_AMMO || (rule->getBattleType() == BT_NONE && rule->getClipSize() > 0))
714 			{
715 				_lstItems->setRowColor(_sel, _colorAmmo);
716 			}
717 		}
718 	}
719 
720 	ss5 << _base->getUsedStores();
721 	if (std::abs(_spaceChange) > 0.05)
722 	{
723 		ss5 << "(";
724 		if (_spaceChange > 0.05)
725 			ss5 << "+";
726 		ss5 << std::fixed << std::setprecision(1) << _spaceChange << ")";
727 	}
728 	ss5 << ":" << _base->getAvailableStores();
729 	_txtSpaceUsed->setText(tr("STR_SPACE_USED").arg(ss5.str()));
730 	if (Options::storageLimitsEnforced)
731 	{
732 		_btnOk->setVisible(!_base->storesOverfull(_spaceChange));
733 	}
734 }
735 
736 /**
737  * Gets the Type of the selected item.
738  * @param selected Currently selected item.
739  * @return The type of the selected item.
740  */
getType(size_t selected) const741 enum SellType SellState::getType(size_t selected) const
742 {
743 	size_t max = _soldiers.size();
744 
745 	if (selected < max)
746 		return SELL_SOLDIER;
747 	if (selected < (max += _crafts.size()) )
748 		return SELL_CRAFT;
749 	if (selected < (max += _hasSci))
750 		return SELL_SCIENTIST;
751 	if (selected < (max += _hasEng))
752 		return SELL_ENGINEER;
753 
754 	return SELL_ITEM;
755 }
756 
757 /**
758  * Gets the index of the selected item.
759  * @param selected Currently selected item.
760  * @return Index of the selected item.
761  */
getItemIndex(size_t selected) const762 size_t SellState::getItemIndex(size_t selected) const
763 {
764 	return selected - _soldiers.size() - _crafts.size() - _hasSci - _hasEng;
765 }
766 
767 }
768