void CGSelectorArea::SetRegionsFromWeapons (CSpaceObject *pSource)

//	SetRegionsFromWeapons
//
//	Creates regions based on installed weapons.

	{
	int i;
	ASSERT(pSource);
	if (pSource == NULL)
		return;

	CShip *pShip = pSource->AsShip();
	if (pShip == NULL)
		return;

	CShipClass *pClass = pShip->GetClass();

	//	Keep track of layouts that have already been used.

	TArray<bool> SlotStatus;
	SlotStatus.InsertEmpty(MISC_DEVICES_LAYOUT_COUNT);
	for (i = 0; i < MISC_DEVICES_LAYOUT_COUNT; i++)
		SlotStatus[i] = true;

	//	If we don't have a launcher, we place the launcher slot first because we
	//	want it to take precedence (position-wise).

	int iIndex;
	bool bHasLauncher = (pShip->GetNamedDevice(devMissileWeapon) != NULL);
	if (!bHasLauncher)
		{
		//	See if we can figure out the proper position for the launcher based
		//	on the class slots

		CVector vLauncherPos;
		SDeviceDesc DeviceDesc;
		if (pClass->FindDeviceSlotDesc(devMissileWeapon, &DeviceDesc))
			vLauncherPos = pClass->GetPosOffset(DeviceDesc.iPosAngle, DeviceDesc.iPosRadius, DeviceDesc.iPosZ, DeviceDesc.b3DPosition);

		//	Find a layout

		if (FindLayoutForPos(vLauncherPos, SlotStatus, &iIndex))
			{
			const SLayoutDesc *pLayout = &g_MiscDevicesLayout[iIndex];

			SEntry *pEntry = m_Regions.Insert();
			pEntry->iType = typeEmptySlot;
			pEntry->iSlotType = devMissileWeapon;

			pEntry->iSlotPosIndex = iIndex;
			pEntry->rcRect.left = pLayout->xLeft;
			pEntry->rcRect.top = pLayout->yTop;
			pEntry->rcRect.right = pEntry->rcRect.left + ITEM_ENTRY_WIDTH;
			pEntry->rcRect.bottom = pEntry->rcRect.top + ITEM_ENTRY_HEIGHT;

			SlotStatus[iIndex] = false;
			}
		}

	//	Create a region for each weapon.

	for (i = 0; i < pShip->GetDeviceCount(); i++)
		{
		CInstalledDevice *pDevice = pShip->GetDevice(i);
		if (pDevice->IsEmpty() 
				|| (pDevice->GetCategory() != itemcatWeapon 
					&& pDevice->GetCategory() != itemcatLauncher))
			continue;

		if (pDevice->GetCategory() == itemcatLauncher)
			bHasLauncher = true;

		//	If the device already has a position index, then use that (assuming
		//	it's free).

		iIndex = pDevice->GetSlotPosIndex();
		if (iIndex < 0 || iIndex >= SlotStatus.GetCount() || !SlotStatus[iIndex])
			iIndex = -1;

		//	If we don't have an assigned slot, figure it out.

		if (iIndex == -1)
			{
			if (!FindLayoutForPos(pDevice->GetPosOffset(pShip), SlotStatus, &iIndex))
				continue;

			//	Remember so we stay in this location.

			pDevice->SetSlotPosIndex(iIndex);
			}

		//	Create the region

		const SLayoutDesc *pLayout = &g_MiscDevicesLayout[iIndex];

		SEntry *pEntry = m_Regions.Insert();
		pEntry->iType = typeInstalledItem;
		pEntry->pItemCtx = new CItemCtx(pShip, pDevice);

		pEntry->iSlotPosIndex = iIndex;
		pEntry->rcRect.left = pLayout->xLeft;
		pEntry->rcRect.top = pLayout->yTop;
		pEntry->rcRect.right = pEntry->rcRect.left + ITEM_ENTRY_WIDTH;
		pEntry->rcRect.bottom = pEntry->rcRect.top + ITEM_ENTRY_HEIGHT;

		//	Mark the layout as used

		SlotStatus[iIndex] = false;
		}

	//	Figure out how many empty weapon slots we should create. We add one 
	//	empty slot for each weapon slot, but we subtract one if we don't have
	//	a launcher and we always have at least 1 empty slot, in case the player
	//	finds a slot-less weapon.

#ifdef SINGLE_FREE_SLOT
	int iEmptySlots = 1;
#else
	int iWeaponSlotsInUse;
	int iTotalSlotsInUse = pShip->CalcDeviceSlotsInUse(&iWeaponSlotsInUse);
	int iEmptySlots = Max(1, Min((pClass->GetMaxDevices() - iTotalSlotsInUse), (pClass->GetMaxWeapons() - iWeaponSlotsInUse)) - (bHasLauncher ? 0 : 1));
#endif

	//	Try to position the empty slots

	CVector vWeaponPos;
	SDeviceDesc DeviceDesc;
	if (pClass->FindDeviceSlotDesc(devPrimaryWeapon, &DeviceDesc))
		vWeaponPos = pClass->GetPosOffset(DeviceDesc.iPosAngle, DeviceDesc.iPosRadius, DeviceDesc.iPosZ, DeviceDesc.b3DPosition);

	for (i = 0; i < iEmptySlots; i++)
		{
		//	Find a position

		if (FindLayoutForPos(vWeaponPos, SlotStatus, &iIndex))
			{
			const SLayoutDesc *pLayout = &g_MiscDevicesLayout[iIndex];

			SEntry *pEntry = m_Regions.Insert();
			pEntry->iType = typeEmptySlot;
			pEntry->iSlotType = devPrimaryWeapon;

			pEntry->iSlotPosIndex = iIndex;
			pEntry->rcRect.left = pLayout->xLeft;
			pEntry->rcRect.top = pLayout->yTop;
			pEntry->rcRect.right = pEntry->rcRect.left + ITEM_ENTRY_WIDTH;
			pEntry->rcRect.bottom = pEntry->rcRect.top + ITEM_ENTRY_HEIGHT;

			SlotStatus[iIndex] = false;
			}
		}
	}
