// ################################
// ##      Objekty predmetu      ##
// ##  a funkce s nimi spojene   ##
// ################################

#include "items.h"

#include <algorithm>
#include <math.h>

#include "datetime.h"
#include "common_routines.h"
#include "independent_constants.h"

#include "cache_data.h"
#include "cache_list.h"
#include "players_realms.h"
#include "fortress_skelet.h"
#include "fortress_classes.h"

#ifdef _DEBUG
#ifndef DEBUG_NEW
#define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__)
#define new DEBUG_NEW
#endif
#endif

// ################################
// ################################

itemsPatternClass * commonItemClass :: getPattern() 
{ 
	itemsPatternClass * pat = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(patternID_); 
    return pat != NULL ? pat : nullPattern;
};

// ================================

void separateItemClass::harm(unsigned short damagePerc)
{
	double damagePart = min(1, max(0, (double)damagePerc / (double)HUNDRED_PERCENT));
	damagePart = min(damagePart, 1 - this->damage_);
	if (this->shadowDamage_ < damagePart)
	{
		damagePart          -= this->shadowDamage_;
		this->shadowDamage_  = 0;
		this->damage_       += damagePart;
	}
	else
	{
		this->shadowDamage_ -= damagePart;
	}
	this->damage_       = min(1, max(0, this->damage_));
	this->shadowDamage_ = max(0, min(this->shadowDamage_, this->damage_));
};

// --------------------------------

void separateItemClass::repair(unsigned long long points)
{
	double totalRepairingPoints  = (double)this->getWeight() * (double)this->getRepairability();
	totalRepairingPoints         = max(1, totalRepairingPoints);
	double repairedPart          = (double)points / dHandleZero(totalRepairingPoints);
	repairedPart                 = min(1, max(0, repairedPart));
	this->shadowDamage_         += repairedPart;
	this->shadowDamage_          = min(this->shadowDamage_, this->damage_);
	double workingPart           = (double)1 - this->damage_ + this->shadowDamage_; // protoze shadowDamage <= damage, je workingPart <= 1
	double workingCheckpoint     = floor(workingPart / dHandleZero(REPAIRING_CHECKPOINT_PART)) * REPAIRING_CHECKPOINT_PART;
	this->shadowDamage_          = workingPart - workingCheckpoint;
	this->damage_                = (double)1 - workingCheckpoint;
};

// --------------------------------

unsigned long long separateItemClass::pointsToCheckpoint()
{
	double workingPart               = (double)1 - this->damage_ + this->shadowDamage_;
	double nextCheckpoint            = min((double)1, floor(workingPart / dHandleZero(REPAIRING_CHECKPOINT_PART) + (double)1) * REPAIRING_CHECKPOINT_PART);
	double missingPart               = nextCheckpoint - workingPart;
	unsigned long long missingPoints = (unsigned long long)ceil(missingPart * (double)this->getWeight() * (double)this->getRepairability());

	return missingPoints;
};

// ================================

void stackableItemClass::harm(unsigned short damagePerc)
{
	double             damagePart = min(1, max(0, (double)damagePerc / dHandleZero(HUNDRED_PERCENT)));
	double             damageCntD = (double)(this->getCount()) * damagePart;
	unsigned long long damageCntU = 0;
	if ((rand() % ulHandleZero(HUNDRED_PERCENT)) < damagePerc)
	{
		damageCntU = (unsigned long long)ceil(damageCntD);
	}
	else
	{
		damageCntU = (unsigned long long)floor(damageCntD);
	}
	damageCntU = min(damageCntU, this->working_ + this->shadowDamaged_);
	if (this->shadowDamaged_ < damageCntU)
	{
		damageCntU           -= this->shadowDamaged_;
		this->shadowDamaged_  = 0;
		ullShiftPile(this->working_, this->damaged_, damageCntU);	
	}
	else
	{
		this->shadowDamaged_ -= damageCntU;
	}
	this->shadowDamaged_ = min(this->shadowDamaged_, this->damaged_);
};

// --------------------------------

void stackableItemClass::repair(unsigned long long points)
{
	if (0 == this->getCount())
	{
		return;
	}
	unsigned long long oneItemRepairingPoints = max((unsigned long long)1, (unsigned long long)this->getWeight() * (unsigned long long)this->getRepairability());
	unsigned long long neededRepairingPoints  = oneItemRepairingPoints * (unsigned long long)this->damaged_;
	unsigned long long repairedItems          = points / ullHandleZero(oneItemRepairingPoints);
	unsigned long long pointsLeft             = points % ullHandleZero(oneItemRepairingPoints);
	if ((rand() % ullHandleZero(oneItemRepairingPoints)) < pointsLeft)
	{
		repairedItems++;
	}
	repairedItems                         = min(repairedItems, this->damaged_ - this->shadowDamaged_);
	this->shadowDamaged_                 += (unsigned long long)repairedItems;
	this->shadowDamaged_                  = min(this->shadowDamaged_, this->damaged_);
	unsigned long long workingTotal       = this->working_ + this->shadowDamaged_;
	unsigned long long workingCheckpoint  = (unsigned long long)((double)(floor(((double)workingTotal / (double)this->getCount()) / REPAIRING_CHECKPOINT_PART) * REPAIRING_CHECKPOINT_PART) * (double)this->getCount());
	this->damaged_                        = this->getCount() - workingCheckpoint;
	this->working_                        = workingCheckpoint;
	this->shadowDamaged_                  = workingTotal - workingCheckpoint;	
};

// --------------------------------

unsigned long long stackableItemClass::pointsToCheckpoint()
{
	unsigned long long workingTotal   = this->working_ + this->shadowDamaged_;
	unsigned long long nextCheckpoint = ullUpRound((double)(floor(((double)workingTotal / (double)this->getCount()) / REPAIRING_CHECKPOINT_PART + (double)1) * REPAIRING_CHECKPOINT_PART) * (double)this->getCount());
	unsigned long long missing        = nextCheckpoint - workingTotal;
	unsigned long long missingPoints  = (unsigned long long)missing * (unsigned long long)this->getWeight() * (unsigned long long)this->getRepairability();

	return missingPoints;
};

// ================================

