Files
source/Tactical/Arms Dealer Init.cpp
T
Overhaul 7a27e7105d New attack busy system
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
2007-06-09 09:01:04 +00:00

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 );
}