mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
Added missing #includes Added LUA scripting and console Changed the way tracers are visualized, so they work more like real tracers instead of a light fountain Fixed signedness of many grid variables used in GetMouseMapPos calls Fixed enemy weapon choosing: Sometimes a mortar is chosen but later rejected, but the grenade class was not reset. Caused assertion failure Added checks so enemies don't try to chuck RPG grenades with their hands Now possible to shoot a someone in the head if he's in water Fixed InitSightArrays to also clear soldier interrupt duel points. This was causing an assertion failure elsewhere in the code because the interrupt list still had soldiers on it sometimes when this function was called. Soldiers are much less willing to forfeit their turn over an attempt to use more APs than they have Removed early setting of muzzle flash. This would allow enemies to get an interrupt before you even fired. Fixed item dropping by AI. If AI tried to drop something while standing it would cause deadlock Change to greatly speed up closing the sector inventory window in an unloaded sector Added conditional compile flag to always give robot weapon ready advantage, even for 360 degree sighting Check builddefines.h for the conditional flags. Of greatest interest might be LUA_CONSOLE. This will cause the game to bring up a command console when run. However this console is severely lacking in many areas. If anybody knows of an open-source terminal/console that could be used instead, it would be appreciated. The existing console does bad things when you try to close it, and since it counts as a separate app, it pauses the game while it has focus. LUA scripting is very limited, basically just proof of concept. There is one global variable, the Soldiers array. An array index gives you the soldier in that slot in the currently loaded sector. The soldier has a few things that can be accessed: name (short name); fullname (long name); grid (current grid #, can be changed); walkto(grid) (function to walk to another grid); runto(grid) (function to run to another grid) git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@924 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2867 lines
89 KiB
C++
2867 lines
89 KiB
C++
#ifdef PRECOMPILEDHEADERS
|
|
#include "Tactical All.h"
|
|
#else
|
|
#include "Types.h"
|
|
#include "stdlib.h"
|
|
#include "Arms Dealer Init.h"
|
|
#include "String.h"
|
|
#include "Debug.h"
|
|
#include "Random.h"
|
|
#include "Weapons.h"
|
|
#include "FileMan.h"
|
|
#include "Game Clock.h"
|
|
#include "ArmsDealerInvInit.h"
|
|
#include "Message.h"
|
|
#include "soldier profile.h"
|
|
#include "Handle Items.h"
|
|
#include "Quests.h"
|
|
#include "Scheduling.h"
|
|
#endif
|
|
|
|
|
|
|
|
// To reduce memory fragmentation from frequent MemRealloc(), we allocate memory for more than one special slot each
|
|
// time we run out of space. Odds are that if we need one, we'll need another soon.
|
|
#define SPECIAL_ITEMS_ALLOCED_AT_ONCE 3
|
|
// Once allocated, the special item slots remain allocated for the duration of the game, or until the dealer dies.
|
|
// This is a little bit wasteful, but saves an awful lot of hassles, and avoid unnecessary memory fragmentation
|
|
|
|
#define MIN_REPAIR_TIME_IN_MINUTES 15 // minutes
|
|
#define MIN_REPAIR_COST 10 // dollars
|
|
|
|
// price classes
|
|
#define PRICE_CLASS_JUNK 0
|
|
#define PRICE_CLASS_CHEAP 1
|
|
#define PRICE_CLASS_EXPENSIVE 2
|
|
|
|
void ConvertCreatureBloodToElixir( void );
|
|
|
|
UINT8 gubLastSpecialItemAddedAtElement = 255;
|
|
|
|
|
|
// THIS STRUCTURE HAS UNCHANGING INFO THAT DOESN'T GET SAVED/RESTORED/RESET
|
|
// TODO: externalize
|
|
ARMS_DEALER_INFO ArmsDealerInfo[ NUM_ARMS_DEALERS ] =
|
|
{
|
|
//Buying Selling Merc ID# Type Initial Flags
|
|
//Price Price Of Cash
|
|
//Modifier Modifier Dealer
|
|
|
|
/* Tony */ { 0.75f, 1.25f, TONY, ARMS_DEALER_BUYS_SELLS, 15000, ARMS_DEALER_SOME_USED_ITEMS | ARMS_DEALER_GIVES_CHANGE },
|
|
/* Franz Hinkle */ { 1.0f, 1.5f, FRANZ, ARMS_DEALER_BUYS_SELLS, 5000, ARMS_DEALER_SOME_USED_ITEMS | ARMS_DEALER_GIVES_CHANGE },
|
|
/* Keith Hemps */ { 0.75f, 1.0f, KEITH, ARMS_DEALER_BUYS_SELLS, 1500, ARMS_DEALER_ONLY_USED_ITEMS | ARMS_DEALER_GIVES_CHANGE },
|
|
/* Jake Cameron */ { 0.8f, 1.1f, JAKE, ARMS_DEALER_BUYS_SELLS, 2500, ARMS_DEALER_ONLY_USED_ITEMS | ARMS_DEALER_GIVES_CHANGE },
|
|
/* Gabby Mulnick*/ { 1.0f, 1.0f, GABBY, ARMS_DEALER_BUYS_SELLS, 3000, ARMS_DEALER_GIVES_CHANGE },
|
|
|
|
/* Devin Connell*/ { 0.75f, 1.25f, DEVIN, ARMS_DEALER_SELLS_ONLY, 5000, ARMS_DEALER_GIVES_CHANGE },
|
|
/* Howard Filmore*/ { 1.0f, 1.0f, HOWARD, ARMS_DEALER_SELLS_ONLY, 3000, ARMS_DEALER_GIVES_CHANGE },
|
|
/* Sam Rozen */ { 1.0f, 1.0f, SAM, ARMS_DEALER_SELLS_ONLY, 3000, ARMS_DEALER_GIVES_CHANGE },
|
|
/* Frank */ { 1.0f, 1.0f, FRANK, ARMS_DEALER_SELLS_ONLY, 500, ARMS_DEALER_ACCEPTS_GIFTS },
|
|
|
|
/* Bar Bro 1 */ { 1.0f, 1.0f, HERVE, ARMS_DEALER_SELLS_ONLY, 250, ARMS_DEALER_ACCEPTS_GIFTS },
|
|
/* Bar Bro 2 */ { 1.0f, 1.0f, PETER, ARMS_DEALER_SELLS_ONLY, 250, ARMS_DEALER_ACCEPTS_GIFTS },
|
|
/* Bar Bro 3 */ { 1.0f, 1.0f, ALBERTO, ARMS_DEALER_SELLS_ONLY, 250, ARMS_DEALER_ACCEPTS_GIFTS },
|
|
/* Bar Bro 4 */ { 1.0f, 1.0f, CARLO, ARMS_DEALER_SELLS_ONLY, 250, ARMS_DEALER_ACCEPTS_GIFTS },
|
|
|
|
/* Micky O'Brien*/ { 1.0f, 1.4f, MICKY, ARMS_DEALER_BUYS_ONLY, 10000, ARMS_DEALER_HAS_NO_INVENTORY | ARMS_DEALER_GIVES_CHANGE },
|
|
|
|
//Repair Repair
|
|
//Speed Cost
|
|
/* Arnie Brunzwell*/{ 0.1f, 0.8f, ARNIE, ARMS_DEALER_REPAIRS, 1500, ARMS_DEALER_HAS_NO_INVENTORY | ARMS_DEALER_GIVES_CHANGE },
|
|
/* Fredo */ { 0.6f, 0.6f, FREDO, ARMS_DEALER_REPAIRS, 1000, ARMS_DEALER_HAS_NO_INVENTORY | ARMS_DEALER_GIVES_CHANGE },
|
|
/* Perko */ { 1.0f, 0.4f, PERKO, ARMS_DEALER_REPAIRS, 1000, ARMS_DEALER_HAS_NO_INVENTORY | ARMS_DEALER_GIVES_CHANGE },
|
|
|
|
/* Elgin */ { 1.0f, 1.0f, DRUGGIST, ARMS_DEALER_SELLS_ONLY, 500, ARMS_DEALER_ACCEPTS_GIFTS },
|
|
/* Manny */ { 1.0f, 1.0f, MANNY, ARMS_DEALER_SELLS_ONLY, 500, ARMS_DEALER_ACCEPTS_GIFTS },
|
|
|
|
};
|
|
|
|
|
|
// THESE GET SAVED/RESTORED/RESET
|
|
ARMS_DEALER_STATUS gArmsDealerStatus[ NUM_ARMS_DEALERS ];
|
|
DEALER_ITEM_HEADER gArmsDealersInventory[ NUM_ARMS_DEALERS ][ MAXITEMS ];
|
|
|
|
|
|
|
|
|
|
|
|
void InitializeOneArmsDealer( UINT8 ubArmsDealer );
|
|
|
|
void AddAmmoToArmsDealerInventory( UINT8 ubArmsDealer, UINT16 usItemIndex, UINT8 ubShotsLeft );
|
|
void AddItemToArmsDealerInventory( UINT8 ubArmsDealer, UINT16 usItemIndex, SPECIAL_ITEM_INFO *pSpclItemInfo, UINT8 ubHowMany );
|
|
void AddSpecialItemToArmsDealerInventoryAtElement( UINT8 ubArmsDealer, UINT16 usItemIndex, UINT8 ubElement, SPECIAL_ITEM_INFO *pSpclItemInfo );
|
|
|
|
void RemoveRandomItemFromArmsDealerInventory( UINT8 ubArmsDealer, UINT16 usItemIndex, UINT8 ubHowMany );
|
|
|
|
void DailyCheckOnItemQuantities();
|
|
void SimulateArmsDealerCustomer();
|
|
|
|
BOOLEAN AdjustCertainDealersInventory();
|
|
void LimitArmsDealersInventory( UINT8 ubArmsDealer, UINT32 uDealerItemType, UINT8 ubMaxNumberOfItemType );
|
|
void GuaranteeAtLeastOneItemOfType( UINT8 ubArmsDealer, UINT32 uiDealerItemType );
|
|
void GuaranteeAtLeastXItemsOfIndex( UINT8 ubArmsDealer, UINT16 usItemIndex, UINT8 ubHowMany );
|
|
|
|
BOOLEAN AllocMemsetSpecialItemArray( DEALER_ITEM_HEADER *pDealerItem, UINT8 ubElementsNeeded );
|
|
BOOLEAN ResizeSpecialItemArray( DEALER_ITEM_HEADER *pDealerItem, UINT8 ubElementsNeeded );
|
|
void FreeSpecialItemArray( DEALER_ITEM_HEADER *pDealerItem );
|
|
|
|
void ArmsDealerGetsFreshStock( UINT8 ubArmsDealer, UINT16 usItemIndex, UINT8 ubNumItems );
|
|
BOOLEAN ItemContainsLiquid( UINT16 usItemIndex );
|
|
UINT8 DetermineDealerItemCondition( UINT8 ubArmsDealer, UINT16 usItemIndex );
|
|
|
|
BOOLEAN IsItemInfoSpecial( SPECIAL_ITEM_INFO *pSpclItemInfo );
|
|
|
|
BOOLEAN DoesItemAppearInDealerInventoryList( UINT8 ubArmsDealer, UINT16 usItemIndex, BOOLEAN fPurchaseFromPlayer );
|
|
|
|
BOOLEAN LoadIncompleteArmsDealersStatus( HWFILE hFile, BOOLEAN fIncludesElgin, BOOLEAN fIncludesManny );
|
|
|
|
//INT16 GetSpecialItemFromArmsDealerInventory( UINT8 ubArmsDealer, UINT16 usItemIndex, SPECIAL_ITEM_INFO *pSpclItemInfo );
|
|
|
|
void GuaranteeMinimumAlcohol( UINT8 ubArmsDealer );
|
|
|
|
BOOLEAN ItemIsARocketRifle( INT16 sItemIndex );
|
|
|
|
BOOLEAN GetArmsDealerShopHours( UINT8 ubArmsDealer, UINT32 *puiOpeningTime, UINT32 *puiClosingTime );
|
|
|
|
|
|
|
|
void InitAllArmsDealers()
|
|
{
|
|
UINT8 ubArmsDealer;
|
|
|
|
//Memset all dealers' status tables to zeroes
|
|
memset( gArmsDealerStatus, 0, sizeof( gArmsDealerStatus ) );
|
|
|
|
//Memset all dealers' inventory tables to zeroes
|
|
memset( gArmsDealersInventory, 0, sizeof( gArmsDealersInventory ) );
|
|
|
|
//Initialize the initial status & inventory for each of the arms dealers
|
|
for( ubArmsDealer = 0; ubArmsDealer < NUM_ARMS_DEALERS; ubArmsDealer++ )
|
|
{
|
|
InitializeOneArmsDealer( ubArmsDealer );
|
|
}
|
|
|
|
//make sure certain items are in stock and certain limits are respected
|
|
AdjustCertainDealersInventory( );
|
|
}
|
|
|
|
|
|
void InitializeOneArmsDealer( UINT8 ubArmsDealer )
|
|
{
|
|
UINT16 usItemIndex;
|
|
UINT8 ubNumItems=0;
|
|
|
|
|
|
memset( &( gArmsDealerStatus[ ubArmsDealer ] ), 0, sizeof( ARMS_DEALER_STATUS ) );
|
|
memset( &( gArmsDealersInventory[ ubArmsDealer ] ), 0, sizeof( DEALER_ITEM_HEADER ) * MAXITEMS );
|
|
|
|
|
|
//Reset the arms dealers cash on hand to the default initial value
|
|
gArmsDealerStatus[ ubArmsDealer ].uiArmsDealersCash = ArmsDealerInfo[ ubArmsDealer ].iInitialCash;
|
|
|
|
//if the arms dealer isn't supposed to have any items (includes all repairmen)
|
|
if( ArmsDealerInfo[ ubArmsDealer ].uiFlags & ARMS_DEALER_HAS_NO_INVENTORY )
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
//loop through all the item types
|
|
for( usItemIndex = 1; usItemIndex < MAXITEMS; usItemIndex++ )
|
|
{
|
|
if ( Item[usItemIndex].usItemClass == 0 )
|
|
break;
|
|
//Can the item be sold by the arms dealer
|
|
if( CanDealerTransactItem( ubArmsDealer, usItemIndex, FALSE ) )
|
|
{
|
|
//Setup an initial amount for the items (treat items as new, how many are used isn't known yet)
|
|
ubNumItems = DetermineInitialInvItems( ubArmsDealer, usItemIndex, GetDealersMaxItemAmount( ubArmsDealer, usItemIndex ), FALSE );
|
|
|
|
//if there are any initial items
|
|
if( ubNumItems > 0 )
|
|
{
|
|
ArmsDealerGetsFreshStock( ubArmsDealer, usItemIndex, ubNumItems );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ShutDownArmsDealers()
|
|
{
|
|
UINT8 ubArmsDealer;
|
|
UINT16 usItemIndex;
|
|
|
|
// loop through all the dealers
|
|
for( ubArmsDealer=0; ubArmsDealer<NUM_ARMS_DEALERS; ubArmsDealer++ )
|
|
{
|
|
//loop through all the item types
|
|
for( usItemIndex = 1; usItemIndex < MAXITEMS; usItemIndex++ )
|
|
{
|
|
if ( Item[usItemIndex].usItemClass == 0 )
|
|
break;
|
|
if( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubElementsAlloced > 0 )
|
|
{
|
|
FreeSpecialItemArray( &gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN SaveArmsDealerInventoryToSaveGameFile( HWFILE hFile )
|
|
{
|
|
UINT32 uiNumBytesWritten;
|
|
UINT8 ubArmsDealer;
|
|
UINT16 usItemIndex;
|
|
|
|
|
|
//Save the arms dealers status
|
|
if (!FileWrite( hFile, gArmsDealerStatus, sizeof( gArmsDealerStatus ), &uiNumBytesWritten ))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
//save the dealers inventory item headers (all at once)
|
|
if (!FileWrite( hFile, gArmsDealersInventory, sizeof( gArmsDealersInventory ), &uiNumBytesWritten ))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
//loop through all the dealers inventories
|
|
for( ubArmsDealer=0; ubArmsDealer<NUM_ARMS_DEALERS; ubArmsDealer++ )
|
|
{
|
|
//loop through this dealer's individual items
|
|
for(usItemIndex = 1; usItemIndex < MAXITEMS; usItemIndex++ )
|
|
{
|
|
if ( Item[usItemIndex].usItemClass == 0 )
|
|
break;
|
|
//if there are any special item elements allocated for this item, save them
|
|
if( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubElementsAlloced > 0 )
|
|
{
|
|
if (!FileWrite( hFile, &gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[0], sizeof( DEALER_SPECIAL_ITEM ) * gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubElementsAlloced, &uiNumBytesWritten ))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
BOOLEAN LoadArmsDealerInventoryFromSavedGameFile( HWFILE hFile, BOOLEAN fIncludesElgin, BOOLEAN fIncludesManny )
|
|
{
|
|
UINT32 uiNumBytesRead;
|
|
UINT8 ubArmsDealer;
|
|
UINT16 usItemIndex;
|
|
|
|
|
|
//Free all the dealers special inventory arrays
|
|
ShutDownArmsDealers();
|
|
|
|
// Elgin was added to the dealers list in Game Version #54, enlarging these 2 tables...
|
|
// Manny was added to the dealers list in Game Version #55, enlarging these 2 tables...
|
|
if ( fIncludesElgin && fIncludesManny )
|
|
{
|
|
// info for all dealers is in the save file
|
|
|
|
//Load the arms dealers status
|
|
if ( !FileRead( hFile, gArmsDealerStatus, sizeof( gArmsDealerStatus ), &uiNumBytesRead ))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
//load the dealers inventory item headers (all at once)
|
|
if ( !FileRead( hFile, gArmsDealersInventory, sizeof( gArmsDealersInventory ), &uiNumBytesRead ))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !LoadIncompleteArmsDealersStatus( hFile, fIncludesElgin, fIncludesManny ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
//loop through all the dealers inventories
|
|
for( ubArmsDealer=0; ubArmsDealer<NUM_ARMS_DEALERS; ubArmsDealer++ )
|
|
{
|
|
//loop through this dealer's individual items
|
|
for(usItemIndex = 1; usItemIndex < MAXITEMS; usItemIndex++ )
|
|
{
|
|
if ( Item[usItemIndex].usItemClass == 0 )
|
|
break;
|
|
//if there are any elements allocated for this item, load them
|
|
if( gArmsDealersInventory[ubArmsDealer][usItemIndex].ubElementsAlloced > 0 )
|
|
{
|
|
//Allocate memory for the inventory
|
|
if ( !AllocMemsetSpecialItemArray( &gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ], gArmsDealersInventory[ubArmsDealer][usItemIndex].ubElementsAlloced ))
|
|
return(FALSE);
|
|
|
|
if (!FileRead( hFile, &gArmsDealersInventory[ubArmsDealer][usItemIndex].SpecialItem[0], sizeof( DEALER_SPECIAL_ITEM ) * gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubElementsAlloced, &uiNumBytesRead ))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
void DailyUpdateOfArmsDealersInventory()
|
|
{
|
|
// if Gabby has creature blood, start turning it into extra elixir
|
|
ConvertCreatureBloodToElixir();
|
|
|
|
//Simulate other customers buying inventory from the dealer
|
|
SimulateArmsDealerCustomer();
|
|
|
|
//if there are some items that are out of stock, order some more
|
|
DailyCheckOnItemQuantities();
|
|
|
|
//make sure certain items are in stock and certain limits are respected
|
|
AdjustCertainDealersInventory( );
|
|
}
|
|
|
|
|
|
// Once a day, loop through each dealer's inventory items and possibly sell some
|
|
void SimulateArmsDealerCustomer()
|
|
{
|
|
UINT8 ubArmsDealer=0;
|
|
UINT16 usItemIndex;
|
|
UINT8 ubItemsSold=0;
|
|
UINT8 ubElement;
|
|
SPECIAL_ITEM_INFO SpclItemInfo;
|
|
|
|
|
|
//loop through all the arms dealers
|
|
for( ubArmsDealer=0;ubArmsDealer<NUM_ARMS_DEALERS;ubArmsDealer++ )
|
|
{
|
|
if( gArmsDealerStatus[ ubArmsDealer ].fOutOfBusiness )
|
|
continue;
|
|
|
|
//if the arms dealer isn't supposed to have any items (includes all repairmen)
|
|
if( ArmsDealerInfo[ ubArmsDealer ].uiFlags & ARMS_DEALER_HAS_NO_INVENTORY )
|
|
continue;
|
|
|
|
//loop through all items of the same type
|
|
for( usItemIndex = 1; usItemIndex < MAXITEMS; usItemIndex++ )
|
|
{
|
|
if ( Item[usItemIndex].usItemClass == 0 )
|
|
break;
|
|
//if there are some of these in stock
|
|
if( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems > 0)
|
|
{
|
|
// first, try to sell all the new (perfect) ones
|
|
if ( usItemIndex == JAR_ELIXIR )
|
|
{
|
|
// only allow selling of standard # of items so those converted from blood given by player will be available
|
|
ubItemsSold = HowManyItemsAreSold( ubArmsDealer, usItemIndex, (UINT8) __min( 3, gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubPerfectItems), FALSE);
|
|
}
|
|
else
|
|
{
|
|
ubItemsSold = HowManyItemsAreSold( ubArmsDealer, usItemIndex, gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubPerfectItems, FALSE);
|
|
}
|
|
if ( ubItemsSold > 0)
|
|
{
|
|
// create item info describing a perfect item
|
|
SetSpecialItemInfoToDefaults( &SpclItemInfo );
|
|
//Now remove that many NEW ones (condition 100) of that item
|
|
RemoveItemFromArmsDealerInventory( ubArmsDealer, usItemIndex, &SpclItemInfo, ubItemsSold);
|
|
}
|
|
|
|
// next, try to sell all the used ones, gotta do these one at a time so we can remove them by element
|
|
for ( ubElement = 0; ubElement < gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubElementsAlloced; ubElement++ )
|
|
{
|
|
// don't worry about negative condition, repairmen can't come this far, they don't sell!
|
|
if ( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].fActive )
|
|
{
|
|
// try selling just this one
|
|
if (HowManyItemsAreSold( ubArmsDealer, usItemIndex, 1, TRUE) > 0)
|
|
{
|
|
//Sold, now remove that particular USED one!
|
|
RemoveSpecialItemFromArmsDealerInventoryAtElement( ubArmsDealer, usItemIndex, ubElement );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void DailyCheckOnItemQuantities()
|
|
{
|
|
UINT8 ubArmsDealer;
|
|
UINT16 usItemIndex;
|
|
UINT8 ubMaxSupply;
|
|
UINT8 ubNumItems;
|
|
UINT32 uiArrivalDay;
|
|
BOOLEAN fPrevElig;
|
|
UINT8 ubReorderDays;
|
|
|
|
|
|
//loop through all the arms dealers
|
|
for( ubArmsDealer=0;ubArmsDealer<NUM_ARMS_DEALERS;ubArmsDealer++ )
|
|
{
|
|
if( gArmsDealerStatus[ ubArmsDealer ].fOutOfBusiness )
|
|
continue;
|
|
|
|
//Reset the arms dealers cash on hand to the default initial value
|
|
gArmsDealerStatus[ ubArmsDealer ].uiArmsDealersCash = ArmsDealerInfo[ ubArmsDealer ].iInitialCash;
|
|
|
|
//if the arms dealer isn't supposed to have any items (includes all repairmen)
|
|
if( ArmsDealerInfo[ ubArmsDealer ].uiFlags & ARMS_DEALER_HAS_NO_INVENTORY )
|
|
continue;
|
|
|
|
|
|
//loop through all items of the same type
|
|
for( usItemIndex = 1; usItemIndex < MAXITEMS; usItemIndex++ )
|
|
{
|
|
if ( Item[usItemIndex].usItemClass == 0 )
|
|
break;
|
|
//if the dealer can sell the item type
|
|
if( CanDealerTransactItem( ubArmsDealer, usItemIndex, FALSE ) )
|
|
{
|
|
//if there are no items on order
|
|
if ( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubQtyOnOrder == 0 )
|
|
{
|
|
ubMaxSupply = GetDealersMaxItemAmount( ubArmsDealer, usItemIndex );
|
|
|
|
//if the qty on hand is half the desired amount or fewer
|
|
if( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems <= (UINT32)( ubMaxSupply / 2 ) )
|
|
{
|
|
// remember value of the "previously eligible" flag
|
|
fPrevElig = gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].fPreviouslyEligible;
|
|
|
|
//determine if the item can be restocked (assume new, use items aren't checked for until the stuff arrives)
|
|
if (ItemTransactionOccurs( ubArmsDealer, usItemIndex, DEALER_BUYING, FALSE ))
|
|
{
|
|
// figure out how many items to reorder (items are reordered an entire batch at a time)
|
|
ubNumItems = HowManyItemsToReorder( ubMaxSupply, gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems );
|
|
|
|
// if this is the first day the player is eligible to have access to this thing
|
|
if ( !fPrevElig )
|
|
{
|
|
// eliminate the ordering delay and stock the items instantly!
|
|
// This is just a way to reward the player right away for making progress without the reordering lag...
|
|
ArmsDealerGetsFreshStock( ubArmsDealer, usItemIndex, ubNumItems );
|
|
}
|
|
else
|
|
{
|
|
if ( ( ubArmsDealer == ARMS_DEALER_TONY ) || ( ubArmsDealer == ARMS_DEALER_DEVIN ) )
|
|
{
|
|
// the stuff Tony and Devin sell is imported, so it takes longer to arrive (for game balance)
|
|
ubReorderDays = ( UINT8) ( 2 + Random( 2 ) ); // 2-3 days
|
|
}
|
|
else
|
|
{
|
|
ubReorderDays = ( UINT8) ( 1 + Random( 2 ) ); // 1-2 days
|
|
}
|
|
|
|
//Determine when the inventory should arrive
|
|
uiArrivalDay = GetWorldDay() + ubReorderDays; // consider changing this to minutes
|
|
|
|
// post new order
|
|
gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubQtyOnOrder = ubNumItems;
|
|
gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].uiOrderArrivalTime = uiArrivalDay;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else //items are on order
|
|
{
|
|
//and today is the day the items come in
|
|
if( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].uiOrderArrivalTime >= GetWorldDay() )
|
|
{
|
|
ArmsDealerGetsFreshStock( ubArmsDealer, usItemIndex, gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubQtyOnOrder);
|
|
|
|
//reset order
|
|
gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubQtyOnOrder = 0;
|
|
gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].uiOrderArrivalTime = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ConvertCreatureBloodToElixir( void )
|
|
{
|
|
UINT8 ubBloodAvailable;
|
|
UINT8 ubAmountToConvert;
|
|
SPECIAL_ITEM_INFO SpclItemInfo;
|
|
|
|
ubBloodAvailable = gArmsDealersInventory[ ARMS_DEALER_GABBY ][ JAR_CREATURE_BLOOD ].ubTotalItems;
|
|
if ( ubBloodAvailable )
|
|
{
|
|
// start converting blood into elixir!
|
|
//ubAmountToConvert = (UINT8) __min( 5 + Random( 3 ), ubBloodAvailable );
|
|
ubAmountToConvert = ubBloodAvailable;
|
|
|
|
// create item info describing a perfect item
|
|
SetSpecialItemInfoToDefaults( &SpclItemInfo );
|
|
|
|
//Now remove that many NEW ones (condition 100) of that item
|
|
RemoveItemFromArmsDealerInventory( ARMS_DEALER_GABBY, JAR_CREATURE_BLOOD, &SpclItemInfo, ubAmountToConvert );
|
|
|
|
ArmsDealerGetsFreshStock( ARMS_DEALER_GABBY, JAR_ELIXIR, ubAmountToConvert );
|
|
}
|
|
}
|
|
|
|
BOOLEAN AdjustCertainDealersInventory( )
|
|
{
|
|
//Adjust Tony's items (this restocks *instantly* 1/day, doesn't use the reorder system)
|
|
GuaranteeAtLeastOneItemOfType( ARMS_DEALER_TONY, ARMS_DEALER_BIG_GUNS );
|
|
LimitArmsDealersInventory( ARMS_DEALER_TONY, ARMS_DEALER_BIG_GUNS, 2 );
|
|
LimitArmsDealersInventory( ARMS_DEALER_TONY, ARMS_DEALER_HANDGUNCLASS, 3 );
|
|
LimitArmsDealersInventory( ARMS_DEALER_TONY, ARMS_DEALER_AMMO, 8 );
|
|
|
|
//Adjust all bartenders' alcohol levels to a minimum
|
|
GuaranteeMinimumAlcohol( ARMS_DEALER_FRANK );
|
|
GuaranteeMinimumAlcohol( ARMS_DEALER_BAR_BRO_1 );
|
|
GuaranteeMinimumAlcohol( ARMS_DEALER_BAR_BRO_2 );
|
|
GuaranteeMinimumAlcohol( ARMS_DEALER_BAR_BRO_3 );
|
|
GuaranteeMinimumAlcohol( ARMS_DEALER_BAR_BRO_4 );
|
|
GuaranteeMinimumAlcohol( ARMS_DEALER_ELGIN );
|
|
GuaranteeMinimumAlcohol( ARMS_DEALER_MANNY );
|
|
|
|
//make sure Sam (hardware guy) has at least one empty jar
|
|
GuaranteeAtLeastXItemsOfIndex( ARMS_DEALER_SAM, JAR, 1 );
|
|
|
|
if ( CheckFact( FACT_ESTONI_REFUELLING_POSSIBLE, 0 ) )
|
|
{
|
|
// gas is restocked regularly, unlike most items
|
|
GuaranteeAtLeastXItemsOfIndex( ARMS_DEALER_JAKE, GAS_CAN, ( UINT8 ) ( 4 + Random( 3 ) ) );
|
|
}
|
|
|
|
//If the player hasn't bought a video camera from Franz yet, make sure Franz has one to sell
|
|
if( !( gArmsDealerStatus[ ARMS_DEALER_FRANZ ].ubSpecificDealerFlags & ARMS_DEALER_FLAG__FRANZ_HAS_SOLD_VIDEO_CAMERA_TO_PLAYER ) )
|
|
{
|
|
GuaranteeAtLeastXItemsOfIndex( ARMS_DEALER_FRANZ, VIDEO_CAMERA, 1 );
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
|
|
void LimitArmsDealersInventory( UINT8 ubArmsDealer, UINT32 uiDealerItemType, UINT8 ubMaxNumberOfItemType )
|
|
{
|
|
UINT16 usItemIndex=0;
|
|
UINT32 uiItemsToRemove=0;
|
|
SPECIAL_ITEM_INFO SpclItemInfo;
|
|
|
|
UINT16 usAvailableItem[ MAXITEMS ] = { NOTHING };
|
|
UINT8 ubNumberOfAvailableItem[ MAXITEMS ] = { 0 };
|
|
UINT32 uiTotalNumberOfItems = 0, uiRandomChoice;
|
|
UINT32 uiNumAvailableItems = 0, uiIndex;
|
|
|
|
// not permitted for repair dealers - would take extra code to avoid counting items under repair!
|
|
Assert( !DoesDealerDoRepairs( ubArmsDealer ) );
|
|
|
|
if( gArmsDealerStatus[ ubArmsDealer ].fOutOfBusiness )
|
|
return;
|
|
|
|
//loop through all items of the same class and count the number in stock
|
|
for( usItemIndex = 1; usItemIndex < MAXITEMS; usItemIndex++ )
|
|
{
|
|
if ( Item[usItemIndex].usItemClass == 0 )
|
|
break;
|
|
//if there is some items in stock
|
|
if( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems > 0)
|
|
{
|
|
//if the item is of the same dealer item type
|
|
if( uiDealerItemType & GetArmsDealerItemTypeFromItemNumber( usItemIndex ) )
|
|
{
|
|
usAvailableItem[ uiNumAvailableItems ] = usItemIndex;
|
|
|
|
//if the dealer item type is ammo
|
|
if( uiDealerItemType == ARMS_DEALER_AMMO )
|
|
{
|
|
// all ammo of same type counts as only one item
|
|
ubNumberOfAvailableItem[ uiNumAvailableItems ] = 1;
|
|
uiTotalNumberOfItems++;
|
|
}
|
|
else
|
|
{
|
|
// items being repaired don't count against the limit
|
|
ubNumberOfAvailableItem[ uiNumAvailableItems ] = gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems;
|
|
uiTotalNumberOfItems += ubNumberOfAvailableItem[ uiNumAvailableItems ];
|
|
}
|
|
uiNumAvailableItems++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//if there is more of the given type than we want
|
|
if( uiNumAvailableItems > ubMaxNumberOfItemType )
|
|
{
|
|
uiItemsToRemove = uiNumAvailableItems - ubMaxNumberOfItemType;
|
|
|
|
do
|
|
{
|
|
uiRandomChoice = Random( uiTotalNumberOfItems );
|
|
|
|
for ( uiIndex = 0; uiIndex < uiNumAvailableItems; uiIndex++ )
|
|
{
|
|
if ( uiRandomChoice <= ubNumberOfAvailableItem[ uiIndex ] )
|
|
{
|
|
usItemIndex = usAvailableItem[ uiIndex ];
|
|
if ( uiDealerItemType == ARMS_DEALER_AMMO )
|
|
{
|
|
// remove all of them, since each ammo item counts as only one "item" here
|
|
// create item info describing a perfect item
|
|
SetSpecialItemInfoToDefaults( &SpclItemInfo );
|
|
// ammo will always be only condition 100, there's never any in special slots
|
|
RemoveItemFromArmsDealerInventory( ubArmsDealer, usItemIndex, &SpclItemInfo, gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems );
|
|
}
|
|
else
|
|
{
|
|
// pick 1 random one, don't care about its condition
|
|
RemoveRandomItemFromArmsDealerInventory( ubArmsDealer, usItemIndex, 1 );
|
|
}
|
|
// now remove entry from the array by replacing it with the last and decrementing
|
|
// the size of the array
|
|
usAvailableItem[ uiIndex ] = usAvailableItem[ uiNumAvailableItems - 1 ];
|
|
ubNumberOfAvailableItem[ uiIndex ] = ubNumberOfAvailableItem[ uiNumAvailableItems - 1 ];
|
|
uiNumAvailableItems--;
|
|
|
|
// decrement count of # of items to remove
|
|
uiItemsToRemove--;
|
|
break; // and out of 'for' loop
|
|
|
|
}
|
|
else
|
|
{
|
|
// next item!
|
|
uiRandomChoice -= ubNumberOfAvailableItem[ uiIndex ];
|
|
}
|
|
}
|
|
|
|
/*
|
|
//loop through all items of the same type
|
|
for( usItemIndex = 1; usItemIndex < MAXITEMS; usItemIndex++ )
|
|
{
|
|
//if there are some non-repairing items in stock
|
|
if( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems )
|
|
{
|
|
//if the item is of the same dealer item type
|
|
if( uiDealerItemType & GetArmsDealerItemTypeFromItemNumber( usItemIndex ) )
|
|
{
|
|
// a random chance that the item will be removed
|
|
if( Random( 100 ) < 30 )
|
|
{
|
|
//remove the item
|
|
|
|
//if the dealer item type is ammo
|
|
if( uiDealerItemType == ARMS_DEALER_AMMO )
|
|
{
|
|
// remove all of them, since each ammo item counts as only one "item" here
|
|
|
|
// create item info describing a perfect item
|
|
SetSpecialItemInfoToDefaults( &SpclItemInfo );
|
|
// ammo will always be only condition 100, there's never any in special slots
|
|
RemoveItemFromArmsDealerInventory( ubArmsDealer, usItemIndex, &SpclItemInfo, gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems );
|
|
}
|
|
else
|
|
{
|
|
// pick 1 random one, don't care about its condition
|
|
RemoveRandomItemFromArmsDealerInventory( ubArmsDealer, usItemIndex, 1 );
|
|
}
|
|
|
|
uiItemsToRemove--;
|
|
if( uiItemsToRemove == 0)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
} while (uiItemsToRemove > 0);
|
|
}
|
|
}
|
|
|
|
|
|
void GuaranteeAtLeastOneItemOfType( UINT8 ubArmsDealer, UINT32 uiDealerItemType )
|
|
{
|
|
UINT16 usItemIndex;
|
|
UINT8 ubChance;
|
|
BOOLEAN fFoundEligibleItemOfSameType = FALSE;
|
|
BOOLEAN fItemHasBeenAdded = FALSE;
|
|
BOOLEAN fFailedOnce = FALSE;
|
|
UINT16 usAvailableItem[ MAXITEMS ] = { NOTHING };
|
|
UINT8 ubChanceForAvailableItem[ MAXITEMS ] = { 0 };
|
|
UINT32 uiTotalChances = 0;
|
|
UINT32 uiNumAvailableItems = 0, uiIndex, uiRandomChoice;
|
|
|
|
// not permitted for repair dealers - would take extra code to avoid counting items under repair!
|
|
Assert( !DoesDealerDoRepairs( ubArmsDealer ) );
|
|
|
|
if( gArmsDealerStatus[ ubArmsDealer ].fOutOfBusiness )
|
|
return;
|
|
|
|
//loop through all items of the same type
|
|
for( usItemIndex = 1; usItemIndex < MAXITEMS; usItemIndex++ )
|
|
{
|
|
if ( Item[usItemIndex].usItemClass == 0 )
|
|
break;
|
|
//if the item is of the same dealer item type
|
|
if( uiDealerItemType & GetArmsDealerItemTypeFromItemNumber( usItemIndex ) )
|
|
{
|
|
//if there are any of these in stock
|
|
if( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems > 0 )
|
|
{
|
|
//there is already at least 1 item of that type, return
|
|
return;
|
|
}
|
|
|
|
// if he can stock it (it appears in his inventory list)
|
|
if( GetDealersMaxItemAmount( ubArmsDealer, usItemIndex ) > 0)
|
|
{
|
|
// and the stage of the game gives him a chance to have it (assume new)
|
|
ubChance = ChanceOfItemTransaction( ubArmsDealer, usItemIndex, DEALER_BUYING, FALSE );
|
|
if ( ubChance > 0 )
|
|
{
|
|
usAvailableItem[ uiNumAvailableItems ] = usItemIndex;
|
|
ubChanceForAvailableItem[ uiNumAvailableItems ] = ubChance;
|
|
uiNumAvailableItems++;
|
|
uiTotalChances += ubChance;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if there aren't any such items, the following loop would never finish, so quit before trying it!
|
|
if (uiNumAvailableItems == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
// CJC: randomly pick one of available items by weighted random selection.
|
|
|
|
// randomize number within uiTotalChances and then loop forwards till we find that item
|
|
uiRandomChoice = Random( uiTotalChances );
|
|
|
|
for ( uiIndex = 0; uiIndex < uiNumAvailableItems; uiIndex++ )
|
|
{
|
|
if ( uiRandomChoice <= ubChanceForAvailableItem[ uiIndex ] )
|
|
{
|
|
ArmsDealerGetsFreshStock( ubArmsDealer, usAvailableItem[ uiIndex ], 1 );
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// next item!
|
|
uiRandomChoice -= ubChanceForAvailableItem[ uiIndex ];
|
|
}
|
|
}
|
|
|
|
// internal logic failure!
|
|
}
|
|
|
|
|
|
void GuaranteeAtLeastXItemsOfIndex( UINT8 ubArmsDealer, UINT16 usItemIndex, UINT8 ubHowMany )
|
|
{
|
|
// not permitted for repair dealers - would take extra code to avoid counting items under repair!
|
|
Assert( !DoesDealerDoRepairs( ubArmsDealer ) );
|
|
|
|
if( gArmsDealerStatus[ ubArmsDealer ].fOutOfBusiness )
|
|
return;
|
|
|
|
//if there are any of these in stock
|
|
if( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems >= ubHowMany )
|
|
{
|
|
// have what we need...
|
|
return;
|
|
}
|
|
|
|
// if he can stock it (it appears in his inventory list)
|
|
// RESTRICTION REMOVED: Jake must be able to guarantee GAS even though it's not in his list, it's presence is conditional
|
|
// if( GetDealersMaxItemAmount( ubArmsDealer, usItemIndex ) > 0)
|
|
{
|
|
//add the item
|
|
ArmsDealerGetsFreshStock( ubArmsDealer, usItemIndex, (UINT8)( ubHowMany - gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems ) );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
UINT32 GetArmsDealerItemTypeFromItemNumber( UINT16 usItem )
|
|
{
|
|
switch( Item[ usItem ].usItemClass )
|
|
{
|
|
case IC_NONE:
|
|
return( 0 );
|
|
break;
|
|
|
|
case IC_GUN:
|
|
switch( Weapon[ Item[ usItem ].ubClassIndex ].ubWeaponClass )
|
|
{
|
|
case HANDGUNCLASS:
|
|
return( ARMS_DEALER_HANDGUNCLASS );
|
|
break;
|
|
case RIFLECLASS:
|
|
if ( ItemIsARocketRifle( usItem ) )
|
|
return( ARMS_DEALER_ROCKET_RIFLE );
|
|
else
|
|
return( ARMS_DEALER_RIFLECLASS );
|
|
break;
|
|
case SHOTGUNCLASS:
|
|
return( ARMS_DEALER_SHOTGUNCLASS );
|
|
break;
|
|
case SMGCLASS:
|
|
return( ARMS_DEALER_SMGCLASS );
|
|
break;
|
|
case MGCLASS:
|
|
return( ARMS_DEALER_MGCLASS );
|
|
break;
|
|
case MONSTERCLASS:
|
|
return( 0 );
|
|
break;
|
|
case KNIFECLASS:
|
|
return( ARMS_DEALER_KNIFECLASS );
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case IC_PUNCH:
|
|
if (usItem == NOTHING)
|
|
{
|
|
return( 0 );
|
|
}
|
|
// else treat as blade
|
|
case IC_BLADE:
|
|
case IC_THROWING_KNIFE:
|
|
return( ARMS_DEALER_BLADE );
|
|
break;
|
|
case IC_LAUNCHER:
|
|
return( ARMS_DEALER_LAUNCHER );
|
|
break;
|
|
case IC_ARMOUR:
|
|
return( ARMS_DEALER_ARMOUR );
|
|
break;
|
|
case IC_MEDKIT:
|
|
return( ARMS_DEALER_MEDKIT );
|
|
break;
|
|
case IC_KIT:
|
|
return( ARMS_DEALER_KIT );
|
|
break;
|
|
case IC_MISC:
|
|
{
|
|
if ( Item [usItem].alcohol )
|
|
return( ARMS_DEALER_ALCOHOL );
|
|
else if ( Item [usItem].electronic )
|
|
return( ARMS_DEALER_ELECTRONICS );
|
|
else if ( Item [usItem].hardware )
|
|
return( ARMS_DEALER_HARDWARE );
|
|
else if ( Item [usItem].medical )
|
|
return( ARMS_DEALER_MEDICAL );
|
|
else if ( Item [usItem].attachment )
|
|
return( ARMS_DEALER_ATTACHMENTS );
|
|
else if ( Item [usItem].detonator || Item [usItem].remotedetonator || Item [usItem].remotetrigger )
|
|
return( ARMS_DEALER_DETONATORS );
|
|
else
|
|
return( ARMS_DEALER_MISC );
|
|
//switch on the type of item
|
|
// switch( usItem )
|
|
// {
|
|
// case BEER:
|
|
// case WINE:
|
|
// case ALCOHOL:
|
|
// return( ARMS_DEALER_ALCOHOL );
|
|
// break;
|
|
//
|
|
// case METALDETECTOR:
|
|
// case LASERSCOPE:
|
|
//// case REMDETONATOR:
|
|
// return( ARMS_DEALER_ELECTRONICS );
|
|
// break;
|
|
//
|
|
// case CANTEEN:
|
|
// case CROWBAR:
|
|
// case WIRECUTTERS:
|
|
// return( ARMS_DEALER_HARDWARE );
|
|
// break;
|
|
//
|
|
// case ADRENALINE_BOOSTER:
|
|
// case REGEN_BOOSTER:
|
|
// case SYRINGE_3:
|
|
// case SYRINGE_4:
|
|
// case SYRINGE_5:
|
|
// return( ARMS_DEALER_MEDICAL );
|
|
// break;
|
|
//
|
|
// case SILENCER:
|
|
// case SNIPERSCOPE:
|
|
// case BIPOD:
|
|
// case DUCKBILL:
|
|
// return( ARMS_DEALER_ATTACHMENTS );
|
|
// break;
|
|
//
|
|
// case DETONATOR:
|
|
// case REMDETONATOR:
|
|
// case REMOTEBOMBTRIGGER:
|
|
// return( ARMS_DEALER_DETONATORS );
|
|
// break;
|
|
//
|
|
// default:
|
|
// return( ARMS_DEALER_MISC );
|
|
//
|
|
// break;
|
|
// }
|
|
|
|
}
|
|
break;
|
|
case IC_AMMO:
|
|
return( ARMS_DEALER_AMMO );
|
|
break;
|
|
case IC_FACE:
|
|
if (Item[usItem].electronic )
|
|
return ARMS_DEALER_ELECTRONICS;
|
|
else
|
|
return ARMS_DEALER_FACE;
|
|
//switch( usItem )
|
|
//{
|
|
// case EXTENDEDEAR:
|
|
// case NIGHTGOGGLES:
|
|
// case ROBOT_REMOTE_CONTROL:
|
|
// return( ARMS_DEALER_ELECTRONICS );
|
|
// break;
|
|
|
|
// default:
|
|
// return( ARMS_DEALER_FACE );
|
|
//}
|
|
break;
|
|
case IC_THROWN:
|
|
return( 0 );
|
|
// return( ARMS_DEALER_THROWN );
|
|
|
|
break;
|
|
case IC_KEY:
|
|
return( 0 );
|
|
// return( ARMS_DEALER_KEY );
|
|
break;
|
|
case IC_GRENADE:
|
|
return( ARMS_DEALER_GRENADE );
|
|
break;
|
|
case IC_BOMB:
|
|
return( ARMS_DEALER_BOMB );
|
|
break;
|
|
case IC_EXPLOSV:
|
|
return( ARMS_DEALER_EXPLOSV );
|
|
break;
|
|
case IC_TENTACLES:
|
|
case IC_MONEY:
|
|
return( 0 );
|
|
break;
|
|
|
|
// case IC_APPLIABLE:
|
|
break;
|
|
|
|
default:
|
|
AssertMsg( FALSE, String( "GetArmsDealerItemTypeFromItemNumber(), invalid class %d for item %d. DF 0.", Item[ usItem ].usItemClass, usItem ) );
|
|
break;
|
|
}
|
|
return( 0 );
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN IsMercADealer( UINT8 ubMercID )
|
|
{
|
|
UINT8 cnt;
|
|
|
|
// Manny is not actually a valid dealer unless a particular event sets that fact
|
|
if( ( ubMercID == MANNY ) && !CheckFact( FACT_MANNY_IS_BARTENDER, 0 ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
//loop through the list of arms dealers
|
|
for( cnt=0; cnt<NUM_ARMS_DEALERS; cnt++ )
|
|
{
|
|
if( ArmsDealerInfo[ cnt ].ubShopKeeperID == ubMercID )
|
|
return( TRUE );
|
|
}
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
|
|
INT8 GetArmsDealerIDFromMercID( UINT8 ubMercID )
|
|
{
|
|
INT8 cnt;
|
|
|
|
//loop through the list of arms dealers
|
|
for( cnt=0; cnt<NUM_ARMS_DEALERS; cnt++ )
|
|
{
|
|
if( ArmsDealerInfo[ cnt ].ubShopKeeperID == ubMercID )
|
|
return( cnt );
|
|
}
|
|
|
|
return( -1 );
|
|
}
|
|
|
|
|
|
|
|
UINT8 GetTypeOfArmsDealer( UINT8 ubDealerID )
|
|
{
|
|
return( ArmsDealerInfo[ ubDealerID ].ubTypeOfArmsDealer );
|
|
}
|
|
|
|
|
|
BOOLEAN DoesDealerDoRepairs( UINT8 ubArmsDealer )
|
|
{
|
|
if( ArmsDealerInfo[ ubArmsDealer ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
|
|
return( TRUE );
|
|
else
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
/*
|
|
INT16 GetSpecialItemFromArmsDealerInventory( UINT8 ubArmsDealer, UINT16 usItemIndex, SPECIAL_ITEM_INFO *pSpclItemInfo )
|
|
{
|
|
UINT8 ubElement;
|
|
|
|
// this function won't find perfect items!
|
|
Assert( IsItemInfoSpecial( pSpclItemInfo ) );
|
|
|
|
for( ubElement = 0; ubElement < gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubElementsAlloced; ubElement++ )
|
|
{
|
|
// if this is the one we're looking for
|
|
if( memcmp( &(gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].Info), pSpclItemInfo, sizeof( SPECIAL_ITEM_INFO ) ) == 0 )
|
|
{
|
|
return( ubElement );
|
|
}
|
|
}
|
|
|
|
// not found!
|
|
return( -1 );
|
|
}
|
|
*/
|
|
|
|
|
|
BOOLEAN RepairmanIsFixingItemsButNoneAreDoneYet( UINT8 ubProfileID )
|
|
{
|
|
INT8 bArmsDealer;
|
|
BOOLEAN fHaveOnlyUnRepairedItems=FALSE;
|
|
UINT8 ubElement;
|
|
UINT16 usItemIndex;
|
|
|
|
|
|
bArmsDealer = GetArmsDealerIDFromMercID( ubProfileID );
|
|
if( bArmsDealer == -1 )
|
|
return( FALSE );
|
|
|
|
//if the dealer is not a repair dealer, return
|
|
if( !DoesDealerDoRepairs( bArmsDealer ) )
|
|
return( FALSE );
|
|
|
|
//loop through the dealers inventory and check if there are only unrepaired items
|
|
for( usItemIndex = 1; usItemIndex < MAXITEMS; usItemIndex++ )
|
|
{
|
|
if ( Item[usItemIndex].usItemClass == 0 )
|
|
break;
|
|
//if there is some items in stock
|
|
if( gArmsDealersInventory[ bArmsDealer ][ usItemIndex ].ubTotalItems )
|
|
{
|
|
//loop through the array of items
|
|
for( ubElement=0; ubElement< gArmsDealersInventory[ bArmsDealer ][ usItemIndex ].ubElementsAlloced; ubElement++ )
|
|
{
|
|
if ( gArmsDealersInventory[ bArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].fActive )
|
|
{
|
|
//if the items status is below 0, the item is being repaired
|
|
if( gArmsDealersInventory[ bArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].Info.bItemCondition < 0 )
|
|
{
|
|
//if the item has been repaired
|
|
if( gArmsDealersInventory[ bArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].uiRepairDoneTime <= GetWorldTotalMin() )
|
|
{
|
|
//A repair item is ready, therefore, return false
|
|
return( FALSE );
|
|
}
|
|
else
|
|
{
|
|
fHaveOnlyUnRepairedItems = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return( fHaveOnlyUnRepairedItems );
|
|
}
|
|
|
|
|
|
UINT32 GetTimeToFixItemBeingRepaired( UINT8 ubArmsDealer, UINT16 usItemIndex, UINT8 ubElement )
|
|
{
|
|
//dealer must be a repair dealer
|
|
Assert( DoesDealerDoRepairs( ubArmsDealer ) );
|
|
// element index must be valid
|
|
Assert( ubElement < gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubElementsAlloced );
|
|
// that item must be active
|
|
Assert( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].fActive );
|
|
// that item must be in repair
|
|
Assert( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].Info.bItemCondition < 0 );
|
|
|
|
//if the item has already been repaired
|
|
if( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].uiRepairDoneTime <= GetWorldTotalMin() )
|
|
return( 0 );
|
|
|
|
//Return how many more minutes it will take to fix the item
|
|
return( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].uiRepairDoneTime - GetWorldTotalMin() );
|
|
}
|
|
|
|
|
|
BOOLEAN CanDealerTransactItem( UINT8 ubArmsDealer, UINT16 usItemIndex, BOOLEAN fPurchaseFromPlayer )
|
|
{
|
|
switch ( ArmsDealerInfo[ ubArmsDealer ].ubTypeOfArmsDealer )
|
|
{
|
|
case ARMS_DEALER_SELLS_ONLY:
|
|
if ( fPurchaseFromPlayer )
|
|
{
|
|
// this dealer only sells stuff to player, so he can't buy anything from him
|
|
return( FALSE );
|
|
}
|
|
break;
|
|
|
|
case ARMS_DEALER_BUYS_ONLY:
|
|
if ( !fPurchaseFromPlayer )
|
|
{
|
|
// this dealer only buys stuff from player, so he can't sell anything to him
|
|
return( FALSE );
|
|
}
|
|
break;
|
|
|
|
case ARMS_DEALER_BUYS_SELLS:
|
|
switch ( ubArmsDealer )
|
|
{
|
|
case ARMS_DEALER_JAKE:
|
|
case ARMS_DEALER_KEITH:
|
|
case ARMS_DEALER_FRANZ:
|
|
if ( fPurchaseFromPlayer )
|
|
{
|
|
// these guys will buy nearly anything from the player, regardless of what they carry for sale!
|
|
return( CalcValueOfItemToDealer( ubArmsDealer, usItemIndex, FALSE ) > 0 );
|
|
}
|
|
//else selling inventory uses their inventory list
|
|
break;
|
|
|
|
default:
|
|
// the others go by their inventory list
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case ARMS_DEALER_REPAIRS:
|
|
// repairmen don't have a complete list of what they'll repair in their inventory,
|
|
// so we must check the item's properties instead.
|
|
return( CanDealerRepairItem( ubArmsDealer, usItemIndex ) );
|
|
|
|
default:
|
|
AssertMsg( FALSE, String( "CanDealerTransactItem(), type of dealer %d. AM 0.", ArmsDealerInfo[ ubArmsDealer ].ubTypeOfArmsDealer ) );
|
|
return(FALSE);
|
|
}
|
|
|
|
return( DoesItemAppearInDealerInventoryList( ubArmsDealer, usItemIndex, fPurchaseFromPlayer ) );
|
|
}
|
|
|
|
|
|
BOOLEAN CanDealerRepairItem( UINT8 ubArmsDealer, UINT16 usItemIndex )
|
|
{
|
|
// UINT32 uiFlags;
|
|
|
|
// uiFlags = Item[ usItemIndex ].fFlags;
|
|
|
|
// can't repair anything that's not repairable!
|
|
// if ( !( uiFlags & ITEM_REPAIRABLE ) )
|
|
if ( !( Item[ usItemIndex ].repairable ) )
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
switch ( ubArmsDealer )
|
|
{
|
|
case ARMS_DEALER_ARNIE:
|
|
case ARMS_DEALER_PERKO:
|
|
// repairs ANYTHING non-electronic
|
|
// if ( !( uiFlags & ITEM_ELECTRONIC ) )
|
|
if ( !( Item[ usItemIndex ].electronic ) )
|
|
{
|
|
return(TRUE);
|
|
}
|
|
break;
|
|
|
|
case ARMS_DEALER_FREDO:
|
|
// repairs ONLY electronics
|
|
// if ( uiFlags & ITEM_ELECTRONIC )
|
|
if ( Item[ usItemIndex ].electronic )
|
|
{
|
|
return(TRUE);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
AssertMsg( FALSE, String( "CanDealerRepairItem(), Arms Dealer %d is not a recognized repairman!. AM 1.", ubArmsDealer ) );
|
|
}
|
|
|
|
// can't repair this...
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
BOOLEAN AllocMemsetSpecialItemArray( DEALER_ITEM_HEADER *pDealerItem, UINT8 ubElementsNeeded )
|
|
{
|
|
Assert(pDealerItem);
|
|
Assert( ubElementsNeeded > 0);
|
|
|
|
pDealerItem->SpecialItem = (DEALER_SPECIAL_ITEM *) MemAlloc( sizeof( DEALER_SPECIAL_ITEM ) * ubElementsNeeded );
|
|
if( pDealerItem->SpecialItem == NULL )
|
|
{
|
|
Assert( 0 );
|
|
return(FALSE);
|
|
}
|
|
|
|
// zero them out (they're inactive until an item is actually added)
|
|
memset( pDealerItem->SpecialItem, 0, sizeof( DEALER_SPECIAL_ITEM ) * ubElementsNeeded );
|
|
|
|
pDealerItem->ubElementsAlloced = ubElementsNeeded;
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
BOOLEAN ResizeSpecialItemArray( DEALER_ITEM_HEADER *pDealerItem, UINT8 ubElementsNeeded )
|
|
{
|
|
Assert(pDealerItem);
|
|
// must already have a ptr allocated!
|
|
Assert(pDealerItem->SpecialItem);
|
|
|
|
|
|
if ( ubElementsNeeded == pDealerItem->ubElementsAlloced)
|
|
{
|
|
// shouldn't have been called, but what they hey, it's not exactly a problem
|
|
return(TRUE);
|
|
}
|
|
|
|
// already allocated, but change its size
|
|
pDealerItem->SpecialItem = (DEALER_SPECIAL_ITEM *) MemRealloc( pDealerItem->SpecialItem, sizeof( DEALER_SPECIAL_ITEM ) * ubElementsNeeded );
|
|
if( pDealerItem->SpecialItem == NULL )
|
|
{
|
|
Assert( 0 );
|
|
return(FALSE);
|
|
}
|
|
|
|
// if adding more elements
|
|
if ( ubElementsNeeded > pDealerItem->ubElementsAlloced)
|
|
{
|
|
// zero them out (they're inactive until an item is actually added)
|
|
memset( &(pDealerItem->SpecialItem[pDealerItem->ubElementsAlloced]), 0, sizeof( DEALER_SPECIAL_ITEM ) * ( ubElementsNeeded - pDealerItem->ubElementsAlloced) );
|
|
}
|
|
|
|
pDealerItem->ubElementsAlloced = ubElementsNeeded;
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
void FreeSpecialItemArray( DEALER_ITEM_HEADER *pDealerItem)
|
|
{
|
|
Assert(pDealerItem);
|
|
// must already have a ptr allocated!
|
|
Assert(pDealerItem->SpecialItem);
|
|
|
|
MemFree( pDealerItem->SpecialItem );
|
|
pDealerItem->SpecialItem = NULL;
|
|
|
|
pDealerItem->ubElementsAlloced = 0;
|
|
pDealerItem->ubTotalItems = pDealerItem->ubPerfectItems;
|
|
|
|
// doesn't effect perfect items, orders or stray bullets!
|
|
}
|
|
|
|
|
|
|
|
void ArmsDealerGetsFreshStock( UINT8 ubArmsDealer, UINT16 usItemIndex, UINT8 ubNumItems )
|
|
{
|
|
UINT8 ubCnt;
|
|
UINT8 ubItemCondition;
|
|
UINT8 ubPerfectOnes = 0;
|
|
SPECIAL_ITEM_INFO SpclItemInfo;
|
|
|
|
// create item info describing a perfect item
|
|
SetSpecialItemInfoToDefaults( &SpclItemInfo );
|
|
|
|
|
|
// determine the condition of each one, counting up new ones, but adding damaged ones right away
|
|
for ( ubCnt = 0; ubCnt < ubNumItems; ubCnt++ )
|
|
{
|
|
ubItemCondition = DetermineDealerItemCondition( ubArmsDealer, usItemIndex);
|
|
|
|
// if the item is brand new
|
|
if ( ubItemCondition == 100)
|
|
{
|
|
ubPerfectOnes++;
|
|
}
|
|
else
|
|
{
|
|
// add a used item with that condition to his inventory
|
|
SpclItemInfo.bItemCondition = (INT8) ubItemCondition;
|
|
AddItemToArmsDealerInventory( ubArmsDealer, usItemIndex, &SpclItemInfo, 1 );
|
|
}
|
|
}
|
|
|
|
// now add all the perfect ones, in one shot
|
|
if ( ubPerfectOnes > 0)
|
|
{
|
|
SpclItemInfo.bItemCondition = 100;
|
|
AddItemToArmsDealerInventory( ubArmsDealer, usItemIndex, &SpclItemInfo, ubPerfectOnes );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
UINT8 DetermineDealerItemCondition( UINT8 ubArmsDealer, UINT16 usItemIndex )
|
|
{
|
|
UINT8 ubCondition = 100;
|
|
|
|
// if it's a damagable item, and not a liquid (those are always sold full)
|
|
// if ( ( Item[ usItemIndex ].fFlags & ITEM_DAMAGEABLE ) && !ItemContainsLiquid( usItemIndex ) )
|
|
if ( ( Item[ usItemIndex ].damageable ) && !ItemContainsLiquid( usItemIndex ) )
|
|
{
|
|
// if he ONLY has used items, or 50% of the time if he carries both used & new items
|
|
if ( ( ArmsDealerInfo[ ubArmsDealer ].uiFlags & ARMS_DEALER_ONLY_USED_ITEMS ) ||
|
|
( ( ArmsDealerInfo[ ubArmsDealer ].uiFlags & ARMS_DEALER_SOME_USED_ITEMS ) && ( Random( 100 ) < 50 ) ) )
|
|
{
|
|
// make the item a used one
|
|
ubCondition = (UINT8)(20 + Random( 60 ));
|
|
}
|
|
}
|
|
|
|
return( ubCondition);
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN ItemContainsLiquid( UINT16 usItemIndex )
|
|
{
|
|
if(Item[usItemIndex].containsliquid)
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
|
|
//switch ( usItemIndex )
|
|
//{
|
|
// case CANTEEN:
|
|
// case BEER:
|
|
// case ALCOHOL:
|
|
// case JAR_HUMAN_BLOOD:
|
|
// case JAR_CREATURE_BLOOD:
|
|
// case JAR_QUEEN_CREATURE_BLOOD:
|
|
// case JAR_ELIXIR:
|
|
// case GAS_CAN:
|
|
// return( TRUE );
|
|
//}
|
|
|
|
//return( FALSE );
|
|
}
|
|
|
|
|
|
/*
|
|
UINT32 CountTotalItemsInArmsDealersInventory( UINT8 ubArmsDealer )
|
|
{
|
|
UINT32 uiNumOfItems=0;
|
|
UINT16 usItemIndex;
|
|
|
|
//loop through all the items in this dealer's inventory
|
|
for( usItemIndex = 1; usItemIndex < MAXITEMS; usItemIndex++ )
|
|
{
|
|
// This counts each pack of ammo or stacked item as one. See the "distinct" version of this for an alternate version
|
|
uiNumOfItems += gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems;
|
|
}
|
|
|
|
return( uiNumOfItems );
|
|
}
|
|
*/
|
|
|
|
|
|
UINT32 CountDistinctItemsInArmsDealersInventory( UINT8 ubArmsDealer )
|
|
{
|
|
UINT32 uiNumOfItems=0;
|
|
UINT16 usItemIndex;
|
|
|
|
|
|
for( usItemIndex = 1; usItemIndex < MAXITEMS; usItemIndex++ )
|
|
{
|
|
if ( Item[usItemIndex].usItemClass == 0 )
|
|
break;
|
|
//if there are any items
|
|
if( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems > 0 )
|
|
{
|
|
// if there are any items in perfect condition
|
|
if( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubPerfectItems > 0 )
|
|
{
|
|
// if the items can be stacked
|
|
// NOTE: This test must match the one inside AddItemsToTempDealerInventory() exactly!
|
|
if ( DealerItemIsSafeToStack( usItemIndex ) )
|
|
{
|
|
// regardless of how many there are, they count as 1 *distinct* item! They will all be together in one box...
|
|
uiNumOfItems++;
|
|
}
|
|
else
|
|
{
|
|
// non-stacking items must be stored in one / box , because each may have unique fields besides bStatus[]
|
|
// Example: guns all have ammo, ammo type, etc. We need these uniquely represented for pricing & manipulation
|
|
uiNumOfItems += gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubPerfectItems;
|
|
}
|
|
}
|
|
|
|
// each *active* special item counts as one additional distinct item (each one occupied a separate shopkeeper box!)
|
|
// NOTE: This is including items being repaired!!!
|
|
uiNumOfItems += CountActiveSpecialItemsInArmsDealersInventory( ubArmsDealer, usItemIndex);
|
|
}
|
|
}
|
|
|
|
return( uiNumOfItems );
|
|
}
|
|
|
|
|
|
UINT8 CountActiveSpecialItemsInArmsDealersInventory( UINT8 ubArmsDealer, UINT16 usItemIndex )
|
|
{
|
|
UINT8 ubActiveSpecialItems = 0;
|
|
UINT8 ubElement;
|
|
|
|
|
|
// next, try to sell all the used ones, gotta do these one at a time so we can remove them by element
|
|
for ( ubElement = 0; ubElement < gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubElementsAlloced; ubElement++ )
|
|
{
|
|
// don't worry about negative condition, repairmen can't come this far, they don't sell!
|
|
if ( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].fActive )
|
|
{
|
|
ubActiveSpecialItems++;
|
|
}
|
|
}
|
|
|
|
return( ubActiveSpecialItems );
|
|
}
|
|
|
|
|
|
UINT16 CountTotalItemsRepairDealerHasInForRepairs( UINT8 ubArmsDealer )
|
|
{
|
|
UINT16 usItemIndex;
|
|
UINT16 usHowManyInForRepairs = 0;
|
|
|
|
//if the dealer is not a repair dealer, no need to count, return 0
|
|
if( !DoesDealerDoRepairs( ubArmsDealer ) )
|
|
return( 0 );
|
|
|
|
//loop through the dealers inventory and count the number of items in for repairs
|
|
for( usItemIndex=0; usItemIndex < MAXITEMS; usItemIndex++ )
|
|
{
|
|
if ( Item[usItemIndex].usItemClass == 0 )
|
|
break;
|
|
usHowManyInForRepairs += CountSpecificItemsRepairDealerHasInForRepairs( ubArmsDealer, usItemIndex );
|
|
}
|
|
|
|
return( usHowManyInForRepairs );
|
|
}
|
|
|
|
|
|
UINT8 CountSpecificItemsRepairDealerHasInForRepairs( UINT8 ubArmsDealer, UINT16 usItemIndex )
|
|
{
|
|
UINT8 ubElement;
|
|
UINT8 ubHowManyInForRepairs = 0;
|
|
|
|
//if the dealer is not a repair dealer, no need to count, return 0
|
|
if( !DoesDealerDoRepairs( ubArmsDealer ) )
|
|
return( 0 );
|
|
|
|
|
|
//if there is some items in stock
|
|
if( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems )
|
|
{
|
|
//loop through the array of items
|
|
for( ubElement = 0; ubElement < gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubElementsAlloced; ubElement++ )
|
|
{
|
|
if( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].fActive)
|
|
{
|
|
//if the item's status is below 0, the item is being repaired
|
|
if( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].Info.bItemCondition < 0 )
|
|
{
|
|
ubHowManyInForRepairs++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return( ubHowManyInForRepairs );
|
|
}
|
|
|
|
|
|
|
|
void AddObjectToArmsDealerInventory( UINT8 ubArmsDealer, OBJECTTYPE *pObject )
|
|
{
|
|
UINT8 ubCnt;
|
|
SPECIAL_ITEM_INFO SpclItemInfo;
|
|
|
|
|
|
SetSpecialItemInfoFromObject( &SpclItemInfo, pObject );
|
|
|
|
|
|
// split up all the components of an objecttype and add them as seperate items into the dealer's inventory
|
|
switch ( Item [ pObject->usItem ].usItemClass )
|
|
{
|
|
case IC_GUN:
|
|
// add the gun (keeps the object's status and imprintID)
|
|
// if the gun was jammed, this will forget about the jam (i.e. dealer immediately unjams anything he buys)
|
|
AddItemToArmsDealerInventory( ubArmsDealer, pObject->usItem, &SpclItemInfo, 1 );
|
|
|
|
// if any GunAmmoItem is specified
|
|
if( pObject->usGunAmmoItem != NONE)
|
|
{
|
|
// if it's regular ammo
|
|
if( Item[ pObject->usGunAmmoItem ].usItemClass == IC_AMMO )
|
|
{
|
|
// and there are some remaining
|
|
if ( pObject->ubGunShotsLeft > 0 )
|
|
{
|
|
// add the bullets of its remaining ammo
|
|
AddAmmoToArmsDealerInventory( ubArmsDealer, pObject->usGunAmmoItem, pObject->ubGunShotsLeft );
|
|
}
|
|
}
|
|
else // assume it's attached ammo (mortar shells, grenades)
|
|
{
|
|
// add the launchable item (can't be imprinted, or have attachments!)
|
|
SetSpecialItemInfoToDefaults( &SpclItemInfo );
|
|
SpclItemInfo.bItemCondition = pObject->bGunAmmoStatus;
|
|
|
|
// if the gun it was in was jammed, get rid of the negative status now
|
|
if ( SpclItemInfo.bItemCondition < 0 )
|
|
{
|
|
SpclItemInfo.bItemCondition *= -1;
|
|
}
|
|
|
|
AddItemToArmsDealerInventory( ubArmsDealer, pObject->usGunAmmoItem, &SpclItemInfo, 1 );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IC_AMMO:
|
|
// add the contents of each magazine (multiple mags may have vastly different #bullets left)
|
|
for ( ubCnt = 0; ubCnt < pObject->ubNumberOfObjects; ubCnt++ )
|
|
{
|
|
AddAmmoToArmsDealerInventory( ubArmsDealer, pObject->usItem, pObject->ubShotsLeft[ ubCnt ] );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// add each object seperately (multiple objects may have vastly different statuses, keep any imprintID)
|
|
for ( ubCnt = 0; ubCnt < pObject->ubNumberOfObjects; ubCnt++ )
|
|
{
|
|
SpclItemInfo.bItemCondition = pObject->bStatus[ ubCnt ];
|
|
AddItemToArmsDealerInventory( ubArmsDealer, pObject->usItem, &SpclItemInfo, 1 );
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
// loop through any detachable attachments and add them as seperate items
|
|
for( ubCnt = 0; ubCnt < MAX_ATTACHMENTS; ubCnt++ )
|
|
{
|
|
if( pObject->usAttachItem[ ubCnt ] != NONE )
|
|
{
|
|
// ARM: Note: this is only used for selling, not repairs, so attachmentes are seperated when sold to a dealer
|
|
// If the attachment is detachable
|
|
// if (! (Item[ pObject->usAttachItem[ubCnt] ].fFlags & ITEM_INSEPARABLE ) )
|
|
if (! (Item[ pObject->usAttachItem[ubCnt] ].inseparable ) )
|
|
{
|
|
// add this particular attachment (they can't be imprinted, or themselves have attachments!)
|
|
SetSpecialItemInfoToDefaults( &SpclItemInfo );
|
|
SpclItemInfo.bItemCondition = pObject->bAttachStatus[ ubCnt ];
|
|
AddItemToArmsDealerInventory( ubArmsDealer, pObject->usAttachItem[ ubCnt ], &SpclItemInfo, 1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// nuke the original object to prevent any possible item duplication
|
|
memset( pObject, 0, sizeof( OBJECTTYPE ) );
|
|
}
|
|
|
|
|
|
void AddAmmoToArmsDealerInventory( UINT8 ubArmsDealer, UINT16 usItemIndex, UINT8 ubShotsLeft )
|
|
{
|
|
UINT8 ubMagCapacity;
|
|
UINT8 *pubStrayAmmo;
|
|
SPECIAL_ITEM_INFO SpclItemInfo;
|
|
|
|
|
|
|
|
// Ammo only, please!!!
|
|
if (Item [ usItemIndex ].usItemClass != IC_AMMO )
|
|
{
|
|
Assert(0);
|
|
return;
|
|
}
|
|
|
|
if ( ubShotsLeft == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
ubMagCapacity = Magazine[ Item[ usItemIndex ].ubClassIndex ].ubMagSize;
|
|
|
|
if ( ubShotsLeft >= ubMagCapacity )
|
|
{
|
|
// add however many FULL magazines the #shot left represents
|
|
SetSpecialItemInfoToDefaults( &SpclItemInfo );
|
|
AddItemToArmsDealerInventory( ubArmsDealer, usItemIndex, &SpclItemInfo, ( UINT8 ) ( ubShotsLeft / ubMagCapacity ) );
|
|
ubShotsLeft %= ubMagCapacity;
|
|
}
|
|
|
|
// any shots left now are "strays" - not enough to completely fill a magazine of this type
|
|
if ( ubShotsLeft > 0 )
|
|
{
|
|
// handle "stray" ammo - add it to the dealer's stray pile
|
|
pubStrayAmmo = &(gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubStrayAmmo);
|
|
*pubStrayAmmo += ubShotsLeft;
|
|
|
|
// if dealer has accumulated enough stray ammo to make another full magazine, convert it!
|
|
if ( *pubStrayAmmo >= ubMagCapacity )
|
|
{
|
|
SetSpecialItemInfoToDefaults( &SpclItemInfo );
|
|
AddItemToArmsDealerInventory( ubArmsDealer, usItemIndex, &SpclItemInfo, ( UINT8 ) ( *pubStrayAmmo / ubMagCapacity ) );
|
|
*pubStrayAmmo = *pubStrayAmmo % ubMagCapacity;
|
|
}
|
|
// I know, I know, this is getting pretty anal... But what the hell, it was easy enough to do. ARM.
|
|
}
|
|
}
|
|
|
|
|
|
//Use AddObjectToArmsDealerInventory() instead of this when converting a complex item in OBJECTTYPE format.
|
|
void AddItemToArmsDealerInventory( UINT8 ubArmsDealer, UINT16 usItemIndex, SPECIAL_ITEM_INFO *pSpclItemInfo, UINT8 ubHowMany )
|
|
{
|
|
UINT8 ubRoomLeft;
|
|
UINT8 ubElement;
|
|
UINT8 ubElementsToAdd;
|
|
BOOLEAN fFoundOne;
|
|
BOOLEAN fSuccess;
|
|
|
|
|
|
Assert( ubHowMany > 0);
|
|
|
|
ubRoomLeft = 255 - gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems;
|
|
|
|
if ( ubHowMany > ubRoomLeft)
|
|
{
|
|
// not enough room to store that many, any extras vanish into thin air!
|
|
ubHowMany = ubRoomLeft;
|
|
}
|
|
|
|
if ( ubHowMany == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
// decide whether this item is "special" or not
|
|
if ( IsItemInfoSpecial( pSpclItemInfo ) )
|
|
{
|
|
// Anything that's used/damaged or imprinted is store as a special item in the SpecialItem array,
|
|
// exactly one item per element. We (re)allocate memory dynamically as necessary to hold the additional items.
|
|
|
|
do
|
|
{
|
|
// search for an already allocated, empty element in the special item array
|
|
fFoundOne = FALSE;
|
|
for ( ubElement = 0; ubElement < gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubElementsAlloced; ubElement++ )
|
|
{
|
|
if ( !( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].fActive ) )
|
|
{
|
|
//Great! Store it here, then.
|
|
AddSpecialItemToArmsDealerInventoryAtElement( ubArmsDealer, usItemIndex, ubElement, pSpclItemInfo );
|
|
fFoundOne = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if we didn't find any inactive elements already allocated
|
|
if (!fFoundOne)
|
|
{
|
|
// then we're going to have to allocate some more space...
|
|
ubElementsToAdd = max( SPECIAL_ITEMS_ALLOCED_AT_ONCE, ubHowMany);
|
|
|
|
// if there aren't any allocated at all right now
|
|
if ( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubElementsAlloced == 0 )
|
|
{
|
|
// allocate new memory for the real buffer
|
|
fSuccess = AllocMemsetSpecialItemArray( &gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ], ubElementsToAdd );
|
|
}
|
|
else
|
|
{
|
|
// we have some allocated, but they're all full and we need more. MemRealloc existing amount + # addition elements
|
|
fSuccess = ResizeSpecialItemArray( &gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ], (UINT8)( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubElementsAlloced + ubElementsToAdd ) );
|
|
}
|
|
|
|
if ( !fSuccess )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// now add the special item at the first of the newly added elements (still stored in ubElement!)
|
|
AddSpecialItemToArmsDealerInventoryAtElement( ubArmsDealer, usItemIndex, ubElement, pSpclItemInfo );
|
|
}
|
|
|
|
// store the # of the element it was placed in globally so anyone who needs that can grab it there
|
|
gubLastSpecialItemAddedAtElement = ubElement;
|
|
|
|
ubHowMany--;
|
|
} while ( ubHowMany > 0);
|
|
}
|
|
else // adding perfect item(s)
|
|
{
|
|
// then it's stored as a "perfect" item, simply add it to that counter!
|
|
gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubPerfectItems += ubHowMany;
|
|
// increase total items of this type
|
|
gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems += ubHowMany;
|
|
}
|
|
}
|
|
|
|
|
|
void AddSpecialItemToArmsDealerInventoryAtElement( UINT8 ubArmsDealer, UINT16 usItemIndex, UINT8 ubElement, SPECIAL_ITEM_INFO *pSpclItemInfo )
|
|
{
|
|
Assert( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems < 255 );
|
|
Assert( ubElement < gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubElementsAlloced );
|
|
Assert( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].fActive == FALSE );
|
|
Assert( IsItemInfoSpecial( pSpclItemInfo ) );
|
|
|
|
|
|
//Store the special values in that element, and make it active
|
|
gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].fActive = TRUE;
|
|
|
|
memcpy( &(gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].Info), pSpclItemInfo, sizeof( SPECIAL_ITEM_INFO ) );
|
|
|
|
// increase the total items
|
|
gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems++;
|
|
}
|
|
|
|
|
|
|
|
// removes ubHowMany items of usItemIndex with the matching Info from dealer ubArmsDealer
|
|
void RemoveItemFromArmsDealerInventory( UINT8 ubArmsDealer, UINT16 usItemIndex, SPECIAL_ITEM_INFO *pSpclItemInfo, UINT8 ubHowMany )
|
|
{
|
|
DEALER_SPECIAL_ITEM *pSpecialItem;
|
|
UINT8 ubElement;
|
|
|
|
|
|
Assert( ubHowMany <= gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems );
|
|
|
|
if ( ubHowMany == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
// decide whether this item is "special" or not
|
|
if ( IsItemInfoSpecial( pSpclItemInfo ) )
|
|
{
|
|
// look through the elements, trying to find special items matching the specifications
|
|
for ( ubElement = 0; ubElement < gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubElementsAlloced; ubElement++ )
|
|
{
|
|
pSpecialItem = &(gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ]);
|
|
|
|
// if this element is in use
|
|
if ( pSpecialItem->fActive )
|
|
{
|
|
// and its contents are exactly what we're looking for
|
|
if( memcmp( &(gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].Info), pSpclItemInfo, sizeof( SPECIAL_ITEM_INFO ) ) == 0 )
|
|
{
|
|
// Got one! Remove it
|
|
RemoveSpecialItemFromArmsDealerInventoryAtElement( ubArmsDealer, usItemIndex, ubElement );
|
|
|
|
ubHowMany--;
|
|
if ( ubHowMany == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// when we've searched all the special item elements, we'd better not have any more items to remove!
|
|
Assert( ubHowMany == 0);
|
|
}
|
|
else // removing perfect item(s)
|
|
{
|
|
// then it's stored as a "perfect" item, simply subtract from tha counter!
|
|
Assert( ubHowMany <= gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubPerfectItems );
|
|
gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubPerfectItems -= ubHowMany;
|
|
// decrease total items of this type
|
|
gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems -= ubHowMany;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void RemoveRandomItemFromArmsDealerInventory( UINT8 ubArmsDealer, UINT16 usItemIndex, UINT8 ubHowMany )
|
|
{
|
|
UINT8 ubWhichOne;
|
|
UINT8 ubSkippedAlready;
|
|
BOOLEAN fFoundIt;
|
|
UINT8 ubElement;
|
|
SPECIAL_ITEM_INFO SpclItemInfo;
|
|
|
|
|
|
// not permitted for repair dealers - would take extra code to subtract items under repair from ubTotalItems!!!
|
|
Assert( !DoesDealerDoRepairs( ubArmsDealer ) );
|
|
// Can't remove any items in for repair, though!
|
|
Assert( ubHowMany <= gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems );
|
|
|
|
while ( ubHowMany > 0)
|
|
{
|
|
// pick a random one to get rid of
|
|
ubWhichOne = (UINT8)Random(gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems );
|
|
|
|
// if we picked one of the perfect ones...
|
|
if ( ubWhichOne < gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubPerfectItems )
|
|
{
|
|
// create item info describing a perfect item
|
|
SetSpecialItemInfoToDefaults( &SpclItemInfo );
|
|
// then that's easy, its condition is 100, so remove one of those
|
|
RemoveItemFromArmsDealerInventory( ubArmsDealer, usItemIndex, &SpclItemInfo, 1 );
|
|
}
|
|
else
|
|
{
|
|
// Yikes! Gotta look through the special items. We already know it's not any of the perfect ones, subtract those
|
|
ubWhichOne -= gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubPerfectItems;
|
|
ubSkippedAlready = 0;
|
|
|
|
fFoundIt = FALSE;
|
|
|
|
for ( ubElement = 0; ubElement < gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubElementsAlloced; ubElement++ )
|
|
{
|
|
// if this is an active special item, not in repair
|
|
if ( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].fActive ) // &&
|
|
// ( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].Info.bItemCondition > 0 ) )
|
|
{
|
|
// if we skipped the right amount of them
|
|
if ( ubSkippedAlready == ubWhichOne )
|
|
{
|
|
// then this one is it! That's the one we're gonna remove
|
|
RemoveSpecialItemFromArmsDealerInventoryAtElement( ubArmsDealer, usItemIndex, ubElement );
|
|
fFoundIt = TRUE;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// keep looking...
|
|
ubSkippedAlready++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// this HAS to work, or the data structure is corrupt!
|
|
Assert(fFoundIt);
|
|
}
|
|
|
|
ubHowMany--;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void RemoveSpecialItemFromArmsDealerInventoryAtElement( UINT8 ubArmsDealer, UINT16 usItemIndex, UINT8 ubElement )
|
|
{
|
|
Assert( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems > 0 );
|
|
Assert( ubElement < gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubElementsAlloced );
|
|
Assert( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].fActive == TRUE );
|
|
|
|
// wipe it out (turning off fActive)
|
|
memset( &( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ] ), 0, sizeof( DEALER_SPECIAL_ITEM ) );
|
|
|
|
// one fewer item remains...
|
|
gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems--;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN AddDeadArmsDealerItemsToWorld( UINT8 ubMercID )
|
|
{
|
|
INT8 bArmsDealer;
|
|
SOLDIERTYPE *pSoldier;
|
|
UINT16 usItemIndex;
|
|
UINT8 ubElement;
|
|
UINT8 ubHowManyMaxAtATime;
|
|
UINT8 ubLeftToDrop;
|
|
UINT8 ubNowDropping;
|
|
OBJECTTYPE TempObject;
|
|
DEALER_SPECIAL_ITEM *pSpecialItem;
|
|
SPECIAL_ITEM_INFO SpclItemInfo;
|
|
|
|
|
|
//Get Dealer ID from from merc Id
|
|
bArmsDealer = GetArmsDealerIDFromMercID( ubMercID );
|
|
if( bArmsDealer == -1 )
|
|
{
|
|
// not a dealer, that's ok, we get called for every dude that croaks.
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
// mark the dealer as being out of business!
|
|
gArmsDealerStatus[ bArmsDealer ].fOutOfBusiness = TRUE;
|
|
|
|
|
|
//Get a pointer to the dealer
|
|
pSoldier = FindSoldierByProfileID( ubMercID, FALSE );
|
|
if( pSoldier == NULL )
|
|
{
|
|
// This should never happen, a dealer getting knocked off without the sector being loaded, should it?
|
|
// If it's possible, we should modify code below to dump his belongings into the sector without using pSoldier->sGridNo
|
|
Assert(0);
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
//loop through all the items in the dealer's inventory, and drop them all where the dealer was set up.
|
|
|
|
for( usItemIndex = 1; usItemIndex < MAXITEMS; usItemIndex++ )
|
|
{
|
|
if ( Item[usItemIndex].usItemClass == 0 )
|
|
break;
|
|
//if the dealer has any items of this type
|
|
if( gArmsDealersInventory[ bArmsDealer ][ usItemIndex ].ubTotalItems > 0)
|
|
{
|
|
// if he has any perfect items of this time
|
|
if ( gArmsDealersInventory[ bArmsDealer ][ usItemIndex ].ubPerfectItems > 0 )
|
|
{
|
|
// drop all the perfect items first
|
|
|
|
// drop stackable items like ammo in stacks of whatever will fit into a large pocket instead of one at a time
|
|
ubHowManyMaxAtATime = ItemSlotLimit( usItemIndex, BIGPOCK1POS );
|
|
if ( ubHowManyMaxAtATime < 1 )
|
|
{
|
|
ubHowManyMaxAtATime = 1;
|
|
}
|
|
|
|
// create item info describing a perfect item
|
|
SetSpecialItemInfoToDefaults( &SpclItemInfo );
|
|
|
|
ubLeftToDrop = gArmsDealersInventory[ bArmsDealer ][ usItemIndex ].ubPerfectItems;
|
|
|
|
// ATE: While it IS leagal here to use pSoldier->sInitialGridNo, cause of where this
|
|
// function is called, there are times when we're not guarenteed that sGridNo is good
|
|
while ( ubLeftToDrop > 0)
|
|
{
|
|
ubNowDropping = min( ubLeftToDrop, ubHowManyMaxAtATime );
|
|
|
|
MakeObjectOutOfDealerItems( usItemIndex, &SpclItemInfo, &TempObject, ubNowDropping );
|
|
AddItemToPool( pSoldier->sInitialGridNo, &TempObject, INVISIBLE, 0, 0, 0 );
|
|
|
|
ubLeftToDrop -= ubNowDropping;
|
|
}
|
|
|
|
// remove them all from his inventory
|
|
RemoveItemFromArmsDealerInventory( bArmsDealer, usItemIndex, &SpclItemInfo, gArmsDealersInventory[ bArmsDealer ][ usItemIndex ].ubPerfectItems );
|
|
}
|
|
|
|
// then drop all the special items
|
|
for ( ubElement = 0; ubElement < gArmsDealersInventory[ bArmsDealer ][ usItemIndex ].ubElementsAlloced; ubElement++ )
|
|
{
|
|
pSpecialItem = &(gArmsDealersInventory[ bArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ]);
|
|
|
|
if ( pSpecialItem->fActive )
|
|
{
|
|
MakeObjectOutOfDealerItems(usItemIndex, &(pSpecialItem->Info), &TempObject, 1 );
|
|
AddItemToPool( pSoldier->sInitialGridNo, &TempObject, INVISIBLE, 0, 0, 0 );
|
|
RemoveItemFromArmsDealerInventory( bArmsDealer, usItemIndex, &(pSpecialItem->Info), 1 );
|
|
}
|
|
}
|
|
|
|
// release any memory allocated for special items, he won't need it now...
|
|
if( gArmsDealersInventory[ bArmsDealer ][ usItemIndex ].ubElementsAlloced > 0 )
|
|
{
|
|
FreeSpecialItemArray( &gArmsDealersInventory[ bArmsDealer ][ usItemIndex ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
//if the dealer has money
|
|
if( gArmsDealerStatus[ bArmsDealer ].uiArmsDealersCash > 0 )
|
|
{
|
|
//Create the object
|
|
memset( &TempObject, 0, sizeof( OBJECTTYPE ) );
|
|
if( !CreateMoney( gArmsDealerStatus[ bArmsDealer ].uiArmsDealersCash, &TempObject ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
//add the money item to the dealers feet
|
|
AddItemToPool( pSoldier->sInitialGridNo, &TempObject, INVISIBLE, 0, 0, 0 );
|
|
|
|
gArmsDealerStatus[ bArmsDealer ].uiArmsDealersCash = 0;
|
|
}
|
|
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
void MakeObjectOutOfDealerItems( UINT16 usItemIndex, SPECIAL_ITEM_INFO *pSpclItemInfo, OBJECTTYPE *pObject, UINT8 ubHowMany )
|
|
{
|
|
INT8 bItemCondition;
|
|
UINT8 ubCnt;
|
|
|
|
|
|
bItemCondition = pSpclItemInfo->bItemCondition;
|
|
|
|
//if the item condition is below 0, the item is in for repairs, so flip the sign
|
|
if( bItemCondition < 0 )
|
|
{
|
|
bItemCondition *= -1;
|
|
}
|
|
|
|
memset( pObject, 0, sizeof( OBJECTTYPE ) );
|
|
|
|
//Create the item object
|
|
CreateItems( usItemIndex, bItemCondition, ubHowMany, pObject );
|
|
|
|
// set the ImprintID
|
|
pObject->ubImprintID = pSpclItemInfo->ubImprintID;
|
|
|
|
// add any attachments we've been storing
|
|
for ( ubCnt = 0; ubCnt < MAX_ATTACHMENTS; ubCnt++ )
|
|
{
|
|
if ( pSpclItemInfo->usAttachment[ ubCnt ] != NONE )
|
|
{
|
|
// store what it is, and its condition
|
|
pObject->usAttachItem[ ubCnt ] = pSpclItemInfo->usAttachment[ ubCnt ];
|
|
pObject->bAttachStatus[ ubCnt ] = pSpclItemInfo->bAttachmentStatus[ ubCnt ];
|
|
}
|
|
}
|
|
|
|
// if it's a gun
|
|
if (Item [ pObject->usItem ].usItemClass == IC_GUN )
|
|
{
|
|
// Empty out the bullets put in by CreateItem(). We now sell all guns empty of bullets. This is so that we don't
|
|
// have to keep track of #bullets in a gun throughout dealer inventory. Without this, players could "reload" guns
|
|
// they don't have ammo for by selling them to Tony & buying them right back fully loaded! One could repeat this
|
|
// ad nauseum (empty the gun between visits) as a (really expensive) way to get unlimited special ammo like rockets.
|
|
pObject->ubGunShotsLeft = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void GiveObjectToArmsDealerForRepair( UINT8 ubArmsDealer, OBJECTTYPE *pObject, UINT8 ubOwnerProfileId )
|
|
{
|
|
// UINT8 ubCnt;
|
|
SPECIAL_ITEM_INFO SpclItemInfo;
|
|
|
|
|
|
Assert( DoesDealerDoRepairs( ubArmsDealer ) );
|
|
|
|
// Any object passed into here must already be:
|
|
// a) Unstacked
|
|
Assert( pObject->ubNumberOfObjects == 1 );
|
|
|
|
// b) Repairable
|
|
Assert( CanDealerRepairItem( ubArmsDealer, pObject->usItem ) );
|
|
|
|
// c) Actually damaged, or a rocket rifle (being reset)
|
|
Assert( ( pObject->bStatus[ 0 ] < 100 ) || ItemIsARocketRifle( pObject->usItem ) );
|
|
|
|
/* ARM: Can now repair with removeable attachments still attached...
|
|
// d) Already stripped of all *detachable* attachments
|
|
for( ubCnt = 0; ubCnt < MAX_ATTACHMENTS; ubCnt++ )
|
|
{
|
|
if ( pObject->usAttachItem[ ubCnt ] != NONE )
|
|
{
|
|
// If the attachment is detachable
|
|
if (! (Item[ pObject->usAttachItem[ubCnt] ].fFlags & ITEM_INSEPARABLE ) )
|
|
{
|
|
Assert( 0 );
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
// e) If a gun, stripped of any non-ammo-class GunAmmoItems, and bullets
|
|
if (Item [ pObject->usItem ].usItemClass == IC_GUN )
|
|
{
|
|
// if any GunAmmoItem is specified
|
|
if( pObject->usGunAmmoItem != NONE)
|
|
{
|
|
// it better be regular ammo, and empty
|
|
Assert( Item[ pObject->usGunAmmoItem ].usItemClass == IC_AMMO );
|
|
Assert( pObject->ubGunShotsLeft == 0 );
|
|
}
|
|
}
|
|
|
|
|
|
SetSpecialItemInfoFromObject( &SpclItemInfo, pObject );
|
|
|
|
// ok, given all that, now everything is easy!
|
|
// if the gun was jammed, this will forget about the jam (i.e. dealer immediately unjams anything he will be repairing)
|
|
GiveItemToArmsDealerforRepair( ubArmsDealer, pObject->usItem, &SpclItemInfo, ubOwnerProfileId );
|
|
}
|
|
|
|
|
|
//PLEASE: Use GiveObjectToArmsDealerForRepair() instead of this when repairing a item in OBJECTTYPE format.
|
|
void GiveItemToArmsDealerforRepair( UINT8 ubArmsDealer, UINT16 usItemIndex, SPECIAL_ITEM_INFO *pSpclItemInfo, UINT8 ubOwnerProfileId )
|
|
{
|
|
UINT32 uiTimeWhenFreeToStartIt;
|
|
UINT32 uiMinutesToFix;
|
|
UINT32 uiMinutesShopClosedBeforeItsDone;
|
|
UINT32 uiDoneWhen;
|
|
|
|
|
|
Assert( DoesDealerDoRepairs( ubArmsDealer ) );
|
|
Assert( pSpclItemInfo->bItemCondition > 0 );
|
|
Assert( ( pSpclItemInfo->bItemCondition < 100 ) || ItemIsARocketRifle( usItemIndex ) );
|
|
|
|
// figure out the earliest the repairman will be free to start repairing this item
|
|
uiTimeWhenFreeToStartIt = WhenWillRepairmanBeAllDoneRepairing( ubArmsDealer );
|
|
|
|
//Determine how long it will take to fix
|
|
uiMinutesToFix = CalculateSpecialItemRepairTime( ubArmsDealer, usItemIndex, pSpclItemInfo );
|
|
|
|
uiMinutesShopClosedBeforeItsDone = CalculateOvernightRepairDelay( ubArmsDealer, uiTimeWhenFreeToStartIt, uiMinutesToFix );
|
|
|
|
// clock time when this will finally be ready
|
|
uiDoneWhen = uiTimeWhenFreeToStartIt + uiMinutesToFix + uiMinutesShopClosedBeforeItsDone;
|
|
|
|
// Negate the status
|
|
pSpclItemInfo->bItemCondition *= -1;
|
|
|
|
// give it to the dealer
|
|
AddItemToArmsDealerInventory( ubArmsDealer, usItemIndex, pSpclItemInfo, 1 );
|
|
|
|
//Set the time at which item will be fixed
|
|
gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ gubLastSpecialItemAddedAtElement ].uiRepairDoneTime = uiDoneWhen;
|
|
//Remember the original owner of the item
|
|
gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ gubLastSpecialItemAddedAtElement ].ubOwnerProfileId = ubOwnerProfileId;
|
|
}
|
|
|
|
|
|
|
|
UINT32 WhenWillRepairmanBeAllDoneRepairing( UINT8 ubArmsDealer )
|
|
{
|
|
UINT32 uiWhenFree;
|
|
UINT16 usItemIndex;
|
|
UINT8 ubElement;
|
|
|
|
Assert( DoesDealerDoRepairs( ubArmsDealer ) );
|
|
|
|
// if nothing is in for repairs, he'll be free RIGHT NOW!
|
|
uiWhenFree = GetWorldTotalMin();
|
|
|
|
//loop through the dealers inventory
|
|
for( usItemIndex = 1; usItemIndex < MAXITEMS; usItemIndex++ )
|
|
{
|
|
if ( Item[usItemIndex].usItemClass == 0 )
|
|
break;
|
|
//if there is some items in stock
|
|
if( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubTotalItems > 0 )
|
|
{
|
|
for ( ubElement = 0; ubElement < gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].ubElementsAlloced; ubElement++ )
|
|
{
|
|
if ( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].fActive )
|
|
{
|
|
//if the item is in for repairs
|
|
if( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].Info.bItemCondition < 0 )
|
|
{
|
|
// if this item will be done later than the latest we've found so far
|
|
if ( gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].uiRepairDoneTime > uiWhenFree )
|
|
{
|
|
// then we're busy til then!
|
|
uiWhenFree = gArmsDealersInventory[ ubArmsDealer ][ usItemIndex ].SpecialItem[ ubElement ].uiRepairDoneTime;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return( uiWhenFree );
|
|
}
|
|
|
|
|
|
|
|
UINT32 CalculateSpecialItemRepairTime( UINT8 ubArmsDealer, UINT16 usItemIndex, SPECIAL_ITEM_INFO *pSpclItemInfo )
|
|
{
|
|
UINT32 uiRepairTime;
|
|
UINT8 ubCnt;
|
|
|
|
uiRepairTime = CalculateSimpleItemRepairTime( ubArmsDealer, usItemIndex, pSpclItemInfo->bItemCondition );
|
|
|
|
// add time to repair any attachments on it
|
|
for ( ubCnt = 0; ubCnt < MAX_ATTACHMENTS; ubCnt++ )
|
|
{
|
|
if ( pSpclItemInfo->usAttachment[ ubCnt ] != NONE )
|
|
{
|
|
// if damaged and repairable
|
|
if ( ( pSpclItemInfo->bAttachmentStatus[ ubCnt ] < 100 ) && CanDealerRepairItem( ubArmsDealer, pSpclItemInfo->usAttachment[ ubCnt ] ) )
|
|
{
|
|
uiRepairTime += CalculateSimpleItemRepairTime( ubArmsDealer, pSpclItemInfo->usAttachment[ ubCnt ], pSpclItemInfo->bAttachmentStatus[ ubCnt ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
return( uiRepairTime );
|
|
}
|
|
|
|
|
|
UINT32 CalculateObjectItemRepairTime( UINT8 ubArmsDealer, OBJECTTYPE *pItemObject )
|
|
{
|
|
UINT32 uiRepairTime;
|
|
UINT8 ubCnt;
|
|
|
|
uiRepairTime = CalculateSimpleItemRepairTime( ubArmsDealer, pItemObject->usItem, pItemObject->bStatus[ 0 ] );
|
|
|
|
// add time to repair any attachments on it
|
|
for ( ubCnt = 0; ubCnt < MAX_ATTACHMENTS; ubCnt++ )
|
|
{
|
|
if ( pItemObject->usAttachItem[ ubCnt ] != NONE )
|
|
{
|
|
// if damaged and repairable
|
|
if ( ( pItemObject->bAttachStatus[ ubCnt ] < 100 ) && CanDealerRepairItem( ubArmsDealer, pItemObject->usAttachItem[ ubCnt ] ) )
|
|
{
|
|
uiRepairTime += CalculateSimpleItemRepairTime( ubArmsDealer, pItemObject->usAttachItem[ ubCnt ], pItemObject->bAttachStatus[ ubCnt ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
return( uiRepairTime );
|
|
}
|
|
|
|
|
|
UINT32 CalculateSimpleItemRepairTime( UINT8 ubArmsDealer, UINT16 usItemIndex, INT8 bItemCondition )
|
|
{
|
|
UINT32 uiTimeToRepair = 0;
|
|
UINT32 uiRepairCost = 0;
|
|
|
|
Assert( DoesDealerDoRepairs( ubArmsDealer ) );
|
|
|
|
// first calc what he'll charge - that takes care of item condition, repair ease, and his repair cost "markup"
|
|
uiRepairCost = CalculateSimpleItemRepairCost( ubArmsDealer, usItemIndex, bItemCondition );
|
|
|
|
// Now adjust that for the repairman's individual repair speed.
|
|
// For a repairman, his BUY modifier controls his REPAIR SPEED (1.0 means minutes to repair = price in $)
|
|
// with a REPAIR SPEED of 1.0, typical gun price of $2000, and a REPAIR COST of 0.5 this works out to 16.6 hrs
|
|
// for a full 100% status repair... Not bad.
|
|
uiTimeToRepair = (UINT32)( uiRepairCost * ArmsDealerInfo[ ubArmsDealer ].dRepairSpeed );
|
|
|
|
// repairs on electronic items take twice as long if the guy doesn't have the skill
|
|
// for dealers, this means anyone but Fredo the Electronics guy takes twice as long (but doesn't charge double)
|
|
// (Mind you, current he's the ONLY one who CAN repair Electronics at all! Oh well.)
|
|
// if( ( Item[ usItemIndex ].fFlags & ITEM_ELECTRONIC ) && ( ubArmsDealer != ARMS_DEALER_FREDO ) )
|
|
if( ( Item[ usItemIndex ].electronic ) && ( ubArmsDealer != ARMS_DEALER_FREDO ) )
|
|
{
|
|
uiTimeToRepair *= 2;
|
|
}
|
|
|
|
// avoid "instant" repairs on really cheap, barely damaged crap...
|
|
if (uiTimeToRepair < MIN_REPAIR_TIME_IN_MINUTES)
|
|
{
|
|
uiTimeToRepair = MIN_REPAIR_TIME_IN_MINUTES;
|
|
}
|
|
|
|
return( uiTimeToRepair );
|
|
}
|
|
|
|
|
|
|
|
UINT32 CalculateSpecialItemRepairCost( UINT8 ubArmsDealer, UINT16 usItemIndex, SPECIAL_ITEM_INFO *pSpclItemInfo )
|
|
{
|
|
UINT32 uiRepairCost;
|
|
UINT8 ubCnt;
|
|
|
|
uiRepairCost = CalculateSimpleItemRepairCost( ubArmsDealer, usItemIndex, pSpclItemInfo->bItemCondition );
|
|
|
|
// add cost of repairing any attachments on it
|
|
for ( ubCnt = 0; ubCnt < MAX_ATTACHMENTS; ubCnt++ )
|
|
{
|
|
if ( pSpclItemInfo->usAttachment[ ubCnt ] != NONE )
|
|
{
|
|
// if damaged and repairable
|
|
if ( ( pSpclItemInfo->bAttachmentStatus[ ubCnt ] < 100 ) && CanDealerRepairItem( ubArmsDealer, pSpclItemInfo->usAttachment[ ubCnt ] ) )
|
|
{
|
|
uiRepairCost += CalculateSimpleItemRepairCost( ubArmsDealer, pSpclItemInfo->usAttachment[ ubCnt ], pSpclItemInfo->bAttachmentStatus[ ubCnt ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
return( uiRepairCost );
|
|
}
|
|
|
|
|
|
UINT32 CalculateObjectItemRepairCost( UINT8 ubArmsDealer, OBJECTTYPE *pItemObject )
|
|
{
|
|
UINT32 uiRepairCost;
|
|
UINT8 ubCnt;
|
|
|
|
uiRepairCost = CalculateSimpleItemRepairCost( ubArmsDealer, pItemObject->usItem, pItemObject->bStatus[ 0 ] );
|
|
|
|
// add cost of repairing any attachments on it
|
|
for ( ubCnt = 0; ubCnt < MAX_ATTACHMENTS; ubCnt++ )
|
|
{
|
|
if ( pItemObject->usAttachItem[ ubCnt ] != NONE )
|
|
{
|
|
// if damaged and repairable
|
|
if ( ( pItemObject->bAttachStatus[ ubCnt ] < 100 ) && CanDealerRepairItem( ubArmsDealer, pItemObject->usAttachItem[ ubCnt ] ) )
|
|
{
|
|
uiRepairCost += CalculateSimpleItemRepairCost( ubArmsDealer, pItemObject->usAttachItem[ ubCnt ], pItemObject->bAttachStatus[ ubCnt ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
return( uiRepairCost );
|
|
}
|
|
|
|
|
|
UINT32 CalculateSimpleItemRepairCost( UINT8 ubArmsDealer, UINT16 usItemIndex, INT8 bItemCondition )
|
|
{
|
|
UINT32 uiItemCost = 0;
|
|
UINT32 uiRepairCost = 0;
|
|
INT16 sRepairCostAdj = 0;
|
|
// UINT32 uiDifFrom10=0;
|
|
|
|
// figure out the full value of the item, modified by this dealer's personal Sell (i.e. repair cost) modifier
|
|
// don't use CalcShopKeeperItemPrice - we want FULL value!!!
|
|
uiItemCost = (UINT32)(( Item[ usItemIndex ].usPrice * ArmsDealerInfo[ ubArmsDealer ].dRepairCost ) );
|
|
|
|
// get item's repair ease, for each + point is 10% easier, each - point is 10% harder to repair
|
|
sRepairCostAdj = 100 - ( 10 * Item[ usItemIndex ].bRepairEase );
|
|
|
|
// make sure it ain't somehow gone too low!
|
|
if (sRepairCostAdj < 10)
|
|
{
|
|
sRepairCostAdj = 10;
|
|
}
|
|
|
|
// calculate repair cost, the more broken it is the more it costs, and the difficulty of repair it is also a factor
|
|
uiRepairCost = (UINT32)( uiItemCost * ( sRepairCostAdj * (100 - bItemCondition) / ((FLOAT)100 * 100) ));
|
|
|
|
/*
|
|
//if the price is not diviseble by 10, make it so
|
|
uiDifFrom10 = 10 - uiRepairCost % 10;
|
|
if( uiDifFrom10 != 0 )
|
|
{
|
|
uiRepairCost += uiDifFrom10;
|
|
}
|
|
*/
|
|
|
|
if ( ItemIsARocketRifle( usItemIndex ) )
|
|
{
|
|
// resetting imprinting for a rocket rifle costs something extra even if rifle is at 100%
|
|
uiRepairCost += 100;
|
|
}
|
|
|
|
|
|
// anything repairable has to have a minimum price
|
|
if ( uiRepairCost < MIN_REPAIR_COST )
|
|
{
|
|
uiRepairCost = MIN_REPAIR_COST;
|
|
}
|
|
|
|
return( uiRepairCost );
|
|
}
|
|
|
|
|
|
|
|
void SetSpecialItemInfoToDefaults( SPECIAL_ITEM_INFO *pSpclItemInfo )
|
|
{
|
|
UINT8 ubCnt;
|
|
|
|
memset( pSpclItemInfo, 0, sizeof( SPECIAL_ITEM_INFO ) );
|
|
|
|
pSpclItemInfo->bItemCondition = 100;
|
|
pSpclItemInfo->ubImprintID = NO_PROFILE;
|
|
|
|
for ( ubCnt = 0; ubCnt < MAX_ATTACHMENTS; ubCnt++ )
|
|
{
|
|
pSpclItemInfo->usAttachment[ ubCnt ] = NONE;
|
|
pSpclItemInfo->bAttachmentStatus[ ubCnt ] = 0;
|
|
}
|
|
}
|
|
|
|
|
|
void SetSpecialItemInfoFromObject( SPECIAL_ITEM_INFO *pSpclItemInfo, OBJECTTYPE *pObject )
|
|
{
|
|
UINT8 ubCnt;
|
|
|
|
|
|
memset(pSpclItemInfo, 0, sizeof( SPECIAL_ITEM_INFO ) );
|
|
|
|
|
|
if( Item[ pObject->usItem ].usItemClass == IC_AMMO )
|
|
{
|
|
// ammo condition is always 100, don't use status, which holds the #bullets
|
|
pSpclItemInfo->bItemCondition = 100;
|
|
}
|
|
else
|
|
{
|
|
pSpclItemInfo->bItemCondition = pObject->bStatus[ 0 ];
|
|
}
|
|
|
|
// only guns currently have imprintID properly initialized...
|
|
if ( Item[ pObject->usItem ].usItemClass == IC_GUN)
|
|
{
|
|
pSpclItemInfo->ubImprintID = pObject->ubImprintID;
|
|
}
|
|
else
|
|
{
|
|
// override garbage imprintIDs (generally 0) for non-guns
|
|
pSpclItemInfo->ubImprintID = NO_PROFILE;
|
|
}
|
|
|
|
for ( ubCnt = 0; ubCnt < MAX_ATTACHMENTS; ubCnt++ )
|
|
{
|
|
if( pObject->usAttachItem[ ubCnt ] != NONE )
|
|
{
|
|
// store what it is
|
|
pSpclItemInfo->usAttachment[ ubCnt ] = pObject->usAttachItem[ ubCnt ];
|
|
pSpclItemInfo->bAttachmentStatus[ ubCnt ] = pObject->bAttachStatus[ ubCnt ];
|
|
}
|
|
else
|
|
{
|
|
pSpclItemInfo->usAttachment[ ubCnt ] = NONE;
|
|
pSpclItemInfo->bAttachmentStatus[ ubCnt ] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN IsItemInfoSpecial( SPECIAL_ITEM_INFO *pSpclItemInfo )
|
|
{
|
|
UINT8 ubCnt;
|
|
|
|
|
|
// being damaged / in repairs makes an item special
|
|
if ( pSpclItemInfo->bItemCondition != 100 )
|
|
{
|
|
return(TRUE);
|
|
}
|
|
|
|
// being imprinted makes an item special
|
|
if (pSpclItemInfo->ubImprintID != NO_PROFILE)
|
|
{
|
|
return(TRUE);
|
|
}
|
|
|
|
// having an attachment makes an item special
|
|
for ( ubCnt = 0; ubCnt < MAX_ATTACHMENTS; ubCnt++ )
|
|
{
|
|
if ( pSpclItemInfo->usAttachment[ ubCnt ] != NONE )
|
|
{
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
// otherwise, it's just a "perfect" item, nothing special about it
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN DoesItemAppearInDealerInventoryList( UINT8 ubArmsDealer, UINT16 usItemIndex, BOOLEAN fPurchaseFromPlayer )
|
|
{
|
|
DEALER_POSSIBLE_INV *pDealerInv=NULL;
|
|
UINT16 usCnt;
|
|
|
|
|
|
// the others will buy only things that appear in their own "for sale" inventory lists
|
|
pDealerInv = GetPointerToDealersPossibleInventory( ubArmsDealer );
|
|
Assert( pDealerInv != NULL );
|
|
|
|
// loop through the dealers' possible inventory and see if the item exists there
|
|
usCnt = 0;
|
|
while( pDealerInv[ usCnt ].sItemIndex != LAST_DEALER_ITEM )
|
|
{
|
|
//if the initial dealer inv contains the required item, the dealer can sell the item
|
|
if( pDealerInv[ usCnt ].sItemIndex == usItemIndex )
|
|
{
|
|
// if optimal quantity listed is 0, it means dealer won't sell it himself, but will buy it from the player!
|
|
if ( ( pDealerInv[ usCnt ].ubOptimalNumber > 0 ) || fPurchaseFromPlayer )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
usCnt++;
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
|
|
UINT16 CalcValueOfItemToDealer( UINT8 ubArmsDealer, UINT16 usItemIndex, BOOLEAN fDealerSelling )
|
|
{
|
|
UINT16 usBasePrice;
|
|
UINT8 ubItemPriceClass;
|
|
UINT8 ubDealerPriceClass;
|
|
UINT16 usValueToThisDealer;
|
|
|
|
|
|
usBasePrice = Item[ usItemIndex ].usPrice;
|
|
|
|
if ( usBasePrice == 0 )
|
|
{
|
|
// worthless to any dealer
|
|
return( 0 );
|
|
}
|
|
|
|
|
|
// figure out the price class this dealer prefers
|
|
switch ( ubArmsDealer )
|
|
{
|
|
case ARMS_DEALER_JAKE:
|
|
ubDealerPriceClass = PRICE_CLASS_JUNK;
|
|
break;
|
|
case ARMS_DEALER_KEITH:
|
|
ubDealerPriceClass = PRICE_CLASS_CHEAP;
|
|
break;
|
|
case ARMS_DEALER_FRANZ:
|
|
ubDealerPriceClass = PRICE_CLASS_EXPENSIVE;
|
|
break;
|
|
|
|
// other dealers don't use this system
|
|
default:
|
|
if ( DoesItemAppearInDealerInventoryList( ubArmsDealer, usItemIndex, TRUE ) )
|
|
{
|
|
return( usBasePrice );
|
|
}
|
|
else
|
|
{
|
|
return( 0 );
|
|
}
|
|
}
|
|
|
|
|
|
// the rest of this function applies only to the "general" dealers ( Jake, Keith, and Franz )
|
|
|
|
// Micky & Gabby specialize in creature parts & such, the others don't buy these at all (exception: jars)
|
|
if ( ( !Item[usItemIndex].jar ) &&
|
|
( DoesItemAppearInDealerInventoryList( ARMS_DEALER_MICKY, usItemIndex, TRUE ) ||
|
|
DoesItemAppearInDealerInventoryList( ARMS_DEALER_GABBY, usItemIndex, TRUE ) ) )
|
|
{
|
|
return( 0 );
|
|
}
|
|
|
|
if ( ( ubArmsDealer == ARMS_DEALER_KEITH ) && ( Item [ usItemIndex].usItemClass & ( IC_GUN | IC_LAUNCHER ) ) )
|
|
{
|
|
// Keith won't buy guns until the Hillbillies are vanquished
|
|
if( CheckFact( FACT_HILLBILLIES_KILLED, KEITH ) == FALSE )
|
|
{
|
|
return( 0 );
|
|
}
|
|
}
|
|
|
|
|
|
// figure out which price class it belongs to
|
|
if ( usBasePrice < 100 )
|
|
{
|
|
ubItemPriceClass = PRICE_CLASS_JUNK;
|
|
}
|
|
else
|
|
if ( usBasePrice < 1000 )
|
|
{
|
|
ubItemPriceClass = PRICE_CLASS_CHEAP;
|
|
}
|
|
else
|
|
{
|
|
ubItemPriceClass = PRICE_CLASS_EXPENSIVE;
|
|
}
|
|
|
|
|
|
if( !fDealerSelling )
|
|
{
|
|
// junk dealer won't buy expensive stuff at all, expensive dealer won't buy junk at all
|
|
if ( abs( (INT8) ubDealerPriceClass - (INT8) ubItemPriceClass ) == 2 )
|
|
{
|
|
return( 0 );
|
|
}
|
|
}
|
|
|
|
// start with the base price
|
|
usValueToThisDealer = usBasePrice;
|
|
|
|
// if it's out of their preferred price class
|
|
if ( ubDealerPriceClass != ubItemPriceClass )
|
|
{
|
|
// exception: Gas (Jake's)
|
|
// if ( usItemIndex != GAS_CAN )
|
|
if ( !Item[usItemIndex].gascan )
|
|
{
|
|
// they pay only 1/3 of true value!
|
|
usValueToThisDealer /= 3;
|
|
}
|
|
}
|
|
|
|
// Tony specializes in guns, weapons, and ammo, so make others pay much less for that kind of stuff
|
|
if ( DoesItemAppearInDealerInventoryList( ARMS_DEALER_TONY, usItemIndex, TRUE ) )
|
|
{
|
|
// others pay only 1/2 of that value!
|
|
usValueToThisDealer /= 2;
|
|
}
|
|
|
|
// minimum bet $1 !
|
|
if ( usValueToThisDealer == 0 )
|
|
{
|
|
usValueToThisDealer = 1;
|
|
}
|
|
|
|
return( usValueToThisDealer );
|
|
}
|
|
|
|
|
|
|
|
// this only exists to support saves made with game versions < 54 or 55!
|
|
BOOLEAN LoadIncompleteArmsDealersStatus( HWFILE hFile, BOOLEAN fIncludesElgin, BOOLEAN fIncludesManny )
|
|
{
|
|
UINT32 uiDealersSaved;
|
|
UINT32 uiNumBytesRead;
|
|
|
|
Assert( !fIncludesElgin || !fIncludesManny );
|
|
|
|
if ( !fIncludesElgin )
|
|
{
|
|
// read 2 fewer element without Elgin or Manny in there...
|
|
uiDealersSaved = NUM_ARMS_DEALERS - 2;
|
|
}
|
|
else
|
|
{
|
|
// read one fewer element without Elgin in there...
|
|
uiDealersSaved = NUM_ARMS_DEALERS - 1;
|
|
}
|
|
|
|
|
|
// read in all other dealer's status
|
|
if (!FileRead( hFile, gArmsDealerStatus, uiDealersSaved * sizeof( ARMS_DEALER_STATUS ), &uiNumBytesRead ))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// read in all other dealer's inventory
|
|
if (!FileRead( hFile, gArmsDealersInventory, uiDealersSaved * sizeof( DEALER_ITEM_HEADER ) * MAXITEMS, &uiNumBytesRead ))
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( !fIncludesElgin )
|
|
{
|
|
// initialize Elgin now...
|
|
InitializeOneArmsDealer( ARMS_DEALER_ELGIN );
|
|
}
|
|
|
|
if ( !fIncludesManny )
|
|
{
|
|
// initialize Manny now...
|
|
InitializeOneArmsDealer( ARMS_DEALER_MANNY );
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN DealerItemIsSafeToStack( UINT16 usItemIndex )
|
|
{
|
|
// basically any item type with nothing unique about it besides its status can be stacked in dealer's inventory boxes...
|
|
// NOTE: This test is only applied to items already KNOWN to be perfect - special items are obviously not-stackable
|
|
|
|
if ( Item[ usItemIndex ].usItemClass == IC_GUN )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
/*
|
|
if ( ItemSlotLimit( usItemIndex, BIGPOCK1POS ) > 1 )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
*/
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
|
|
void GuaranteeMinimumAlcohol( UINT8 ubArmsDealer )
|
|
{
|
|
GuaranteeAtLeastXItemsOfIndex( ubArmsDealer, BEER, ( UINT8 ) ( GetDealersMaxItemAmount( ubArmsDealer, BEER ) / 3 ) );
|
|
GuaranteeAtLeastXItemsOfIndex( ubArmsDealer, WINE, ( UINT8 ) ( GetDealersMaxItemAmount( ubArmsDealer, WINE ) / 3 ) );
|
|
GuaranteeAtLeastXItemsOfIndex( ubArmsDealer, ALCOHOL, ( UINT8 ) ( GetDealersMaxItemAmount( ubArmsDealer, ALCOHOL ) / 3 ) );
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN ItemIsARocketRifle( INT16 sItemIndex )
|
|
{
|
|
if ( Item[sItemIndex].fingerprintid )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN GetArmsDealerShopHours( UINT8 ubArmsDealer, UINT32 *puiOpeningTime, UINT32 *puiClosingTime )
|
|
{
|
|
SOLDIERTYPE *pSoldier;
|
|
|
|
pSoldier = FindSoldierByProfileID( ArmsDealerInfo[ ubArmsDealer ].ubShopKeeperID, FALSE );
|
|
if ( pSoldier == NULL )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( ExtractScheduleDoorLockAndUnlockInfo( pSoldier, puiOpeningTime, puiClosingTime ) == FALSE )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
Assert( *puiOpeningTime < *puiClosingTime );
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
|
|
UINT32 CalculateOvernightRepairDelay( UINT8 ubArmsDealer, UINT32 uiTimeWhenFreeToStartIt, UINT32 uiMinutesToFix )
|
|
{
|
|
UINT32 uiOpeningTime, uiClosingTime;
|
|
UINT32 uiMinutesClosedOvernight;
|
|
UINT32 uiDelayInDays = 0;
|
|
UINT32 uiDoneToday;
|
|
|
|
|
|
Assert( uiMinutesToFix > 0 );
|
|
|
|
// convert world time into 24hr military time for the day he's gonna start on it
|
|
uiTimeWhenFreeToStartIt = uiTimeWhenFreeToStartIt % NUM_MIN_IN_DAY;
|
|
|
|
if ( GetArmsDealerShopHours( ubArmsDealer, &uiOpeningTime, &uiClosingTime ) == FALSE )
|
|
{
|
|
return( 0 );
|
|
}
|
|
|
|
// if it won't get done by the end of a day
|
|
while ( ( uiTimeWhenFreeToStartIt + uiMinutesToFix ) > uiClosingTime )
|
|
{
|
|
// this is to handle existing saves with overnight repairs
|
|
if ( uiTimeWhenFreeToStartIt < uiClosingTime )
|
|
{
|
|
// he gets this much done before closing
|
|
uiDoneToday = uiClosingTime - uiTimeWhenFreeToStartIt;
|
|
// subtract how much he got done
|
|
uiMinutesToFix -= uiDoneToday;
|
|
Assert( uiMinutesToFix > 0 );
|
|
}
|
|
|
|
// he starts back at it first thing in the morning
|
|
uiTimeWhenFreeToStartIt = uiOpeningTime;
|
|
uiDelayInDays++;
|
|
}
|
|
|
|
uiMinutesClosedOvernight = NUM_MIN_IN_DAY - ( uiClosingTime - uiOpeningTime );
|
|
|
|
return ( uiDelayInDays * uiMinutesClosedOvernight );
|
|
}
|
|
|
|
|
|
|
|
UINT32 CalculateMinutesClosedBetween( UINT8 ubArmsDealer, UINT32 uiStartTime, UINT32 uiEndTime )
|
|
{
|
|
UINT32 uiOpeningTime, uiClosingTime;
|
|
UINT32 uiMinutesClosedOvernight;
|
|
UINT32 uiDaysDifference = 0;
|
|
UINT32 uiMinutesClosed = 0;
|
|
|
|
Assert( uiStartTime <= uiEndTime );
|
|
|
|
if ( GetArmsDealerShopHours( ubArmsDealer, &uiOpeningTime, &uiClosingTime ) == FALSE )
|
|
{
|
|
return( 0 );
|
|
}
|
|
|
|
uiMinutesClosedOvernight = NUM_MIN_IN_DAY - ( uiClosingTime - uiOpeningTime );
|
|
|
|
// NOTE: this assumes stored are only closed overnight, so all we have to do is compare the day portion
|
|
uiDaysDifference = ( uiEndTime / NUM_MIN_IN_DAY ) - ( uiStartTime / NUM_MIN_IN_DAY );
|
|
|
|
if ( uiDaysDifference >= 2 )
|
|
{
|
|
// close for 1 less than that many full nights...
|
|
uiMinutesClosed = ( uiDaysDifference - 1 ) * uiMinutesClosedOvernight;
|
|
}
|
|
|
|
|
|
// add partial day's closing
|
|
|
|
// convert start and end times into 24hr military time
|
|
uiStartTime = uiStartTime % NUM_MIN_IN_DAY;
|
|
uiEndTime = uiEndTime % NUM_MIN_IN_DAY;
|
|
|
|
// treat end time of midnight as 24:00 hours to prevent indefinite recursion and make formulas work
|
|
if ( uiEndTime == 0 )
|
|
{
|
|
uiEndTime = NUM_MIN_IN_DAY;
|
|
}
|
|
|
|
|
|
if ( uiStartTime == uiEndTime )
|
|
{
|
|
if ( uiDaysDifference == 0 )
|
|
{
|
|
return( 0 );
|
|
}
|
|
else
|
|
{
|
|
uiMinutesClosed += uiMinutesClosedOvernight;
|
|
}
|
|
}
|
|
if ( uiStartTime < uiEndTime )
|
|
{
|
|
if ( uiStartTime < uiOpeningTime )
|
|
{
|
|
// add how many minutes in the time range BEFORE the store opened that day
|
|
uiMinutesClosed += ( min( uiOpeningTime, uiEndTime ) - uiStartTime );
|
|
}
|
|
|
|
if ( uiEndTime > uiClosingTime )
|
|
{
|
|
// add how many minutes in the time range AFTER the store closed that day
|
|
uiMinutesClosed += ( uiEndTime - max( uiClosingTime, uiStartTime ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Assert( uiEndTime < uiStartTime );
|
|
|
|
// recursive calls! Add two separate times: before midnight, and after midnight
|
|
uiMinutesClosed += CalculateMinutesClosedBetween( ubArmsDealer, uiStartTime, NUM_MIN_IN_DAY );
|
|
uiMinutesClosed += CalculateMinutesClosedBetween( ubArmsDealer, 0, uiEndTime );
|
|
}
|
|
|
|
return ( uiMinutesClosed );
|
|
}
|