New Feature: gear templates allow us to save and load a merc's equipment, thereby speeding up the equipment process.

For more info, see http://thepit.ja-galaxy-forum.com/index.php?t=msg&th=23462&goto=350497&#msg_350497

Requires GameDir >= r2384

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8440 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
Flugente
2017-08-06 21:06:49 +00:00
parent 35cdd64a92
commit f7423782db
15 changed files with 1345 additions and 108 deletions
+781
View File
@@ -78,6 +78,9 @@
#include "Game Clock.h" // added by Flugente
#include <vfs/Core/vfs_file_raii.h> // added by Flugente for vfs-stuff
#include "DynamicDialogue.h" // added by Flugente for SoldierRelation()
#include "MessageBoxScreen.h" // added by Flugente
#include "Map Screen Interface.h" // added by Flugente
#include "Map Screen Interface Map.h" // added by Flugente
#endif
#ifdef JA2UB
@@ -92,6 +95,18 @@
// directory for fortification plans (located in Profiles sub-folder)
#define FORTIFICATIONPLAN_DIRECTORY "FortificationPlan\\"
#define EQUIPMENTTEMPLATE_DIRECTORY "GearTemplate\\"
extern BOOL GetBetterObject_InventoryPool( UINT16 usItem, INT16 status, UINT32& arLoop, UINT8& arIndex );
extern BOOL GetFittingAmmo_InventoryPool( UINT8 usCalibre, UINT8 usAmmoType, UINT32& arLoop );
extern BOOLEAN HandleNailsVestFetish( SOLDIERTYPE *pSoldier, UINT32 uiHandPos, UINT16 usReplaceItem );
extern std::vector<WORLDITEM> pInventoryPoolList;
extern INT32 GetAttachmentInfoIndex( UINT16 usItem );
extern INT32 giItemDescAmmoButton;
ITEM_POOL_LOCATOR FlashItemSlots[ NUM_ITEM_FLASH_SLOTS ];
UINT32 guiNumFlashItemSlots = 0;
// sevenfm: remember network settings for last planted tripwire
@@ -8970,3 +8985,769 @@ BOOLEAN SpendMoney( SOLDIERTYPE *pSoldier, UINT32 aAmount )
return (aAmount == 0);
}
// Flugente: gear templates
extern std::vector<std::string> gTemplateVector;
// this class is used to store our gear templates
class GEAR_NODE
{
public:
GEAR_NODE(INT8 a, UINT16 b, UINT16 c) :
slot( a ), item( b ), ammoitem(c){}
GEAR_NODE() :
slot( NOWHERE ), item( 0 ), ammoitem(0) {}
void swap(GEAR_NODE& other)
{
std::swap( slot, other.slot );
std::swap( item, other.item );
std::swap( ammoitem, other.ammoitem );
std::vector<UINT16> tmp = other.attachments;
other.attachments = attachments;
attachments = tmp;
}
INT8 slot;
UINT16 item;
UINT16 ammoitem;
std::vector<UINT16> attachments;
};
std::ostream& operator<<(std::ostream& stream, GEAR_NODE const& data)
{
stream << (int)(data.slot) << " " << (UINT16)(data.item) << " " << (UINT16)(data.ammoitem);
std::vector<UINT16> tmp = data.attachments;
for ( std::vector<UINT16>::iterator it = tmp.begin( ); it != tmp.end( ); ++it )
stream << " " << (*it);
stream << std::endl;
return stream;
}
std::vector<GEAR_NODE> LoadEquipmentTemplate( std::string aName )
{
CHAR8 filename[MAX_PATH];
sprintf(filename, "%s%s", EQUIPMENTTEMPLATE_DIRECTORY, aName.c_str());
// get full path to save file
vfs::Path vfsPath;
vfs::COpenWriteFile rfile( filename, true );
rfile->_getRealPath(vfsPath);
std::string str = vfsPath.to_string();
const char* filenamewithpath = str.c_str();
// Read data
std::ifstream file;
file.open(filenamewithpath, std::ios::in);
std::vector<GEAR_NODE> vec;
std::string line;
while ( std::getline( file >> std::ws, line ) )
{
if ( line[0] != '#' && line[0] != '/' )
{
std::istringstream iss( line );
GEAR_NODE node;
int num;
if ( iss >> num && iss >> (UINT16)node.item )
{
node.slot = num;
// additional data is optional
if ( iss >> (UINT16)node.ammoitem )
{
while ( iss >> num )
{
node.attachments.push_back( num );
}
}
vec.push_back(node);
}
}
}
file.close();
return vec;
}
void SaveEquipmentTemplate(std::vector<GEAR_NODE> aVec, STR16 aName)
{
CHAR8 filename[MAX_PATH];
sprintf(filename, "%s%S.txt", EQUIPMENTTEMPLATE_DIRECTORY, aName);
// get full path to save file
// what we are doing here might seem rather odd. If the file does not exist, we are creating a new file via vfs... and then create a fresh binary file afterwards.
// I found this to be the easiest solution for using both vfs pathing and being able to use a std::fstream
// If you don't like it, fix it yourself, or make vfs not such a pain to use
vfs::Path vfsPath;
vfs::COpenWriteFile rfile(filename, true);
rfile->_getRealPath(vfsPath);
std::string str = vfsPath.to_string();
const char* filenamewithpath = str.c_str();
// write
std::fstream binary_file(filenamewithpath, std::ios::out);
for (std::vector<GEAR_NODE>::iterator it = aVec.begin(); it != aVec.end(); ++it)
{
GEAR_NODE node = (*it);
binary_file << node;
}
binary_file.close();
}
void WriteEquipmentTemplate(SOLDIERTYPE* pSoldier, STR16 name)
{
if (pSoldier)
{
std::vector<GEAR_NODE> vec;
INT8 invsize = (INT8)pSoldier->inv.size(); // remember inventorysize, so we don't call size() repeatedly
for (INT8 bLoop = 0; bLoop < invsize; ++bLoop) // ... for all items in our inventory ...
{
if (pSoldier->inv[bLoop].exists())
{
// if this item is a gun, note what magazine is currently loaded
UINT16 ammoitem = 0;
if ( Item[(pSoldier->inv[bLoop]).usItem].usItemClass & IC_GUN )
ammoitem = (pSoldier->inv[bLoop])[0]->data.gun.usGunAmmoItem;
GEAR_NODE node( bLoop, (pSoldier->inv[bLoop]).usItem, ammoitem );
// add all attachments
attachmentList::iterator iterend = (pSoldier->inv[bLoop])[0]->attachments.end( );
for ( attachmentList::iterator iter = (pSoldier->inv[bLoop])[0]->attachments.begin( ); iter != iterend; ++iter )
{
if ( iter->exists( ) )
node.attachments.push_back( iter->usItem );
}
vec.push_back(node);
}
}
SaveEquipmentTemplate(vec, name);
}
}
std::vector<std::string> get_all_files_names_within_folder(std::string folder)
{
std::vector<std::string> names;
std::string search_path = folder + "/*.*";
WIN32_FIND_DATA fd;
HANDLE hFind = ::FindFirstFile(search_path.c_str(), &fd);
if (hFind != INVALID_HANDLE_VALUE)
{
do
{
// read all (real) files in current folder
// , delete '!' read other 2 default folder . and ..
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
names.push_back(fd.cFileName);
}
}
while (::FindNextFile(hFind, &fd));
::FindClose(hFind);
}
return names;
}
// get a vector of all tilesets that are allowed to be built in this sector (the above filtered by structure construct/deconstruct basically)
void GetEquipmentTemplates()
{
CHAR8 filename[MAX_PATH];
sprintf(filename, "%sUpdate Gear.txt", EQUIPMENTTEMPLATE_DIRECTORY);
// get full path to save file
// what we are doing here might seem rather odd. If the file does not exist, we are creating a new empty file via vfs
// in this case, this is rather useful, as the above file simply updates our gear, which is already neat
// I found this to be the easiest solution for using both vfs pathing and being able to use a std::fstream
// If you don't like it, fix it yourself, or make vfs not such a pain to use
vfs::Path vfsPath;
vfs::COpenWriteFile rfile( filename, true );
rfile->_getRealPath(vfsPath);
vfs::Path dir, file;
vfsPath.splitLast(dir, file);
std::string str = dir.to_string();
const char* filenamewithpath = str.c_str();
gTemplateVector = get_all_files_names_within_folder( filenamewithpath );
}
void ReadEquipmentTable( SOLDIERTYPE* pSoldier, std::string name )
{
// make sure the merc is actually in this sector
if ( pSoldier )
{
UINT16 color = FONT_MCOLOR_WHITE;
// for safety, test again
if ( pSoldier->sSectorX != sSelMapX ||
pSoldier->sSectorY != sSelMapY ||
pSoldier->bSectorZ != iCurrentMapSectorZ )
{
ScreenMsg( color, MSG_INTERFACE, szGearTemplateText[3], pSoldier->GetName( ) );
return;
}
if ( (gTacticalStatus.uiFlags & INCOMBAT || gTacticalStatus.fEnemyInSector) )
{
ScreenMsg( color, MSG_INTERFACE, szGearTemplateText[1] );
return;
}
std::vector<GEAR_NODE> vec = LoadEquipmentTemplate(name);
UINT16 usReloadSound = 0;
BOOLEAN attachmentsound = FALSE;
// 1. loop over the gear we should pick up and remove mismatching items if we can find a fitting one in the sector
for (std::vector<GEAR_NODE>::iterator it = vec.begin(); it != vec.end(); ++it)
{
GEAR_NODE node = (*it);
if ( node.slot >= 0 && node.slot < NUM_INV_SLOTS && node.item != NOTHING )
{
// is there already an item here?
if (pSoldier->inv[node.slot].exists())
{
if ( (pSoldier->inv[node.slot]).usItem != node.item )
{
// can we get the requested item in the first place?
UINT32 poolslot = 0;
UINT8 index = 0;
if ( GetBetterObject_InventoryPool( node.item, 0, poolslot, index ) )
{
// We have to check whether we have any LBE equipped that isn't in the template
// in that case we have to dump it and it's contents, otherwise it can block slots that are used in the template
if ( node.slot >= VESTPOCKPOS && node.slot <= BPACKPOCKPOS )
{
std::vector<INT8> pocketKey;
GetLBESlots( node.slot, pocketKey );
// remove everything inside LBE
for ( std::vector<INT8>::iterator lbeslotit = pocketKey.begin( ); lbeslotit != pocketKey.end( ); ++lbeslotit )
{
if ( pSoldier->inv[(*lbeslotit)].exists( ) )
{
AutoPlaceObjectInInventoryStash( &pSoldier->inv[(*lbeslotit)], pSoldier->sGridNo, pSoldier->pathing.bLevel );
DeleteObj( &pSoldier->inv[(*lbeslotit)] );
}
}
}
// if we want to equip a two-handed item in our first hand, also drop whatever we have in the second hand
else if ( node.slot == HANDPOS && Item[node.item].twohanded && pSoldier->inv[SECONDHANDPOS].exists( ) )
{
AutoPlaceObjectInInventoryStash( &pSoldier->inv[SECONDHANDPOS], pSoldier->sGridNo, pSoldier->pathing.bLevel );
DeleteObj( &pSoldier->inv[SECONDHANDPOS] );
}
// if we are Nails and are ordered to drop our vest, refuse and complain!
else if ( node.slot == VESTPOS && HandleNailsVestFetish( pSoldier, node.slot, NOTHING ) )
continue;
// drop item
AutoPlaceObjectInInventoryStash( &pSoldier->inv[node.slot], pSoldier->sGridNo, pSoldier->pathing.bLevel );
DeleteObj( &pSoldier->inv[node.slot] );
}
}
}
}
}
// 2. loop over the gear again and pick up what fits
for ( std::vector<GEAR_NODE>::iterator it = vec.begin( ); it != vec.end( ); ++it )
{
GEAR_NODE node = (*it);
if ( node.slot >= 0 && node.slot < NUM_INV_SLOTS )
{
// only if slot is still empty...
if ( !pSoldier->inv[node.slot].exists( ) && node.item != NOTHING )
{
// see if we can find a better object in this sector and put it into our slot
UINT32 poolslot = 0;
UINT8 index = 0;
if ( GetBetterObject_InventoryPool( node.item, 0, poolslot, index ) )
{
(pInventoryPoolList[poolslot].object).RemoveObjectAtIndex( index, &gItemPointer );
if ( !PlaceObject( pSoldier, node.slot, &gItemPointer ) )
{
ScreenMsg( color, MSG_INTERFACE, szGearTemplateText[4], pSoldier->GetName( ), Item[node.item].szItemName );
AutoPlaceObjectInInventoryStash( &gItemPointer, pSoldier->sGridNo, pSoldier->pathing.bLevel );
DeleteObj( &gItemPointer );
}
}
else
{
// if this is ammo, we might still be able to create a mag from different-sized mags
if ( Item[node.item].usItemClass & IC_AMMO )
{
UINT16 usMagIndex = Item[node.item].ubClassIndex;
UINT16 neededammo = Magazine[usMagIndex].ubMagSize;
if ( neededammo > 0 )
{
UINT32 poolslot = 0;
if ( GetFittingAmmo_InventoryPool( Magazine[usMagIndex].ubCalibre, Magazine[usMagIndex].ubAmmoType, poolslot ) )
{
UINT32 takeammo = min( neededammo, pInventoryPoolList[poolslot].object[pInventoryPoolList[poolslot].object.ubNumberOfObjects - 1]->data.ubShotsLeft );
pInventoryPoolList[poolslot].object[pInventoryPoolList[poolslot].object.ubNumberOfObjects - 1]->data.ubShotsLeft -= takeammo;
if ( !pInventoryPoolList[poolslot].object[pInventoryPoolList[poolslot].object.ubNumberOfObjects - 1]->data.ubShotsLeft )
{
pInventoryPoolList[poolslot].object.RemoveObjectAtIndex( pInventoryPoolList[poolslot].object.ubNumberOfObjects - 1 );
}
CreateAmmo( node.item, &gItemPointer, takeammo );
if ( !PlaceObject( pSoldier, node.slot, &gItemPointer ) )
{
ScreenMsg( color, MSG_INTERFACE, szGearTemplateText[4], pSoldier->GetName( ), Item[node.item].szItemName );
AutoPlaceObjectInInventoryStash( &gItemPointer, pSoldier->sGridNo, pSoldier->pathing.bLevel );
DeleteObj( &gItemPointer );
}
}
}
}
//if ( !pSoldier->inv[node.slot].exists( ) )
//ScreenMsg( color, MSG_INTERFACE, L"%s found no %s to equip.", pSoldier->GetName( ), Item[node.item].szItemName );
}
}
// if this slot is a LBE slot, we need to add attachments here. Otherwise we add the attachments after we fail to pick up other items as their LBE slots won't exist
if ( pSoldier->inv[node.slot].exists() && node.slot >= VESTPOCKPOS && node.slot <= BPACKPOCKPOS )
{
// try to add attachments if nod entry fits the item, or if the node item is NOTHING (essentially a wildcard for this)
if ( !node.attachments.empty( ) && (pSoldier->inv[node.slot]).usItem == node.item || NOTHING == node.item )
{
OBJECTTYPE* pObj = &(pSoldier->inv[node.slot]);
for ( INT16 i = 0; i < pObj->ubNumberOfObjects; ++i )
{
for ( std::vector<UINT16>::iterator attit = node.attachments.begin( ); attit != node.attachments.end( ); ++attit )
{
UINT16 item_attachment = (*attit);
// only apply attachments if they don't require any checks!
INT32 bAttachInfoIndex = GetAttachmentInfoIndex( item_attachment );
if ( bAttachInfoIndex < 0 || AttachmentInfo[bAttachInfoIndex].bAttachmentSkillCheck == NO_CHECK )
{
UINT32 poolslot = 0;
UINT8 index = 0;
if ( GetBetterObject_InventoryPool( item_attachment, 0, poolslot, index ) )
{
(pInventoryPoolList[poolslot].object).RemoveObjectAtIndex( index, &gItemPointer );
BOOLEAN isAttachedNow = pObj->AttachObject( pSoldier, &gItemPointer, FALSE, i ); //do the actual attaching
if ( !isAttachedNow )
{
attachmentsound = TRUE;
//ScreenMsg( color, MSG_INTERFACE, L"%s could not attach %s to %s", pSoldier->GetName( ), Item[item_attachment].szItemName, Item[pObj->usItem].szItemName );
AutoPlaceObjectInInventoryStash( &gItemPointer, pSoldier->sGridNo, pSoldier->pathing.bLevel );
DeleteObj( &gItemPointer );
}
}
}
}
}
}
}
}
}
// 3. loop over all slots (not just those of the gear vector), improve, merge and fill stacks
for ( int slot = HELMETPOS; slot < NUM_INV_SLOTS; ++slot )
{
if (pSoldier->inv[slot].exists())
{
OBJECTTYPE* pObj = &(pSoldier->inv[slot]);
if ( Item[pObj->usItem].usItemClass & (IC_KIT | IC_MEDKIT) && pObj->ubNumberOfObjects > 1 )
{
CleanUpStack( pObj, pObj );
}
// if this is ammo, try merging mags first before replacing them, the not-full mag being the visible one
// by doing this we won't have so many used magazines lying around
if ( (Item[pObj->usItem].usItemClass & IC_AMMO) && pObj->ubNumberOfObjects > 1 )
{
UINT16 usMagIndex = Item[pObj->usItem].ubClassIndex;
UINT16 magsize = Magazine[usMagIndex].ubMagSize;
if ( magsize )
{
UINT32 ammocount = 0;
for ( INT16 i = 0; i < pObj->ubNumberOfObjects; ++i )
ammocount += (*pObj)[i]->data.ubShotsLeft;
UINT8 fullmags = ammocount / magsize;
UINT16 leftover = ammocount - fullmags * magsize;
UINT8 totalmags = fullmags + (leftover > 0 ? 1 : 0);
if ( totalmags < pObj->ubNumberOfObjects )
pObj->RemoveObjectsFromStack( pObj->ubNumberOfObjects - totalmags );
for ( INT16 i = 0; i < totalmags; ++i )
(*pObj)[i]->data.ubShotsLeft = magsize;
if ( leftover > 0 )
(*pObj)[0]->data.ubShotsLeft = leftover;
}
}
// for every object in the stack, try to find a better improvement
for ( INT16 i = 0; i < pObj->ubNumberOfObjects; ++i )
{
// see if we can find a better object in this sector and put it into our slot
UINT32 poolslot = 0;
UINT8 index = 0;
if ( (*pObj)[i]->data.objectStatus < 100 && GetBetterObject_InventoryPool( pObj->usItem, (*pObj)[i]->data.objectStatus, poolslot, index ) )
{
// WARNING! The correct way of doing this would be to exchange both objects
// we are not doing this here on purpose, as we want to save time
// instead, we switch status and a few other properties
// Only do this if you know what you are doing - in the wrong location, we could do odd things to global pointers. Consider yourself warned!
// tmp data
INT16 objectStatus = (*pObj)[i]->data.objectStatus;
INT8 bTrap = (*pObj)[i]->data.bTrap;
UINT8 fUsed = (*pObj)[i]->data.fUsed;
UINT8 ubImprintID = (*pObj)[i]->data.ubImprintID;
FLOAT bTemperature = (*pObj)[i]->data.bTemperature;
UINT8 ubDirection = (*pObj)[i]->data.ubDirection;
UINT32 ubWireNetworkFlag = (*pObj)[i]->data.ubWireNetworkFlag;
INT8 bDefuseFrequency = (*pObj)[i]->data.bDefuseFrequency;
INT16 sRepairThreshold = (*pObj)[i]->data.sRepairThreshold;
UINT64 sObjectFlag = (*pObj)[i]->data.sObjectFlag;
// set data on our object
(*pObj)[i]->data.objectStatus = (pInventoryPoolList[poolslot].object)[index]->data.objectStatus;
(*pObj)[i]->data.bTrap = (pInventoryPoolList[poolslot].object)[index]->data.bTrap;
(*pObj)[i]->data.fUsed = (pInventoryPoolList[poolslot].object)[index]->data.fUsed;
(*pObj)[i]->data.ubImprintID = (pInventoryPoolList[poolslot].object)[index]->data.ubImprintID;
(*pObj)[i]->data.bTemperature = (pInventoryPoolList[poolslot].object)[index]->data.bTemperature;
(*pObj)[i]->data.ubDirection = (pInventoryPoolList[poolslot].object)[index]->data.ubDirection;
(*pObj)[i]->data.ubWireNetworkFlag = (pInventoryPoolList[poolslot].object)[index]->data.ubWireNetworkFlag;
(*pObj)[i]->data.bDefuseFrequency = (pInventoryPoolList[poolslot].object)[index]->data.bDefuseFrequency;
(*pObj)[i]->data.sRepairThreshold = (pInventoryPoolList[poolslot].object)[index]->data.sRepairThreshold;
(*pObj)[i]->data.sObjectFlag = (pInventoryPoolList[poolslot].object)[index]->data.sObjectFlag;
// set data on world object
(pInventoryPoolList[poolslot].object)[index]->data.objectStatus = objectStatus;
(pInventoryPoolList[poolslot].object)[index]->data.bTrap = bTrap;
(pInventoryPoolList[poolslot].object)[index]->data.fUsed = fUsed;
(pInventoryPoolList[poolslot].object)[index]->data.ubImprintID = ubImprintID;
(pInventoryPoolList[poolslot].object)[index]->data.bTemperature = bTemperature;
(pInventoryPoolList[poolslot].object)[index]->data.ubDirection = ubDirection;
(pInventoryPoolList[poolslot].object)[index]->data.ubWireNetworkFlag = ubWireNetworkFlag;
(pInventoryPoolList[poolslot].object)[index]->data.bDefuseFrequency = bDefuseFrequency;
(pInventoryPoolList[poolslot].object)[index]->data.sRepairThreshold = sRepairThreshold;
(pInventoryPoolList[poolslot].object)[index]->data.sObjectFlag = sObjectFlag;
}
}
// pick up new items from the sector and put them on the stack
UINT8 ubSlotLimit = ItemSlotLimit(pObj, slot, pSoldier, FALSE);
while (pObj->ubNumberOfObjects < ubSlotLimit)
{
UINT32 poolslot = 0;
UINT8 index = 0;
if ( GetBetterObject_InventoryPool( pObj->usItem, 0, poolslot, index ) )
{
(pInventoryPoolList[poolslot].object).RemoveObjectAtIndex( index, &gItemPointer );
if ( !TryToStackInSlot( pSoldier, &gItemPointer, slot ) )
break;
}
else
{
break;
}
}
// try to improve attachments, too
for ( INT16 i = 0; i < pObj->ubNumberOfObjects; ++i )
{
for ( attachmentList::iterator iter = (*pObj)[i]->attachments.begin(); iter != (*pObj)[i]->attachments.end(); ++iter )
{
if ( iter->exists() )
{
// see if we can find a better object in this sector and put it into our slot
UINT32 poolslot = 0;
UINT8 index = 0;
if ( (*iter)[0]->data.objectStatus < 100 && GetBetterObject_InventoryPool( iter->usItem, (*iter)[0]->data.objectStatus, poolslot, index ) )
{
// WARNING! The correct way of doing this would be to exchange both objects
// we are not doing this here on purpose, as we want to save time
// instead, we switch status and a few other properties
// Only do this if you know what you are doing - in the wrong location, we could do odd things to global pointers. Consider yourself warned!
// tmp data
INT16 objectStatus = (*iter)[0]->data.objectStatus;
INT8 bTrap = (*iter)[0]->data.bTrap;
UINT8 fUsed = (*iter)[0]->data.fUsed;
UINT8 ubImprintID = (*iter)[0]->data.ubImprintID;
FLOAT bTemperature = (*iter)[0]->data.bTemperature;
UINT8 ubDirection = (*iter)[0]->data.ubDirection;
UINT32 ubWireNetworkFlag = (*iter)[0]->data.ubWireNetworkFlag;
INT8 bDefuseFrequency = (*iter)[0]->data.bDefuseFrequency;
INT16 sRepairThreshold = (*iter)[0]->data.sRepairThreshold;
UINT64 sObjectFlag = (*iter)[0]->data.sObjectFlag;
// set data on our object
(*iter)[0]->data.objectStatus = (pInventoryPoolList[poolslot].object)[index]->data.objectStatus;
(*iter)[0]->data.bTrap = (pInventoryPoolList[poolslot].object)[index]->data.bTrap;
(*iter)[0]->data.fUsed = (pInventoryPoolList[poolslot].object)[index]->data.fUsed;
(*iter)[0]->data.ubImprintID = (pInventoryPoolList[poolslot].object)[index]->data.ubImprintID;
(*iter)[0]->data.bTemperature = (pInventoryPoolList[poolslot].object)[index]->data.bTemperature;
(*iter)[0]->data.ubDirection = (pInventoryPoolList[poolslot].object)[index]->data.ubDirection;
(*iter)[0]->data.ubWireNetworkFlag = (pInventoryPoolList[poolslot].object)[index]->data.ubWireNetworkFlag;
(*iter)[0]->data.bDefuseFrequency = (pInventoryPoolList[poolslot].object)[index]->data.bDefuseFrequency;
(*iter)[0]->data.sRepairThreshold = (pInventoryPoolList[poolslot].object)[index]->data.sRepairThreshold;
(*iter)[0]->data.sObjectFlag = (pInventoryPoolList[poolslot].object)[index]->data.sObjectFlag;
// set data on world object
(pInventoryPoolList[poolslot].object)[index]->data.objectStatus = objectStatus;
(pInventoryPoolList[poolslot].object)[index]->data.bTrap = bTrap;
(pInventoryPoolList[poolslot].object)[index]->data.fUsed = fUsed;
(pInventoryPoolList[poolslot].object)[index]->data.ubImprintID = ubImprintID;
(pInventoryPoolList[poolslot].object)[index]->data.bTemperature = bTemperature;
(pInventoryPoolList[poolslot].object)[index]->data.ubDirection = ubDirection;
(pInventoryPoolList[poolslot].object)[index]->data.ubWireNetworkFlag = ubWireNetworkFlag;
(pInventoryPoolList[poolslot].object)[index]->data.bDefuseFrequency = bDefuseFrequency;
(pInventoryPoolList[poolslot].object)[index]->data.sRepairThreshold = sRepairThreshold;
(pInventoryPoolList[poolslot].object)[index]->data.sObjectFlag = sObjectFlag;
}
}
}
}
}
}
// 4. add attachments. If an attachment cannot be attached - for example because a different one is blocking it - tell us so
for ( std::vector<GEAR_NODE>::iterator it = vec.begin( ); it != vec.end( ); ++it )
{
GEAR_NODE node = (*it);
// try to add attachments if nod entry fits the item, or if the node item is NOTHING (essentially a wildcard for this)
if ( node.slot >= 0 && node.slot < NUM_INV_SLOTS &&
(node.slot < VESTPOCKPOS || node.slot > BPACKPOCKPOS) && // we already did that for LBE slots though, no need to repeat
!node.attachments.empty( ) && pSoldier->inv[node.slot].exists( ) &&
((pSoldier->inv[node.slot]).usItem == node.item || NOTHING == node.item) )
{
OBJECTTYPE* pObj = &(pSoldier->inv[node.slot]);
for ( INT16 i = 0; i < pObj->ubNumberOfObjects; ++i )
{
for ( std::vector<UINT16>::iterator attit = node.attachments.begin( ); attit != node.attachments.end( ); ++attit )
{
UINT16 item_attachment = (*attit);
// only apply attachments if they don't require any checks!
INT32 bAttachInfoIndex = GetAttachmentInfoIndex( item_attachment );
if ( bAttachInfoIndex < 0 || AttachmentInfo[bAttachInfoIndex].bAttachmentSkillCheck == NO_CHECK )
{
UINT32 poolslot = 0;
UINT8 index = 0;
if ( GetBetterObject_InventoryPool( item_attachment, 0, poolslot, index ) )
{
(pInventoryPoolList[poolslot].object).RemoveObjectAtIndex( index, &gItemPointer );
BOOLEAN isAttachedNow = pObj->AttachObject( pSoldier, &gItemPointer, FALSE, i ); //do the actual attaching
if ( !isAttachedNow )
{
attachmentsound = TRUE;
//ScreenMsg( color, MSG_INTERFACE, L"%s could not attach %s to %s", pSoldier->GetName( ), Item[item_attachment].szItemName, Item[pObj->usItem].szItemName );
AutoPlaceObjectInInventoryStash( &gItemPointer, pSoldier->sGridNo, pSoldier->pathing.bLevel );
DeleteObj( &gItemPointer );
}
}
}
}
}
}
}
// 5. reload guns, fill mags
for ( int slot = HELMETPOS; slot < NUM_INV_SLOTS; ++slot )
{
if ( pSoldier->inv[slot].exists( ) )
{
if ( Item[(pSoldier->inv[slot]).usItem].usItemClass & (IC_KIT | IC_MEDKIT) )
{
UINT32 poolslot = 0;
UINT8 index = 0;
if ( GetBetterObject_InventoryPool( (pSoldier->inv[slot]).usItem, 0, poolslot, index ) )
{
DistributeStatus( &(pInventoryPoolList[poolslot].object), &(pSoldier->inv[slot]), 100);
}
}
if ( (Item[(pSoldier->inv[slot]).usItem].usItemClass & (IC_GUN | IC_AMMO)))
{
OBJECTTYPE* pObj = &(pSoldier->inv[slot]);
for ( INT16 i = 0; i < pObj->ubNumberOfObjects; ++i )
{
UINT16 ammoitem = (pSoldier->inv[slot]).usItem;
if ( (Item[(pSoldier->inv[slot]).usItem].usItemClass & IC_GUN) )
{
UINT8 calibre = Weapon[pObj->usItem].ubCalibre;
UINT16 magsize = GetMagSize( pObj, i );
ammoitem = (*pObj)[i]->data.gun.usGunAmmoItem;
for ( std::vector<GEAR_NODE>::iterator it = vec.begin( ); it != vec.end( ); ++it )
{
if ( (*it).slot == slot && (*it).item == pObj->usItem )
{
ammoitem = (*it).ammoitem;
break;
}
}
// if we somehow don't have a valid ammoitem (for example, if the gun was empty), find one
if ( !ammoitem )
ammoitem = DefaultMagazine( pObj->usItem );
UINT16 usMagIndex = Item[ammoitem].ubClassIndex;
// safety check: if the calibre or mag size doesn't fit, get a better mag
if ( calibre != Magazine[usMagIndex].ubCalibre || magsize != Magazine[usMagIndex].ubMagSize )
{
ammoitem = FindReplacementMagazine( calibre, magsize, Magazine[usMagIndex].ubAmmoType );
usMagIndex = Item[ammoitem].ubClassIndex;
}
// if the requested ammo isn't the only currently loaded, take that one out
if ( (*pObj)[i]->data.gun.usGunAmmoItem != ammoitem && (*pObj)[i]->data.gun.usGunAmmoItem != NOTHING && ammoitem != NOTHING )
{
CreateAmmo( (*pObj)[i]->data.gun.usGunAmmoItem, &gItemPointer, (*pObj)[i]->data.gun.ubGunShotsLeft );
AutoPlaceObjectInInventoryStash( &gItemPointer, pSoldier->sGridNo, pSoldier->pathing.bLevel );
DeleteObj( &gItemPointer );
(*pObj)[i]->data.gun.usGunAmmoItem = NOTHING;
(*pObj)[i]->data.gun.ubGunShotsLeft = 0;
(*pObj)[i]->data.gun.ubGunAmmoType = 0;
}
}
UINT16 usMagIndex = Item[ammoitem].ubClassIndex;
UINT16 neededammo = Magazine[usMagIndex].ubMagSize - (*pObj)[i]->data.ubShotsLeft;
if ( (Item[(pSoldier->inv[slot]).usItem].usItemClass & IC_GUN) )
neededammo = Magazine[usMagIndex].ubMagSize - (*pObj)[i]->data.gun.ubGunShotsLeft;
while ( neededammo > 0 )
{
UINT32 poolslot = 0;
if ( GetFittingAmmo_InventoryPool( Magazine[usMagIndex].ubCalibre, Magazine[usMagIndex].ubAmmoType, poolslot ) )
{
for ( INT16 j = pInventoryPoolList[poolslot].object.ubNumberOfObjects - 1; j >= 0; --j )
{
UINT32 takeammo = min( neededammo, pInventoryPoolList[poolslot].object[j]->data.ubShotsLeft );
neededammo -= takeammo;
pInventoryPoolList[poolslot].object[j]->data.ubShotsLeft -= takeammo;
if ( (Item[(pSoldier->inv[slot]).usItem].usItemClass & IC_GUN) )
{
(*pObj)[i]->data.gun.usGunAmmoItem = ammoitem;
(*pObj)[i]->data.gun.ubGunAmmoType = Magazine[usMagIndex].ubAmmoType;
(*pObj)[i]->data.gun.ubGunShotsLeft += takeammo;
usReloadSound = Weapon[pObj->usItem].sReloadSound;
}
else
{
(*pObj)[i]->data.ubShotsLeft += takeammo;
}
if ( !pInventoryPoolList[poolslot].object[j]->data.ubShotsLeft )
{
pInventoryPoolList[poolslot].object.RemoveObjectAtIndex( j );
}
if ( !neededammo )
break;
}
}
else
{
break;
}
if ( (Item[(pSoldier->inv[slot]).usItem].usItemClass & IC_GUN) )
neededammo = Magazine[usMagIndex].ubMagSize - (*pObj)[i]->data.gun.ubGunShotsLeft;
else
neededammo = Magazine[usMagIndex].ubMagSize - (*pObj)[i]->data.ubShotsLeft;
}
}
}
}
}
// Immersion, let's play a reload sound
if ( usReloadSound != 0 )
PlayJA2Sample( usReloadSound, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
else if ( attachmentsound )
PlayJA2Sample( ATTACH_TO_GUN, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
// 6. redraw inventory
fTeamPanelDirty = TRUE;
fMapPanelDirty = TRUE;
fInterfacePanelDirty = DIRTYLEVEL2;
fCharacterInfoPanelDirty = TRUE;
// refresh ammo button in description box if it exists
if ( giItemDescAmmoButton > -1 )
MarkAButtonDirty( giItemDescAmmoButton ); // Required for tactical screen
}
}