diff --git a/src/Basescape/ManufactureInfoState.cpp b/src/Basescape/ManufactureInfoState.cpp index 05d7da3333..c995c619f3 100644 --- a/src/Basescape/ManufactureInfoState.cpp +++ b/src/Basescape/ManufactureInfoState.cpp @@ -424,7 +424,7 @@ void ManufactureInfoState::lessEngineerClick(Action *action) void ManufactureInfoState::moreUnit(int change) { if (change <= 0) return; - if (_production->getRules()->getProducedCraft() && _base->getAvailableHangars() - _base->getUsedHangars() <= 0) + if (!_base->addHangarCraftProduction(_production)) { _timerMoreUnit->stop(); _game->pushState(new ErrorMessageState(tr("STR_NO_FREE_HANGARS_FOR_CRAFT_PRODUCTION"), _palette, _game->getMod()->getInterface("basescape")->getElement("errorMessage")->color, "BACK17.SCR", _game->getMod()->getInterface("basescape")->getElement("errorPalette")->color)); diff --git a/src/Basescape/ManufactureStartState.cpp b/src/Basescape/ManufactureStartState.cpp index 30560b790a..9b7c007dc1 100644 --- a/src/Basescape/ManufactureStartState.cpp +++ b/src/Basescape/ManufactureStartState.cpp @@ -172,7 +172,7 @@ void ManufactureStartState::btnCancelClick(Action *) */ void ManufactureStartState::btnStartClick(Action *) { - if (_item->getProducedCraft() && _base->getAvailableHangars() - _base->getUsedHangars() <= 0) + if (!_base->haveHangarForCrafrType(_item->getProducedCraft())) { _game->pushState(new ErrorMessageState(tr("STR_NO_FREE_HANGARS_FOR_CRAFT_PRODUCTION"), _palette, _game->getMod()->getInterface("basescape")->getElement("errorMessage")->color, "BACK17.SCR", _game->getMod()->getInterface("basescape")->getElement("errorPalette")->color)); } diff --git a/src/Basescape/PurchaseState.cpp b/src/Basescape/PurchaseState.cpp index 6b20cfe992..b8c419dfa2 100644 --- a/src/Basescape/PurchaseState.cpp +++ b/src/Basescape/PurchaseState.cpp @@ -38,6 +38,7 @@ #include "../Mod/RuleCraft.h" #include "../Mod/RuleItem.h" #include "../Savegame/Base.h" +#include "../Savegame/HangarAllocation.h" #include "../Engine/Action.h" #include "../Savegame/Craft.h" #include "../Savegame/ItemContainer.h" @@ -69,6 +70,7 @@ PurchaseState::PurchaseState(Base *base) : _base(base), _sel(0), _total(0), _pQt _txtQuantity = new Text(60, 9, 256, 44); _cbxCategory = new ComboBox(this, 120, 16, 10, 36); _lstItems = new TextList(287, 120, 8, 54); + _hangarAllocation = new HangarAllocation(_base); // Set palette setInterface("buyMenu"); @@ -232,6 +234,7 @@ PurchaseState::~PurchaseState() { delete _timerInc; delete _timerDec; + delete _hangarAllocation; } /** @@ -359,14 +362,16 @@ void PurchaseState::btnOkClick(Action *) _base->getTransfers()->push_back(t); break; case TRANSFER_CRAFT: - for (int c = 0; c < i->amount; c++) { RuleCraft *rule = (RuleCraft*)i->rule; - t = new Transfer(rule->getTransferTime()); - Craft *craft = new Craft(rule, _base, _game->getSavedGame()->getId(rule->getType())); - craft->setStatus("STR_REFUELLING"); - t->setCraft(craft); - _base->getTransfers()->push_back(t); + for (int c = 0; c < i->amount; c++) + { + t = new Transfer(rule->getTransferTime()); + Craft *craft = new Craft(rule, _base, _game->getSavedGame()->getId(rule->getType())); + craft->setStatus("STR_REFUELLING"); + t->setCraft(craft); + _base->getTransfers()->push_back(t); + } } break; case TRANSFER_ITEM: @@ -380,6 +385,7 @@ void PurchaseState::btnOkClick(Action *) } } } + _base->initHangars(); _game->popState(); } @@ -522,7 +528,6 @@ void PurchaseState::increaseByValue(int change) } else { - RuleItem *rule = nullptr; switch (getRow().type) { case TRANSFER_SOLDIER: @@ -534,18 +539,27 @@ void PurchaseState::increaseByValue(int change) } break; case TRANSFER_CRAFT: - if (_cQty + 1 > _base->getAvailableHangars() - _base->getUsedHangars()) { - errorMessage = tr("STR_NO_FREE_HANGARS_FOR_PURCHASE"); + RuleCraft *rule = (RuleCraft*)getRow().rule; + if (_cQty + 1 > _base->getAvailableHangars() - _base->getUsedHangars()) + { + errorMessage = tr("STR_NO_FREE_HANGARS_FOR_PURCHASE"); + } + else if (!_hangarAllocation->addCraftType(rule)) + { + errorMessage = tr("STR_NO_FREE_HANGARS_FOR_PURCHASE"); + } + break; } - break; case TRANSFER_ITEM: - rule = (RuleItem*)getRow().rule; - if (_iQty + rule->getSize() > _base->getAvailableStores() - _base->getUsedStores()) { - errorMessage = tr("STR_NOT_ENOUGH_STORE_SPACE"); + RuleItem *rule = (RuleItem*)getRow().rule; + if (_iQty + rule->getSize() > _base->getAvailableStores() - _base->getUsedStores()) + { + errorMessage = tr("STR_NOT_ENOUGH_STORE_SPACE"); + } + break; } - break; } } @@ -567,24 +581,24 @@ void PurchaseState::increaseByValue(int change) break; case TRANSFER_CRAFT: { - int maxByHangars = _base->getAvailableHangars() - _base->getUsedHangars() - _cQty; + int maxByHangars = 1; //alwas 1 becasue we need check each craft change = std::min(maxByHangars, change); _cQty += change; } break; case TRANSFER_ITEM: - { - RuleItem *rule = (RuleItem*)getRow().rule; - double storesNeededPerItem = rule->getSize(); - double freeStores = _base->getAvailableStores() - _base->getUsedStores() - _iQty; - double maxByStores = (double)(INT_MAX); - if (!AreSame(storesNeededPerItem, 0.0)) { - maxByStores = (freeStores + 0.05) / storesNeededPerItem; + RuleItem *rule = (RuleItem*)getRow().rule; + double storesNeededPerItem = rule->getSize(); + double freeStores = _base->getAvailableStores() - _base->getUsedStores() - _iQty; + double maxByStores = (double)(INT_MAX); + if (!AreSame(storesNeededPerItem, 0.0)) + { + maxByStores = (freeStores + 0.05) / storesNeededPerItem; + } + change = std::min((int)maxByStores, change); + _iQty += change * storesNeededPerItem; } - change = std::min((int)maxByStores, change); - _iQty += change * storesNeededPerItem; - } break; } getRow().amount += change; @@ -618,7 +632,6 @@ void PurchaseState::decreaseByValue(int change) if (0 >= change || 0 >= getRow().amount) return; change = std::min(getRow().amount, change); - RuleItem *rule = nullptr; switch (getRow().type) { case TRANSFER_SOLDIER: @@ -627,11 +640,20 @@ void PurchaseState::decreaseByValue(int change) _pQty -= change; break; case TRANSFER_CRAFT: - _cQty -= change; + { + RuleCraft *rule = (RuleCraft*)getRow().rule; + _cQty -= change; + for (int i = 0; i < change; ++i) + { + _hangarAllocation->removeCraftType(rule); + } + } break; case TRANSFER_ITEM: - rule = (RuleItem*)getRow().rule; - _iQty -= rule->getSize() * change; + { + RuleItem *rule = (RuleItem*)getRow().rule; + _iQty -= rule->getSize() * change; + } break; } getRow().amount -= change; diff --git a/src/Basescape/PurchaseState.h b/src/Basescape/PurchaseState.h index 6509a12887..62da6e3586 100644 --- a/src/Basescape/PurchaseState.h +++ b/src/Basescape/PurchaseState.h @@ -33,6 +33,7 @@ class TextList; class ComboBox; class Timer; class Base; +class HangarAllocation; /** * Purchase/Hire screen that lets the player buy @@ -48,6 +49,8 @@ class PurchaseState : public State Text *_txtTitle, *_txtFunds, *_txtPurchases, *_txtCost, *_txtQuantity, *_txtSpaceUsed; ComboBox *_cbxCategory; TextList *_lstItems; + HangarAllocation *_hangarAllocation; + std::vector _items; std::vector _rows; std::vector _cats; diff --git a/src/Basescape/TransferItemsState.cpp b/src/Basescape/TransferItemsState.cpp index 09efc9e15a..f4ebc96a06 100644 --- a/src/Basescape/TransferItemsState.cpp +++ b/src/Basescape/TransferItemsState.cpp @@ -33,6 +33,7 @@ #include "../Savegame/BaseFacility.h" #include "../Savegame/SavedGame.h" #include "../Savegame/Base.h" +#include "../Savegame/HangarAllocation.h" #include "../Savegame/Soldier.h" #include "../Savegame/Craft.h" #include "../Savegame/ItemContainer.h" @@ -68,6 +69,7 @@ TransferItemsState::TransferItemsState(Base *baseFrom, Base *baseTo) : _baseFrom _txtAmountDestination = new Text(60, 17, 260, 24); _cbxCategory = new ComboBox(this, 120, 16, 10, 24); _lstItems = new TextList(287, 128, 8, 44); + _hangarAllocation = new HangarAllocation(_baseTo); // Set palette setInterface("transferMenu"); @@ -221,6 +223,7 @@ TransferItemsState::~TransferItemsState() { delete _timerInc; delete _timerDec; + delete _hangarAllocation; } /** @@ -359,13 +362,17 @@ void TransferItemsState::completeTransfer() break; case TRANSFER_CRAFT: craft = (Craft*)i->rule; + craft->setHangar(nullptr); // Transfer soldiers inside craft for (std::vector::iterator s = _baseFrom->getSoldiers()->begin(); s != _baseFrom->getSoldiers()->end();) { if ((*s)->getCraft() == craft) { if ((*s)->isInPsiTraining()) (*s)->setPsiTraining(); - if (craft->getStatus() == "STR_OUT") _baseTo->getSoldiers()->push_back(*s); + if (craft->getStatus() == "STR_OUT") + { + _baseTo->getSoldiers()->push_back(*s); + } else { t = new Transfer(time); @@ -443,6 +450,7 @@ void TransferItemsState::completeTransfer() } } } + _baseTo->initHangars(); } /** @@ -597,6 +605,10 @@ void TransferItemsState::increaseByValue(int change) { errorMessage = tr("STR_NO_FREE_HANGARS_FOR_TRANSFER"); } + else if (_hangarAllocation->addCraftType(craft->getRules())) + { + errorMessage = tr("STR_NO_FREE_HANGARS_FOR_TRANSFER"); + } else if (_pQty + craft->getNumSoldiers() > _baseTo->getAvailableQuarters() - _baseTo->getUsedQuarters()) { errorMessage = tr("STR_NO_FREE_ACCOMODATION_CREW"); @@ -672,7 +684,7 @@ void TransferItemsState::increaseByValue(int change) { _timerInc->stop(); RuleInterface *menuInterface = _game->getMod()->getInterface("transferMenu"); - _game->pushState(new ErrorMessageState(errorMessage, _palette, menuInterface->getElement("errorMessage")->color, "BACK13.SCR", menuInterface->getElement("errorPalette")->color)); + _game->pushState(new ErrorMessageState(errorMessage, _palette, menuInterface->getElement("errorMessage")->color, "BACK13.SCR", menuInterface->getElement("errorPalette")->color)); } } @@ -695,7 +707,7 @@ void TransferItemsState::decreaseByValue(int change) if (0 >= change || 0 >= getRow().amount) return; Craft *craft = 0; change = std::min(getRow().amount, change); - + switch (getRow().type) { case TRANSFER_SOLDIER: @@ -705,6 +717,7 @@ void TransferItemsState::decreaseByValue(int change) break; case TRANSFER_CRAFT: craft = (Craft*)getRow().rule; + _hangarAllocation->removeCraftType(craft->getRules()); _cQty--; _pQty -= craft->getNumSoldiers(); _iQty -= craft->getItems()->getTotalSize(_game->getMod()); diff --git a/src/Basescape/TransferItemsState.h b/src/Basescape/TransferItemsState.h index fdc01eb8b7..eef1d61e8d 100644 --- a/src/Basescape/TransferItemsState.h +++ b/src/Basescape/TransferItemsState.h @@ -33,6 +33,7 @@ class TextList; class ComboBox; class Timer; class Base; +class HangarAllocation; /** * Transfer screen that lets the player pick @@ -42,11 +43,14 @@ class TransferItemsState : public State { private: Base *_baseFrom, *_baseTo; + TextButton *_btnOk, *_btnCancel; Window *_window; Text *_txtTitle, *_txtQuantity, *_txtAmountTransfer, *_txtAmountDestination; ComboBox *_cbxCategory; TextList *_lstItems; + HangarAllocation *_hangarAllocation; + std::vector _items; std::vector _rows; std::vector _cats; diff --git a/src/Engine/Collections.h b/src/Engine/Collections.h index e0caed9cf3..d0103857b4 100644 --- a/src/Engine/Collections.h +++ b/src/Engine/Collections.h @@ -20,6 +20,7 @@ #include #include #include +#include #include "Exception.h" namespace OpenXcom @@ -28,9 +29,66 @@ namespace OpenXcom /** * Helper class for managing object colections */ -class Collections +struct Collections { -public: + template + class ForwardPosIterator + { + C* _colection; + std::size_t _position; + + public: + + ForwardPosIterator() : _colection{ nullptr }, _position{ 0 } + { + + } + + ForwardPosIterator(C* c, std::size_t pos) : _colection{ nullptr }, _position{ pos } + { + + } + + bool operator==(const ForwardPosIterator& other) + { + return other._colection == _colection && other._position == _position; + } + + typename C::reference operator*() const + { + return _colection->raw_data(_position); + } + + ForwardPosIterator operator++() + { + *this = _colection->find(_position + 1); + return this; + } + + ForwardPosIterator operator++(int) + { + auto pos = _position; + ++(*this); + return { _colection, pos }; + } + }; + + template + class SkipVector + { + struct SkipTable + { + Uint8 groupSkip; + Uint8 itemSkip[64]; + }; + + std::vector _skipTable; + std::vector _items; + + public: + + }; + template static void deleteAll(const T& p) { @@ -181,6 +239,246 @@ class Collections } return numberToRemove; } + + + + template + struct ValueIterator + { + T value; + + T operator*() + { + return value; + } + + void operator++() + { + ++value; + } + + void operator--() + { + --value; + } + + bool operator!=(const ValueIterator& r) + { + return value != r.value; + } + }; + + template + struct ZipIterator + { + ItA first; + ItB second; + + auto operator*() -> decltype(std::make_pair(*first, *second)) + { + return std::make_pair(*first, *second); + } + + void operator++() + { + ++first; + ++second; + } + + bool operator!=(const ZipIterator& r) + { + return first != r.first && second != r.second; //zip will stop when one of ranges ends, this is why `&&` isntade of `||` + } + }; + + template + class FilterIterator + { + It _curr; + It _end; + Filter _filter; + + public: + + FilterIterator(It curr, It end, Filter filter) : + _curr { std::move(curr)}, + _end { std::move(end)}, + _filter { std::move(filter)} + { + + } + + auto operator*() -> decltype(*_curr) + { + return *_curr; + } + + void operator++() + { + ++_curr; + while (_curr != _end && !_filter(*_curr)) + { + ++_curr; + } + } + + bool operator!=(const FilterIterator& r) + { + return _curr != r._curr; + } + }; + + template + struct ReverseIterator + { + It _curr; + + public: + + ReverseIterator(It curr) : + _curr{ std::move(curr) } + { + + } + + auto operator*() -> decltype(*_curr) + { + auto copy = _curr; + --copy; + return *copy; + } + + void operator++() + { + --_curr; + } + + bool operator!=(const ReverseIterator& r) + { + return _curr != r._curr; + } + }; + + template + class Range + { + It _begin; + It _end; + public: + + Range(It begin, It end) : + _begin { std::move(begin) }, + _end { std::move(end) } + { + } + + It begin() + { + return _begin; + } + It end() + { + return _end; + } + }; + + /** + * Crate range from `min` to `max` (excluding), if `max > min` it return empty range. + * @param min minimum value of range + * @param max maximum value of range + * @return + */ + template + static Range> rangeValue(T begin, T end) + { + if (begin < end) + { + return { ValueIterator{ begin }, ValueIterator{ end } }; + } + else + { + return { ValueIterator{ end }, ValueIterator{ end } }; + } + } + + /** + * Crate range from zero to less than `end` + * @param end + * @return + */ + template + static Range> rangeValueLess(T end) + { + return rangeValue(T{}, end); + } + + template + static Range range(It begin, It end) + { + return { begin, end }; + } + + template + static Range range(std::vector& v) + { + return { v.data(), v.data() + v.size() }; + } + + template + static Range range(const std::vector& v) + { + return { v.data(), v.data() + v.size() }; + } + + template + static Range range(T (&a)[N]) + { + return { std::begin(a), std::end(a) }; + } + + template + static Range> zip(Range a, Range b) + { + return { { a.begin(), b.begin() }, { a.end(), b.end() } }; + } + + template + static Range> reverse(Range a) + { + return { a.end(), a.begin() }; + } + + template + static Range reverse(Range> a) + { + return { a.end().curr, a.begin().curr }; + } + + template + static Range> filter(Range a, Filter f) + { + auto begin = a.begin(); + auto end = a.end(); + if (begin != end) + { + auto fbegin = FilterIterator{ begin, end, f }; + if (!f(*begin)) + { + ++fbegin; + } + return { fbegin, { end, end, f } }; + } + else + { + return { { end, end, f }, { end, end, f } }; + } + } + + template + static Range> nonDeref(Range a) + { + return { { a.begin() }, { a.end() } }; + } }; } diff --git a/src/Mod/RuleBaseFacility.cpp b/src/Mod/RuleBaseFacility.cpp index 4d2192e0ff..843cadec2e 100644 --- a/src/Mod/RuleBaseFacility.cpp +++ b/src/Mod/RuleBaseFacility.cpp @@ -347,6 +347,18 @@ int RuleBaseFacility::getCrafts() const return _crafts; } +/** + * Gets if given craft type can be stroed in hangar. + */ +bool RuleBaseFacility::isCraftTypeAllowed(const RuleCraft* craft) const +{ + auto x = (unsigned long long)craft; + x ^= x >> 12; // a + x ^= x << 25; // b + x ^= x >> 27; // c + return (x * 2685821657736338717ULL) & 0x1000; +} + /** * Gets the amount of laboratory space this facility provides * for research projects. diff --git a/src/Mod/RuleBaseFacility.h b/src/Mod/RuleBaseFacility.h index 41df80e127..ea73c34fae 100644 --- a/src/Mod/RuleBaseFacility.h +++ b/src/Mod/RuleBaseFacility.h @@ -27,6 +27,7 @@ namespace OpenXcom { class Mod; +class RuleCraft; /** * Represents a specific type of base facility. @@ -97,6 +98,8 @@ class RuleBaseFacility int getAliens() const; /// Gets the facility's craft capacity. int getCrafts() const; + /// Gets if given craft type can be stroed in hangar. + bool isCraftTypeAllowed(const RuleCraft* craft) const; /// Gets the facility's laboratory space. int getLaboratories() const; /// Gets the facility's workshop space. diff --git a/src/Savegame/Base.cpp b/src/Savegame/Base.cpp index f85d941254..d706f08150 100644 --- a/src/Savegame/Base.cpp +++ b/src/Savegame/Base.cpp @@ -20,6 +20,8 @@ #include "../fmath.h" #include #include +#include +#include #include "BaseFacility.h" #include "../Mod/RuleBaseFacility.h" #include "Craft.h" @@ -45,6 +47,7 @@ #include "../Mod/RuleSoldier.h" #include "../Engine/Logger.h" #include "../Engine/Collections.h" +#include "HangarAllocation.h" namespace OpenXcom { @@ -214,7 +217,7 @@ void Base::load(const YAML::Node &node, SavedGame *save, bool newGame, bool newB if (_mod->getManufacture(item)) { Production *p = new Production(_mod->getManufacture(item), 0); - p->load(*i); + p->load(*i, this); _productions.push_back(p); } else @@ -225,6 +228,8 @@ void Base::load(const YAML::Node &node, SavedGame *save, bool newGame, bool newB } _retaliationTarget = node["retaliationTarget"].as(_retaliationTarget); + + initHangars(); } /** @@ -261,7 +266,7 @@ YAML::Node Base::save() const } for (std::vector::const_iterator i = _productions.begin(); i != _productions.end(); ++i) { - node["productions"].push_back((*i)->save()); + node["productions"].push_back((*i)->save(this)); } if (_retaliationTarget) node["retaliationTarget"] = _retaliationTarget; @@ -311,6 +316,51 @@ std::vector *Base::getFacilities() return &_facilities; } +/** + * Returns the list of facilities in the base. + * @return Pointer to the facility list. + */ +const std::vector *Base::getFacilities() const +{ + return &_facilities; +} + +/** + * Get facilitie from reference. + */ +BaseFacility* Base::loadFacilitieReference(const YAML::Node &node) const +{ + if (node) + { + BaseFacility temp(nullptr, nullptr); + temp.load(node); + auto x = temp.getX(); + auto y = temp.getY(); + + for (auto f : _facilities) + { + if (f->getX() == x && f->getY() == y) + { + return f; + } + } + } + return nullptr; +} +/** + * Save facilitie reference. + */ +YAML::Node Base::saveFacilitieReference(BaseFacility* fac) const +{ + YAML::Node n; + if (fac) + { + // we need only x and y but we save copy of all data for debuging + n = fac->save(); + } + return n; +} + /** * Returns the list of soldiers in the base. * @return Pointer to the soldier list. @@ -329,6 +379,15 @@ std::vector *Base::getCrafts() return &_crafts; } +/** + * Returns the list of crafts in the base. + * @return Pointer to the craft list. + */ +const std::vector *Base::getCrafts() const +{ + return &_crafts; +} + /** * Returns the list of transfers destined * to this base. @@ -339,6 +398,16 @@ std::vector *Base::getTransfers() return &_transfers; } +/** + * Returns the list of transfers destined + * to this base. + * @return Pointer to the transfer list. + */ +const std::vector *Base::getTransfers() const +{ + return &_transfers; +} + /** * Returns the list of items in the base storage rooms. * Does NOT return items assigned to craft or in transfer. @@ -799,6 +868,116 @@ int Base::getAvailableHangars() const return total; } +/** + * Allocate all crafts to hangars. + */ +void Base::initHangars() +{ + auto sortValue = [](BaseFacility* a) { return std::make_tuple(a->getX(), a->getY()); }; + std::sort(_facilities.begin(), _facilities.end(), [&](BaseFacility* a, BaseFacility* b){ return sortValue(a) < sortValue(b); }); + + HangarAllocation allocation(this); + allocation.assignAll(); +} + +/** + * Get hangar by it number. + */ +bool Base::haveHangarForCrafrType(const RuleCraft* type) const +{ + HangarAllocation allocation(this); + return allocation.addCraftType(type); +} + +/** + * Add new craft to base. + */ +bool Base::addHangarCraft(Craft* craft) +{ + HangarAllocation::Skip s; + s.craft = craft; + HangarAllocation allocation(this, s); + return allocation.addCraft(craft); +} + +/* + * Add craft transfer to base + */ +bool Base::addHangarCraftTransfer(Transfer* transfer) +{ + HangarAllocation::Skip s; + s.transfer = transfer; + HangarAllocation allocation(this, s); + return allocation.addCraftTransfer(transfer); +} + +/** + * Add craft production to base. + */ +bool Base::addHangarCraftProduction(Production* prod) +{ + HangarAllocation::Skip s; + s.production = prod; + HangarAllocation allocation(this, s); + return allocation.addCraftProduction(prod); +} + +/** + * Can remove hangar from base. + */ +bool Base::isHangarNeeded(const BaseFacility* hangar) const +{ + return false; +} + +/** + * Remove hangar from base. + */ +bool Base::removeHangar(BaseFacility* hangar) +{ + HangarAllocation::Skip s; + s.hangar = hangar; + HangarAllocation allocation(this, s); + allocation.assignAll(); + + Collections::deleteIf(_crafts, _crafts.size(), + [&](Craft* c) + { + if (c->getHangar() == hangar) + { + c->unload(_mod); + return true; + } + else + { + return false; + } + } + ); + Collections::deleteIf(_productions, _productions.size(), + [&](Production* i) + { + if (i->getHangar() == hangar) + { + _engineers += i->getAssignedEngineers(); + return true; + } + else + { + return false; + } + } + ); + Collections::deleteIf(_transfers, _transfers.size(), + [&](Transfer* i) + { + return i->getHangar() == hangar; + } + ); + + return true; +} + /** * Return laboratories space not used by a ResearchProject * @return laboratories space not used by a ResearchProject @@ -1609,55 +1788,7 @@ void Base::destroyFacility(std::vector::iterator facility) { // hangar destruction - destroy crafts and any production of crafts // if this will mean there is no hangar to contain it - if ((*facility)->getCraftForDrawing()) - { - // remove all soldiers - for (Soldier *s : _soldiers) - { - if (s->getCraft() == (*facility)->getCraftForDrawing()) - { - s->setCraft(0); - } - } - - // remove all items - while (!(*facility)->getCraftForDrawing()->getItems()->getContents()->empty()) - { - std::map::iterator i = (*facility)->getCraftForDrawing()->getItems()->getContents()->begin(); - _items->addItem(i->first, i->second); - (*facility)->getCraftForDrawing()->getItems()->removeItem(i->first, i->second); - } - Collections::deleteIf(_crafts, 1, - [&](Craft* c) - { - return c == (*facility)->getCraftForDrawing(); - } - ); - } - else - { - auto remove = -(getAvailableHangars() - getUsedHangars() - (*facility)->getRules()->getCrafts()); - remove = Collections::deleteIf(_productions, remove, - [&](Production* i) - { - if (i->getRules()->getProducedCraft()) - { - _engineers += i->getAssignedEngineers(); - return true; - } - else - { - return false; - } - } - ); - remove = Collections::deleteIf(_transfers, remove, - [&](Transfer* i) - { - return i->getType() == TRANSFER_CRAFT; - } - ); - } + removeHangar((*facility)); } if ((*facility)->getRules()->getPsiLaboratories() > 0) { diff --git a/src/Savegame/Base.h b/src/Savegame/Base.h index 8511b872c0..4d2f362c77 100644 --- a/src/Savegame/Base.h +++ b/src/Savegame/Base.h @@ -80,18 +80,29 @@ class Base : public Target YAML::Node save() const; /// Saves the base's ID to YAML. YAML::Node saveId() const; + /// Get facilitie by reference. + BaseFacility* loadFacilitieReference(const YAML::Node &node) const; + /// Save facilitie reference. + YAML::Node saveFacilitieReference(BaseFacility* fac) const; + /// Gets the base's name. std::wstring getName(Language *lang = 0) const; /// Gets the base's marker. int getMarker() const; /// Gets the base's facilities. std::vector *getFacilities(); + /// Gets the base's facilities. + const std::vector *getFacilities() const; /// Gets the base's soldiers. std::vector *getSoldiers(); /// Gets the base's crafts. std::vector *getCrafts(); + /// Gets the base's crafts. + const std::vector *getCrafts() const; /// Gets the base's transfers. std::vector *getTransfers(); + /// Gets the base's transfers. + const std::vector *getTransfers() const; /// Gets the base's items. ItemContainer *getStorageItems(); /// Gets the base's scientists. @@ -140,6 +151,20 @@ class Base : public Target int getUsedHangars() const; /// Gets the base's available hangars. int getAvailableHangars() const; + /// Allocate all crafts to hangars. + void initHangars(); + /// Get hangar by it number. + bool haveHangarForCrafrType(const RuleCraft* type) const; + /// Add new craft to base. + bool addHangarCraft(Craft* craft); + /// Add craft transfer to base + bool addHangarCraftTransfer(Transfer* transfer); + /// Add craft production to base. + bool addHangarCraftProduction(Production* prod); + /// Can remove hangar from base. + bool isHangarNeeded(const BaseFacility* hangar) const; + /// Remove hangar from base. + bool removeHangar(BaseFacility* hangar); /// Get the number of available space lab (not used by a ResearchProject) int getFreeLaboratories() const; /// Get the number of available space lab (not used by a Production) diff --git a/src/Savegame/BaseFacility.cpp b/src/Savegame/BaseFacility.cpp index e7f8f5de61..dfc8ee7046 100644 --- a/src/Savegame/BaseFacility.cpp +++ b/src/Savegame/BaseFacility.cpp @@ -169,7 +169,7 @@ bool BaseFacility::inUse() const (_rules->getStorage() > 0 && _base->getAvailableStores() - _rules->getStorage() < _base->getUsedStores()) || (_rules->getLaboratories() > 0 && _base->getAvailableLaboratories() - _rules->getLaboratories() < _base->getUsedLaboratories()) || (_rules->getWorkshops() > 0 && _base->getAvailableWorkshops() - _rules->getWorkshops() < _base->getUsedWorkshops()) || - (_rules->getCrafts() > 0 && _base->getAvailableHangars() - _rules->getCrafts() < _base->getUsedHangars()) || + (_rules->getCrafts() > 0 && !_base->isHangarNeeded(this)) || (_rules->getPsiLaboratories() > 0 && _base->getAvailablePsiLabs() - _rules->getPsiLaboratories() < _base->getUsedPsiLabs()) || (_rules->getTrainingFacilities() > 0 && _base->getAvailableTraining() - _rules->getTrainingFacilities() < _base->getUsedTraining()) || (_rules->getAliens() > 0 && _base->getAvailableContainment() - _rules->getAliens() < _base->getUsedContainment())); diff --git a/src/Savegame/BaseFacility.h b/src/Savegame/BaseFacility.h index baef6e56ae..a5ce787341 100644 --- a/src/Savegame/BaseFacility.h +++ b/src/Savegame/BaseFacility.h @@ -38,6 +38,7 @@ class BaseFacility private: RuleBaseFacility *_rules; Base *_base; + int _id; int _x, _y, _buildTime; Craft *_craftForDrawing; // craft, used for drawing facility public: diff --git a/src/Savegame/Craft.cpp b/src/Savegame/Craft.cpp index 7508d46714..41eb59a3a7 100644 --- a/src/Savegame/Craft.cpp +++ b/src/Savegame/Craft.cpp @@ -37,6 +37,7 @@ #include "../Mod/RuleItem.h" #include "../Mod/AlienDeployment.h" #include "../Engine/Logger.h" +#include "BaseFacility.h" namespace OpenXcom { @@ -49,7 +50,7 @@ namespace OpenXcom * @param id ID to assign to the craft (0 to not assign). */ Craft::Craft(const RuleCraft *rules, Base *base, int id) : MovingTarget(), - _rules(rules), _base(base), _id(0), _fuel(0), _damage(0), + _rules(rules), _base(base), _hangar(nullptr), _id(0), _fuel(0), _damage(0), _interceptionOrder(0), _takeoff(0), _weapons(), _status("STR_READY"), _lowFuel(false), _mission(false), _inBattlescape(false), _inDogfight(false), _stats() @@ -225,6 +226,11 @@ void Craft::load(const YAML::Node &node, const Mod *mod, SavedGame *save) _inBattlescape = node["inBattlescape"].as(_inBattlescape); if (_inBattlescape) setSpeed(0); + //normal loading have base, but in save converter or craft tranfer base is not link to craft. + if (_base) + { + _hangar = _base->loadFacilitieReference(node["hangar"]); + } } /** @@ -267,6 +273,10 @@ YAML::Node Craft::save() const node["interceptionOrder"] = _interceptionOrder; if (_takeoff != 0) node["takeoff"] = _takeoff; + if (_base) + { + node["hangar"] = _base->saveFacilitieReference(_hangar); + } return node; } @@ -1120,4 +1130,20 @@ void Craft::reuseItem(const std::string& item) _status = "STR_REFUELLING"; } +/** + * Sets hangar. + */ +void Craft::setHangar(BaseFacility* f) +{ + _hangar = f; +} + +/* + * Get hangar. + */ +BaseFacility* Craft::getHangar() const +{ + return _hangar; +} + } diff --git a/src/Savegame/Craft.h b/src/Savegame/Craft.h index 4e0279d59c..c2c6369d66 100644 --- a/src/Savegame/Craft.h +++ b/src/Savegame/Craft.h @@ -30,6 +30,7 @@ typedef std::pair CraftId; class RuleCraft; class Base; +class BaseFacility; class Soldier; class CraftWeapon; class ItemContainer; @@ -48,6 +49,7 @@ class Craft : public MovingTarget private: const RuleCraft *_rules; Base *_base; + BaseFacility *_hangar; int _id, _fuel, _damage, _interceptionOrder, _takeoff; std::vector _weapons; ItemContainer *_items; @@ -186,6 +188,10 @@ class Craft : public MovingTarget void unload(const Mod *mod); /// Reuses a base item. void reuseItem(const std::string &item); + /// Sets hangar. + void setHangar(BaseFacility* f); + /// Get hangar. + BaseFacility* getHangar() const; }; } diff --git a/src/Savegame/HangarAllocation.cpp b/src/Savegame/HangarAllocation.cpp new file mode 100644 index 0000000000..31e1c1e587 --- /dev/null +++ b/src/Savegame/HangarAllocation.cpp @@ -0,0 +1,724 @@ +/* + * Copyright 2010-2016 OpenXcom Developers. + * + * This file is part of OpenXcom. + * + * OpenXcom is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenXcom is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenXcom. If not, see . + */ +#include +#include +#include +#include "HangarAllocation.h" +#include "../Engine/Collections.h" +#include "../Savegame/Craft.h" +#include "../Savegame/Base.h" +#include "../Savegame/Production.h" +#include "../Savegame/BaseFacility.h" +#include "../Savegame/Transfer.h" +#include "../Mod/RuleCraft.h" +#include "../Mod/RuleManufacture.h" +#include "../Mod/RuleBaseFacility.h" + + +namespace OpenXcom +{ + +inline unsigned operator+(HangarAllocation::CraftPos pos) +{ + return static_cast(pos); +} + +inline unsigned operator+(HangarAllocation::HangarPos pos) +{ + return static_cast(pos); +} + +inline HangarAllocation::CraftPos& operator++(HangarAllocation::CraftPos& pos) +{ + pos = static_cast((+pos) + 1); + return pos; +} + +inline HangarAllocation::HangarPos& operator++(HangarAllocation::HangarPos& pos) +{ + pos = static_cast((+pos) + 1); + return pos; +} + +inline HangarAllocation::CraftPos& operator--(HangarAllocation::CraftPos& pos) +{ + pos = static_cast((+pos) - 1); + return pos; +} + +inline HangarAllocation::HangarPos& operator--(HangarAllocation::HangarPos& pos) +{ + pos = static_cast((+pos) - 1); + return pos; +} + +inline bool operator<(HangarAllocation::CraftPos a, HangarAllocation::CraftPos b) +{ + return +a < +b; +} + +inline bool operator<(HangarAllocation::HangarPos a, HangarAllocation::HangarPos b) +{ + return +a < +b; +} + +inline unsigned operator<<(unsigned i, HangarAllocation::CraftPos pos) +{ + return i << (+pos); +} + +inline unsigned operator<<(unsigned i, HangarAllocation::HangarPos pos) +{ + return i << (+pos); +} + +/** + * Initializes a craft weapon of the specified type. + * @param rules Pointer to ruleset. + * @param ammo Initial ammo. + */ +HangarAllocation::HangarAllocation(const Base *base, const Skip s) : _base(base) +{ + // load of data + for (auto craft : *_base->getCrafts()) + { + if (s.craft != craft && initCraft(craft, nullptr, nullptr, craft->getRules()) != CraftPos::CraftInvaild) + { + break; + } + } + + for (auto prod : _base->getProductions()) + { + auto rule = prod->getRules()->getProducedCraft(); + if (s.production != prod && rule && initCraft(nullptr, nullptr, prod, rule) != CraftPos::CraftInvaild) + { + break; + } + } + + for (auto trans : *_base->getTransfers()) + { + auto craft = trans->getCraft(); + if (s.transfer != trans && craft && initCraft(nullptr, trans, nullptr, craft->getRules()) != CraftPos::CraftInvaild) + { + break; + } + } + + using FacPair = std::pair; + auto facPairLess = [](FacPair a, FacPair b){ return a.second < b.second; }; + + FacPair tempFaci[MaxHangarsSlots] = { }; + + auto outputRange = Collections::range(tempFaci); + auto inputRange = Collections::filter( + Collections::range(*_base->getFacilities()), + [&](BaseFacility* fac) + { + return s.hangar != fac && fac->getBuildTime() == 0 && fac->getRules()->getCrafts(); + } + ); + + auto begin = outputRange.begin(); + auto end = begin; + for (auto zip : Collections::nonDeref(Collections::zip(outputRange, inputRange))) + { + auto in = *zip.second; + *zip.first = FacPair(in, in->getRules()->getCrafts()); + end = zip.first + 1; + } + + [&] //loop return scope + { + std::sort(begin, end, facPairLess); + for (auto hangarSize : Collections::rangeValueLess(MaxHangarsSlots)) + { + auto lowerLimit = std::upper_bound(begin, end, FacPair(nullptr, hangarSize), facPairLess); + if (lowerLimit == end) + { + return; + } + for (FacPair pair : Collections::range(lowerLimit, end)) + { + if (initHangar(pair.first) == HangarPos::HangarInvaild) + { + return; + } + } + } + }(); +} + +/** + * Destructor. + */ +HangarAllocation::~HangarAllocation() +{ +} + + +bool HangarAllocation::getUse(CraftPos craft, HangarPos hangar) const +{ + assert(craft != CraftPos::CraftInvaild); + assert(hangar != HangarPos::HangarInvaild); + assert(+craft < MaxCraftSlots); + assert(+hangar < MaxHangarsSlots); + return _useArrayHangar[+hangar] & (1 << craft); +} + +void HangarAllocation::setUse(CraftPos craft, HangarPos hangar, bool set) +{ + auto currentUse = getUse(craft, hangar); + if (!currentUse && set) + { + ++_avaiableCraftsMatchs[+craft]; + ++_avaiableHangarsMatchs[+hangar]; + _useArrayCraft[+craft] |= (1 << hangar); + _useArrayHangar[+hangar] |= (1 << craft); + } + else if (currentUse && !set) + { + --_avaiableCraftsMatchs[+craft]; + --_avaiableHangarsMatchs[+hangar]; + _useArrayCraft[+craft] &= ~(1 << hangar); + _useArrayHangar[+hangar] &= ~(1 << craft); + } +} + +Uint8 HangarAllocation::getCraftMatchsNum(CraftPos craft) const +{ + return _avaiableCraftsMatchs[+craft]; +} + +Uint8 HangarAllocation::getHangarMatchsNum(HangarPos hangar) const +{ + return _avaiableHangarsMatchs[+hangar]; +} + +HangarAllocation::UseMaskType HangarAllocation::getCraftMatchs(CraftPos craft) const +{ + return _useArrayCraft[+craft]; +} + +HangarAllocation::UseMaskType HangarAllocation::getHangarsMatchs(HangarPos hangar) const +{ + return _useArrayHangar[+hangar]; +} + +BaseFacility* HangarAllocation::getCurrCraftHangar(CraftPos c) const +{ + if (_avaiableCrafts[+c]) + { + return _avaiableCrafts[+c]->getHangar(); + } + else if (_avaiableTransfers[+c]) + { + return _avaiableTransfers[+c]->getHangar(); + } + else if (_avaiableProductions[+c]) + { + return _avaiableProductions[+c]->getHangar(); + } + else + { + return nullptr; + } +}; + +void HangarAllocation::setCurrCraftHangar(CraftPos c, BaseFacility* hangar) +{ + if (_avaiableCrafts[+c]) + { + _avaiableCrafts[+c]->setHangar(hangar); + } + else if (_avaiableTransfers[+c]) + { + _avaiableTransfers[+c]->setHangar(hangar); + } + else if (_avaiableProductions[+c]) + { + _avaiableProductions[+c]->setHangar(hangar); + } + else + { + //nothing + } +}; + +BaseFacility* HangarAllocation::getHangar(HangarPos h) const +{ + return _avaiableHangars[+h]; +} + +void HangarAllocation::initUse(CraftPos craft, HangarPos hangar) +{ + assert(+craft < MaxCraftSlots); + assert(+hangar < MaxHangarsSlots); + + bool set = _avaiableCraftTypes[+craft] && _avaiableHangars[+hangar] && _avaiableHangars[+hangar]->getRules()->isCraftTypeAllowed(_avaiableCraftTypes[+craft]); + if (set) + { + _useArrayHangarDefault[+hangar] |= (1 << craft); + } + else + { + _useArrayHangarDefault[+hangar] &= ~(1 << craft); + } + + resetUse(craft, hangar); +} + +void HangarAllocation::resetUse(CraftPos craft, HangarPos hangar) +{ + setUse(craft, hangar, _useArrayHangarDefault[+hangar] & (1 << craft)); +} + +/** + * Reset all connetions to match prevoius ones. + */ +void HangarAllocation::resetAllByPrevoiusMatch() +{ + for (auto c : Collections::rangeValueLess(_currCraftMax)) + { + auto hangar = getCurrCraftHangar(c); + if (hangar != nullptr) + { + for (auto h : Collections::rangeValueLess(_currHangarMax)) + { + if (getHangar(h) == hangar) + { + for (auto o : Collections::rangeValueLess(_currHangarMax)) + { + if (o != h) + { + setUse(c, o, false); + } + } + } + } + } + } +} + +/** + * Reset all connections to max possible ones. + */ +void HangarAllocation::resetAll() +{ + for (auto c : Collections::rangeValueLess(_currCraftMax)) + { + for (auto h : Collections::rangeValueLess(_currHangarMax)) + { + resetUse(c, h); + } + } +} + +HangarAllocation::CraftPos HangarAllocation::initCraft(Craft* craft, Transfer* transfer, Production* production, const RuleCraft* rule) +{ + if (_currCraftNum != +_currCraftMax) + { + for (auto curr : Collections::reverse(Collections::rangeValueLess(_currCraftMax))) + { + if (_avaiableCraftTypes[+curr] == nullptr) + { + _avaiableCrafts[+curr] = craft; + _avaiableTransfers[+curr] = transfer; + _avaiableProductions[+curr] = production; + _avaiableCraftTypes[+curr] = rule; + ++_currCraftNum; + return curr; + } + } + } + else + { + if (+_currCraftMax < MaxCraftSlots) + { + auto curr = _currCraftMax; + ++_currCraftMax; + ++_currCraftNum; + + _avaiableCrafts[+curr] = craft; + _avaiableTransfers[+curr] = transfer; + _avaiableProductions[+curr] = production; + _avaiableCraftTypes[+curr] = rule; + return curr; + } + } + + return CraftPos::CraftInvaild; +}; + +HangarAllocation::HangarPos HangarAllocation::initHangar(BaseFacility* hangar) +{ + if (+_currHangarMax < MaxHangarsSlots) + { + auto curr = _currHangarMax; + ++_currHangarMax; + + _avaiableHangars[+curr] = hangar; + for (auto c : Collections::rangeValueLess(_currCraftMax)) + { + initUse(c, curr); + } + return curr; + } + return HangarPos::HangarInvaild; +}; + +bool HangarAllocation::craftAllocation() +{ + //brutal force search, in wrose case `O(n^m)`, used as last step after some reductions + auto findAllocation = [&](CraftPos* be, CraftPos* en) + { + std::sort(be, en, [&](CraftPos a, CraftPos b){ return getCraftMatchsNum(a) < getCraftMatchsNum(b); }); + + auto curr = be; + UseMaskType used = {}; + UseMaskType seleced[MaxCraftSlots] = {}; + int selPos = 0; + while (true) + { + auto first = [](UseMaskType c) { return c & ~(c - 1); }; // (01100 & ~(01100 - 1)) -> (01100 & ~01011) -> 00100 + auto upper = [](UseMaskType c, UseMaskType u) { return (u & ~(c + (c - 1))); }; // (1011010 & ~(0001000 + 0001000 - 1)) -> (1011010 & ~0001111) -> 1010000 + + used &= ~seleced[selPos]; + auto n = first(upper(seleced[selPos], getCraftMatchs(*curr) & ~used)); + if (n) + { + //finded position + used |= n; + seleced[selPos] = n; + ++selPos; + ++curr; + if (curr == en) + { + break; + } + } + else + { + if (curr == be) + { + //no more postion to backtrack, imposible to match + return false; + } + else + { + //can find mach, check if we could match prevous differently + seleced[selPos] = 0; + --selPos; + --curr; + } + } + } + + for (auto p : Collections::zip(Collections::range(be, en), Collections::range(seleced))) + { + for (auto h : Collections::rangeValueLess(_currHangarMax)) + { + if ((1 << h) == p.second) + { + setUse(p.first, h, false); + } + } + } + + return true; + }; + + //find all required combinations and remove that are impossible, worst case is `O(m*m)` + //algorithm is for any `curr` + //if number of all `j` position that have `_useArrayCraft[j]` is subset of `_useArrayCraft[curr]` + //is equal to `_avaiableCraftsOptions[curr]` then we can remove `_useArrayCraft[curr]` from any other positions. + UseMaskType ResultImposible = ~UseMaskType{}; + UseMaskType ResultNone = 0u; + auto reduceCombinations = [&](CraftPos* be, CraftPos* en) + { + std::sort(be, en, [&](CraftPos a, CraftPos b){ return getCraftMatchsNum(a) < getCraftMatchsNum(b); }); + + auto currentRange = Collections::range(be, en); + for (auto curr : currentRange) + { + const auto size = getCraftMatchsNum(curr); + const auto include = getCraftMatchs(curr); + const auto exclude = ~include; + auto subsets = 0; + for (auto j : currentRange) + { + const auto js = getCraftMatchsNum(j); + if (js > size) + { + //have more elements, impossible to be subset + break; + } + const auto u = getCraftMatchs(j); + if ((u & include) && !(u & exclude)) + { + ++subsets; + if (subsets == size) + { + //finded, we can stop serching + for (auto j : currentRange) + { + const auto u = getCraftMatchs(j); + if (subsets > 0 && (u & include) && !(u & exclude)) + { + //we do protect usage of positions that include to subsets + //caveat: `curr` can be outside of this "protection" but this could happens when we can't assign all crafts in first place + --subsets; + } + else if (u & include) + { + if (!(u & exclude)) + { + //imposible to find free hangar, becasue if we remove everything nothig will left + return ResultImposible; + } + for (auto h : Collections::rangeValueLess(_currHangarMax)) + { + if ((1 << h) & include) + { + setUse(j, h, false); + } + } + } + } + return static_cast(exclude); + } + } + } + } + return ResultNone; + }; + + CraftPos offsets[MaxCraftSlots]; + auto begin = std::begin(offsets); + auto end = std::end(offsets); + std::iota(begin, end, CraftPos{}); + + //skip emply slots + end = std::partition(begin, end, [&](CraftPos a){ return _avaiableCraftTypes[+a]; }); + + auto workToDo = std::accumulate(begin, end, UseMaskType{}, [&](UseMaskType acc, CraftPos a){ return acc | getCraftMatchs(a); }); + while (begin != end) + { + auto exclude = reduceCombinations(begin, end); + if (exclude == ResultImposible) + { + return false; + } + + workToDo &= exclude; + auto split = std::partition(begin, end, [&](CraftPos a){ return !(getCraftMatchs(a) & workToDo); }); + if (split == begin) + { + assert(false && "partition bug in craftAllocation"); + } + + if (!findAllocation(begin, split)) + { + return false; + } + begin = split; + } + + return true; +} + +BaseFacility* HangarAllocation::getFreeHangar(const RuleCraft* rule) +{ + auto pos = initCraft(nullptr, nullptr, nullptr, rule); + if (pos == CraftPos::CraftInvaild) + { + return nullptr; + } + + //init new craft + for (auto h : Collections::rangeValueLess(_currHangarMax)) + { + initUse(pos, h); + } + + if (getCraftMatchsNum(pos) == 0) + { + removeCraftType(rule); + return nullptr; + } + + resetAllByPrevoiusMatch(); + if (!craftAllocation()) + { + resetAll(); + if (!craftAllocation()) + { + removeCraftType(rule); + return nullptr; + } + } + + //find what hangar exacly have free space + for (auto h : Collections::rangeValueLess(_currHangarMax)) + { + if (getUse(pos, h)) + { + return getHangar(h); + } + } + + assert(false && "return bug in getFreeHangar"); + + return nullptr; +} + + + + +/** + * Add new craft type to base. + */ +bool HangarAllocation::addCraftType(const RuleCraft* rule) +{ + return getFreeHangar(rule); +} + +/** + * Remove craft type from base. + */ +void HangarAllocation::removeCraftType(const RuleCraft* rule) +{ + for (auto c : Collections::reverse(Collections::rangeValueLess(_currCraftMax))) + { + if (_avaiableCraftTypes[+c] == rule) + { + --_currCraftNum; + _avaiableCraftTypes[+c] = nullptr; + _avaiableCrafts[+c] = nullptr; + _avaiableProductions[+c] = nullptr; + _avaiableTransfers[+c] = nullptr; + for (auto h : Collections::rangeValueLess(_currHangarMax)) + { + initUse(c, h); + } + } + } + + assert(false && "return bug in removeCraftType"); +} + +/** + * Add new craft to base. + */ +bool HangarAllocation::addCraft(Craft* craft) +{ + auto hangar = getFreeHangar(craft->getRules()); + if (hangar) + { + craft->setHangar(hangar); + return true; + } + else + { + return false; + } +} + +/** + * Add craft transfer to base + */ +bool HangarAllocation::addCraftTransfer(Transfer* transfer) +{ + auto craft = transfer->getCraft(); + if (craft) + { + auto hangar = getFreeHangar(craft->getRules()); + if (hangar) + { + transfer->setHangar(hangar); + return true; + } + else + { + return false; + } + } + else + { + return true; + } +} + +/** + * Add craft production to base. + */ +bool HangarAllocation::addCraftProduction(Production* prod) +{ + auto craftType = prod->getRules()->getProducedCraft(); + if (craftType) + { + auto hangar = getFreeHangar(craftType); + if (hangar) + { + prod->setHangar(hangar); + return true; + } + else + { + return false; + } + } + else + { + return true; + } +} + +/** + * Assign crafts, transfers and productuions to avaiable hangars + */ +void HangarAllocation::assignAll() +{ + resetAllByPrevoiusMatch(); + + craftAllocation(); + + for (auto c : Collections::rangeValueLess(_currCraftMax)) + { + if (getCraftMatchsNum(c) != 1) + { + continue; + } + for (auto h : Collections::rangeValueLess(_currHangarMax)) + { + if (getHangarMatchsNum(h) != 1) + { + continue; + } + if (getUse(c, h)) + { + setCurrCraftHangar(c, getHangar(h)); + break; + } + } + } +} + +} diff --git a/src/Savegame/HangarAllocation.h b/src/Savegame/HangarAllocation.h new file mode 100644 index 0000000000..dafd97b7da --- /dev/null +++ b/src/Savegame/HangarAllocation.h @@ -0,0 +1,134 @@ +#pragma once +/* + * Copyright 2010-2016 OpenXcom Developers. + * + * This file is part of OpenXcom. + * + * OpenXcom is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenXcom is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenXcom. If not, see . + */ + +#include +#include +#include + +namespace OpenXcom +{ + +class RuleCraft; +class Craft; +class Mod; +class Transfer; +class Production; +class Base; +class BaseFacility; + +/** + * + */ +class HangarAllocation +{ +public: + enum class CraftPos : unsigned { CraftInvaild = (unsigned)-1, CraftStart = 0, }; + enum class HangarPos : unsigned { HangarInvaild = (unsigned)-1, HangarStart = 0, }; + + struct Skip + { + const BaseFacility* hangar = nullptr; + const Transfer* transfer = nullptr; + const Craft* craft = nullptr; + const Production* production = nullptr; + + Skip(){ } + }; + +private: + using UseMaskType = Uint16; + static constexpr size_t MaxCraftSlots = CHAR_BIT * sizeof(UseMaskType); + static constexpr size_t MaxHangarsSlots = CHAR_BIT * sizeof(UseMaskType); + + UseMaskType _useArrayCraft[MaxCraftSlots] = { }; + UseMaskType _useArrayHangar[MaxHangarsSlots] = { }; + UseMaskType _useArrayHangarDefault[MaxHangarsSlots] = { }; + + Uint8 _avaiableCraftsMatchs[MaxCraftSlots] = { }; + const RuleCraft* _avaiableCraftTypes[MaxCraftSlots] = { }; + Craft* _avaiableCrafts[MaxCraftSlots] = { }; + Transfer* _avaiableTransfers[MaxCraftSlots] = { }; + Production* _avaiableProductions[MaxCraftSlots] = { }; + CraftPos _currCraftMax = CraftPos::CraftStart; + size_t _currCraftNum = 0u; + + Uint8 _avaiableHangarsMatchs[MaxHangarsSlots] = { }; + BaseFacility* _avaiableHangars[MaxHangarsSlots] = { }; + HangarPos _currHangarMax = HangarPos::HangarStart; + + const Base* _base = nullptr; + + /// Get curent possible usage of craft for hangar. + bool getUse(CraftPos craft, HangarPos hangar) const; + /// Set curent possible usage of craft for hangar. + void setUse(CraftPos craft, HangarPos hangar, bool set); + + Uint8 getCraftMatchsNum(CraftPos craft) const; + Uint8 getHangarMatchsNum(HangarPos hangar) const; + + UseMaskType getCraftMatchs(CraftPos craft) const; + UseMaskType getHangarsMatchs(HangarPos hangar) const; + + BaseFacility* getCurrCraftHangar(CraftPos c) const; + void setCurrCraftHangar(CraftPos c, BaseFacility* hangar); + + BaseFacility* getHangar(HangarPos hangar) const; + + /// Init flag of avaiabli of craft x hangar. + void initUse(CraftPos craft, HangarPos hangar); + /// Reset to starting state. + void resetUse(CraftPos craft, HangarPos hangar); + /// Reset all connetions to match prevoius ones. + void resetAllByPrevoiusMatch(); + /// Reset all connections to max possible ones. + void resetAll(); + + /// Init craft info. + CraftPos initCraft(Craft* craft, Transfer* transfer, Production* production, const RuleCraft* rule); + /// Init hangar info facility. + HangarPos initHangar(BaseFacility* hangar); + /// Check space for next craft. + BaseFacility* getFreeHangar(const RuleCraft* rule); + /// Run code and allocate all crafts to hangars. + bool craftAllocation(); + +public: + /// Constructor. + HangarAllocation(const Base *base, const Skip s = {}); + /// Destructor. + ~HangarAllocation(); + + /// Add new craft type to base. + bool addCraftType(const RuleCraft* rule); + /// Remove craft type from base. + void removeCraftType(const RuleCraft* rule); + + /// Add new craft to base. + bool addCraft(Craft* craft); + /// Add craft transfer to base + bool addCraftTransfer(Transfer* transfer); + /// Add craft production to base. + bool addCraftProduction(Production* prod); + + /// Assign crafts, transfers and productuions to avaiable hangars + void assignAll(); +}; + +} diff --git a/src/Savegame/Production.cpp b/src/Savegame/Production.cpp index 1d68077923..ad9aaf519c 100644 --- a/src/Savegame/Production.cpp +++ b/src/Savegame/Production.cpp @@ -34,7 +34,7 @@ namespace OpenXcom { -Production::Production(const RuleManufacture * rules, int amount) : _rules(rules), _amount(amount), _infinite(false), _timeSpent(0), _engineers(0), _sell(false) +Production::Production(const RuleManufacture * rules, int amount) : _rules(rules), _hangar(nullptr), _amount(amount), _infinite(false), _timeSpent(0), _engineers(0), _sell(false) { } @@ -132,7 +132,9 @@ productionProgress_e Production::step(Base * b, SavedGame * g, const Mod *m) { Craft *craft = new Craft(ruleCraft, b, g->getId(ruleCraft->getType())); craft->setStatus("STR_REFUELLING"); + craft->setHangar(getHangar()); b->getCrafts()->push_back(craft); + setHangar(nullptr); } else { @@ -159,6 +161,7 @@ productionProgress_e Production::step(Base * b, SavedGame * g, const Mod *m) // We need to ensure that player has enough cash/item to produce a new unit if (!haveEnoughMoneyForOneMoreUnit(g)) return PROGRESS_NOT_ENOUGH_MONEY; if (!haveEnoughMaterialsForOneMoreUnit(b, m)) return PROGRESS_NOT_ENOUGH_MATERIALS; + if (!b->addHangarCraftProduction(this)) return PROGRESS_NOT_ENOUGH_FREE_HANGARS; startItem(b, g, m); } } @@ -170,6 +173,7 @@ productionProgress_e Production::step(Base * b, SavedGame * g, const Mod *m) // We need to ensure that player has enough cash/item to produce a new unit if (!haveEnoughMoneyForOneMoreUnit(g)) return PROGRESS_NOT_ENOUGH_MONEY; if (!haveEnoughMaterialsForOneMoreUnit(b, m)) return PROGRESS_NOT_ENOUGH_MATERIALS; + if (!b->addHangarCraftProduction(this)) return PROGRESS_NOT_ENOUGH_FREE_HANGARS; startItem(b, g, m); } return PROGRESS_NOT_COMPLETE; @@ -227,7 +231,7 @@ void Production::startItem(Base * b, SavedGame * g, const Mod *m) const } } -YAML::Node Production::save() const +YAML::Node Production::save(const Base *b) const { YAML::Node node; node["item"] = getRules()->getName(); @@ -237,10 +241,11 @@ YAML::Node Production::save() const node["infinite"] = getInfiniteAmount(); if (getSellItems()) node["sell"] = getSellItems(); + node["hangar"] = b->saveFacilitieReference(_hangar); return node; } -void Production::load(const YAML::Node &node) +void Production::load(const YAML::Node &node, Base* base) { setAssignedEngineers(node["assigned"].as(getAssignedEngineers())); setTimeSpent(node["spent"].as(getTimeSpent())); @@ -254,6 +259,23 @@ void Production::load(const YAML::Node &node) setInfiniteAmount(true); setSellItems(true); } + _hangar = base->loadFacilitieReference(node["hangar"]); +} + +/** + * Sets hangar. + */ +void Production::setHangar(BaseFacility* f) +{ + _hangar = f; +} + +/* + * Get hangar. + */ +BaseFacility* Production::getHangar() const +{ + return _hangar; } } diff --git a/src/Savegame/Production.h b/src/Savegame/Production.h index 568b2e15bc..3952585d35 100644 --- a/src/Savegame/Production.h +++ b/src/Savegame/Production.h @@ -24,9 +24,10 @@ namespace OpenXcom class RuleManufacture; class Base; +class BaseFacility; class SavedGame; class Mod; -enum productionProgress_e { PROGRESS_NOT_COMPLETE, PROGRESS_COMPLETE, PROGRESS_NOT_ENOUGH_MONEY, PROGRESS_NOT_ENOUGH_MATERIALS, PROGRESS_MAX, PROGRESS_CONSTRUCTION }; +enum productionProgress_e { PROGRESS_NOT_COMPLETE, PROGRESS_COMPLETE, PROGRESS_NOT_ENOUGH_MONEY, PROGRESS_NOT_ENOUGH_MATERIALS, PROGRESS_NOT_ENOUGH_FREE_HANGARS, PROGRESS_MAX, PROGRESS_CONSTRUCTION }; class Production { @@ -46,10 +47,16 @@ class Production productionProgress_e step(Base * b, SavedGame * g, const Mod *m); const RuleManufacture * getRules() const; void startItem(Base * b, SavedGame * g, const Mod *m) const; - YAML::Node save() const; - void load(const YAML::Node &node); + YAML::Node save(const Base *b) const; + void load(const YAML::Node &node, Base* base); + /// Sets hangar. + void setHangar(BaseFacility* f); + /// Get hangar. + BaseFacility* getHangar() const; + private: const RuleManufacture * _rules; + BaseFacility *_hangar; int _amount; bool _infinite; int _timeSpent; diff --git a/src/Savegame/SaveConverter.cpp b/src/Savegame/SaveConverter.cpp index 304708231d..ad109d17d2 100644 --- a/src/Savegame/SaveConverter.cpp +++ b/src/Savegame/SaveConverter.cpp @@ -215,6 +215,11 @@ SavedGame *SaveConverter::loadOriginal() loadDatBProd(); loadDatXBases(); + for (auto b : *_save->getBases()) + { + b->initHangars(); + } + return _save; } diff --git a/src/Savegame/Transfer.cpp b/src/Savegame/Transfer.cpp index c8a79abc15..191262b816 100644 --- a/src/Savegame/Transfer.cpp +++ b/src/Savegame/Transfer.cpp @@ -24,6 +24,7 @@ #include "../Engine/Language.h" #include "../Mod/Mod.h" #include "../Engine/Logger.h" +#include "BaseFacility.h" namespace OpenXcom { @@ -32,7 +33,7 @@ namespace OpenXcom * Initializes a transfer. * @param hours Hours in-transit. */ -Transfer::Transfer(int hours) : _hours(hours), _soldier(0), _craft(0), _itemQty(0), _scientists(0), _engineers(0), _delivered(false) +Transfer::Transfer(int hours) : _hours(hours), _soldier(0), _craft(0), _hangar(nullptr), _itemQty(0), _scientists(0), _engineers(0), _delivered(false) { } @@ -104,6 +105,7 @@ bool Transfer::load(const YAML::Node& node, Base *base, const Mod *mod, SavedGam _scientists = node["scientists"].as(_scientists); _engineers = node["engineers"].as(_engineers); _delivered = node["delivered"].as(_delivered); + _hangar = base->loadFacilitieReference(node["hangar"]); return true; } @@ -140,6 +142,7 @@ YAML::Node Transfer::save(const Base *b, const Mod *mod) const { node["delivered"] = _delivered; } + node["hangar"] = b->saveFacilitieReference(_hangar); return node; } @@ -335,4 +338,20 @@ Soldier *Transfer::getSoldier() return _soldier; } +/** + * Sets hangar. + */ +void Transfer::setHangar(BaseFacility* f) +{ + _hangar = f; +} + +/* + * Get hangar. + */ +BaseFacility* Transfer::getHangar() const +{ + return _hangar; +} + } diff --git a/src/Savegame/Transfer.h b/src/Savegame/Transfer.h index bc0915139d..860566c9f2 100644 --- a/src/Savegame/Transfer.h +++ b/src/Savegame/Transfer.h @@ -39,6 +39,7 @@ class Soldier; class Craft; class Language; class Base; +class BaseFacility; class Mod; class SavedGame; @@ -53,6 +54,7 @@ class Transfer int _hours; Soldier *_soldier; Craft *_craft; + BaseFacility *_hangar; std::string _itemId; int _itemQty, _scientists, _engineers; bool _delivered; @@ -91,7 +93,10 @@ class Transfer void advance(Base *base); /// Get a pointer to the soldier being transferred. Soldier *getSoldier(); - + /// Sets hangar. + void setHangar(BaseFacility* f); + /// Get hangar. + BaseFacility* getHangar() const; }; }