stackableItemClass * droneMissilesClass::createEmptyCopyOfItemOfTheSameClass()
{
	return new droneMissilesClass(getWeight(), 0, 0);
};

// ================================

stackableItemClass * magneticalCollectorsOfPlasmaClass::createEmptyCopyOfItemOfTheSameClass()
{
	return new magneticalCollectorsOfPlasmaClass(getWeight(), 0, 0);
};

// ================================

stackableItemClass * submersibleFiltrationTankClass::createEmptyCopyOfItemOfTheSameClass()
{
	return new submersibleFiltrationTankClass(getWeight(), 0, 0);
};

// ================================

stackableItemClass * submarineMiningMachineClass::createEmptyCopyOfItemOfTheSameClass()
{
	return new submarineMiningMachineClass(getWeight(), 0, 0);
};

// ================================

stackableItemClass * undergroundDrillingMachineClass::createEmptyCopyOfItemOfTheSameClass()
{
	return new undergroundDrillingMachineClass(getWeight(), 0, 0);
};

// ================================

unsigned long androidsClass::actualPointsPerSecond(fortressClass * owner)
{ 
	unsigned long basicPower   = this->actualLevel(owner->getFortressRealmID());
	unsigned long effEngineers = owner->crew.getEffectiveCountOf(CREW_ENGINEER);
	double        engRating    = 0;
	if (0 < this->getWorking())
	{
		engRating              = pow((double)effEngineers, (double)2) / (double)this->getWorking();
	}
	double        engBonus     = engRating / (engRating + (double)0.05);
	return ulRound((double)basicPower * ((double)1 + (double)engBonus));
}

// --------------------------------

stackableItemClass * androidsClass::createEmptyCopyOfItemOfTheSameClass()
{
	return new androidsClass(getWeight(), 0, 0);
};

// ================================

stackableItemClass * magneticalGridClass::createEmptyCopyOfItemOfTheSameClass()
{
	return new magneticalGridClass(getWeight(), 0, 0);
};

// ================================

stackableItemClass * transportContainerClass::createEmptyCopyOfItemOfTheSameClass()
{
	return new transportContainerClass(getWeight(), 0, 0);
};

// ================================

stackableItemClass * energeticCellClass::createEmptyCopyOfItemOfTheSameClass()
{
	return new energeticCellClass(getWeight(), 0, 0);
};

// ================================

stackableItemClass * magneticalFilterClass::createEmptyCopyOfItemOfTheSameClass()
{
	return new magneticalFilterClass(getWeight(), 0, 0);
};

// ================================

stackableItemClass * deuteriumDecomposerClass::createEmptyCopyOfItemOfTheSameClass()
{
	return new deuteriumDecomposerClass(getWeight(), 0, 0);
};

// ================================

stackableItemClass * zirconiumReactorClass::createEmptyCopyOfItemOfTheSameClass()
{
	return new zirconiumReactorClass(getWeight(), 0, 0);
};

// ================================

stackableItemClass * ilmeniteFurnaceClass::createEmptyCopyOfItemOfTheSameClass()
{
	return new ilmeniteFurnaceClass(getWeight(), 0, 0);
};

// ================================

unsigned long long hangarClass::androidsPowerPerGameSecondInThisHangarForActualItem(fortressClass * constructingFortress)
{
	if (((NOT_VALID_ID == newShipClassID_) && (NOT_VALID_ID == newItemPatternID_)) ||
		(NULL == constructingFortress))
	{
		return 0;
	}

	realmClass * realm = constructingFortress->getFortressRealm();

	unsigned long long androidsPower   = constructingFortress->mechanics.androids()->actualPointsPerSecond(constructingFortress);
	unsigned long long hangarPower     = this->actualPointsPerSecond();
	unsigned long long mechanicsPower  = min(androidsPower, hangarPower);
	unsigned long long androidsCount   = constructingFortress->getBuildingAndroidsCountForHangar(this->ID_);
	unsigned long long androidsSpace   = structuralIntegrityVoidSectionClass::getMaxRepairingMechanicsBySize(this->getCreationsSize()) * this->getCreationsCount();	
	unsigned long long androidsWorking = min(androidsCount, androidsSpace);

	return (unsigned long long)mechanicsPower * (unsigned long long)androidsWorking;
};

// --------------------------------

unsigned long long hangarClass::actualShipTotalCost() const
{
	if (0 == newShipClassID_)
	{
		return 0;
	}

	return (unsigned long long)SHIPS_REPAIRABILITY_FOR_ALL_CLASSES       *
		   (unsigned long long)SHIPS_CONSTRUCTION_TO_REPAIRABILITY_RATIO *
		   (unsigned long long)newItemDimensions_.weight_;
};

// --------------------------------

unsigned long long hangarClass::actualItemTotalCost() const
{
	if (0 == newItemPatternID_)
	{
		return 0;
	}

	return (unsigned long long)(actx->cacheItemsPatternsList.getItem<itemsPatternClass>(newItemPatternID_)->getRepairability()) *
		   (unsigned long long)ITEMS_CONSTRUCTION_TO_REPAIRABILITY_RATIO                                                        *
		   (unsigned long long)newItemDimensions_.weight_                                                                       *
		   (unsigned long long)newItemsCount_;
};

// --------------------------------

bool hangarClass::dockAShip(fortressClass * ship)
{
	if ((!isHangerFree()) || (NULL == ship))
	{
		return 0;
	}

	this->dockingShipID_            = ship->getID();
	this->newShipClassID_           = NOT_VALID_ID;
	this->newItemPatternID_         = NOT_VALID_ID;
	this->checkConstructionTrigger_ = 0;

	return 1;
};

// --------------------------------

bool hangarClass::unboundAShip()
{
	if (NOT_VALID_ID == this->dockingShipID_)
	{
		return 0;
	}

	this->dockingShipID_            = NOT_VALID_ID;
	this->newShipClassID_           = NOT_VALID_ID;
	this->newItemPatternID_         = NOT_VALID_ID;
	this->checkConstructionTrigger_ = 0;

	return 1;
};

// --------------------------------

