示例#1
0
void ModuleManager::UnfitModule(uint32 itemID)
{
    GenericModule * mod = m_Modules->GetModule(itemID);
    if( mod != NULL )
    {
		if( mod->isLoaded() )
		{
			InventoryItemRef loadedChargeRef = mod->getLoadedChargeRef();
			if( IsStation(m_Ship->locationID()) )
				loadedChargeRef->Move(m_Ship->locationID(), flagHangar);		// used to be (m_pOperator->GetLocationID(), flag)
			else
			{
				m_Ship->ValidateAddItem(flagCargoHold,loadedChargeRef);
				//if( m_Ship->ValidateAddItem(flagCargoHold,loadedChargeRef) )
				//{
					loadedChargeRef->Move(m_Ship->itemID(), flagCargoHold);		// used to be (m_pOperator->GetLocationID(), flag)
					mod->unload();
				//}
				//else
				//	throw PyException( MakeCustomError( "Not enough cargo space!") );
			}
		}

		mod->offline();
		m_Modules->RemoveModule(itemID);
    }
}
示例#2
0
PyResult Command_createitem( Client* who, CommandDB* db, PyServiceMgr* services, const Seperator& args )
{
	if( args.argCount() < 2 ) {
		throw PyException( MakeCustomError("Correct Usage: /create [typeID]") );
	}
	
	//basically, a copy/paste from Command_create. The client seems to call this multiple times, 
	//each time it creates an item
	if( !args.isNumber( 1 ) )
		throw PyException( MakeCustomError( "Argument 1 must be type ID." ) );
    const uint32 typeID = atoi( args.arg( 1 ).c_str() );
 
	uint32 qty = 1;
    if( 2 < args.argCount() )
    {
	    if( args.isNumber( 2 ) )
		    qty = atoi( args.arg( 2 ).c_str() );
    }

    sLog.Log("command message", "Create %s %u times", args.arg( 1 ).c_str(), qty );

	//create into their cargo hold unless they are docked in a station,
	//then stick it in their hangar instead.
	uint32 locationID;
	EVEItemFlags flag;
	if( who->IsInSpace() )
    {
		locationID = who->GetShipID();
		flag = flagCargoHold;
	}
    else
    {
		locationID = who->GetStationID();
		flag = flagHangar;
	}
	
	ItemData idata(
		typeID,
		who->GetCharacterID(),
		0, //temp location
		flag,
		qty
	);

	InventoryItemRef i = services->item_factory.SpawnItem( idata );
	if( !i )
		throw PyException( MakeCustomError( "Unable to create item of type %s.", args.arg( 1 ).c_str() ) );

	//Move to location
	i->Move( locationID, flag, true );

	return new PyString( "Creation successful." );
}
示例#3
0
void Ship::AddItem(EVEItemFlags flag, InventoryItemRef item)
{
	
	ValidateAddItem( flag, item );
					
	//it's a new module, make sure it's state starts at offline so that it is added correctly
	if( item->categoryID() != EVEDB::invCategories::Charge )
		item->PutOffline();

	item->Move(m_pOperator->GetLocationID(), flag);  //TODO - check this

	m_ModuleManager->FitModule(item);
}
示例#4
0
uint32 Ship::AddItem(EVEItemFlags flag, InventoryItemRef item)
{
    ValidateAddItem( flag, item );

    //it's a new module, make sure it's state starts at offline so that it is added correctly
    if( item->categoryID() == EVEDB::invCategories::Module )
        item->PutOffline();

	switch( item->categoryID() )
	{
		case EVEDB::invCategories::Charge:
			{
				m_ModuleManager->LoadCharge(item, flag);
				InventoryItemRef loadedChargeOnModule = m_ModuleManager->GetLoadedChargeOnModule(flag);
				if( loadedChargeOnModule )
				{
					return loadedChargeOnModule->itemID();
				}
				else
					return 0;
			}
			break;

		case EVEDB::invCategories::Module:
			if( m_ModuleManager->FitModule(item, flag) )
				item->Move(itemID(), flag);
			break;

		// The default case handles ANY other items added to ship and assumes they go into one of the valid cargo holds on this ship:
		default:
			//Log::Error( "Ship::AddItem(flag,item)", "ERROR! Function called with item '%s' (id: %u) of category neither Charge nor Module!", item->itemName().c_str(), item->itemID() );
            _IncreaseCargoHoldsUsedVolume(item->flag(), (item->getAttribute(AttrVolume).get_float() * item->quantity()));
			item->Move(itemID(), flag);
			break;
	}

	return 0;
}
示例#5
0
void Ship::AddItem(EVEItemFlags flag, InventoryItemRef item)
{

    ValidateAddItem( flag, item );

    //it's a new module, make sure it's state starts at offline so that it is added correctly
    if( item->categoryID() != EVEDB::invCategories::Charge )
        item->PutOffline();

    // TODO: Somehow, if this returns FALSE, the item->Move() above has to be "undone"... can we do the move AFTER attempting to fit?
    // what if we pass the flag into FitModule().... then if it returns true, the item->Move() can be called
    if( m_ModuleManager->FitModule(item, flag) )
        item->Move(m_pOperator->GetLocationID(), flag);  //TODO - check this
}
示例#6
0
void Contract::Delete()
{
    // Return the items to the hangar
    std::map<uint32, ContractGetItemsRef>::iterator cur, end;

    cur = items().begin();
    end = items().end();

    for(; cur != end; cur++)
    {
        InventoryItemRef item = m_itemFactory.GetItem( cur->second->m_itemID );
        item->Move( startStationID(), flagHangar, true );
        item->ChangeOwner( issuerID(), true );
    }

    // take ourself out of the DB
    m_factory.db().DeleteContract( contractID() );

    // Delete ourselves from factory cache
    m_factory.DeleteContract( contractID() );
}
示例#7
0
InventoryItemRef InventoryItem::Split(int32 qty_to_take, bool notify) {
    if(qty_to_take <= 0) {
        _log(ITEM__ERROR, "%s (%u): Asked to split into a chunk of %d", itemName().c_str(), itemID(), qty_to_take);
        return InventoryItemRef();
    }
    if(!AlterQuantity(-qty_to_take, notify)) {
        _log(ITEM__ERROR, "%s (%u): Failed to remove quantity %d during split.", itemName().c_str(), itemID(), qty_to_take);
        return InventoryItemRef();
    }

    ItemData idata(
        typeID(),
        ownerID(),
        (notify ? 1 : locationID()), //temp location to cause the spawn via update
        flag(),
        qty_to_take
    );

    InventoryItemRef res = m_factory.SpawnItem(idata);
    if(notify)
        res->Move( locationID(), flag() );

    return( res );
}
示例#8
0
void ModuleManager::LoadCharge(InventoryItemRef chargeRef, EVEItemFlags flag)
{
    ActiveModule * mod = (ActiveModule *)(m_Modules->GetModule(flag));			// Should not be dangrous to assume ALL modules where charges are loaded are ACTIVE modules
    if( mod != NULL )
    {
		// Scenarios to handle:
		// + no charge loaded: check capacity >= volume of charge to add, if true, LOAD
		//     - ELSE: if charge to load is qty > 1, calculate smallest integer qty that will EQUAL capacity, SPLIT remainder off, then LOAD!
		// + some charge loaded: check capacity >= volume of charge to add, if true, MERGE new charge to existing
		//     - ELSE: if charge to load is qty > 1, calculate smallest integer qty that added to existing charge qty will EQUAL capacity, SPLIT remainder off, then LOAD!

		// Key facts to get:
		// * existing charge ref -> qty and volume/unit
		// * module ref -> capacity of module
		// * charge to add ref -> qty and volume/unit

		EvilNumber modCapacity = mod->getItem()->GetAttribute(AttrCapacity);
		EvilNumber chargeToLoadVolume = chargeRef->GetAttribute(AttrVolume);
		EvilNumber chargeToLoadQty = EvilNumber(chargeRef->quantity());

		/////////////////////////////////////////
		// chargeRef->Split();
		// chargeRef->Merge();
		// mod->Load(chargeRef);
		// chargeRef->Move(m_Ship->itemID(), flag);		// used to be (m_pOperator->GetLocationID(), flag)
		/////////////////////////////////////////

		//m_Ship->GetOperator()->Client()->MoveItem(chargeRef->itemID(), m_Ship->itemID(), flag);

		if( mod->isLoaded() )
		{
			// Module is loaded, let's check available capacity:
			InventoryItemRef loadedChargeRef = mod->getLoadedChargeRef();
			EvilNumber loadedChargeVolume = loadedChargeRef->GetAttribute(AttrVolume);
			EvilNumber loadedChargeQty = EvilNumber(loadedChargeRef->quantity());
			modCapacity -= (loadedChargeVolume * loadedChargeQty);		// Calculate remaining capacity
			if( chargeRef->typeID() != loadedChargeRef->typeID() )
			{
				// Different charge type is being swapped into this module, so unload what's loaded
				if( IsStation(m_Ship->GetOperator()->GetLocationID()) )
					loadedChargeRef->Move(m_Ship->locationID(), flagHangar);
				else
				{
					m_Ship->ValidateAddItem(flagCargoHold,loadedChargeRef);
					loadedChargeRef->Move(m_Ship->itemID(), flagCargoHold);
				}
				mod->unload();

				// Loading of charge will be performed below
			}
			else
			{
				if( modCapacity > chargeToLoadVolume )
				{
					// Great!  We can load at least one, let's top off the loaded charges:
					uint32 quantityWeCanLoad = floor((modCapacity / chargeToLoadVolume).get_float());
					if( quantityWeCanLoad > 0 )
					{
						if( quantityWeCanLoad < chargeToLoadQty.get_int() )
						{
							// Split chargeRef to qty 'quantityWeCanLoad'
							// Merge new smaller qty 'quantityWeCanLoad' with loadedChargeRef
							// Load this merged charge Ref into module
							InventoryItemRef loadableChargeQtyRef = chargeRef->Split( quantityWeCanLoad );
							loadableChargeQtyRef->ChangeOwner( chargeRef->ownerID() );
							loadedChargeRef->Merge( loadableChargeQtyRef );
							mod->load( loadedChargeRef );
							loadedChargeRef->Move(m_Ship->itemID(), flag);		// used to be (m_pOperator->GetLocationID(), flag)
						}
						else
						{
							// Merge chargeRef with loadedChargeRef
							// Load this merged charge Ref into module
							loadedChargeRef->Merge( chargeRef );
							mod->load( loadedChargeRef );
							loadedChargeRef->Move(m_Ship->itemID(), flag);		// used to be (m_pOperator->GetLocationID(), flag)
						}
					}
					else
						throw PyException( MakeCustomError( "Cannot load even one unit of this charge!" ) );
				}
				else
				{
					throw PyException( MakeCustomError( "Charge is full!" ) );
				}
			}
		}

		// Refresh ammo capacity of module in case it was modified in previous code block ahead of a load action:
		modCapacity = mod->getItem()->GetAttribute(AttrCapacity);

		// Load charge supplied if this module was either never loaded, or just unloaded from a different type right above:
		if( !(mod->isLoaded()) )
		{
			// Module is not loaded at all, let's check total volume of charge to load against available capacity:
			if( modCapacity >= (chargeToLoadVolume * chargeToLoadQty) )
			{
				// We can insert entire stack of chargeRef into module
				// Load chargeRef as-is into module
				mod->load( chargeRef );
				chargeRef->Move(m_Ship->itemID(), flag);		// used to be (m_pOperator->GetLocationID(), flag)
			}
			else
			{
				// We need to split off only as many charge units as can fit into this module
				// Split chargeRef
				uint32 quantityWeCanLoad = floor((modCapacity / chargeToLoadVolume).get_float());
				if( quantityWeCanLoad > 0 )
				{
					// Split chargeRef to qty 'quantityWeCanLoad'
					// Merge new smaller qty 'quantityWeCanLoad' with loadedChargeRef
					// Load this merged charge Ref into module
					InventoryItemRef loadableChargeQtyRef = chargeRef->Split( quantityWeCanLoad );
					loadableChargeQtyRef->ChangeOwner( chargeRef->ownerID() );
					mod->load( loadableChargeQtyRef );
					loadableChargeQtyRef->Move(m_Ship->itemID(), flag);		// used to be (m_pOperator->GetLocationID(), flag)
				}
				else
		            throw PyException( MakeCustomError( "Cannot load even one unit of this charge!" ) );
			}
		}
    }
}
PyResult ContractMgrService::Handle_CreateContract( PyCallArgs& call )
{
	Call_CreateContract info;
	PyList* requestItemTypeList = new PyList;
	PyList* itemList = new PyList;
	uint32 flag = 0;
	bool forCorp = false;
	double volume = 0;
	uint32 maxCharContracts = 0;

	if( call.byname.find( "forCorp" ) != call.byname.end() )
	{
		forCorp = call.byname.find( "forCorp" )->second->AsBool()->value();
	}


	// Let's see the players limit of contracts
	CharacterRef ch = call.client->GetChar();
	
	if( forCorp )
	{
		if( ch->HasSkill( 25233 ) )
		{
			SkillRef skill = ch->GetSkill( 25233 );
			uint32 skillLevel = skill->GetAttribute( 280 ).get_int();
			maxCharContracts = ( 10 * skillLevel ) + 10;
		}
		else
		{
			maxCharContracts = 10;
		}
	}
	else
	{
		if( ch->HasSkill( 25235 ) )
		{
			SkillRef skill = ch->GetSkill( 25235 );
			uint32 skillLevel = skill->GetAttribute( 280 ).get_int();
			maxCharContracts = ( 4 * skillLevel ) + 1;
		}
		else
		{
			maxCharContracts = 1;
		}

		uint32 numOutstandingContractsNonCorp = 0;
		uint32 numOutstandingContractsForCorp = 0;
		std::map<uint32, ContractRef>::const_iterator cur, end;
		std::map<uint32, ContractRef> contracts = m_contractManager->GetContractList();
		
		cur = contracts.begin();
		end = contracts.end();

		for(; cur != end; cur++ )
		{
			ContractRef contract = cur->second;
			if( contract->issuerID() == call.client->GetCharacterID() )
			{
				if( contract->forCorp() ) numOutstandingContractsForCorp += 1;
				else numOutstandingContractsNonCorp += 1;
			}
		}

		if( ( forCorp ) && ( numOutstandingContractsForCorp >= maxCharContracts ) )
		{
			call.client->SendInfoModalMsg( "Your Corporation Contracting skill level only allows you to create %d public contracts for your corp/alliance", maxCharContracts );
			return new PyBool( false );
		}

		if( ( !forCorp ) && ( numOutstandingContractsNonCorp >= maxCharContracts ) )
		{
			call.client->SendInfoModalMsg( "Your Contracting skill level only allows you to create %d public contracts", maxCharContracts );
			return new PyBool( false );
		}
	}

	if( !info.Decode( &call.tuple ) )
	{
		codelog(SERVICE__ERROR, "%s: Bad arguments to CreateContract in contractMgr", call.client->GetCharacterName() );
		return NULL;
	}

	if( call.byname.find( "requestItemTypeList" ) != call.byname.end() )
	{
		requestItemTypeList = call.byname.find( "requestItemTypeList" )->second->AsList();
	}

	if( call.byname.find( "flag" ) != call.byname.end() )
	{
		flag = call.byname.find( "flag" )->second->AsInt()->value();
	}

	if( call.byname.find( "itemList" ) != call.byname.end() )
	{
		itemList = call.byname.find( "itemList" )->second->AsList();
	}

	if( info.endStationID == 0 )info.endStationID = info.startStationID;

	ContractData* cData = new ContractData(
		call.client->GetCharacterID(),
		call.client->GetCorporationID(),
		info.type,
		info.avail,
		info.assigneeID,
		info.expiretime,
		info.expiretime,
		info.startStationID,
		info.endStationID,
		call.client->GetSystemID(),
		call.client->GetSystemID(),
		call.client->GetRegionID(),
		call.client->GetRegionID(),
		info.price,
		info.reward,
		info.collateral,
		info.title,
		info.description,
		forCorp,
		conStatusOutstanding,
		false,
		0,
		Win32TimeNow(),
		Win32TimeNow() + ( info.duration * Win32Time_Day ),
		Win32TimeNow(),
		Win32TimeNow(),
		0,
		false,
		0,
		0,
		0
		);

	std::map<uint32, ContractRequestItemRef> requestItems;
	std::map<uint32, ContractGetItemsRef> items;

	uint32 itemID = 0;
	uint32 typeID = 0;

	for( size_t i = 0; i < itemList->size(); i ++ )
	{
		if( itemList->IsList() )
		{
			if( itemList->GetItem( i )->AsList()->GetItem( 0 )->IsInt() )
				itemID = itemList->GetItem( i )->AsList()->GetItem( 0 )->AsInt()->value();
			else{
				sLog.Error( "ContractMgrService", "Wrong list args" );
				break;
			}
		}

		InventoryItemRef item = m_manager->item_factory.GetItem( itemID );

		if( item == NULL )
		{
			sLog.Error( "ContractMgrService", "GetItem returned NULL" );
			break;
		}

		item->Move( call.client->GetStationID(), flagBriefcase, true );
		item->ChangeOwner( 1, true );
		items.insert( std::make_pair( itemID, ContractGetItemsRef( new ContractGetItems( itemID,  itemList->GetItem( i )->AsList()->GetItem( 1 )->AsInt()->value() ) ) ) );
	}

	if( cData->m_type == conTypeItemExchange )
	{
		for( size_t i = 0; i < requestItemTypeList->size(); i ++ )
		{
			if( itemList->IsList() )
			{
				if( requestItemTypeList->GetItem( i )->AsList()->GetItem( 0 )->IsInt() )
					typeID = requestItemTypeList->GetItem( i )->AsList()->GetItem( 0 )->AsInt()->value();
				else{
					sLog.Error( "ContractMgrService", "Wrong list args" );
					break;
				}
			}

			requestItems.insert( std::make_pair( itemID, ContractRequestItemRef( new ContractRequestItem(itemID, requestItemTypeList->GetItem( i )->AsList()->GetItem( 1 )->AsInt()->value() ) ) ) );
		}
	}

	uint32 contractID = 0;
	sLog.Debug( "ContractMgrService", "Creating contract..." );
	ContractRef _contract = ContractRef( new Contract( contractID, *cData, requestItems, items, m_manager->item_factory, *m_contractManager ) );

	contractID = m_contractManager->CreateContract( _contract );
	sLog.Debug( "ContractMgrService", "Contract created" );
	
	return new PyInt( contractID );
}
示例#10
0
PyResult RamProxyService::Handle_CompleteJob(PyCallArgs &call) {
    Call_CompleteJob args;

    if(!args.Decode(&call.tuple)) {
        _log(CLIENT__ERROR, "Failed to decode args.");
        return NULL;
    }

    _VerifyCompleteJob(args, call.client);

    // hundreds of variables to allocate ... maybe we can make struct for GetJobProperties and InstallJob?
    uint32 installedItemID, ownerID, runs, licensedProductionRuns;
    EVEItemFlags outputFlag;
    EVERamActivity activity;
    if(!m_db.GetJobProperties(args.jobID, installedItemID, ownerID, outputFlag, runs, licensedProductionRuns, activity))
        return NULL;

    // return item
    InventoryItemRef installedItem = m_manager->item_factory.GetItem( installedItemID );
    if( !installedItem )
        return NULL;
    installedItem->Move( installedItem->locationID(), outputFlag );

    std::vector<RequiredItem> reqItems;
    if( !m_db.GetRequiredItems( installedItem->typeID(), activity, reqItems ) )
        return NULL;

    // return materials which weren't completely consumed
    std::vector<RequiredItem>::iterator cur, end;
    cur = reqItems.begin();
    end = reqItems.end();
    for(; cur != end; cur++) {
        if(!cur->isSkill && cur->damagePerJob != 1.0) {
            uint32 quantity = static_cast<uint32>(cur->quantity * runs * (1.0 - cur->damagePerJob));
            if(quantity == 0)
                continue;

            ItemData idata(
                cur->typeID,
                ownerID,
                0, //temp location
                outputFlag,
                quantity
            );

            InventoryItemRef item = m_manager->item_factory.SpawnItem( idata );
            if( !item )
                return NULL;

            item->Move(args.containerID, outputFlag);
        }
    }

    // if not cancelled, realize result of activity
    if(!args.cancel) {
        switch(activity) {
            /*
             * Manufacturing
             */
            case ramActivityManufacturing: {
                BlueprintRef bp = BlueprintRef::StaticCast( installedItem );

                ItemData idata(
                    bp->productTypeID(),
                    ownerID,
                    0,  // temp location
                    outputFlag,
                    bp->productType().portionSize() * runs
                );

                InventoryItemRef item = m_manager->item_factory.SpawnItem( idata );
                if( !item )
                    return NULL;

                item->Move(args.containerID, outputFlag);
            } break;
            /*
             * Time productivity research
             */
            case ramActivityResearchingTimeProductivity: {
                BlueprintRef bp = BlueprintRef::StaticCast( installedItem );

                bp->AlterProductivityLevel( runs );
            } break;
            /*
             * Material productivity research
             */
            case ramActivityResearchingMaterialProductivity: {
                BlueprintRef bp = BlueprintRef::StaticCast( installedItem );

                bp->AlterMaterialLevel( runs) ;
            } break;
            /*
             * Copying
             */
            case ramActivityCopying: {
                BlueprintRef bp = BlueprintRef::StaticCast( installedItem );

                ItemData idata(
                    installedItem->typeID(),
                    ownerID,
                    0, //temp location
                    outputFlag,
                    runs
                );
                BlueprintData bdata(
                    true,
                    bp->materialLevel(),
                    bp->productivityLevel(),
                    licensedProductionRuns
                );

                BlueprintRef copy = m_manager->item_factory.SpawnBlueprint( idata, bdata );
                if( !copy )
                    return NULL;

                copy->Move(args.containerID, outputFlag);
            } break;
            /*
             * The rest is unsupported
             */
            case ramActivityResearchingTechnology:
            case ramActivityDuplicating:
            case ramActivityReverseEngineering:
            case ramActivityInvention:
            default: {
                _log(SERVICE__ERROR, "Activity %u is currently unsupported.", activity);
            } break;
        }
    }

    // regardless on success of this, we will return NULL, so there's no condition here
    m_db.CompleteJob(args.jobID, args.cancel ? ramCompletedStatusAbort : ramCompletedStatusDelivered);

    return NULL;
}
示例#11
0
PyResult RamProxyService::Handle_InstallJob(PyCallArgs &call) {
    Call_InstallJob args;
    if(!args.Decode(&call.tuple)) {
        _log(SERVICE__ERROR, "Failed to decode args.");
        return NULL;
    }

    // load installed item
    InventoryItemRef installedItem = m_manager->item_factory.GetItem( args.installedItemID );
    if( !installedItem )
        return NULL;

    // if output flag not set, put it where it was
    if(args.flagOutput == flagAutoFit)
        args.flagOutput = installedItem->flag();

    // decode path to BOM location
    PathElement pathBomLocation;
    if( !pathBomLocation.Decode( args.bomPath->GetItem(0) ) ) {
        _log(SERVICE__ERROR, "Failed to decode BOM location.");
        return NULL;
    }

    // verify call
    _VerifyInstallJob_Call( args, (InventoryItemRef)installedItem, pathBomLocation, call.client );

    // this calculates some useful multipliers ... Rsp_InstallJob is used as container ...
    Rsp_InstallJob rsp;
    if(!_Calculate(args, (InventoryItemRef)installedItem, call.client, rsp))
        return NULL;

    // I understand sent maxJobStartTime as a limit, so this checks whether it's in limit
    if(rsp.maxJobStartTime > call.byname["maxJobStartTime"]->AsInt()->value())
        throw(PyException(MakeUserError("RamCannotGuaranteeStartTime")));

    // query required items for activity
    std::vector<RequiredItem> reqItems;
    if(!m_db.GetRequiredItems(installedItem->typeID(), (EVERamActivity)args.activityID, reqItems))
        return NULL;

    // if 'quoteOnly' is 1 -> send quote, if 0 -> install job
    if(call.byname["quoteOnly"]->AsInt()->value())
    {
        _EncodeBillOfMaterials(reqItems, rsp.materialMultiplier, rsp.charMaterialMultiplier, args.runs, rsp.bom);
        _EncodeMissingMaterials(reqItems, pathBomLocation, call.client, rsp.materialMultiplier, rsp.charMaterialMultiplier, args.runs, rsp.missingMaterials);

        return rsp.Encode();
    }
    else
    {
        // verify install
        _VerifyInstallJob_Install(rsp, pathBomLocation, reqItems, args.runs, call.client);

        // now we are sure everything from the client side is right, we can start it ...

        // calculate proper start time
        uint64 beginProductionTime = Win32TimeNow();
        if(beginProductionTime < (uint32)rsp.maxJobStartTime)
            beginProductionTime = rsp.maxJobStartTime;

        // register our job
        if( !m_db.InstallJob(
            args.isCorpJob ? call.client->GetCorporationID() : call.client->GetCharacterID(),
            call.client->GetCharacterID(),
            args.installationAssemblyLineID,
            installedItem->itemID(),
            beginProductionTime,
            beginProductionTime + uint64(rsp.productionTime) * Win32Time_Second,
            args.description.c_str(),
            args.runs,
            (EVEItemFlags)args.flagOutput,
            pathBomLocation.locationID,
            args.licensedProductionRuns ) )
        {
            return NULL;
        }

        // do some activity-specific actions
        switch(args.activityID) {
            case ramActivityManufacturing: {
                // decrease licensed production runs
                BlueprintRef bp = BlueprintRef::StaticCast( installedItem );
                if(!bp->infinite())
                    bp->AlterLicensedProductionRunsRemaining(-1);
            }
        }

        // pay for assembly lines, move the item away
        call.client->AddBalance(-rsp.cost);
        installedItem->Move( installedItem->locationID(), flagFactoryBlueprint );

        // query all items contained in "Bill of Materials" location
        std::vector<InventoryItemRef> items;
        _GetBOMItems( pathBomLocation, items );

        std::vector<RequiredItem>::iterator cur, end;
        cur = reqItems.begin();
        end = reqItems.end();
        for(; cur != end; cur++) {
            if(cur->isSkill)
                continue;       // not interested

            // calculate needed quantity
            uint32 qtyNeeded = static_cast<uint32>(ceil(cur->quantity * rsp.materialMultiplier * args.runs));
            if(cur->damagePerJob == 1.0)
                qtyNeeded = static_cast<uint32>(ceil(qtyNeeded * rsp.charMaterialMultiplier));   // skill multiplier is applied only on fully consumed materials

            std::vector<InventoryItemRef>::iterator curi, endi;
            curi = items.begin();
            endi = items.end();
            // consume required materials
            for(; curi != endi; curi++) {
                if((*curi)->typeID() == cur->typeID && (*curi)->ownerID() == call.client->GetCharacterID()) {
                    if(qtyNeeded >= (*curi)->quantity()) {
                        qtyNeeded -= (*curi)->quantity();
                        (*curi)->Delete();
                    } else {
                        (*curi)->AlterQuantity(-(int32)qtyNeeded);
                        break;  // we are done, stop searching
                    }
                }
            }
        }

        return NULL;
    }
}