void CGSelectorArea::SetRegionsFromMiscDevices (CSpaceObject *pSource)

//	SetRegionsFromMiscDevices
//
//	Generates regions showing misc devices (including reactor, drive, 
//	and cargo hold).

	{
	int i;
	ASSERT(pSource);
	if (pSource == NULL)
		return;

	CShip *pShip = pSource->AsShip();
	if (pShip == NULL)
		return;

	CShipClass *pClass = pShip->GetClass();

	//	Keep track of layouts that have already been used.

	TArray<bool> SlotStatus;
	SlotStatus.InsertEmpty(MISC_DEVICES_LAYOUT_COUNT);
	for (i = 0; i < MISC_DEVICES_LAYOUT_COUNT; i++)
		SlotStatus[i] = true;

	//	Reserve the slots for named device types

	SlotStatus[REACTOR_SLOT_INDEX] = false;
	SlotStatus[DRIVE_SLOT_INDEX] = false;
	SlotStatus[CARGO_SLOT_INDEX] = false;

	//	Count the number of miscellaneous devices with 0
	//	slots (because we may need to bump them).

	int iSlottedDevices = 0;
	int iNonSlotDevices = 0;
	for (i = 0; i < pShip->GetDeviceCount(); i++)
		{
		CInstalledDevice *pDevice = pShip->GetDevice(i);
		if (pDevice->IsEmpty() || pDevice->GetCategory() != itemcatMiscDevice)
			continue;

		if (pDevice->GetClass()->GetSlotsRequired() > 0)
			iSlottedDevices++;
		else
			iNonSlotDevices++;
		}

	//	We try to fit all other devices (and placeholders) before we add a
	//	non-slotted device.

	int iNonSlotDeviceSlotsAvail = Max(0, MISC_DEVICES_LAYOUT_COUNT - 4 - iSlottedDevices);

	//	Create a region for each device.

	int iIndex = -1;
	bool bHasReactor = false;
	bool bHasDrive = false;
	bool bHasCargo = false;
	int iNextUnamedSlot = FIRST_UNNAMED_SLOT_INDEX;
	for (i = 0; i < pShip->GetDeviceCount(); i++)
		{
		CInstalledDevice *pDevice = pShip->GetDevice(i);
		if (pDevice->IsEmpty())
			continue;

		//	Figure out the layout descriptor

		iIndex = -1;
		const SLayoutDesc *pLayout = NULL;
		switch (pDevice->GetCategory())
			{
			case itemcatCargoHold:
				pLayout = &g_MiscDevicesLayout[CARGO_SLOT_INDEX];
				bHasCargo = true;
				break;

			case itemcatDrive:
				pLayout = &g_MiscDevicesLayout[DRIVE_SLOT_INDEX];
				bHasDrive = true;
				break;

			case itemcatMiscDevice:
				{
				//	If this is a 0-slot device and we have no more room for
				//	0-slot devices, then we skip it.

				if (pDevice->GetClass()->GetSlotsRequired() == 0
						&& iNonSlotDeviceSlotsAvail <= 0)
					continue;

				//	If the device already has a position index, then use that (assuming
				//	it's free).

				iIndex = pDevice->GetSlotPosIndex();
				if (iIndex < 0 || iIndex >= SlotStatus.GetCount() || !SlotStatus[iIndex])
					iIndex = -1;

				//	If we don't have an assigned slot, figure it out.

				if (iIndex == -1)
					{
					//	Look for a new position

					if (!FindLayoutForPos(pDevice->GetPosOffset(pShip), SlotStatus, &iIndex))
						continue;

					//	Remember so we stay in this location.

					pDevice->SetSlotPosIndex(iIndex);
					}

				//	Remember the layout and mark it as used.

				pLayout = &g_MiscDevicesLayout[iIndex];
				SlotStatus[iIndex] = false;
				break;
				}

			case itemcatReactor:
				pLayout = &g_MiscDevicesLayout[REACTOR_SLOT_INDEX];
				bHasReactor = true;
				break;
			}

		//	Create the region (but only if we have a layout position
		//	for it).

		if (pLayout)
			{
			SEntry *pEntry = m_Regions.Insert();
			pEntry->iType = typeInstalledItem;
			pEntry->pItemCtx = new CItemCtx(pShip, pDevice);

			pEntry->iSlotPosIndex = iIndex;
			pEntry->rcRect.left = pLayout->xLeft;
			pEntry->rcRect.top = pLayout->yTop;
			pEntry->rcRect.right = pEntry->rcRect.left + ITEM_ENTRY_WIDTH;
			pEntry->rcRect.bottom = pEntry->rcRect.top + ITEM_ENTRY_HEIGHT;
			}
		}

	//	Add empty slots, if necessary

	if (!bHasReactor)
		{
		const SLayoutDesc *pLayout = &g_MiscDevicesLayout[REACTOR_SLOT_INDEX];

		SEntry *pEntry = m_Regions.Insert();
		pEntry->iType = typeEmptySlot;
		pEntry->iSlotType = devReactor;

		pEntry->rcRect.left = pLayout->xLeft;
		pEntry->rcRect.top = pLayout->yTop;
		pEntry->rcRect.right = pEntry->rcRect.left + ITEM_ENTRY_WIDTH;
		pEntry->rcRect.bottom = pEntry->rcRect.top + ITEM_ENTRY_HEIGHT;
		}

	if (!bHasDrive)
		{
		const SLayoutDesc *pLayout = &g_MiscDevicesLayout[DRIVE_SLOT_INDEX];

		SEntry *pEntry = m_Regions.Insert();
		pEntry->iType = typeEmptySlot;
		pEntry->iSlotType = devDrive;

		pEntry->rcRect.left = pLayout->xLeft;
		pEntry->rcRect.top = pLayout->yTop;
		pEntry->rcRect.right = pEntry->rcRect.left + ITEM_ENTRY_WIDTH;
		pEntry->rcRect.bottom = pEntry->rcRect.top + ITEM_ENTRY_HEIGHT;
		}

	if (!bHasCargo)
		{
		const SLayoutDesc *pLayout = &g_MiscDevicesLayout[CARGO_SLOT_INDEX];

		SEntry *pEntry = m_Regions.Insert();
		pEntry->iType = typeEmptySlot;
		pEntry->iSlotType = devCargo;

		pEntry->rcRect.left = pLayout->xLeft;
		pEntry->rcRect.top = pLayout->yTop;
		pEntry->rcRect.right = pEntry->rcRect.left + ITEM_ENTRY_WIDTH;
		pEntry->rcRect.bottom = pEntry->rcRect.top + ITEM_ENTRY_HEIGHT;
		}

	//	Figure out how many empty weapon slots we should create. We add one 
	//	empty slot for each weapon slot, but we subtract one if we don't have
	//	a launcher and we always have at least 1 empty slot, in case the player
	//	finds a slot-less weapon.

#ifdef SINGLE_FREE_SLOT
	int iEmptySlots = 1;
#else
	int iNonWeaponSlotsInUse;
	int iTotalSlotsInUse = pShip->CalcDeviceSlotsInUse(NULL, &iNonWeaponSlotsInUse);
	int iEmptySlots = Max(1, Min((pClass->GetMaxDevices() - iTotalSlotsInUse), (pClass->GetMaxNonWeapons() - iNonWeaponSlotsInUse)) - (bHasReactor ? 0 : 1) - (bHasDrive ? 0 : 1) - (bHasCargo ? 0 : 1));
#endif

	for (i = 0; i < iEmptySlots; i++)
		{
		if (FindLayoutForPos(CVector(), SlotStatus, &iIndex))
			{
			const SLayoutDesc *pLayout = &g_MiscDevicesLayout[iIndex];

			SEntry *pEntry = m_Regions.Insert();
			pEntry->iType = typeEmptySlot;
			pEntry->iSlotType = devNone;

			pEntry->iSlotPosIndex = iIndex;
			pEntry->rcRect.left = pLayout->xLeft;
			pEntry->rcRect.top = pLayout->yTop;
			pEntry->rcRect.right = pEntry->rcRect.left + ITEM_ENTRY_WIDTH;
			pEntry->rcRect.bottom = pEntry->rcRect.top + ITEM_ENTRY_HEIGHT;

			SlotStatus[iIndex] = false;
			}
		}
	}