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 "NewBattleState.h"
20 #include <cmath>
21 #include <fstream>
22 #include <algorithm>
23 #include <yaml-cpp/yaml.h>
24 #include "../Engine/Game.h"
25 #include "../Resource/ResourcePack.h"
26 #include "../Ruleset/RuleItem.h"
27 #include "../Ruleset/Ruleset.h"
28 #include "../Engine/Language.h"
29 #include "../Engine/Palette.h"
30 #include "../Interface/TextButton.h"
31 #include "../Interface/Window.h"
32 #include "../Interface/Text.h"
33 #include "../Interface/ComboBox.h"
34 #include "../Interface/Slider.h"
35 #include "../Interface/Frame.h"
36 #include "../Savegame/SavedBattleGame.h"
37 #include "../Savegame/SavedGame.h"
38 #include "../Savegame/Base.h"
39 #include "../Savegame/Craft.h"
40 #include "../Savegame/ItemContainer.h"
41 #include "../Battlescape/BattlescapeGenerator.h"
42 #include "../Battlescape/BattlescapeState.h"
43 #include "../Battlescape/BriefingState.h"
44 #include "../Savegame/Ufo.h"
45 #include "../Savegame/TerrorSite.h"
46 #include "../Savegame/AlienBase.h"
47 #include "../Ruleset/RuleCraft.h"
48 #include "../Ruleset/RuleTerrain.h"
49 #include "../Ruleset/AlienDeployment.h"
50 #include "../Engine/Music.h"
51 #include "../Engine/RNG.h"
52 #include "../Engine/Action.h"
53 #include "../Engine/Options.h"
54 #include "../Engine/Logger.h"
55 #include "../Basescape/CraftInfoState.h"
56
57 namespace OpenXcom
58 {
59
60 /**
61 * Initializes all the elements in the New Battle window.
62 * @param game Pointer to the core game.
63 */
NewBattleState(Game * game)64 NewBattleState::NewBattleState(Game *game) : State(game), _craft(0)
65 {
66 // Create objects
67 _window = new Window(this, 320, 200, 0, 0, POPUP_BOTH);
68 _txtTitle = new Text(320, 17, 0, 9);
69
70 _txtMapOptions = new Text(148, 9, 8, 68);
71 _frameLeft = new Frame(148, 96, 8, 78);
72 _txtAlienOptions = new Text(148, 9, 164, 68);
73 _frameRight = new Frame(148, 96, 164, 78);
74
75 _txtMission = new Text(100, 9, 8, 30);
76 _cbxMission = new ComboBox(this, 214, 16, 98, 26);
77
78 _txtCraft = new Text(100, 9, 8, 50);
79 _cbxCraft = new ComboBox(this, 106, 16, 98, 46);
80 _btnEquip = new TextButton(106, 16, 206, 46);
81
82 _txtDarkness = new Text(120, 9, 22, 83);
83 _slrDarkness = new Slider(120, 16, 22, 93);
84
85 _txtTerrain = new Text(120, 9, 22, 113);
86 _cbxTerrain = new ComboBox(this, 120, 16, 22, 123);
87
88 _txtDifficulty = new Text(120, 9, 178, 83);
89 _cbxDifficulty = new ComboBox(this, 120, 16, 178, 93);
90
91 _txtAlienRace = new Text(120, 9, 178, 113);
92 _cbxAlienRace = new ComboBox(this, 120, 16, 178, 123);
93
94 _txtAlienTech = new Text(120, 9, 178, 143);
95 _slrAlienTech = new Slider(120, 16, 178, 153);
96
97 _btnOk = new TextButton(100, 16, 8, 176);
98 _btnCancel = new TextButton(100, 16, 110, 176);
99 _btnRandom = new TextButton(100, 16, 212, 176);
100
101 // Set palette
102 setPalette("PAL_GEOSCAPE", 0);
103
104 add(_window);
105 add(_txtTitle);
106 add(_txtMapOptions);
107 add(_frameLeft);
108 add(_txtAlienOptions);
109 add(_frameRight);
110
111 add(_txtMission);
112 add(_txtCraft);
113 add(_btnEquip);
114
115 add(_txtDarkness);
116 add(_slrDarkness);
117 add(_txtTerrain);
118 add(_txtDifficulty);
119 add(_txtAlienRace);
120 add(_txtAlienTech);
121 add(_slrAlienTech);
122
123 add(_btnOk);
124 add(_btnCancel);
125 add(_btnRandom);
126
127 add(_cbxTerrain);
128 add(_cbxAlienRace);
129 add(_cbxDifficulty);
130 add(_cbxCraft);
131 add(_cbxMission);
132
133 centerAllSurfaces();
134
135 // Set up objects
136 _window->setColor(Palette::blockOffset(8)+5);
137 _window->setBackground(_game->getResourcePack()->getSurface("BACK01.SCR"));
138
139 _txtTitle->setColor(Palette::blockOffset(15)-1);
140 _txtTitle->setAlign(ALIGN_CENTER);
141 _txtTitle->setBig();
142 _txtTitle->setText(tr("STR_MISSION_GENERATOR"));
143
144 _txtMapOptions->setColor(Palette::blockOffset(15)-1);
145 _txtMapOptions->setText(tr("STR_MAP_OPTIONS"));
146
147 _frameLeft->setThickness(3);
148 _frameLeft->setColor(Palette::blockOffset(15)-1);
149
150 _txtAlienOptions->setColor(Palette::blockOffset(15)-1);
151 _txtAlienOptions->setText(tr("STR_ALIEN_OPTIONS"));
152
153 _frameRight->setThickness(3);
154 _frameRight->setColor(Palette::blockOffset(15)-1);
155
156 _txtMission->setColor(Palette::blockOffset(8)+10);
157 _txtMission->setText(tr("STR_MISSION"));
158
159 _txtCraft->setColor(Palette::blockOffset(8)+10);
160 _txtCraft->setText(tr("STR_CRAFT"));
161
162 _txtDarkness->setColor(Palette::blockOffset(8)+10);
163 _txtDarkness->setText(tr("STR_MAP_DARKNESS"));
164
165 _txtTerrain->setColor(Palette::blockOffset(8)+10);
166 _txtTerrain->setText(tr("STR_MAP_TERRAIN"));
167
168 _txtDifficulty->setColor(Palette::blockOffset(8)+10);
169 _txtDifficulty->setText(tr("STR_ALIEN_DIFFICULTY"));
170
171 _txtAlienRace->setColor(Palette::blockOffset(8)+10);
172 _txtAlienRace->setText(tr("STR_ALIEN_RACE"));
173
174 _txtAlienTech->setColor(Palette::blockOffset(8)+10);
175 _txtAlienTech->setText(tr("STR_ALIEN_TECH_LEVEL"));
176
177 _missionTypes = _game->getRuleset()->getDeploymentsList();
178
179 const std::vector<std::string> &terrainTypes = _game->getRuleset()->getTerrainList();
180 std::vector<std::string> terrainStrings;
181 for (std::vector<std::string>::const_iterator i = terrainTypes.begin(); i != terrainTypes.end(); ++i)
182 {
183 if (!_game->getRuleset()->getTerrain(*i)->getTextures()->empty())
184 {
185 _terrainTypes.push_back(*i);
186 terrainStrings.push_back("STR_" + *i);
187 _textures.push_back(_game->getRuleset()->getTerrain(*i)->getTextures()->at(0));
188 }
189 }
190
191 _alienRaces = _game->getRuleset()->getAlienRacesList();
192
193 std::vector<std::string> difficulty;
194 difficulty.push_back("STR_1_BEGINNER");
195 difficulty.push_back("STR_2_EXPERIENCED");
196 difficulty.push_back("STR_3_VETERAN");
197 difficulty.push_back("STR_4_GENIUS");
198 difficulty.push_back("STR_5_SUPERHUMAN");
199
200 const std::vector<std::string> &crafts = _game->getRuleset()->getCraftsList();
201 for (std::vector<std::string>::const_iterator i = crafts.begin(); i != crafts.end(); ++i)
202 {
203 RuleCraft *rule = _game->getRuleset()->getCraft(*i);
204 if (rule->getSoldiers() > 0)
205 {
206 _crafts.push_back(*i);
207 }
208 }
209
210 _cbxMission->setColor(Palette::blockOffset(15)-1);
211 _cbxMission->setOptions(_missionTypes);
212 _cbxMission->onChange((ActionHandler)&NewBattleState::cbxMissionChange);
213
214 _cbxCraft->setColor(Palette::blockOffset(15)-1);
215 _cbxCraft->setOptions(_crafts);
216 _cbxCraft->onChange((ActionHandler)&NewBattleState::cbxCraftChange);
217
218 _slrDarkness->setColor(Palette::blockOffset(15)-1);
219 _slrDarkness->setRange(0, 15);
220
221 _cbxTerrain->setColor(Palette::blockOffset(15)-1);
222 _cbxTerrain->setOptions(terrainStrings);
223
224 _cbxDifficulty->setColor(Palette::blockOffset(15)-1);
225 _cbxDifficulty->setOptions(difficulty);
226
227 _cbxAlienRace->setColor(Palette::blockOffset(15)-1);
228 _cbxAlienRace->setOptions(_alienRaces);
229
230 _slrAlienTech->setColor(Palette::blockOffset(15)-1);
231 _slrAlienTech->setRange(0, _game->getRuleset()->getAlienItemLevels().size()-1);
232
233 _btnEquip->setColor(Palette::blockOffset(15)-1);
234 _btnEquip->setText(tr("STR_EQUIP_CRAFT"));
235 _btnEquip->onMouseClick((ActionHandler)&NewBattleState::btnEquipClick);
236
237 _btnRandom->setColor(Palette::blockOffset(8)+5);
238 _btnRandom->setText(tr("STR_RANDOMIZE"));
239 _btnRandom->onMouseClick((ActionHandler)&NewBattleState::btnRandomClick);
240
241 _btnOk->setColor(Palette::blockOffset(8)+5);
242 _btnOk->setText(tr("STR_OK"));
243 _btnOk->onMouseClick((ActionHandler)&NewBattleState::btnOkClick);
244 _btnOk->onKeyboardPress((ActionHandler)&NewBattleState::btnOkClick, Options::keyOk);
245
246 _btnCancel->setColor(Palette::blockOffset(8)+5);
247 _btnCancel->setText(tr("STR_CANCEL"));
248 _btnCancel->onMouseClick((ActionHandler)&NewBattleState::btnCancelClick);
249 _btnCancel->onKeyboardPress((ActionHandler)&NewBattleState::btnCancelClick, Options::keyCancel);
250
251 load();
252 }
253
254 /**
255 *
256 */
~NewBattleState()257 NewBattleState::~NewBattleState()
258 {
259
260 }
261
262 /**
263 * Resets the menu music and savegame
264 * when coming back from the battlescape.
265 */
init()266 void NewBattleState::init()
267 {
268 State::init();
269
270 if (_craft == 0)
271 {
272 load();
273 }
274 }
275
276 /**
277 * Loads new battle data from a YAML file.
278 * @param filename YAML filename.
279 */
load(const std::string & filename)280 void NewBattleState::load(const std::string &filename)
281 {
282 std::string s = Options::getConfigFolder() + filename + ".cfg";
283 try
284 {
285 YAML::Node doc = YAML::LoadFile(s);
286 _cbxMission->setSelected(std::min(doc["mission"].as<size_t>(0), _missionTypes.size()-1));
287 _cbxCraft->setSelected(std::min(doc["craft"].as<size_t>(0), _crafts.size()-1));
288 _slrDarkness->setValue(doc["darkness"].as<size_t>(0));
289 _cbxTerrain->setSelected(std::min(doc["terrain"].as<size_t>(0), _terrainTypes.size()-1));
290 _cbxAlienRace->setSelected(std::min(doc["alienRace"].as<size_t>(0), _alienRaces.size()-1));
291 _cbxDifficulty->setSelected(doc["difficulty"].as<size_t>(0));
292 _slrAlienTech->setValue(doc["alienTech"].as<size_t>(0));
293 cbxMissionChange(NULL);
294
295 if (doc["base"])
296 {
297 const Ruleset *rule = _game->getRuleset();
298 SavedGame *save = new SavedGame();
299
300 Base *base = new Base(rule);
301 base->load(doc["base"], save, false);
302 save->getBases()->push_back(base);
303
304 // Add research
305 const std::vector<std::string> &research = rule->getResearchList();
306 for (std::vector<std::string>::const_iterator i = research.begin(); i != research.end(); ++i)
307 {
308 save->addFinishedResearch(rule->getResearch(*i));
309 }
310
311 // Generate items
312 base->getItems()->getContents()->clear();
313 const std::vector<std::string> &items = rule->getItemsList();
314 for (std::vector<std::string>::const_iterator i = items.begin(); i != items.end(); ++i)
315 {
316 RuleItem *rule = _game->getRuleset()->getItem(*i);
317 if (rule->getBattleType() != BT_CORPSE && rule->isRecoverable())
318 {
319 base->getItems()->addItem(*i, 1);
320 }
321 }
322
323 // Clear invalid contents
324 _craft = base->getCrafts()->front();
325 for (std::map<std::string, int>::iterator i = _craft->getItems()->getContents()->begin(); i != _craft->getItems()->getContents()->end(); ++i)
326 {
327 RuleItem *rule = _game->getRuleset()->getItem(i->first);
328 if (!rule)
329 {
330 i->second = 0;
331 }
332 }
333
334 _game->setSavedGame(save);
335 }
336 else
337 {
338 initSave();
339 }
340 }
341 catch (YAML::Exception e)
342 {
343 Log(LOG_WARNING) << e.what();
344 initSave();
345 }
346 }
347
348 /**
349 * Saves new battle data to a YAML file.
350 * @param filename YAML filename.
351 */
save(const std::string & filename)352 void NewBattleState::save(const std::string &filename)
353 {
354 std::string s = Options::getConfigFolder() + filename + ".cfg";
355 std::ofstream sav(s.c_str());
356 if (!sav)
357 {
358 Log(LOG_WARNING) << "Failed to save " << filename << ".cfg";
359 return;
360 }
361 YAML::Emitter out;
362
363 YAML::Node node;
364 node["mission"] = _cbxMission->getSelected();
365 node["craft"] = _cbxCraft->getSelected();
366 node["darkness"] = _slrDarkness->getValue();
367 node["terrain"] = _cbxTerrain->getSelected();
368 node["alienRace"] = _cbxAlienRace->getSelected();
369 node["difficulty"] = _cbxDifficulty->getSelected();
370 node["alienTech"] = _slrAlienTech->getValue();
371 node["base"] = _game->getSavedGame()->getBases()->front()->save();
372 out << node;
373
374 sav << out.c_str();
375 sav.close();
376 }
377
378 /**
379 * Initializes a new savegame with
380 * everything available.
381 */
initSave()382 void NewBattleState::initSave()
383 {
384 const Ruleset *rule = _game->getRuleset();
385 SavedGame *save = new SavedGame();
386 Base *base = new Base(rule);
387 const YAML::Node &starter = _game->getRuleset()->getStartingBase();
388 base->load(starter, save, true, true);
389 save->getBases()->push_back(base);
390
391 // kill everything we don't want in this base
392 for (std::vector<Soldier*>::iterator i = base->getSoldiers()->begin(); i != base->getSoldiers()->end(); ++i) delete (*i);
393 base->getSoldiers()->clear();
394 for (std::vector<Craft*>::iterator i = base->getCrafts()->begin(); i != base->getCrafts()->end(); ++i) delete (*i);
395 base->getCrafts()->clear();
396 base->getItems()->getContents()->clear();
397
398 _craft = new Craft(rule->getCraft(_crafts[_cbxCraft->getSelected()]), base, 1);
399 base->getCrafts()->push_back(_craft);
400
401 // Generate soldiers
402 for (int i = 0; i < 30; ++i)
403 {
404 Soldier *soldier = rule->genSoldier(save);
405
406 for (int n = 0; n < 5; ++n)
407 {
408 if (RNG::percent(70))
409 continue;
410 soldier->promoteRank();
411
412 UnitStats* stats = soldier->getCurrentStats();
413 stats->tu += RNG::generate(0, 5);
414 stats->stamina += RNG::generate(0, 5);
415 stats->health += RNG::generate(0, 5);
416 stats->bravery += RNG::generate(0, 5);
417 stats->reactions += RNG::generate(0, 5);
418 stats->firing += RNG::generate(0, 5);
419 stats->throwing += RNG::generate(0, 5);
420 stats->strength += RNG::generate(0, 5);
421 stats->psiStrength += RNG::generate(0, 5);
422 stats->melee += RNG::generate(0, 5);
423 stats->psiSkill += RNG::generate(0, 20);
424 }
425 UnitStats* stats = soldier->getCurrentStats();
426 stats->bravery = (int)ceil(stats->bravery / 10.0) * 10; // keep it a multiple of 10
427
428 base->getSoldiers()->push_back(soldier);
429 if (i < _craft->getRules()->getSoldiers())
430 soldier->setCraft(_craft);
431 }
432
433 // Generate items
434 const std::vector<std::string> &items = rule->getItemsList();
435 for (std::vector<std::string>::const_iterator i = items.begin(); i != items.end(); ++i)
436 {
437 RuleItem *rule = _game->getRuleset()->getItem(*i);
438 if (rule->getBattleType() != BT_CORPSE && rule->isRecoverable())
439 {
440 base->getItems()->addItem(*i, 1);
441 if (rule->getBattleType() != BT_NONE && !rule->isFixed() && rule->getBigSprite() > -1)
442 {
443 _craft->getItems()->addItem(*i, 1);
444 }
445 }
446 }
447
448 // Add research
449 const std::vector<std::string> &research = rule->getResearchList();
450 for (std::vector<std::string>::const_iterator i = research.begin(); i != research.end(); ++i)
451 {
452 save->addFinishedResearch(rule->getResearch(*i));
453 }
454
455 _game->setSavedGame(save);
456 }
457
458 /**
459 * Starts the battle.
460 * @param action Pointer to an action.
461 */
btnOkClick(Action *)462 void NewBattleState::btnOkClick(Action *)
463 {
464 save();
465 if (_missionTypes[_cbxMission->getSelected()] != "STR_BASE_DEFENSE" && _craft->getNumSoldiers() == 0 && _craft->getNumVehicles() == 0)
466 {
467 return;
468 }
469
470 SavedBattleGame *bgame = new SavedBattleGame();
471 _game->getSavedGame()->setBattleGame(bgame);
472 bgame->setMissionType(_missionTypes[_cbxMission->getSelected()]);
473 BattlescapeGenerator bgen = BattlescapeGenerator(_game);
474
475 bgen.setWorldTexture(_textures[_cbxTerrain->getSelected()]);
476
477 if (_missionTypes[_cbxMission->getSelected()] == "STR_TERROR_MISSION")
478 {
479 TerrorSite *t = new TerrorSite();
480 t->setId(1);
481 _craft->setDestination(t);
482 bgen.setTerrorSite(t);
483 bgen.setCraft(_craft);
484 _game->getSavedGame()->getTerrorSites()->push_back(t);
485 }
486 else if (_missionTypes[_cbxMission->getSelected()] == "STR_BASE_DEFENSE")
487 {
488 bgen.setBase(_craft->getBase());
489 }
490 else if (_missionTypes[_cbxMission->getSelected()] == "STR_ALIEN_BASE_ASSAULT")
491 {
492 AlienBase *b = new AlienBase();
493 b->setId(1);
494 _craft->setDestination(b);
495 bgen.setAlienBase(b);
496 bgen.setCraft(_craft);
497 _game->getSavedGame()->getAlienBases()->push_back(b);
498 }
499 else if (_missionTypes[_cbxMission->getSelected()] == "STR_MARS_CYDONIA_LANDING" || _missionTypes[_cbxMission->getSelected()] == "STR_MARS_THE_FINAL_ASSAULT")
500 {
501 bgen.setCraft(_craft);
502 }
503 else if (_craft)
504 {
505 Ufo *u = new Ufo(_game->getRuleset()->getUfo(_missionTypes[_cbxMission->getSelected()]));
506 u->setId(1);
507 _craft->setDestination(u);
508 bgen.setUfo(u);
509 bgen.setCraft(_craft);
510 if (_terrainTypes[_cbxTerrain->getSelected()] == "FOREST")
511 {
512 u->setLatitude(-0.5);
513 }
514 // either ground assault or ufo crash
515 if (RNG::generate(0,1) == 1)
516 bgame->setMissionType("STR_UFO_GROUND_ASSAULT");
517 else
518 bgame->setMissionType("STR_UFO_CRASH_RECOVERY");
519 _game->getSavedGame()->getUfos()->push_back(u);
520 }
521 if (_craft)
522 _craft->setSpeed(0);
523 _game->getSavedGame()->setDifficulty((GameDifficulty)_cbxDifficulty->getSelected());
524
525 bgen.setWorldShade(_slrDarkness->getValue());
526 bgen.setAlienRace(_alienRaces[_cbxAlienRace->getSelected()]);
527 bgen.setAlienItemlevel(_slrAlienTech->getValue());
528
529 bgen.run();
530 //_game->pushState(new BattlescapeState(_game));
531 Base *base = 0;
532 if (_missionTypes[_cbxMission->getSelected()] == "STR_BASE_DEFENSE")
533 {
534 base = _craft->getBase();
535 _craft = 0;
536 }
537 _game->popState();
538 _game->popState();
539 _game->pushState(new BriefingState(_game, _craft, base));
540 _craft = 0;
541 }
542
543 /**
544 * Returns to the previous screen.
545 * @param action Pointer to an action.
546 */
btnCancelClick(Action *)547 void NewBattleState::btnCancelClick(Action *)
548 {
549 save();
550 _game->setSavedGame(0);
551 _game->popState();
552 }
553
554 /**
555 * Randomize the state
556 * @param action Pointer to an action.
557 */
btnRandomClick(Action *)558 void NewBattleState::btnRandomClick(Action *)
559 {
560 _cbxMission->setSelected(RNG::generate(0, _missionTypes.size()-1));
561 _cbxCraft->setSelected(RNG::generate(0, _crafts.size()-1));
562 _slrDarkness->setValue(RNG::generate(0, 15));
563 _cbxTerrain->setSelected(RNG::generate(0, _terrainTypes.size()-1));
564 _cbxAlienRace->setSelected(RNG::generate(0, _alienRaces.size()-1));
565 _cbxDifficulty->setSelected(RNG::generate(0, 4));
566 _slrAlienTech->setValue(RNG::generate(0, _game->getRuleset()->getAlienItemLevels().size()-1));
567 cbxMissionChange(0);
568 cbxCraftChange(0);
569
570 initSave();
571 }
572
573 /**
574 * Shows the Craft Info screen.
575 * @param action Pointer to an action.
576 */
btnEquipClick(Action *)577 void NewBattleState::btnEquipClick(Action *)
578 {
579 _game->pushState(new CraftInfoState(_game, _game->getSavedGame()->getBases()->front(), 0));
580 }
581
582 /**
583 * Updates Map Options based on the
584 * current Mission type.
585 * @param action Pointer to an action.
586 */
cbxMissionChange(Action *)587 void NewBattleState::cbxMissionChange(Action *)
588 {
589 AlienDeployment *ruleDeploy = _game->getRuleset()->getDeployment(_missionTypes[_cbxMission->getSelected()]);
590 _txtDarkness->setVisible(ruleDeploy->getShade() == -1);
591 _slrDarkness->setVisible(ruleDeploy->getShade() == -1);
592 _txtTerrain->setVisible(ruleDeploy->getTerrains().empty());
593 _cbxTerrain->setVisible(ruleDeploy->getTerrains().empty());
594 }
595
596 /**
597 * Updates craft accordingly.
598 * @param action Pointer to an action.
599 */
cbxCraftChange(Action *)600 void NewBattleState::cbxCraftChange(Action *)
601 {
602 _craft->setRules(_game->getRuleset()->getCraft(_crafts[_cbxCraft->getSelected()]));
603 int current = _craft->getNumSoldiers();
604 int max = _craft->getRules()->getSoldiers();
605 if (current > max)
606 {
607 for (std::vector<Soldier*>::reverse_iterator i = _craft->getBase()->getSoldiers()->rbegin(); i != _craft->getBase()->getSoldiers()->rend() && current > max; ++i)
608 {
609 if ((*i)->getCraft() == _craft)
610 {
611 (*i)->setCraft(0);
612 current--;
613 }
614 }
615 }
616 }
617
618 }
619