itemsCreationResult hangarClass::createNewShip(fortressClass * constructingFortress, dtIdentifier newShipClassID, unsigned long long buildingTitanium)
{
	if (!isHangerFree())
	{
		return ICR_FULL_HANGAR;
	}

	fortressClassClass * shipsClass = actx->cacheFortressClassesList.getItem<fortressClassClass>(newShipClassID);

	if ((NULL             == shipsClass                                   ) ||
		(NULL             == constructingFortress                         ) ||
		(NOT_VALID_ID     == newShipClassID                               ) ||
		(FT_SHIP          != shipsClass->getClassType()                   ) ||
		(buildingTitanium <  shipsClass->getMinWeight()                   ) ||
		(buildingTitanium >  shipsClass->getMaxWeight()                   ) ||
		(!(constructingFortress->isFortressClassAvailable(newShipClassID))))
	{
		return ICR_INVALID_PARAMS;
	}

	if (!(shipsClass->shipFitsIntoHangar(this, buildingTitanium)))
	{
		return ICR_INVALID_PARAMS;
	}

	if (constructingFortress->titaniumInKg(0) < buildingTitanium)
	{
		return ICR_NOT_ENOUGH_TITANIUM;
	}

	constructingFortress->subTitanium(buildingTitanium);

	itemDimensionsClass shipsDimensions(buildingTitanium);

	dockingShipID_            = NOT_VALID_ID;
	newShipClassID_           = newShipClassID;
	newItemPatternID_         = NOT_VALID_ID;
	pointsConstructed_        = 0;
	pointsConstructedUpdate_  = wt2gt(dv_time::now());
	newItemDimensions_        = shipsDimensions;
	newItemsCount_            = 1; // nova lod je vzdy jedna
	checkConstructionTrigger_ = 0;

	//checkConstruction(constructingFortress);
	// tohle nemusime volat, protoze to pak zavola energyDistributionPhase

	constructingFortress->energyDistributionPhaseAndWeaponsFire();

	return ICR_OK;
};

// --------------------------------

itemsCreationResult hangarClass::createNewItem(fortressClass * constructingFortress, dtIdentifier newItemPatternID, unsigned long itemsCount, itemDimensionsClass itemDimensions)
{
	if (!isHangerFree())
	{
		return ICR_FULL_HANGAR;
	}

	itemsPatternClass * itemPattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(newItemPatternID);
	if ((NULL == itemPattern) || (NOT_VALID_ID == newItemPatternID) || (NULL == constructingFortress) || (0 == itemPattern->getDensity()))
	{
		return ICR_INVALID_PARAMS;
	}

	if ((0 < itemDimensions.length_                                                    ) &&
		(0 < itemDimensions.width_                                                     ) &&
		(0 < itemDimensions.height_                                                    ) &&
		(0 < itemDimensions.weight_                                                    ) &&
		(itemDimensions.weight_ != itemDimensions.getSize() * itemPattern->getDensity()))
	{
		return ICR_INVALID_PARAMS;
	}
	
	if ((0 == itemDimensions.length_) || (0 == itemDimensions.width_) || (0 == itemDimensions.height_))
	{
		double size             = (double)itemDimensions.weight_ / (double)itemPattern->getDensity();
		itemDimensions.length_  = pow(size, (double)1/(double)3);
		itemDimensions.width_   = pow(size, (double)1/(double)3);
		itemDimensions.height_  = pow(size, (double)1/(double)3);
	}
	if (0 == itemDimensions.weight_)
	{
		itemDimensions.weight_ = ullUpRound((double)itemDimensions.getSize() * (double)itemPattern->getDensity());
	}

	if ((NULL         == itemPattern            ) || 
		(NULL         == constructingFortress   ) || 
		(NOT_VALID_ID == newItemPatternID       ) ||
		(0            == itemDimensions.weight_ ) || 
		(0            == itemDimensions.length_ ) ||
		(0            == itemDimensions.width_  ) ||
		(0            == itemDimensions.height_ ))
	{
		return ICR_INVALID_PARAMS;
	}

	if ((itemPattern->isItemStackable()                            ) &&
		(itemPattern->getMinWeight() != itemPattern->getMaxWeight()))
	{
		errLogicalError("Stackable item (pattern ID %u) with positive weight range.", (unsigned long long)itemPattern->getID());
	}

	if ((itemPattern->getMaxWeight() < itemDimensions.weight_) || (itemDimensions.weight_ < itemPattern->getMinWeight()))
	{
		return ICR_INVALID_PARAMS;
	}

	if (actx->cacheItemsPatternsList.getItem<itemsPatternClass>(newItemPatternID)->isItemSeparate())
	{
		itemsCount = 1; // separatni item je vzdy jeden
	}
	if (0 == itemsCount)
	{
		return ICR_INVALID_PARAMS;
	}
	newItemsCount_ = itemsCount;

	double constructionRatio = (double)newItemsCount_ * (double)itemDimensions.weight_ / (double)HUNDRED_PERCENT; // constructionSurovina ( krom ulrei ) jsou procenta z hmotnosti
	if (constructingFortress->ulreiCount(0) < itemPattern->getConstructionUlrei() * newItemsCount_) // tady se to nenasobi vahou , ulrei jsou dany na pevno bez ohledu na hmotnost
	{
		return ICR_NOT_ENOUGH_ULREI;
	}
	if (constructingFortress->titaniumInKg(0) < (unsigned long long)((double)itemPattern->getConstructionTitanium() * constructionRatio))
	{
		return ICR_NOT_ENOUGH_TITANIUM;
	}
	if (constructingFortress->deuteriumInKg(1) < (unsigned long long)((double)itemPattern->getConstructionDeuterium() * constructionRatio))
	{
		return ICR_NOT_ENOUGH_DEUTERIUM;
	}
	if (constructingFortress->heavyWaterInKg(1) < (unsigned long long)((double)itemPattern->getConstructionHeavyWater() * constructionRatio))
	{
		return ICR_NOT_ENOUGH_HEAVY_WATER;
	}

	// tady uz to proslo vsechny kontroly , tak je to OK a muzeme odecist suroviny a stavet

	unsigned long long ulreiCost      =                     (        itemPattern->getConstructionUlrei()                         );
	unsigned long long titaniumCost   = (unsigned long long)((double)itemPattern->getConstructionTitanium()   * constructionRatio);
	unsigned long long deuteriumCost  = (unsigned long long)((double)itemPattern->getConstructionDeuterium()  * constructionRatio);
	unsigned long long heavyWaterCost = (unsigned long long)((double)itemPattern->getConstructionHeavyWater() * constructionRatio);

	constructingFortress->subUlrei     (ulreiCost     );
	constructingFortress->subTitanium  (titaniumCost  );
	constructingFortress->subDeuterium (deuteriumCost );
	constructingFortress->subHeavyWater(heavyWaterCost);

	dockingShipID_            = NOT_VALID_ID;
	newShipClassID_           = NOT_VALID_ID;
	newItemPatternID_         = newItemPatternID;
	pointsConstructed_        = 0;
	pointsConstructedUpdate_  = wt2gt(dv_time::now());
	newItemDimensions_        = itemDimensions;
	checkConstructionTrigger_ = 0;

	//checkConstruction(constructingFortress);
	// tohle nemusime volat, protoze to pak zavola energyDistributionPhase

	constructingFortress->energyDistributionPhaseAndWeaponsFire();

	return ICR_OK;
};

