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 "TransferItemsState.h"
20 #include <sstream>
21 #include <climits>
22 #include <cfloat>
23 #include <cmath>
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 "../Savegame/CraftWeapon.h"
42 #include "../Ruleset/RuleCraftWeapon.h"
43 #include "../Engine/Timer.h"
44 #include "../Menu/ErrorMessageState.h"
45 #include "TransferConfirmState.h"
46 #include "../Engine/Options.h"
47 #include "../fmath.h"
48 
49 namespace OpenXcom
50 {
51 
52 /**
53  * Initializes all the elements in the Transfer screen.
54  * @param game Pointer to the core game.
55  * @param baseFrom Pointer to the source base.
56  * @param baseTo Pointer to the destination base.
57  */
TransferItemsState(Game * game,Base * baseFrom,Base * baseTo)58 TransferItemsState::TransferItemsState(Game *game, Base *baseFrom, Base *baseTo) : State(game), _baseFrom(baseFrom), _baseTo(baseTo), _baseQty(), _transferQty(), _soldiers(), _crafts(), _items(), _sel(0), _itemOffset(0), _total(0), _pQty(0), _cQty(0), _aQty(0), _iQty(0.0), _hasSci(0), _hasEng(0), _distance(0.0)
59 {
60 	// Create objects
61 	_window = new Window(this, 320, 200, 0, 0);
62 	_btnOk = new TextButton(148, 16, 8, 176);
63 	_btnCancel = new TextButton(148, 16, 164, 176);
64 	_txtTitle = new Text(310, 17, 5, 8);
65 	_txtItem = new Text(130, 9, 10, 24);
66 	_txtQuantity = new Text(50, 9, 150, 24);
67 	_txtAmountTransfer = new Text(60, 17, 200, 24);
68 	_txtAmountDestination = new Text(60, 17, 260, 24);
69 	_lstItems = new TextList(287, 120, 8, 44);
70 
71 	// Set palette
72 	setPalette("PAL_BASESCAPE", 0);
73 
74 	add(_window);
75 	add(_btnOk);
76 	add(_btnCancel);
77 	add(_txtTitle);
78 	add(_txtItem);
79 	add(_txtQuantity);
80 	add(_txtAmountTransfer);
81 	add(_txtAmountDestination);
82 	add(_lstItems);
83 
84 	centerAllSurfaces();
85 
86 	// Set up objects
87 	_window->setColor(Palette::blockOffset(13)+10);
88 	_window->setBackground(_game->getResourcePack()->getSurface("BACK13.SCR"));
89 
90 	_btnOk->setColor(Palette::blockOffset(15)+6);
91 	_btnOk->setText(tr("STR_TRANSFER"));
92 	_btnOk->onMouseClick((ActionHandler)&TransferItemsState::btnOkClick);
93 	_btnOk->onKeyboardPress((ActionHandler)&TransferItemsState::btnOkClick, Options::keyOk);
94 
95 	_btnCancel->setColor(Palette::blockOffset(15)+6);
96 	_btnCancel->setText(tr("STR_CANCEL"));
97 	_btnCancel->onMouseClick((ActionHandler)&TransferItemsState::btnCancelClick);
98 	_btnCancel->onKeyboardPress((ActionHandler)&TransferItemsState::btnCancelClick, Options::keyCancel);
99 
100 	_txtTitle->setColor(Palette::blockOffset(13)+10);
101 	_txtTitle->setBig();
102 	_txtTitle->setAlign(ALIGN_CENTER);
103 	_txtTitle->setText(tr("STR_TRANSFER"));
104 
105 	_txtItem->setColor(Palette::blockOffset(13)+10);
106 	_txtItem->setText(tr("STR_ITEM"));
107 
108 	_txtQuantity->setColor(Palette::blockOffset(13)+10);
109 	_txtQuantity->setText(tr("STR_QUANTITY_UC"));
110 
111 	_txtAmountTransfer->setColor(Palette::blockOffset(13)+10);
112 	_txtAmountTransfer->setText(tr("STR_AMOUNT_TO_TRANSFER"));
113 	_txtAmountTransfer->setWordWrap(true);
114 
115 	_txtAmountDestination->setColor(Palette::blockOffset(13)+10);
116 	_txtAmountDestination->setText(tr("STR_AMOUNT_AT_DESTINATION"));
117 	_txtAmountDestination->setWordWrap(true);
118 
119 	_lstItems->setColor(Palette::blockOffset(13)+10);
120 	_lstItems->setArrowColor(Palette::blockOffset(13)+10);
121 	_lstItems->setArrowColumn(193, ARROW_VERTICAL);
122 	_lstItems->setColumns(4, 162, 58, 40, 20);
123 	_lstItems->setSelectable(true);
124 	_lstItems->setBackground(_window);
125 	_lstItems->setMargin(2);
126 	_lstItems->onLeftArrowPress((ActionHandler)&TransferItemsState::lstItemsLeftArrowPress);
127 	_lstItems->onLeftArrowRelease((ActionHandler)&TransferItemsState::lstItemsLeftArrowRelease);
128 	_lstItems->onLeftArrowClick((ActionHandler)&TransferItemsState::lstItemsLeftArrowClick);
129 	_lstItems->onRightArrowPress((ActionHandler)&TransferItemsState::lstItemsRightArrowPress);
130 	_lstItems->onRightArrowRelease((ActionHandler)&TransferItemsState::lstItemsRightArrowRelease);
131 	_lstItems->onRightArrowClick((ActionHandler)&TransferItemsState::lstItemsRightArrowClick);
132 	_lstItems->onMousePress((ActionHandler)&TransferItemsState::lstItemsMousePress);
133 
134 	for (std::vector<Soldier*>::iterator i = _baseFrom->getSoldiers()->begin(); i != _baseFrom->getSoldiers()->end(); ++i)
135 	{
136 		if ((*i)->getCraft() == 0)
137 		{
138 			_baseQty.push_back(1);
139 			_transferQty.push_back(0);
140 			_soldiers.push_back(*i);
141 			_lstItems->addRow(4, (*i)->getName().c_str(), L"1", L"0", L"0");
142 			++_itemOffset;
143 		}
144 	}
145 	for (std::vector<Craft*>::iterator i = _baseFrom->getCrafts()->begin(); i != _baseFrom->getCrafts()->end(); ++i)
146 	{
147 		if ((*i)->getStatus() != "STR_OUT" || (Options::canTransferCraftsWhileAirborne && (*i)->getFuel() >= (*i)->getFuelLimit(_baseTo)))
148 		{
149 			_baseQty.push_back(1);
150 			_transferQty.push_back(0);
151 			_crafts.push_back(*i);
152 			_lstItems->addRow(4, (*i)->getName(_game->getLanguage()).c_str(), L"1", L"0", L"0");
153 			++_itemOffset;
154 		}
155 	}
156 	if (_baseFrom->getAvailableScientists() > 0)
157 	{
158 		_baseQty.push_back(_baseFrom->getAvailableScientists());
159 		_transferQty.push_back(0);
160 		_hasSci = 1;
161 		std::wostringstream ss, ss2;
162 		ss << _baseQty.back();
163 		ss2 << _baseTo->getAvailableScientists();
164 		_lstItems->addRow(4, tr("STR_SCIENTIST").c_str(), ss.str().c_str(), L"0", ss2.str().c_str());
165 		++_itemOffset;
166 	}
167 	if (_baseFrom->getAvailableEngineers() > 0)
168 	{
169 		_baseQty.push_back(_baseFrom->getAvailableEngineers());
170 		_transferQty.push_back(0);
171 		_hasEng = 1;
172 		std::wostringstream ss, ss2;
173 		ss << _baseQty.back();
174 		ss2 << _baseTo->getAvailableEngineers();
175 		_lstItems->addRow(4, tr("STR_ENGINEER").c_str(), ss.str().c_str(), L"0", ss2.str().c_str());
176 		++_itemOffset;
177 	}
178 	const std::vector<std::string> &items = _game->getRuleset()->getItemsList();
179 	for (std::vector<std::string>::const_iterator i = items.begin(); i != items.end(); ++i)
180 	{
181 		int qty = _baseFrom->getItems()->getItem(*i);
182 		if (qty > 0)
183 		{
184 			_baseQty.push_back(qty);
185 			_transferQty.push_back(0);
186 			_items.push_back(*i);
187 			RuleItem *rule = _game->getRuleset()->getItem(*i);
188 			std::wostringstream ss, ss2;
189 			ss << qty;
190 			ss2 << _baseTo->getItems()->getItem(*i);
191 			std::wstring item = tr(*i);
192 			if (rule->getBattleType() == BT_AMMO || (rule->getBattleType() == BT_NONE && rule->getClipSize() > 0))
193 			{
194 				item.insert(0, L"  ");
195 				_lstItems->addRow(4, item.c_str(), ss.str().c_str(), L"0", ss2.str().c_str());
196 				_lstItems->setRowColor(_baseQty.size() - 1, Palette::blockOffset(15) + 6);
197 			}
198 			else
199 			{
200 				_lstItems->addRow(4, item.c_str(), ss.str().c_str(), L"0", ss2.str().c_str());
201 			}
202 		}
203 	}
204 	_distance = getDistance();
205 
206 	_timerInc = new Timer(250);
207 	_timerInc->onTimer((StateHandler)&TransferItemsState::increase);
208 	_timerDec = new Timer(250);
209 	_timerDec->onTimer((StateHandler)&TransferItemsState::decrease);
210 }
211 
212 /**
213  *
214  */
~TransferItemsState()215 TransferItemsState::~TransferItemsState()
216 {
217 	delete _timerInc;
218 	delete _timerDec;
219 }
220 
221 /**
222  * Runs the arrow timers.
223  */
think()224 void TransferItemsState::think()
225 {
226 	State::think();
227 
228 	_timerInc->think(this, 0);
229 	_timerDec->think(this, 0);
230 }
231 
232 /**
233  * Transfers the selected items.
234  * @param action Pointer to an action.
235  */
btnOkClick(Action *)236 void TransferItemsState::btnOkClick(Action *)
237 {
238 	_game->pushState(new TransferConfirmState(_game, _baseTo, this));
239 }
240 
241 /**
242  * Completes the transfer between bases.
243  */
completeTransfer()244 void TransferItemsState::completeTransfer()
245 {
246 	int time = (int)floor(6 + _distance / 10.0);
247 	_game->getSavedGame()->setFunds(_game->getSavedGame()->getFunds() - _total);
248 	for (size_t i = 0; i < _transferQty.size(); ++i)
249 	{
250 		if (_transferQty[i] > 0)
251 		{
252 			// Transfer soldiers
253 			if (i < _soldiers.size())
254 			{
255 				for (std::vector<Soldier*>::iterator s = _baseFrom->getSoldiers()->begin(); s != _baseFrom->getSoldiers()->end(); ++s)
256 				{
257 					if (*s == _soldiers[i])
258 					{
259 						 if((*s)->isInPsiTraining())
260 						 {
261 							 (*s)->setPsiTraining();
262 						 }
263 						Transfer *t = new Transfer(time);
264 						t->setSoldier(*s);
265 						_baseTo->getTransfers()->push_back(t);
266 						_baseFrom->getSoldiers()->erase(s);
267 						break;
268 					}
269 				}
270 			}
271 			// Transfer crafts
272 			else if (i >= _soldiers.size() && i < _soldiers.size() + _crafts.size())
273 			{
274 				Craft *craft =  _crafts[i - _soldiers.size()];
275 
276 				// Transfer soldiers inside craft
277 				for (std::vector<Soldier*>::iterator s = _baseFrom->getSoldiers()->begin(); s != _baseFrom->getSoldiers()->end();)
278 				{
279 					if ((*s)->getCraft() == craft)
280 					{
281 						if ((*s)->isInPsiTraining()) (*s)->setPsiTraining();
282 						if (craft->getStatus() == "STR_OUT") _baseTo->getSoldiers()->push_back(*s);
283 						else
284 						{
285 							Transfer *t = new Transfer(time);
286 							t->setSoldier(*s);
287 							_baseTo->getTransfers()->push_back(t);
288 						}
289 						s = _baseFrom->getSoldiers()->erase(s);
290 					}
291 					else
292 					{
293 						++s;
294 					}
295 				}
296 
297 				// Transfer craft
298 				for (std::vector<Craft*>::iterator c = _baseFrom->getCrafts()->begin(); c != _baseFrom->getCrafts()->end(); ++c)
299 				{
300 					if (*c == craft)
301 					{
302 						if (craft->getStatus() == "STR_OUT")
303 						{
304 							bool returning = (craft->getDestination() == (Target*)craft->getBase());
305 							_baseTo->getCrafts()->push_back(craft);
306 							craft->setBaseOnly(_baseTo);
307 							if (craft->getFuel() <= craft->getFuelLimit(_baseTo))
308 							{
309 								craft->setLowFuel(true);
310 								craft->returnToBase();
311 							}
312 							else if (returning)
313 							{
314 								craft->setLowFuel(false);
315 								craft->returnToBase();
316 							}
317 						}
318 						else
319 						{
320 							Transfer *t = new Transfer(time);
321 							t->setCraft(*c);
322 							_baseTo->getTransfers()->push_back(t);
323 						}
324 						// Clear Hangar
325 						for (std::vector<BaseFacility*>::iterator f = _baseFrom->getFacilities()->begin(); f != _baseFrom->getFacilities()->end(); ++f)
326 						{
327 							if ((*f)->getCraft() == *c)
328 							{
329 								(*f)->setCraft(0);
330 								break;
331 							}
332 						}
333 
334 						_baseFrom->getCrafts()->erase(c);
335 						break;
336 					}
337 				}
338 			}
339 			// Transfer scientists
340 			else if (_baseFrom->getAvailableScientists() > 0 && i == _soldiers.size() + _crafts.size())
341 			{
342 				_baseFrom->setScientists(_baseFrom->getScientists() - _transferQty[i]);
343 				Transfer *t = new Transfer(time);
344 				t->setScientists(_transferQty[i]);
345 				_baseTo->getTransfers()->push_back(t);
346 			}
347 			// Transfer engineers
348 			else if (_baseFrom->getAvailableEngineers() > 0 && i == _soldiers.size() + _crafts.size() + _hasSci)
349 			{
350 				_baseFrom->setEngineers(_baseFrom->getEngineers() - _transferQty[i]);
351 				Transfer *t = new Transfer(time);
352 				t->setEngineers(_transferQty[i]);
353 				_baseTo->getTransfers()->push_back(t);
354 			}
355 			// Transfer items
356 			else
357 			{
358 				_baseFrom->getItems()->removeItem(_items[ getItemIndex(i) ], _transferQty[i]);
359 				Transfer *t = new Transfer(time);
360 				t->setItems(_items[getItemIndex(i)], _transferQty[i]);
361 				_baseTo->getTransfers()->push_back(t);
362 			}
363 		}
364 	}
365 }
366 
367 /**
368  * Returns to the previous screen.
369  * @param action Pointer to an action.
370  */
btnCancelClick(Action *)371 void TransferItemsState::btnCancelClick(Action *)
372 {
373 	_game->popState();
374 	_game->popState();
375 }
376 
377 /**
378  * Starts increasing the item.
379  * @param action Pointer to an action.
380  */
lstItemsLeftArrowPress(Action * action)381 void TransferItemsState::lstItemsLeftArrowPress(Action *action)
382 {
383 	_sel = _lstItems->getSelectedRow();
384 	if (action->getDetails()->button.button == SDL_BUTTON_LEFT && !_timerInc->isRunning()) _timerInc->start();
385 }
386 
387 /**
388  * Stops increasing the item.
389  * @param action Pointer to an action.
390  */
lstItemsLeftArrowRelease(Action * action)391 void TransferItemsState::lstItemsLeftArrowRelease(Action *action)
392 {
393 	if (action->getDetails()->button.button == SDL_BUTTON_LEFT)
394 	{
395 		_timerInc->stop();
396 	}
397 }
398 
399 /**
400  * Increases the selected item;
401  * by one on left-click; to max on right-click.
402  * @param action Pointer to an action.
403  */
lstItemsLeftArrowClick(Action * action)404 void TransferItemsState::lstItemsLeftArrowClick(Action *action)
405 {
406 	if (action->getDetails()->button.button == SDL_BUTTON_RIGHT) increaseByValue(INT_MAX);
407 	if (action->getDetails()->button.button == SDL_BUTTON_LEFT)
408 	{
409 		increaseByValue(1);
410 		_timerInc->setInterval(250);
411 		_timerDec->setInterval(250);
412 	}
413 }
414 
415 /**
416  * Starts decreasing the item.
417  * @param action Pointer to an action.
418  */
lstItemsRightArrowPress(Action * action)419 void TransferItemsState::lstItemsRightArrowPress(Action *action)
420 {
421 	_sel = _lstItems->getSelectedRow();
422 	if (action->getDetails()->button.button == SDL_BUTTON_LEFT && !_timerDec->isRunning()) _timerDec->start();
423 }
424 
425 /**
426  * Stops decreasing the item.
427  * @param action Pointer to an action.
428  */
lstItemsRightArrowRelease(Action * action)429 void TransferItemsState::lstItemsRightArrowRelease(Action *action)
430 {
431 	if (action->getDetails()->button.button == SDL_BUTTON_LEFT)
432 	{
433 		_timerDec->stop();
434 	}
435 }
436 
437 /**
438  * Decreases the selected item;
439  * by one on left-click; to 0 on right-click.
440  * @param action Pointer to an action.
441  */
lstItemsRightArrowClick(Action * action)442 void TransferItemsState::lstItemsRightArrowClick(Action *action)
443 {
444 	if (action->getDetails()->button.button == SDL_BUTTON_RIGHT) decreaseByValue(INT_MAX);
445 	if (action->getDetails()->button.button == SDL_BUTTON_LEFT)
446 	{
447 		decreaseByValue(1);
448 		_timerInc->setInterval(250);
449 		_timerDec->setInterval(250);
450 	}
451 }
452 
453 /**
454  * Handles the mouse-wheels on the arrow-buttons.
455  * @param action Pointer to an action.
456  */
lstItemsMousePress(Action * action)457 void TransferItemsState::lstItemsMousePress(Action *action)
458 {
459 	_sel = _lstItems->getSelectedRow();
460 	if (action->getDetails()->button.button == SDL_BUTTON_WHEELUP)
461 	{
462 		_timerInc->stop();
463 		_timerDec->stop();
464 		if (action->getAbsoluteXMouse() >= _lstItems->getArrowsLeftEdge() &&
465 			action->getAbsoluteXMouse() <= _lstItems->getArrowsRightEdge())
466 		{
467 			increaseByValue(Options::changeValueByMouseWheel);
468 		}
469 	}
470 	else if (action->getDetails()->button.button == SDL_BUTTON_WHEELDOWN)
471 	{
472 		_timerInc->stop();
473 		_timerDec->stop();
474 		if (action->getAbsoluteXMouse() >= _lstItems->getArrowsLeftEdge() &&
475 			action->getAbsoluteXMouse() <= _lstItems->getArrowsRightEdge())
476 		{
477 			decreaseByValue(Options::changeValueByMouseWheel);
478 		}
479 	}
480 }
481 
482 /**
483  * Gets the transfer cost of the currently selected item.
484  * @return Transfer cost.
485  */
getCost() const486 int TransferItemsState::getCost() const
487 {
488 	int cost = 0;
489 	// Item cost
490 	if (TRANSFER_ITEM == getType(_sel))
491 	{
492 		cost = 1;
493 	}
494 	// Craft cost
495 	else if (TRANSFER_CRAFT == getType(_sel))
496 	{
497 		cost = 25;
498 	}
499 	// Personnel cost
500 	else
501 	{
502 		cost = 5;
503 	}
504 	return (int)floor(_distance * cost);
505 }
506 
507 /**
508  * Gets the quantity of the currently selected item
509  * on the base.
510  * @return Item quantity.
511  */
getQuantity() const512 int TransferItemsState::getQuantity() const
513 {
514 	switch(getType(_sel))
515 	{
516 	case TRANSFER_SOLDIER:
517 	case TRANSFER_CRAFT:
518 		return 1;
519 	case TRANSFER_SCIENTIST:
520 		return _baseFrom->getAvailableScientists();
521 	case TRANSFER_ENGINEER:
522 		return _baseFrom->getAvailableEngineers();
523 	case TRANSFER_ITEM:
524 		return _baseFrom->getItems()->getItem(_items[getItemIndex(_sel)]);
525 	}
526 	return 1;
527 }
528 
529 /**
530  * Increases the quantity of the selected item to transfer by one.
531  */
increase()532 void TransferItemsState::increase()
533 {
534 	_timerDec->setInterval(50);
535 	_timerInc->setInterval(50);
536 	increaseByValue(1);
537 }
538 
539 /**
540  * Increases the quantity of the selected item to transfer by "change".
541  * @param change How much we want to add.
542  */
increaseByValue(int change)543 void TransferItemsState::increaseByValue(int change)
544 {
545 	const enum TransferType selType = getType(_sel);
546 	RuleItem * selItem = NULL;
547 
548 	if (TRANSFER_ITEM == selType)
549 		selItem = _game->getRuleset()->getItem(_items[getItemIndex(_sel)]);
550 
551 	if (0 >= change || getQuantity() <= _transferQty[_sel]) return;
552 	// Check Living Quarters
553 	if ((TRANSFER_SOLDIER == selType || TRANSFER_SCIENTIST == selType || TRANSFER_ENGINEER == selType)
554 		&& _pQty + 1 > _baseTo->getAvailableQuarters() - _baseTo->getUsedQuarters())
555 	{
556 		_timerInc->stop();
557 		_game->pushState(new ErrorMessageState(_game, "STR_NO_FREE_ACCOMODATION", _palette, Palette::blockOffset(15)+1, "BACK13.SCR", 0));
558 		return;
559 	}
560 	if (TRANSFER_CRAFT == selType )
561 	{
562 		Craft *craft =  _crafts[_sel - _soldiers.size()];
563 		if (_cQty + 1 > _baseTo->getAvailableHangars() - _baseTo->getUsedHangars())
564 		{
565 			_timerInc->stop();
566 			_game->pushState(new ErrorMessageState(_game, "STR_NO_FREE_HANGARS_FOR_TRANSFER", _palette, Palette::blockOffset(15)+1, "BACK13.SCR", 0));
567 			return;
568 		}
569 		if (_pQty + craft->getNumSoldiers() > _baseTo->getAvailableQuarters() - _baseTo->getUsedQuarters())
570 		{
571 			_timerInc->stop();
572 			_game->pushState(new ErrorMessageState(_game, "STR_NO_FREE_ACCOMODATION_CREW", _palette, Palette::blockOffset(15)+1, "BACK13.SCR", 0));
573 			return;
574 		}
575 		if (Options::storageLimitsEnforced && _baseTo->storesOverfull(_iQty + craft->getItems()->getTotalSize(_game->getRuleset())))
576 		{
577 			_timerInc->stop();
578 			_game->pushState(new ErrorMessageState(_game, "STR_NOT_ENOUGH_STORE_SPACE_FOR_CRAFT", _palette, Palette::blockOffset(15)+1, "BACK13.SCR", 0));
579 			return;
580 		}
581 	}
582 	if (TRANSFER_ITEM == selType && !selItem->getAlien()
583 		&& _baseTo->storesOverfull(selItem->getSize() + _iQty))
584 	{
585 		_timerInc->stop();
586 		_game->pushState(new ErrorMessageState(_game, "STR_NOT_ENOUGH_STORE_SPACE", _palette, Palette::blockOffset(15)+1, "BACK13.SCR", 0));
587 		return;
588 	}
589 	if (TRANSFER_ITEM == selType && selItem->getAlien()
590 		&& Options::storageLimitsEnforced * _aQty + 1 > _baseTo->getAvailableContainment() - Options::storageLimitsEnforced * _baseTo->getUsedContainment())
591 	{
592 		_timerInc->stop();
593 		_game->pushState(new ErrorMessageState(_game, "STR_NO_ALIEN_CONTAINMENT_FOR_TRANSFER", _palette, Palette::blockOffset(15)+1, "BACK13.SCR", 0));
594 		return;
595 	}
596 
597 	// Personnel count
598 	if (TRANSFER_SOLDIER == selType || TRANSFER_SCIENTIST == selType || TRANSFER_ENGINEER == selType)
599 	{
600 		int freeQuarters = _baseTo->getAvailableQuarters() - _baseTo->getUsedQuarters() - _pQty;
601 		change = std::min(std::min(freeQuarters, getQuantity() - _transferQty[_sel]), change);
602 		_pQty += change;
603 		_baseQty[_sel] -= change;
604 		_transferQty[_sel] += change;
605 		_total += getCost() * change;
606 	}
607 	// Craft count
608 	else if (TRANSFER_CRAFT == selType)
609 	{
610 		Craft *craft = _crafts[_sel - _soldiers.size()];
611 		_cQty++;
612 		_pQty += craft->getNumSoldiers();
613 		_iQty += craft->getItems()->getTotalSize(_game->getRuleset());
614 		_baseQty[_sel]--;
615 		_transferQty[_sel]++;
616 		if (!Options::canTransferCraftsWhileAirborne || craft->getStatus() != "STR_OUT") _total += getCost();
617 	}
618 	// Item count
619 	else if (TRANSFER_ITEM == selType && !selItem->getAlien() )
620 	{
621 		double storesNeededPerItem = _game->getRuleset()->getItem(_items[getItemIndex(_sel)])->getSize();
622 		double freeStores = _baseTo->getAvailableStores() - _baseTo->getUsedStores() - _iQty;
623 		double freeStoresForItem = DBL_MAX;
624 		if (!AreSame(storesNeededPerItem, 0.0))
625 		{
626 			freeStoresForItem = (freeStores + 0.05) / storesNeededPerItem;
627 		}
628 		change = std::min(std::min((int)freeStoresForItem, getQuantity() - _transferQty[_sel]), change);
629 		_iQty += change * storesNeededPerItem;
630 		_baseQty[_sel] -= change;
631 		_transferQty[_sel] += change;
632 		_total += getCost() * change;
633 	}
634 	// Live Aliens count
635 	else if (TRANSFER_ITEM == selType && selItem->getAlien() )
636 	{
637 		int freeContainment = Options::storageLimitsEnforced ? _baseTo->getAvailableContainment() - _baseTo->getUsedContainment() - _aQty : INT_MAX;
638 		change = std::min(std::min(freeContainment, getQuantity() - _transferQty[_sel]), change);
639 		_aQty += change;
640 		_baseQty[_sel] -= change;
641 		_transferQty[_sel] += change;
642 		_total += getCost() * change;
643 	}
644 	updateItemStrings();
645 }
646 
647 /**
648  * Decreases the quantity of the selected item to transfer by one.
649  */
decrease()650 void TransferItemsState::decrease()
651 {
652 	_timerInc->setInterval(50);
653 	_timerDec->setInterval(50);
654 	decreaseByValue(1);
655 }
656 
657 /**
658  * Decreases the quantity of the selected item to transfer by "change".
659  * @param change How much we want to remove.
660  */
decreaseByValue(int change)661 void TransferItemsState::decreaseByValue(int change)
662 {
663 	const enum TransferType selType = getType(_sel);
664 	if (0 >= change || 0 >= _transferQty[_sel]) return;
665 	Craft *craft = 0;
666 	change = std::min(_transferQty[_sel], change);
667 	// Personnel count
668 	if (TRANSFER_SOLDIER == selType || TRANSFER_SCIENTIST == selType || TRANSFER_ENGINEER == selType)
669 	{
670 		_pQty -= change;
671 	}
672 	// Craft count
673 	else if (TRANSFER_CRAFT == selType)
674 	{
675 		craft = _crafts[_sel - _soldiers.size()];
676 		_cQty--;
677 		_pQty -= craft->getNumSoldiers();
678 		_iQty -= craft->getItems()->getTotalSize(_game->getRuleset());
679 	}
680 	// Item count
681 	else if (TRANSFER_ITEM == selType)
682 	{
683 		const RuleItem * selItem = _game->getRuleset()->getItem(_items[getItemIndex(_sel)]);
684 		if (!selItem->getAlien())
685 		{
686 			_iQty -= selItem->getSize() * change;
687 		}
688 		else
689 		{
690 			_aQty -= change;
691 		}
692 	}
693 	_baseQty[_sel] += change;
694 	_transferQty[_sel] -= change;
695 	if (!Options::canTransferCraftsWhileAirborne || 0 == craft || craft->getStatus() != "STR_OUT")
696 		_total -= getCost() * change;
697 	updateItemStrings();
698 }
699 
700 /**
701  * Updates the quantity-strings of the selected item.
702  */
updateItemStrings()703 void TransferItemsState::updateItemStrings()
704 {
705 	std::wostringstream ss1, ss2;
706 	ss1 << _baseQty[_sel];
707 	ss2 << _transferQty[_sel];
708 	_lstItems->setCellText(_sel, 1, ss1.str());
709 	_lstItems->setCellText(_sel, 2, ss2.str());
710 	if (_transferQty[_sel] > 0)
711 	{
712 		_lstItems->setRowColor(_sel, Palette::blockOffset(13));
713 	}
714 	else
715 	{
716 		_lstItems->setRowColor(_sel, Palette::blockOffset(13) + 10);
717 		if (_sel > _itemOffset)
718 		{
719 			RuleItem *rule = _game->getRuleset()->getItem(_items[_sel - _itemOffset]);
720 			if (rule->getBattleType() == BT_AMMO || (rule->getBattleType() == BT_NONE && rule->getClipSize() > 0))
721 			{
722 				_lstItems->setRowColor(_sel, Palette::blockOffset(15) + 6);
723 			}
724 		}
725 	}
726 }
727 
728 /**
729  * Gets the total cost of the current transfer.
730  * @return Total cost.
731  */
getTotal() const732 int TransferItemsState::getTotal() const
733 {
734 	return _total;
735 }
736 
737 /**
738  * Gets the shortest distance between the two bases.
739  * @return Distance.
740  */
getDistance() const741 double TransferItemsState::getDistance() const
742 {
743 	double x[3], y[3], z[3], r = 51.2;
744 	Base *base = _baseFrom;
745 	for (int i = 0; i < 2; ++i) {
746 		x[i] = r * cos(base->getLatitude()) * cos(base->getLongitude());
747 		y[i] = r * cos(base->getLatitude()) * sin(base->getLongitude());
748 		z[i] = r * -sin(base->getLatitude());
749 		base = _baseTo;
750 	}
751 	x[2] = x[1] - x[0];
752 	y[2] = y[1] - y[0];
753 	z[2] = z[1] - z[0];
754 	return sqrt(x[2] * x[2] + y[2] * y[2] + z[2] * z[2]);
755 }
756 
757 /**
758  * Gets type of selected item.
759  * @param selected The selected item.
760  * @return The type of the selected item.
761  */
getType(size_t selected) const762 enum TransferType TransferItemsState::getType(size_t selected) const
763 {
764 	size_t max = _soldiers.size();
765 
766 	if (selected < max)
767 		return TRANSFER_SOLDIER;
768 	if (selected < (max += _crafts.size()) )
769 		return TRANSFER_CRAFT;
770 	if (selected < (max += _hasSci))
771 		return TRANSFER_SCIENTIST;
772 	if (selected < (max += _hasEng))
773 		return TRANSFER_ENGINEER;
774 
775 	return TRANSFER_ITEM;
776 }
777 
778 /**
779  * Gets the index of the selected item.
780  * @param selected Currently selected item.
781  * @return Index of the selected item.
782  */
getItemIndex(size_t selected) const783 size_t TransferItemsState::getItemIndex(size_t selected) const
784 {
785 	return selected - _soldiers.size() - _crafts.size() - _hasSci - _hasEng;
786 }
787 
788 }
789