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