// --------------------------------

bool hangarClass::finishShipConstruction(fortressClass * constructingFortress)
{
	if ((NOT_VALID_ID == dockingShipID_) && (NULL != constructingFortress))
	{
		saveConstructedShip(constructingFortress);
		cancelConstruction();		
		return 1;
	}
	else
	{
		return 0;
	}
};

// --------------------------------

bool hangarClass::finishItemConstruction(fortressClass * constructingFortress)
{
	if (NULL == constructingFortress)
	{
		return 0;
	}

	unsigned long long newItemsTotalWeight =                     newItemDimensions_.weight_   * newItemsCount_;
	unsigned long long newItemsTotalSize   = (unsigned long long)newItemDimensions_.getSize() * newItemsCount_;

	if ((newItemsTotalWeight <= constructingFortress->freeWeight(0, 0, 0)) &&
		(newItemsTotalSize   <= constructingFortress->freeCapacity()     )) // item se tam vejde
	{
		saveConstructedItem(constructingFortress);
		cancelConstruction();
		return 1;
	}
	else
	{
		return 0;
	}
};

// --------------------------------

bool hangarClass::checkConstruction(fortressClass * constructingFortress)
{
	this->buildingGradient_ = 0;
	if (((NOT_VALID_ID == newItemPatternID_) && (NOT_VALID_ID == newShipClassID_)) || (!constructingFortress))
	{
		return 0;
	}
	
	dv_time::datetime  actualGameTime           = wt2gt(dv_time::now());
	unsigned long long secondsFromTheLastUpdate = dv_time::secondsBetween(pointsConstructedUpdate_, actualGameTime, 1);
	unsigned long long targetCost               = actualConstructionTotalCost();

	pointsConstructedUpdate_ = actualGameTime;
	if (pointsConstructed_ < targetCost)
	{
		unsigned long long pps  = androidsPowerPerGameSecondInThisHangarForActualItem(constructingFortress);
		pointsConstructed_      = min(pointsConstructed_ + pps * secondsFromTheLastUpdate, targetCost);
		this->buildingGradient_ = (double)pps;
	}

	if (pointsConstructed_ >= targetCost) // konstrukce dokoncena
	{
		if (NOT_VALID_ID != newShipClassID_)
		{
			bool somethingFinished = finishShipConstruction(constructingFortress);
			if (somethingFinished)
			{
				constructingFortress->energyDistributionPhaseAndWeaponsFire();
			}
			return somethingFinished;
		}
		if (NOT_VALID_ID != newItemPatternID_)
		{
			bool somethingFinished = finishItemConstruction(constructingFortress);
			if (somethingFinished)
			{
				constructingFortress->energyDistributionPhaseAndWeaponsFire();
			}
			return somethingFinished;
		}
	}
	else
	{
		setConstructionTriggerIfItIsToBe(constructingFortress);
		return 0;
	}

	return 1; // asi zbytecne, nejaky pozustatek z doby kamenne
};

// --------------------------------

unsigned long long hangarClass::science2Value(double science, unsigned long long lowEnd, unsigned long long highEnd, unsigned long long weight, unsigned long long maxWeight, unsigned long long minWeight)
{
	double scienceRatio     = 0;
	double weightRatio      = 1;
	if (0 < MAX_TOTAL_SCIENCE_LEVEL)
	{
		scienceRatio        = (double)(science           ) / (double)(MAX_TOTAL_SCIENCE_LEVEL);
	}
	if (0 < (maxWeight - minWeight))
	{
		weightRatio         = (double)(weight - minWeight) / (double)(maxWeight - minWeight  );
	}
	double ratio            = min(scienceRatio, weightRatio);
	       ratio            = max((double)0, min((double)1, ratio));
	if (lowEnd <= highEnd)
	{
		return (unsigned long long)((double)lowEnd  + (double)(highEnd - lowEnd) * (double)(            ratio));
	}
	else
	{
		return (unsigned long long)((double)highEnd + (double)(lowEnd - highEnd) * (double)((double)1 - ratio));
	}
};

// --------------------------------

void hangarClass::setConstructionTriggerIfItIsToBe(fortressClass * constructingFortress)
{
	if (((NOT_VALID_ID == newShipClassID_) && (NOT_VALID_ID == newItemPatternID_)) || (NULL == constructingFortress))
	{
		return;
	}

	unsigned long long targetCost     = this->actualConstructionTotalCost();
	unsigned long long buildingSpeed  = this->androidsPowerPerGameSecondInThisHangarForActualItem(constructingFortress);
	unsigned long long constructedPts = this->pointsConstructed_;
	if (0 == buildingSpeed)
	{
		return;
	}
	unsigned long long secondsLeft    = ullUpRoundDiv((targetCost - constructedPts), buildingSpeed);
	dv_time::datetime  targetTime     = pointsConstructedUpdate_ + dv_time::encodeSeconds(secondsLeft);

	if ((targetTime < checkConstructionTrigger_) || (checkConstructionTrigger_ < wt2gt(dv_time::now()) + dv_time::encodeSeconds(1)))
	{
		gctx.eng.registerEvent(targetTime, new checkConstructionTriggerClass(constructingFortress->getID(), this->getID()),
            constructionTriggerIdentificator);
		checkConstructionTrigger_ = targetTime;
	}
};

// --------------------------------

unsigned long long hangarClass::science2Value(double science, unsigned long long lowEnd, unsigned long long highEnd, unsigned long long nativeInfluence, unsigned long long bestNativeInfluence, unsigned long long worstNativeInfluence, unsigned long long weight, unsigned long long maxWeight, unsigned long long minWeight)
{
	if (bestNativeInfluence == worstNativeInfluence)
	{
		return science2Value(science, lowEnd, highEnd, weight, maxWeight, minWeight);
	}

	double nativeInfluenceRatio;
	if (worstNativeInfluence < bestNativeInfluence)
	{
		nativeInfluenceRatio = (double)(nativeInfluence - worstNativeInfluence) / (double)(bestNativeInfluence - worstNativeInfluence);
	}
	else
	{
		nativeInfluenceRatio = (double)1 - (double)(nativeInfluence - bestNativeInfluence) / (double)(worstNativeInfluence - bestNativeInfluence);
	}

	double scienceRatio = science / (double)MAX_TOTAL_SCIENCE_LEVEL;

	double betterRatio  = max(scienceRatio, nativeInfluenceRatio);
	double worseRatio   = min(scienceRatio, nativeInfluenceRatio);
	// tento mechanismus dava zacinajicim hracum bez vedy unosnost hry, tem s maximalni vedou zas velkou vyhodu
	double totalRatio   = (9 * betterRatio + worseRatio) / 10;

	return science2Value((double)MAX_TOTAL_SCIENCE_LEVEL * totalRatio, lowEnd, highEnd, weight, maxWeight, minWeight);
};

// --------------------------------

void hangarClass::saveConstructedShip(fortressClass * constructingFortress)
{
	if ((NULL == constructingFortress) || (FT_BASE != constructingFortress->getFortressType()))
	{
		return;
	}
	baseClass * constructingBase = (baseClass *)constructingFortress;
	unsigned long long   buildingTitanium = newItemDimensions_.weight_;
	fortressClassClass * newShipClass     = actx->cacheFortressClassesList.getItem<fortressClassClass>(this->newShipClassID_);
	if ((!newShipClass                          ) || 
		(FT_SHIP != newShipClass->getClassType()))
	{
		errExceptionError("Not ship class (ID %u) in hangar (ID %u) of fortress (ID %u).", (unsigned long long)this->newShipClassID_, (unsigned long long)this->getID(), (unsigned long long)constructingFortress->getID());
		return;
	}
	shipClass          * newShip          = (shipClass *)(newShipClass->deriveNewFortress(this, buildingTitanium, 0));
	if ((!newShip                             ) ||
		(FT_SHIP != newShip->getFortressType()))
	{
		errExceptionError("NULL or bad ship constructed after all controls (at fortress ID %u in hangar ID %u); epic fail.", (unsigned long long)constructingFortress->getID(), (unsigned long long)this->getID());
		return;
	}
	this->dockingShipID_                  = actx->cacheFortressesList.insertItem(newShip);
	newShip->addIDBehindName();
	newShip->MovementLand(constructingFortress->getID());
	actx->spaceObjectIndex.addShip(newShip->getID(), constructingBase->getFortressPlanet()->getStarSystem());
	constructingFortress->getFortressRealm()->addUncontrollableFortress(newShip);
};

// --------------------------------

void hangarClass::saveConstructedItem(fortressClass * constructingFortress)
{
	if (NULL == constructingFortress)
	{
		return;
	}

	commonItemClass   * newItem;
	itemsPatternClass * pattern;
	realmClass        * realm = constructingFortress->getFortressRealm();

	unsigned long jetForce;
	unsigned long shieldsAccumulator;

	switch (newItemPatternID_)
	{
		case PATTERN_ID_ENERGETIC_ACCUMULATOR          :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_ENERGETIC_ACCUMULATOR);
			newItem = new energeticAccumulatorClass
				(
				(unsigned long)newItemDimensions_.weight_, 
				science2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_ENERGETIC_ACCUMULATOR, INDEX_ENERGETIC_ACCUMULATOR_ACCUMULATOR), RANGE_ENERGETIC_ACCUMULATOR_ACCUMULATOR_MIN, RANGE_ENERGETIC_ACCUMULATOR_ACCUMULATOR_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight()),
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_ENERGETIC_ACCUMULATOR, INDEX_ENERGETIC_ACCUMULATOR_CHARGING), RANGE_ENERGETIC_ACCUMULATOR_CHARGING_MIN, RANGE_ENERGETIC_ACCUMULATOR_CHARGING_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight())
				);
			break;
		case PATTERN_ID_NAVIGATION_COMPUTER            : 
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_NAVIGATION_COMPUTER);
			newItem = new navigationComputerClass
				(
				(unsigned long)newItemDimensions_.weight_,
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_NAVIGATION_COMPUTER, INDEX_NAVIGATION_COMPUTER_SPEED), RANGE_NAVIGATION_COMPUTER_SPEED_MIN, RANGE_NAVIGATION_COMPUTER_SPEED_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight())
				);
			break;
		case PATTERN_ID_QUARK_GLUON_CORE               :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_QUARK_GLUON_CORE);
			newItem = new quarkGluonCoreClass
				(
				(unsigned long)newItemDimensions_.weight_,
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_QUARK_GLUON_CORE, INDEX_QUARK_GLUON_CORE_SPEED), RANGE_QUARK_GLUON_CORE_SPEED_MIN, RANGE_QUARK_GLUON_CORE_SPEED_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight()),
				(unsigned short)science2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_QUARK_GLUON_CORE, INDEX_QUARK_GLUON_CORE_CONSUMPTION), RANGE_QUARK_GLUON_CORE_CONSUMPTION_MAX, RANGE_QUARK_GLUON_CORE_CONSUMPTION_MIN, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight())
				);
			break;
		case PATTERN_ID_SUBSPACE_BEACON                : 
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_SUBSPACE_BEACON);
			newItem = new subspaceBeaconClass
				(
				(unsigned long)newItemDimensions_.weight_,
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_SUBSPACE_BEACON, INDEX_SUBSPACE_BEACON_SPEED), RANGE_SUBSPACE_BEACON_SPEED_MIN, RANGE_SUBSPACE_BEACON_SPEED_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight())
				);
			break;
		case PATTERN_ID_INERTIAL_DAMPERS               : 
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_INERTIAL_DAMPERS);
			newItem = new inertialDampersClass
				(
				(unsigned long)newItemDimensions_.weight_,
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_INERTIAL_DAMPERS, INDEX_INERTIAL_DAMPERS_SPEED), RANGE_INERTIAL_DAMPERS_SPEED_MIN, RANGE_INERTIAL_DAMPERS_SPEED_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight())
				);
			break;
		case PATTERN_ID_IMPULS_JET_ENGINE              :
			pattern  = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_IMPULS_JET_ENGINE);
			jetForce = ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_IMPULS_JET_ENGINE, INDEX_IMPULS_ENGINE_FORCE), RANGE_IMPULS_ENGINE_FORCE_MIN, RANGE_IMPULS_ENGINE_FORCE_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight());
			newItem  = new impulsJetEngineClass
				(
				(unsigned long)newItemDimensions_.weight_,
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_IMPULS_JET_ENGINE, INDEX_IMPULS_ENGINE_CONSUMPTION), RANGE_IMPULS_ENGINE_CONSUMPTION_MAX, RANGE_IMPULS_ENGINE_CONSUMPTION_MIN, jetForce, RANGE_IMPULS_ENGINE_FORCE_MIN, RANGE_IMPULS_ENGINE_FORCE_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight()),
				jetForce
				);
			break;
		case PATTERN_ID_MECHANICAL_DAMPERS             :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_MECHANICAL_DAMPERS);
			newItem = new mechanicalDampersClass
				(
				(unsigned long)newItemDimensions_.weight_,
				(unsigned short)ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_MECHANICAL_DAMPERS, INDEX_MECHANICAL_DAMPERS_ACCELERATION), RANGE_MECHANICAL_DAMPERS_ACCELERATION_MIN, RANGE_MECHANICAL_DAMPERS_ACCELERATION_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight())
				);
			break;
		case PATTERN_ID_DEFLECTOR_SHIELDS              :
			pattern            = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_DEFLECTOR_SHIELDS);
			shieldsAccumulator = ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_DEFLECTOR_SHIELDS, INDEX_DEFLECTOR_SHIELDS_GENERATOR_ACCUMULATOR), RANGE_DEFLECTOR_SHIELDS_ACCUMULATOR_MIN, RANGE_DEFLECTOR_SHIELDS_ACCUMULATOR_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight());
			newItem            = new deflectorShieldsGeneratorClass
				(
				(unsigned long)newItemDimensions_.weight_,
			 	(unsigned long)realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_DEFLECTOR_SHIELDS, INDEX_DEFLECTOR_SHIELDS_GENERATOR_SOLIDITY),
				shieldsAccumulator,
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_DEFLECTOR_SHIELDS, INDEX_DEFLECTOR_SHIELDS_GENERATOR_CHARGING), RANGE_DEFLECTOR_SHIELDS_CHARGING_MIN, RANGE_DEFLECTOR_SHIELDS_CHARGING_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight()),
				(double)shieldsAccumulator * DEFLECTOR_SHIELDS_LEAK_TO_ACCUMULATOR
				);
			break;
		case PATTERN_ID_WEAPON_ENERGY_CIRCUIT          :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_WEAPON_ENERGY_CIRCUIT);
			newItem = new weaponEnergyCircuitClass
				(
				(unsigned long)newItemDimensions_.weight_,
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_WEAPON_ENERGY_CIRCUIT, INDEX_WEAPON_ENERGY_CIRCUIT_CHARGING), RANGE_WEAPON_ENERGY_CIRCUIT_CHARGING_MIN, RANGE_WEAPON_ENERGY_CIRCUIT_CHARGING_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight())
				);
			break;
		case PATTERN_ID_DRONE_MISSILES                 :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_DRONE_MISSILES);
			newItem = new droneMissilesClass
				(
				(unsigned long)newItemDimensions_.weight_,
				newItemsCount_,
				0
				);
			break;
		case PATTERN_ID_DRONE_MISSILE_LAUNCHER         :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_DRONE_MISSILE_LAUNCHER);
			newItem = new droneMissileLauncherClass
				(
				(unsigned long)newItemDimensions_.weight_,
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_DRONE_MISSILE_LAUNCHER, INDEX_DRONE_MISSILE_LAUNCHER_ACCUMULATOR), RANGE_DRONE_MISSILE_LAUNCHER_ACCUMULATOR_MIN, RANGE_DRONE_MISSILE_LAUNCHER_ACCUMULATOR_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight()),
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_DRONE_MISSILE_LAUNCHER, INDEX_DRONE_MISSILE_LAUNCHER_CHARGING), RANGE_DRONE_MISSILE_LAUNCHER_CHARGING_MIN, RANGE_DRONE_MISSILE_LAUNCHER_CHARGING_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight())
				);
			break;
		case PATTERN_ID_LASER_GUN                      :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_LASER_GUN);
			newItem = new laserGunClass
				(
				(unsigned long)newItemDimensions_.weight_,
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_LASER_GUN, INDEX_LASER_GUN_ACCUMULATOR), RANGE_LASER_GUN_ACCUMULATOR_MIN, RANGE_LASER_GUN_ACCUMULATOR_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight()),
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_LASER_GUN, INDEX_LASER_GUN_CHARGING), RANGE_LASER_GUN_CHARGING_MIN, RANGE_LASER_GUN_CHARGING_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight()),
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_LASER_GUN, INDEX_LASER_GUN_RANGE), RANGE_LASER_GUN_RANGE_MIN, RANGE_LASER_GUN_RANGE_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight()),
				(unsigned long)realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_LASER_GUN, INDEX_LASER_GUN_PRECISION),
				(unsigned long)realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_LASER_GUN, INDEX_LASER_GUN_FORCIBILITY)
				);
			break;
		case PATTERN_ID_TRANSPORT_BEAM_GENERATOR       :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_TRANSPORT_BEAM_GENERATOR);
			newItem = new transportBeamGeneratorClass
				(
				(unsigned long)newItemDimensions_.weight_,
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_TRANSPORT_BEAM_GENERATOR, INDEX_TRANSPORT_BEAM_GENERATOR_ACCUMULATOR), RANGE_TRANSPORT_BEAM_GENERATOR_ACCUMULATOR_MIN, RANGE_TRANSPORT_BEAM_GENERATOR_ACCUMULATOR_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight()),
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_TRANSPORT_BEAM_GENERATOR, INDEX_TRANSPORT_BEAM_GENERATOR_CHARGING), RANGE_TRANSPORT_BEAM_GENERATOR_CHARGING_MIN, RANGE_TRANSPORT_BEAM_GENERATOR_CHARGING_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight()),
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_TRANSPORT_BEAM_GENERATOR, INDEX_TRANSPORT_BEAM_GENERATOR_RANGE), RANGE_TRANSPORT_BEAM_GENERATOR_RANGE_MIN, RANGE_TRANSPORT_BEAM_GENERATOR_RANGE_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight()),
				(unsigned long)realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_TRANSPORT_BEAM_GENERATOR, INDEX_TRANSPORT_BEAM_GENERATOR_PRECISION),
				(unsigned long)realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_TRANSPORT_BEAM_GENERATOR, INDEX_TRANSPORT_BEAM_GENERATOR_FORCIBILITY)
				);
			break;
		case PATTERN_ID_MAGNETICAL_COLLECTORS_OF_PLASMA:
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_MAGNETICAL_COLLECTORS_OF_PLASMA);
			newItem = new magneticalCollectorsOfPlasmaClass
				(
				(unsigned long)newItemDimensions_.weight_,
				newItemsCount_,
				0
				);
			break;
		case PATTERN_ID_SUBMERSIBLE_FILTRATION_TANK    :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_SUBMERSIBLE_FILTRATION_TANK);
			newItem = new submersibleFiltrationTankClass
				(
				(unsigned long)newItemDimensions_.weight_,
				newItemsCount_,
				0
				);
			break;
		case PATTERN_ID_SUBMARINE_MINING_MACHINE       :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_SUBMARINE_MINING_MACHINE);
			newItem = new submarineMiningMachineClass
				(
				(unsigned long)newItemDimensions_.weight_,
				newItemsCount_,
				0
				);
			break;
		case PATTERN_ID_UNDERGROUND_DRILLIG_MACHINE    :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_UNDERGROUND_DRILLIG_MACHINE);
			newItem = new undergroundDrillingMachineClass
				(
				(unsigned long)newItemDimensions_.weight_,
				newItemsCount_,
				0
				);
			break;
		case PATTERN_ID_PROBE                          :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_PROBE);
			newItem = new probeClass
				(
				(unsigned long)newItemDimensions_.weight_,
				(unsigned long)realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_PROBE, INDEX_PROBE_DEVICE_LEVEL)
				);
			break;
		case PATTERN_ID_HIGH_RESOLUTION_SENSORS       :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_HIGH_RESOLUTION_SENSORS);
			newItem = new highResolutionSensorsClass
				(
				(unsigned long)newItemDimensions_.weight_,
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_HIGH_RESOLUTION_SENSORS, INDEX_HIGH_RESOLUTION_SENSORS_RANGE), RANGE_HIGH_RESOLUTION_SENSORS_RANGE_MIN, RANGE_HIGH_RESOLUTION_SENSORS_RANGE_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight()),
				(unsigned long)realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_HIGH_RESOLUTION_SENSORS, INDEX_HIGH_RESOLUTION_SENSORS_LEVEL),
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_HIGH_RESOLUTION_SENSORS, INDEX_HIGH_RESOLUTION_SENSORS_CONSUMPTION), RANGE_HIGH_RESOLUTION_SENSORS_CONSUMPTION_MAX, RANGE_HIGH_RESOLUTION_SENSORS_CONSUMPTION_MIN, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight())
				);
			break;
		case PATTERN_ID_LONG_RANGE_SENSORS             :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_LONG_RANGE_SENSORS);
			newItem = new highResolutionSensorsClass
				(
				(unsigned long)newItemDimensions_.weight_,
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_LONG_RANGE_SENSORS, INDEX_LONG_RANGE_SENSORS_RANGE), RANGE_LONG_RANGE_SENSORS_RANGE_MIN, RANGE_LONG_RANGE_SENSORS_RANGE_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight()),
				(unsigned long)realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_LONG_RANGE_SENSORS, INDEX_LONG_RANGE_SENSORS_LEVEL),
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_LONG_RANGE_SENSORS, INDEX_LONG_RANGE_SENSORS_CONSUMPTION), RANGE_LONG_RANGE_SENSORS_CONSUMPTION_MAX, RANGE_LONG_RANGE_SENSORS_CONSUMPTION_MIN, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight())
				);
			break;
		case PATTERN_ID_CLOAKING_DEVICE                :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_CLOAKING_DEVICE);
			newItem = new cloakingDeviceClass
				(
				(unsigned long)newItemDimensions_.weight_,
				(unsigned long)realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_CLOAKING_DEVICE, INDEX_CLOAKING_DEVICE_LEVEL),
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_CLOAKING_DEVICE, INDEX_CLOAKING_DEVICE_CONSUMPTION), RANGE_CLOAKING_DEVICE_CONSUMPTION_MAX, RANGE_CLOAKING_DEVICE_CONSUMPTION_MIN, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight())
				);
			break;
		case PATTERN_ID_ANDROIDS                       :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_ANDROIDS);
			newItem = new androidsClass
				(
				(unsigned long)newItemDimensions_.weight_,
				newItemsCount_,
				0
				);
			break;
		case PATTERN_ID_MAGNETICAL_GRID                :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_MAGNETICAL_GRID);
			newItem = new magneticalGridClass
				(
				(unsigned long)newItemDimensions_.weight_,
				newItemsCount_,
				0
				);
			break;
		case PATTERN_ID_TRANSPORT_CONTAINER            :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_TRANSPORT_CONTAINER);
			newItem = new transportContainerClass
				(
				(unsigned long)newItemDimensions_.weight_,
				newItemsCount_,
				0
				);
			break;
		case PATTERN_ID_ENERGETIC_CELL                 :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_ENERGETIC_CELL);
			newItem = new energeticCellClass
				(
				(unsigned long)newItemDimensions_.weight_,
				newItemsCount_,
				0
				);
			break;
		case PATTERN_ID_PLASMA_FILTER                  :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_PLASMA_FILTER);
			newItem = new plasmaFilterClass
				(
				(unsigned long)newItemDimensions_.weight_
				);
			break;
		case PATTERN_ID_SCIENTIFIC_LAB                 :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_SCIENTIFIC_LAB);
			newItem = new scientificLabClass
				(
				(unsigned long)newItemDimensions_.weight_,
				(unsigned long)realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_SCIENTIFIC_LAB, INDEX_SCIENTIFIC_LABS_LEVEL),
				(unsigned long)newItemDimensions_.weight_ / SCIENTIFIC_LAB_WEIGHT_FOR_ONE_CAPACITY
				);
			break;
		case PATTERN_ID_LIFE_SUPPORT                   :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_LIFE_SUPPORT);
			newItem = new lifeSupportClass
				(
				(unsigned long)newItemDimensions_.weight_,
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_LIFE_SUPPORT, INDEX_LIFE_SUPPORT_CAPACITY), RANGE_LIFE_SUPPORT_CAPACITY_MIN, RANGE_LIFE_SUPPORT_CAPACITY_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight()),
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_LIFE_SUPPORT, INDEX_LIFE_SUPPORT_CONSUMPTION), RANGE_LIFE_SUPPORT_CONSUMPTION_MAX, RANGE_LIFE_SUPPORT_CONSUMPTION_MIN, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight())
				);
			break;
		case PATTERN_ID_SUBSPACE_TRANSMITTER           :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_SUBSPACE_TRANSMITTER);
			newItem = new subspaceTransmitterClass
				(
				(unsigned long)newItemDimensions_.weight_,
				SUBSPACE_MESSAGE_COST
				);
			break;
		case PATTERN_ID_MAINFRAME                      :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_MAINFRAME);
			newItem = new mainframeClass
				(
				(unsigned long)newItemDimensions_.weight_,
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_MAINFRAME, INDEX_MAINFRAME_BIO_NODES), RANGE_MAINFRAME_BIO_NODES_MIN, RANGE_MAINFRAME_BIO_NODES_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight()),
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_MAINFRAME, INDEX_MAINFRAME_PREDICTION), RANGE_MAINFRAME_PREDICTION_MIN, RANGE_MAINFRAME_PREDICTION_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight()),
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_MAINFRAME, INDEX_MAINFRAME_TIME_REUSABILITY), RANGE_MAINFRAME_TIME_REUSABILITY_MIN, RANGE_MAINFRAME_TIME_REUSABILITY_MAX, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight()),
				ulScience2Value(realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_MAINFRAME, INDEX_MAINFRAME_CONSUMPTION), RANGE_MAINFRAME_CONSUMPTION_MAX, RANGE_MAINFRAME_CONSUMPTION_MIN, newItemDimensions_.weight_, pattern->getMaxWeight(), pattern->getMinWeight())
				);
			break;
		case PATTERN_ID_HANGAR                         :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_HANGAR);
			newItem = new hangarClass
				(
				(unsigned long)newItemDimensions_.weight_,
				(unsigned long)realm->getScientificTree()->getRealScienceLevel(SCIENCE_ENTITY_HANGAR, INDEX_HANGARS_LEVEL),
				(unsigned long)newItemDimensions_.length_,
				(unsigned long)newItemDimensions_.width_,
				(unsigned long)newItemDimensions_.height_
				);
			break;
		case PATTERN_ID_MAGNETICAL_FILTER              :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_MAGNETICAL_FILTER);
			newItem = new magneticalFilterClass
				(
				(unsigned long)newItemDimensions_.weight_,
				newItemsCount_,
				0
				);
			break;
		case PATTERN_ID_DEUTERIUM_DECOMPOSER           :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_DEUTERIUM_DECOMPOSER);
			newItem = new deuteriumDecomposerClass
				(
				(unsigned long)newItemDimensions_.weight_,
				newItemsCount_,
				0
				);
			break;
		case PATTERN_ID_ZIRCONIUM_REACTOR              :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_ZIRCONIUM_REACTOR);
			newItem = new zirconiumReactorClass
				(
				(unsigned long)newItemDimensions_.weight_,
				newItemsCount_,
				0
				);
			break;
		case PATTERN_ID_ILMENITE_FURNACE               :
			pattern = actx->cacheItemsPatternsList.getItem<itemsPatternClass>(PATTERN_ID_ILMENITE_FURNACE);
			newItem = new ilmeniteFurnaceClass
				(
				(unsigned long)newItemDimensions_.weight_,
				newItemsCount_,
				0
				);
			break;
		default:			
            errLogicalError("bad items type");
			return;
			break;
	}
	
	if (newItem->isStackable())
	{
		stackableItemClass * newStackableItem = (stackableItemClass *)newItem;
		unsigned long long working = newStackableItem->getWorking();
		unsigned long long damaged = newStackableItem->getDamaged();
		newStackableItem->setWorking(0);
		newStackableItem->setDamaged(0);
		constructingFortress->warehouse.addItem(newItem, working, damaged, 1);
	}
	else
	{
		constructingFortress->warehouse.addItem(newItem, 0);
	}
};

// --------------------------------

bool hangarClass::canShipDock(fortressClass * ship)
{
	if ((NULL              != ship                   ) &&
		(FT_SHIP           == ship->getFortressType()) &&
		(ship->getLength() <= this->length_          ) &&
		(ship->getWidth()  <= this->width_           ) &&
		(ship->getHeight() <= this->height_          ) &&
		(isHangerFree()                              ))
	{
		return 1;
	}
	else
	{
		return 0;
	}
};

// ################################
// ################################