Files
source/Tactical/ShopKeeper Interface.cpp
T
00fb898600 Delete builddefines.h
Nothing was left in it but the include of profiler.h, and 132 translation units
were including it for that alone. Fifteen files were leaning on profiler.h to
drag in <set>, <vector> and <ostream> for them; those now include what they use.

This is the commit that moves line numbers. Removing an include line shifts
__LINE__ by one for everything below it, and __LINE__ is an immediate operand in
every Assert() and DebugMsg() call, so the four game executables differ from
their predecessors by roughly a thousand 32-bit constants each. Every one of
those is accounted for: each is a single immediate that moved by -1 where the
builddefines.h include went away, or +1 where a <set>/<vector> include was
added. Nothing else in .text, .rdata or .data moves, no object file's section
sizes change, and symbolize_crash and Ja2Export stay bit-identical.

The one non-immediate difference is that the 24 Editor translation units of the
non-editor apps stop emitting __Avx2WmemEnabledWeakValue, a 4-byte weak COMDAT
they only ever instantiated through profiler.h's <vector>. It is a UCRT weak
default that other translation units still provide.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 01:07:26 -03:00

7330 lines
248 KiB
C++

#include "types.h"
#include "ShopKeeper Interface.h"
#include "Utilities.h"
#include "Game Clock.h"
#include "Render Dirty.h"
#include "Font Control.h"
#include "WordWrap.h"
#include "Text Input.h"
#include "Interface.h"
#include "Interface Control.h"
#include "Overhead.h"
#include "Cursors.h"
#include "sysutil.h"
#include "Interface Panels.h"
#include "Radar Screen.h"
#include "Interface Items.h"
#include "Interface Utils.h"
#include "vobject_blitters.h"
#include "finances.h"
#include "Text.h"
#include "Cursor Control.h"
#include "input.h"
#include "Arms Dealer Init.h"
#include "english.h"
#include "Soldier Add.h"
#include "faces.h"
#include "Dialogue Control.h"
#include "ShopKeeper Quotes.h"
#include "GameSettings.h"
#include "MercTextBox.h"
#include "random.h"
#include "Squads.h"
#include "Soldier Profile.h"
#include "message.h"
#include "LaptopSave.h"
#include "Quests.h"
#include "Weapons.h"
#include "MessageBoxScreen.h"
#include "Drugs And Alcohol.h"
#include "Soldier macros.h"
#include "ArmsDealerInvInit.h"
#include "opplist.h"
#include "LOS.h"
#include "NPC.h"
#include "Soldier Create.h"
#include "PATHAI.H"
#include "Points.h"
#include "InterfaceItemImages.h"
#include "Animation Control.h" // added by Flugente
#include "Town Militia.h" // added by Flugente
#ifdef JA2UB
#include "Ja25_Tactical.h"
#include "Ja25 Strategic Ai.h"
#include "soldier profile type.h"
#include <mousesystem.h>
#include "ub_config.h"
#endif
#include <algorithm>
////////////////////////////////////////////
//
// global defines
//
///////////////////////////////////////////
#define SKI_INTERFACE_WIDTH 536
#define SKI_INTERFACE_HEIGHT 340
#define SCREEN_X_OFFSET (((SCREEN_WIDTH - SKI_INTERFACE_WIDTH) / 2))
#define SCREEN_Y_OFFSET ((((SCREEN_HEIGHT - INV_INTERFACE_HEIGHT) - (SKI_INTERFACE_HEIGHT)) / 2))
#define SKI_BUTTON_FONT MILITARYFONT1//FONT14ARIAL
#define SKI_BUTTON_COLOR 73
#define SKI_TITLE_FONT MILITARYFONT1//FONT14ARIAL
#define SKI_TITLE_COLOR 169//FONT_MCOLOR_LTYELLOW
#define SKI_LABEL_FONT MILITARYFONT1
#define SKI_ITEM_DESC_FONT SMALLCOMPFONT
#define SKI_ITEM_PRICE_COLOR FONT_MCOLOR_WHITE
#define SKIT_NUMBER_FONT BLOCKFONT2
//Number of Inventory slots
#define SKI_NUM_ARMS_DEALERS_INV_SLOTS 500 // Flugente: simply a resonably huge number
#define SKI_NUM_TRADING_INV_SLOTS 500 // Flugente: simply a resonably huge number
//Inventory Slots size and offsets
#define SKI_INV_SLOT_WIDTH 67
#define SKI_INV_SLOT_HEIGHT 35 // bfant: increased from 31 to fit item name text
#define SKI_INV_HEIGHT (SKI_INV_SLOT_HEIGHT - 7)
#define SKI_INV_WIDTH 60
#define SKI_INV_OFFSET_X 74
#define SKI_INV_OFFSET_Y 40 // bfant: increased from 36
#define SKI_INV_PRICE_OFFSET_X 1
#define SKI_INV_PRICE_OFFSET_Y 24
// the delay for glow cycling in glow areas, in millisecs
#define SKI_GLOW_DELAY 70
#define SKI_TACTICAL_BACKGROUND_START_X (SCREEN_X_OFFSET + SKI_INTERFACE_WIDTH)
#define SKI_TACTICAL_BACKGROUND_START_Y 0
#define SKI_DROP_ITEM_TO_GROUND_START_X SKI_TACTICAL_BACKGROUND_START_X
#define SKI_DROP_ITEM_TO_GROUND_START_Y (SCREEN_Y_OFFSET + 262)
#define SKI_DROP_ITEM_TO_GROUND_TEXT_START_Y (SCREEN_Y_OFFSET + 262)
#define SKI_ITEM_MOVEMENT_AREA_X (SCREEN_X_OFFSET + 85)
#define SKI_ITEM_MOVEMENT_AREA_Y (SCREEN_Y_OFFSET + 263)
#define SKI_ITEM_MOVEMENT_AREA_WIDTH (SCREEN_WIDTH - SKI_ITEM_MOVEMENT_AREA_X)
#define SKI_ITEM_MOVEMENT_AREA_HEIGHT (SCREEN_Y_OFFSET + 215)//72
#define SKI_DEALER_OFFER_AREA_Y (SCREEN_Y_OFFSET + 148)
#define SKI_ITEM_NUMBER_TEXT_OFFSET_X 50
#define SKI_ITEM_NUMBER_TEXT_OFFSET_Y 19 // bfant: increased from 15 to fit item name text
#define SKI_ITEM_NUMBER_TEXT_WIDTH 15
#define SKI_SUBTITLE_TEXT_SIZE 512
#define SKI_POSITION_SUBTITLES_Y (SCREEN_Y_OFFSET + INV_INTERFACE_HEIGHT)//100
#define SKI_SMALL_FACE_WIDTH 16
#define SKI_SMALL_FACE_HEIGHT 14
#define SKI_SMALL_FACE_OFFSET_X 52
#define SKI_SMALL_FACE_OFFSET_Y 17
#define SKI_ATTACHMENT_SYMBOL_X_OFFSET 56
#define SKI_ATTACHMENT_SYMBOL_Y_OFFSET 14
#define SKI_MAX_AMOUNT_OF_ITEMS_DEALER_CAN_REPAIR_AT_A_TIME 4
// round off reapir times shown to the near quarter-hour
#define REPAIR_MINUTES_INTERVAL 15
#define SKI_DEALERS_RANDOM_QUOTE_DELAY 15000
#define SKI_DEALERS_RANDOM_QUOTE_DELAY_INCREASE_RATE 5000
#define DELAY_FOR_SHOPKEEPER_IDLE_QUOTE 20000
#define CHANCE_FOR_SHOPKEEPER_IDLE_QUOTE 40
#define REALLY_BADLY_DAMAGED_THRESHOLD 30
#define REPAIR_DELAY_IN_HOURS 6
#define FLO_DISCOUNT_PERCENTAGE 10
#ifdef JA2UB
#define SKI_BETTY_MINIMUM_AMOUNT_TO_SET_FACT 10
#define SKI_RAUL_MINIMUM_AMOUNT_TO_SET_FACT 100
#endif
// Flugente: new defines for shopkeeper interface with a resolution-dependent size
// the main tradescreen is a rectangle, which contains trader inventory, trader offer area and player offer area as sub-rectangles
#define SKI_TOTAL_END_X (SCREEN_X_OFFSET + SKI_INTERFACE_WIDTH)
#define SKI_TOTAL_END_Y (SCREEN_HEIGHT - INV_INTERFACE_HEIGHT)
#define SKI_TOTAL_BEGIN_Y 0
// we determine how many item boxes we could display in a row, and then depend where the shopkeeper display should start
// as a result, the margins will always look the same
#define SKI_TOTAL_BEGIN_X (SKI_TOTAL_END_X - 166 - ((SKI_TOTAL_END_X - 166) / SKI_INV_OFFSET_X) * SKI_INV_OFFSET_X)
// first: trader inventory
#define SKI_TRADER_INVENTORY_BEGIN_X SKI_TOTAL_BEGIN_X
#define SKI_TRADER_INVENTORY_BEGIN_Y SKI_TOTAL_BEGIN_Y
#define SKI_TRADER_INVENTORY_END_X SKI_TOTAL_END_X
#define SKI_TRADER_INVENTORY_END_Y (143 + (SKI_TOTAL_END_Y - SKI_INTERFACE_HEIGHT) / 2)
#define SKI_MAIN_TITLE_X (SKI_TRADER_INVENTORY_BEGIN_X + 112)
#define SKI_MAIN_TITLE_Y (SKI_TRADER_INVENTORY_BEGIN_Y + 12)
#define SKI_MAIN_TITLE_WIDTH 420
// merchant face
#define SKI_FACE_X (SKI_TRADER_INVENTORY_BEGIN_X + 13)
#define SKI_FACE_Y (SKI_TRADER_INVENTORY_BEGIN_Y + 13)
#define SKI_FACE_WIDTH 90
#define SKI_FACE_HEIGHT 100
// trader inventory page buttons
#define SKI_PAGE_UP_ARROWS_X (SKI_TRADER_INVENTORY_BEGIN_X + 121)
#define SKI_PAGE_UP_ARROWS_Y (SKI_TRADER_INVENTORY_BEGIN_Y + 35)
#define SKI_PAGE_DOWN_ARROWS_X SKI_PAGE_UP_ARROWS_X
#define SKI_PAGE_DOWN_ARROWS_Y (SKI_TRADER_INVENTORY_BEGIN_Y + 102)
#define SKI_PAGE_X (SKI_TRADER_INVENTORY_BEGIN_X + 112)
#define SKI_PAGE_Y (SKI_TRADER_INVENTORY_BEGIN_Y + 70)
#define SKI_PAGE_WIDTH 45
#define SKI_PAGE_HEIGHT 27
#define SKI_BUDGET_X (SKI_TRADER_INVENTORY_BEGIN_X + 9)
#define SKI_BUDGET_Y (SKI_FACE_Y + SKI_FACE_HEIGHT + 5)
#define SKI_BUDGET_WIDTH 73
#define SKI_BUDGET_OFFSET_TO_VALUE (SKI_BUDGET_Y + 10)
// trader inventory item boxes
#define SKI_TRADER_INVENTORY_BOXES_BEGIN_X (SKI_TRADER_INVENTORY_BEGIN_X + 166)
#define SKI_TRADER_INVENTORY_BOXES_BEGIN_Y (SKI_TRADER_INVENTORY_BEGIN_Y + 30)
#define SKI_TRADER_INVENTORY_BOXES_PER_ROW ((SKI_TRADER_INVENTORY_END_X - SKI_TRADER_INVENTORY_BOXES_BEGIN_X) / SKI_INV_OFFSET_X)
#define SKI_TRADER_INVENTORY_BOXES_PER_COL ((SKI_TRADER_INVENTORY_END_Y - SKI_TRADER_INVENTORY_BOXES_BEGIN_Y) / SKI_INV_OFFSET_Y)
// second: trader offer area
#define SKI_TRADER_OFFER_BEGIN_X SKI_TOTAL_BEGIN_X
#define SKI_TRADER_OFFER_BEGIN_Y SKI_TRADER_INVENTORY_END_Y
#define SKI_TRADER_OFFER_END_X SKI_TOTAL_END_X
#define SKI_TRADER_OFFER_END_Y (90 + SKI_TRADER_INVENTORY_END_Y) // bfant: adjusted so 2 rows fit again after item text modification
// trader offer area item boxes
#define SKI_TRADER_OFFER_BOXES_BEGIN_X (SKI_TRADER_OFFER_BEGIN_X + 91)
#define SKI_TRADER_OFFER_BOXES_BEGIN_Y (SKI_TRADER_OFFER_BEGIN_Y + 8)
#define SKI_TRADER_OFFER_BOXES_PER_ROW ((SKI_TRADER_OFFER_END_X - SKI_TRADER_OFFER_BOXES_BEGIN_X) / SKI_INV_OFFSET_X)
#define SKI_TRADER_OFFER_BOXES_PER_COL ((SKI_TRADER_OFFER_END_Y - SKI_TRADER_OFFER_BOXES_BEGIN_Y) / SKI_INV_OFFSET_Y)
#define SKI_TOTAL_COST_X (SKI_TRADER_OFFER_BEGIN_X + 9)
#define SKI_TOTAL_COST_Y (SKI_TRADER_OFFER_BEGIN_Y + 35)//159
#define SKI_TOTAL_COST_WIDTH 73
#define SKI_ARMS_DEALER_TOTAL_COST_X (SKI_TRADER_OFFER_BEGIN_X + 16)
#define SKI_ARMS_DEALER_TOTAL_COST_Y (SKI_TRADER_OFFER_BEGIN_Y + 51)//191
#define SKI_ARMS_DEALER_TOTAL_COST_WIDTH 59
#define SKI_ARMS_DEALER_TOTAL_COST_HEIGHT 20
// third: player offer area
#define SKI_PLAYER_OFFER_BEGIN_X SKI_TOTAL_BEGIN_X
#define SKI_PLAYER_OFFER_BEGIN_Y SKI_TRADER_OFFER_END_Y
#define SKI_PLAYER_OFFER_END_X SKI_TOTAL_END_X
#define SKI_PLAYER_OFFER_END_Y SKI_TOTAL_END_Y
// player offer area item boxes
#define SKI_PLAYER_OFFER_BOXES_BEGIN_X (SKI_PLAYER_OFFER_BEGIN_X + 91)
#define SKI_PLAYER_OFFER_BOXES_BEGIN_Y (SKI_PLAYER_OFFER_BEGIN_Y + 41)
#define SKI_PLAYER_OFFER_BOXES_PER_ROW ((SKI_PLAYER_OFFER_END_X - SKI_PLAYER_OFFER_BOXES_BEGIN_X) / SKI_INV_OFFSET_X)
#define SKI_PLAYER_OFFER_BOXES_PER_COL ((SKI_PLAYER_OFFER_END_Y - SKI_PLAYER_OFFER_BOXES_BEGIN_Y) / SKI_INV_OFFSET_Y)
#define SKI_PLAYERS_CURRENT_BALANCE_X (SKI_PLAYER_OFFER_BEGIN_X + 9)
#define SKI_PLAYERS_CURRENT_BALANCE_Y (SKI_PLAYER_OFFER_BEGIN_Y + 35)
#define SKI_PLAYERS_CURRENT_BALANCE_WIDTH SKI_TOTAL_COST_WIDTH
#define SKI_PLAYERS_CURRENT_BALANCE_OFFSET_TO_VALUE (SKI_PLAYERS_CURRENT_BALANCE_Y + 20)
#define SKI_TOTAL_VALUE_X (SKI_PLAYER_OFFER_BEGIN_X + 9)
#define SKI_TOTAL_VALUE_Y (SKI_PLAYERS_CURRENT_BALANCE_Y + 50)
#define SKI_TOTAL_VALUE_WIDTH SKI_TOTAL_COST_WIDTH
#define SKI_TOTAL_VALUE_OFFSET_TO_VALUE (SKI_TOTAL_VALUE_Y + 20)
#define SKI_TRANSACTION_BUTTON_X (SKI_PLAYER_OFFER_BEGIN_X + 147)//214
#define SKI_TRANSACTION_BUTTON_Y (SKI_PLAYER_OFFER_BEGIN_Y + 8)
#define SKI_DONE_BUTTON_X (SKI_PLAYER_OFFER_BEGIN_X + 292)//414
#define SKI_DONE_BUTTON_Y SKI_TRANSACTION_BUTTON_Y
// when colouring the tradescreen, we use this margin
#define SKI_MARGIN 4
////////////////////////////////////////////
//
// Global Variables
//
///////////////////////////////////////////
#ifdef JA2UB
//ja25 UB
enum
{
TRNSMTR_MSG_BOX__NONE,
TRNSMTR_MSG_BOX__1_FRAME_WAIT,
TRNSMTR_MSG_BOX__DISPLAY_BOX,
};
void AskUserToAttachTransmitterToLaptop();
void AttachLaptopTransmitterToLaptop( UINT8 ubExitValue );
#endif
UINT32 guiCornerWhereTacticalIsStillSeenImage; //This image is for where the corner of tactical is still seen through the shop keeper interface
BOOLEAN gfSKIScreenEntry = TRUE;
BOOLEAN gfSKIScreenExit = FALSE;
BOOLEAN gfUserHasRequestedToLeave = FALSE;
BOOLEAN gfRenderScreenOnNextLoop = FALSE;
UINT8 gubSkiDirtyLevel = SKI_DIRTY_LEVEL0;
INT32 giSKIMessageBox=-1;
INT8 gbSelectedArmsDealerID = -1; //Contains the enum value for the currently selected arms dealer
// Flugente: while we're trading, store the ID of non-NPC dealers
SoldierID gusIDOfCivTrader = NOBODY;
//the quote that is in progress, in certain circumstances, we don't want queuing of related but different quotes
INT32 giShopKeepDialogueEventinProgress = - 1;
INVENTORY_IN_SLOT gMoveingItem;
OBJECTTYPE *gpHighLightedItemObject=NULL;
BOOLEAN gfResetShopKeepIdleQuote = FALSE;
BOOLEAN gfDoEvaluationAfterOpening = FALSE;
typedef struct
{
UINT8 ubCurrentPage;
UINT8 ubNumberOfPages;
UINT16 ubFirstItemIndexOnPage;
} SELECTED_ARMS_DEALERS_STATS;
SELECTED_ARMS_DEALERS_STATS gSelectArmsDealerInfo;
//This pointer is used to store the inventory the arms dealer has for sale
std::vector<INVENTORY_IN_SLOT> gpTempDealersInventory;
INVENTORY_IN_SLOT ArmsDealerOfferArea[ SKI_NUM_TRADING_INV_SLOTS ];
INVENTORY_IN_SLOT PlayersOfferArea[ SKI_NUM_TRADING_INV_SLOTS ];
std::vector<OBJECTTYPE> subObjects;
BOOLEAN gfHavePurchasedItemsFromTony = FALSE;
BOOLEAN gfDealerHasSaidTheEvaluateQuoteOnceThisSession=FALSE;
BOOLEAN gfAlreadySaidTooMuchToRepair = FALSE;
UINT32 guiRandomQuoteDelayTime=SKI_DEALERS_RANDOM_QUOTE_DELAY;
//Index for the shopkeepers face
INT32 giShopKeeperFaceIndex;
//Id for the popup box
INT32 giPopUpBoxId=-1;
BOOLEAN gfIsTheShopKeeperTalking;
#ifdef JA2UB
UINT8 gubDisplayMsgBoxAskingUserToAttachTransmitter=0; // Ja25 UB
#endif
// the glow for unwanted items
BOOLEAN fDeltaColorForShopkeepUnwanted = TRUE;
INT8 bShopKeepGlowIndex = 9;
BOOLEAN fDoShopkeepGlow = FALSE;
BOOLEAN gfRemindedPlayerToPickUpHisStuff = FALSE;
BOOLEAN gfDoneBusinessThisSession = FALSE;
// this is used within SKI exclusively, to handle small faces
UINT8 gubArrayOfEmployedMercs[ CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ];
UINT32 guiSmallSoldiersFace[ CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ];
UINT16 gubNumberMercsInArray;
//The subutitled text for what the merc is saying
CHAR16 gsShopKeeperTalkingText[ SKI_SUBTITLE_TEXT_SIZE ];
UINT16 gusPositionOfSubTitlesX=0;
BOOLEAN gfExitSKIDueToMessageBox=FALSE;
OBJECTTYPE *pShopKeeperItemDescObject=NULL;
#ifdef JA2UB
BOOLEAN gfCanSayMakeTransactionQuote; //ja25 UB
#endif
UINT32 guiNextFreeInvSlot;
BOOLEAN gfStartWithRepairsDelayedQuote = FALSE;
BOOLEAN gfPerformTransactionInProgress = FALSE;
BOOLEAN gfCommonQuoteUsedThisSession[ NUM_COMMON_SK_QUOTES ];
extern SOLDIERTYPE *gpSMCurrentMerc;
extern SOLDIERTYPE *gpItemDescSoldier;
extern MOUSE_REGION gItemDescAttachmentRegions[MAX_ATTACHMENTS];
extern MOUSE_REGION gInvDesc;
extern BOOLEAN gfSMDisableForItems;
extern OBJECTTYPE *gpItemDescObject;
extern OBJECTTYPE *gpItemDescPrevObject;
extern OBJECTTYPE *gpItemDescPrevObject;
extern void HandleShortCutExitState( void );
extern SoldierID gubSelectSMPanelToMerc;
extern INT32 giItemDescAmmoButton;
extern BOOLEAN BltVSurfaceUsingDD( HVSURFACE hDestVSurface, HVSURFACE hSrcVSurface, UINT32 fBltFlags, INT32 iDestX, INT32 iDestY, RECT *SrcRect );
extern UINT8 gubLastSpecialItemAddedAtElement;
// Enums for possible evaluation results
enum
{
EVAL_RESULT_NORMAL,
EVAL_RESULT_OK_BUT_REALLY_DAMAGED,
EVAL_RESULT_DONT_HANDLE,
EVAL_RESULT_WORTHLESS,
EVAL_RESULT_NOT_DAMAGED,
EVAL_RESULT_NON_REPAIRABLE,
EVAL_RESULT_ROCKET_RIFLE,
NUM_EVAL_RESULTS
};
BOOLEAN gfEvalResultQuoteSaid[ NUM_EVAL_RESULTS ];
UINT32 guiLastTimeDealerSaidNormalEvaluationQuote = 0;
BOOLEAN gfSkiDisplayDropItemToGroundText = FALSE;
class ITEM_TO_ADD_AFTER_SKI_OPEN
{
public:
ITEM_TO_ADD_AFTER_SKI_OPEN() {initialize();};
void initialize() {fActive = FALSE; ItemObject.initialize();};
BOOLEAN fActive;
OBJECTTYPE ItemObject;
};
ITEM_TO_ADD_AFTER_SKI_OPEN gItemToAdd;
//Page up buttons for the merchants
void BtnSKI_InvPageUpButtonCallback(GUI_BUTTON *btn,INT32 reason);
UINT32 guiSKI_InvPageUpButton;
INT32 guiSKI_InvPageUpButtonImage;
//Page down buttons for the merchants
void BtnSKI_InvPageDownButtonCallback(GUI_BUTTON *btn,INT32 reason);
UINT32 guiSKI_InvPageDownButton;
INT32 guiSKI_InvPageDownButtonImage;
//Transaction buttons
void BtnSKI_TransactionButtonCallback(GUI_BUTTON *btn,INT32 reason);
UINT32 guiSKI_TransactionButton;
INT32 guiSKI_TransactionButtonImage;
//Done buttons
void BtnSKI_DoneButtonCallback(GUI_BUTTON *btn,INT32 reason);
UINT32 guiSKI_DoneButton;
INT32 guiSKI_DoneButtonImage;
UINT32 guiItemCrossOut;
UINT32 guiMainTradeScreenImage = -1;
BOOLEAN gfDisplayNoRoomMsg = FALSE;
//Blanket the entire screen
MOUSE_REGION gSKI_EntireScreenMouseRegions;
MOUSE_REGION gDealersInventoryMouseRegions[ SKI_NUM_ARMS_DEALERS_INV_SLOTS ];
MOUSE_REGION gRepairmanInventorySmallFaceMouseRegions[ SKI_NUM_ARMS_DEALERS_INV_SLOTS ];
int gDealersInventoryActiveRegions = 0;
void SelectDealersInventoryRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason );
void SelectDealersInventoryMovementRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason );
MOUSE_REGION gDealersOfferSlotsMouseRegions[ SKI_NUM_TRADING_INV_SLOTS ];
MOUSE_REGION gDealersOfferSlotsSmallFaceMouseRegions[SKI_NUM_TRADING_INV_SLOTS];
int gDealersOfferActiveRegions = 0;
void SelectDealersOfferSlotsRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason );
void SelectDealersOfferSlotsMovementRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason );
MOUSE_REGION gPlayersOfferSlotsMouseRegions[ SKI_NUM_TRADING_INV_SLOTS ];
MOUSE_REGION gPlayersOfferSlotsSmallFaceMouseRegions[SKI_NUM_TRADING_INV_SLOTS];
int gPlayersOfferActiveRegions = 0;
void SelectPlayersOfferSlotsRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason );
void SelectPlayersOfferSlotsMovementRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason );
MOUSE_REGION gSkiInventoryMovementAreaMouseRegions;
//Mouse region for the subtitles region when the merc is talking
MOUSE_REGION gShopKeeperSubTitleMouseRegion;
void ShopKeeperSubTitleRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason );
MOUSE_REGION gArmsDealersFaceMouseRegions;
void SelectArmsDealersFaceRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason );
//Region to allow the user to drop items to the ground
MOUSE_REGION gArmsDealersDropItemToGroundMouseRegions;
void SelectArmsDealersDropItemToGroundRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason );
void SelectArmsDealersDropItemToGroundMovementRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason );
////////////////////////////////////////////
//
// Local Function Prototypes
//
///////////////////////////////////////////
BOOLEAN EnterShopKeeperInterface();
BOOLEAN ExitShopKeeperInterface();
void HandleShopKeeperInterface();
BOOLEAN RenderShopKeeperInterface();
void GetShopKeeperInterfaceUserInput();
void DestroySkiInventorySlotMouseRegions( );
void CreateSkiInventorySlotMouseRegions( );
void InitializeShopKeeper( BOOLEAN fResetPage );
void CalculateFirstItemIndexOnPage( );
void DisplayArmsDealerCurrentInventoryPage( );
BOOLEAN DetermineArmsDealersSellingInventory( );
void StoreObjectsInNextFreeDealerInvSlot( DEALER_SPECIAL_ITEM* pSpecial, std::vector<INVENTORY_IN_SLOT>& pInventory, UINT8 ubOwner );
BOOLEAN RepairIsDone(DEALER_SPECIAL_ITEM* pSpecial);
UINT32 DisplayInvSlot( UINT16 ubSlotNum, UINT16 usItemIndex, UINT16 usPosX, UINT16 usPosY, OBJECTTYPE *ItemObject, BOOLEAN fHatchedOut, UINT8 ubItemArea );
void DisplayArmsDealerOfferArea();
INT16 AddItemToArmsDealerOfferArea( INVENTORY_IN_SLOT* pInvSlot, INT16 bSlotIdInOtherLocation );
BOOLEAN RemoveItemFromArmsDealerOfferArea( INT16 bSlotId, BOOLEAN fKeepItem );
void SetSkiRegionHelpText( INVENTORY_IN_SLOT *pInv, MOUSE_REGION* pRegion, UINT8 ubScreenArea );
void SetSkiFaceRegionHelpText( INVENTORY_IN_SLOT *pInv, MOUSE_REGION* pRegion, UINT8 ubScreenArea );
void RestoreTacticalBackGround();
void DisplayPlayersOfferArea();
UINT32 CalcShopKeeperItemPrice( BOOLEAN fDealerSelling, BOOLEAN fUnitPriceOnly, UINT16 usItemID, FLOAT dModifier, OBJECTTYPE *pItemObject );
FLOAT ItemConditionModifier(UINT16 usItemIndex, INT16 bStatus);
BOOLEAN RemoveItemFromPlayersOfferArea( INT16 bSlotIdInOtherLocation );
UINT32 CalculateTotalArmsDealerCost();
UINT32 CalculateTotalPlayersValue();
void MoveAllArmsDealersItemsInOfferAreaToPlayersOfferArea( );
void PerformTransaction( UINT32 uiMoneyFromPlayersAccount );
void MovePlayerOfferedItemsOfValueToArmsDealersInventory( );
INT16 AddInventoryToSkiLocation( INVENTORY_IN_SLOT *pInv, UINT16 ubSpotLocation, UINT8 ubWhere );
BOOLEAN RemoveItemFromDealersInventory( INVENTORY_IN_SLOT* pInvSlot, UINT16 ubSlot );
BOOLEAN InitShopKeepersFace( UINT8 usProfileID );
void DisplayTalkingArmsDealer();
BOOLEAN StartShopKeeperTalking( UINT16 usQuoteNum );
void HandleShopKeeperDialog( UINT8 ubInit );
BOOLEAN IsGunOrAmmoOfSameTypeSelected( OBJECTTYPE *pItemObject );
void RemoveShopKeeperSubTitledText();
BOOLEAN AreThereItemsInTheArmsDealersOfferArea( );
BOOLEAN AreThereItemsInThePlayersOfferArea( );
void ShutUpShopKeeper();
int CountNumberOfValuelessItemsInThePlayersOfferArea( );
int CountNumberOfItemsInThePlayersOfferArea( );
int CountNumberOfItemsInTheArmsDealersOfferArea( );
INT8 GetSlotNumberForMerc( UINT8 ubProfile );
void HandleCurrentModeText( UINT8 ubMode );
void EnableDisableDealersInventoryPageButtons();
void EnableDisableEvaluateAndTransactionButtons();
BOOLEAN IsMoneyTheOnlyItemInThePlayersOfferArea( );
UINT32 CalculateHowMuchMoneyIsInPlayersOfferArea( );
void MovePlayersItemsToBeRepairedToArmsDealersInventory();
BOOLEAN RemoveRepairItemFromDealersOfferArea( INT16 bSlot );
INT8 GetInvSlotOfUnfullMoneyInMercInventory( SOLDIERTYPE *pSoldier );
void ClearPlayersOfferSlot( INT32 ubSlotToClear );
void ClearArmsDealerOfferSlot( INT32 ubSlotToClear );
BOOLEAN DoSkiMessageBox( UINT8 ubStyle, STR16 zString, UINT32 uiExitScreen, UINT8 ubFlags, MSGBOX_CALLBACK ReturnCallback );
BOOLEAN WillShopKeeperRejectObjectsFromPlayer( INT8 bDealerId, INT8 bSlotId );
void CheckAndHandleClearingOfPlayerOfferArea( void );
BOOLEAN CanShopkeeperOverrideDialogue( void );
INT16 GetNumberOfItemsInPlayerOfferArea( void );
void HandleCheckIfEnoughOnTheTable( void );
void InitShopKeeperItemDescBox( OBJECTTYPE *pObject, UINT16 ubPocket, UINT8 ubFromLocation );
void StartSKIDescriptionBox( void );
BOOLEAN ShopkeeperAutoPlaceObject( SOLDIERTYPE * pSoldier, OBJECTTYPE * pObj, BOOLEAN fNewItem );
void ShopkeeperAddItemToPool( INT32 sGridNo, OBJECTTYPE *pObject, INT8 bVisible, UINT8 ubLevel, UINT16 usFlags, INT8 bRenderZHeightAboveLevel );
void IfMercOwnedCopyItemToMercInv( INVENTORY_IN_SLOT *pInv );
void IfMercOwnedRemoveItemFromMercInv( INVENTORY_IN_SLOT *pInv );
void IfMercOwnedRemoveItemFromMercInv2( UINT8 ubOwnerProfileId, INT16 bOwnerSlotId );
void SplitComplexObjectIntoSubObjects( OBJECTTYPE *pComplexObject );
void CountSubObjectsInObject( OBJECTTYPE *pComplexObject, UINT8 *pubRepairableSubObjects, UINT8 *pubNonRepairableSubObjects );
BOOLEAN AddObjectForEvaluation(OBJECTTYPE *pObject, UINT8 ubOwnerProfileId, INT16 bOwnerSlotId, BOOLEAN fFirstOne );
BOOLEAN OfferObjectToDealer( OBJECTTYPE *pComplexObject, UINT8 ubOwnerProfileId, INT16 bOwnerSlotId );
BOOLEAN SKITryToReturnInvToOwnerOrCurrentMerc( INVENTORY_IN_SLOT *pInv );
BOOLEAN SKITryToAddInvToMercsInventory( INVENTORY_IN_SLOT *pInv, SOLDIERTYPE *pSoldier );
void ExitSKIRequested();
void EvaluateItemAddedToPlayersOfferArea( INT8 bSlotID, BOOLEAN fFirstOne );
void ResetAllQuoteSaidFlags();
INVENTORY_IN_SLOT *GetPtrToOfferSlotWhereThisItemIs( UINT8 ubProfileID, INT16 bInvPocket );
void DealWithItemsStillOnTheTable();
void ReturnItemToPlayerSomehow( INVENTORY_IN_SLOT *pInvSlot, SOLDIERTYPE *pDropSoldier );
void GivePlayerSomeChange( UINT32 uiAmount );
void HandlePossibleRepairDelays();
BOOLEAN RepairmanFixingAnyItemsThatShouldBeDoneNow( UINT32 *puiHoursSinceOldestItemRepaired );
void DelayRepairsInProgressBy( UINT32 uiMinutesDelayed );
BOOLEAN CanTheDropItemToGroundStringBeDisplayed();
void DisplayTheSkiDropItemToGroundString();
UINT32 EvaluateInvSlot( INVENTORY_IN_SLOT *pInvSlot );
void BuildItemHelpTextString( CHAR16 sString[], INVENTORY_IN_SLOT *pInv, UINT8 ubScreenArea );
void BuildRepairTimeString( CHAR16 sString[], UINT32 uiTimeInMinutesToFixItem );
void BuildDoneWhenTimeString( CHAR16 sString[], UINT8 ubArmsDealer, INVENTORY_IN_SLOT* pObject );
void DisableAllDealersInventorySlots( void );
void EnableAllDealersInventorySlots( void );
void DisableAllDealersOfferSlots( void );
void EnableAllDealersOfferSlots( void );
void HatchOutInvSlot( UINT16 usPosX, UINT16 usPosY );
#ifdef JA2TESTVERSION
BOOLEAN gfTestDisplayDealerCash = FALSE;
void DisplayAllDealersCash();
#endif;
void INVENTORY_IN_SLOT::initialize()
{
ItemObject.initialize();
fActive = 0;
sItemIndex = 0;
uiFlags = 0;
ubLocationOfObject = 0;
bSlotIdInOtherLocation = 0;
ubIdOfMercWhoOwnsTheItem = 0;
uiItemPrice = 0;
uiRepairDoneTime = 0;
}
INVENTORY_IN_SLOT& INVENTORY_IN_SLOT::operator=(OLD_INVENTORY_IN_SLOT_101& src)
{
this->ItemObject = src.oldItemObject;
this->fActive = src.fActive;
this->sItemIndex = src.sItemIndex;
this->uiFlags = src.uiFlags;
this->ubLocationOfObject = src.ubLocationOfObject;
this->bSlotIdInOtherLocation = src.bSlotIdInOtherLocation;
this->ubIdOfMercWhoOwnsTheItem = src.ubIdOfMercWhoOwnsTheItem;
this->uiItemPrice = src.uiItemPrice;
this->uiRepairDoneTime = 0;
return *this;
}
//ppp
//
// screen handler functions
//
UINT32 ShopKeeperScreenInit()
{
//Set so next time we come in, we can set up
gfSKIScreenEntry = TRUE;
return( TRUE );
}
UINT32 ShopKeeperScreenHandle()
{
StartFrameBufferRender();
if( gfSKIScreenEntry )
{
PauseGame();
if( !EnterShopKeeperInterface() )
{
gfSKIScreenExit = TRUE;
EnterTacticalScreen( );
//ADB if we fail, unpause the game
UnPauseGame();
return( SHOPKEEPER_SCREEN );
}
else
{
gfSKIScreenEntry = FALSE;
gfSKIScreenExit = FALSE;
}
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
gfRenderScreenOnNextLoop = TRUE;
InvalidateRegion( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
}
if( gfRenderScreenOnNextLoop )
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
RestoreBackgroundRects();
GetShopKeeperInterfaceUserInput();
// Check for any newly added items...
if ( gpSMCurrentMerc->flags.fCheckForNewlyAddedItems )
{
// Startup any newly added items....
CheckForAnyNewlyAddedItems( gpSMCurrentMerc );
gpSMCurrentMerc->flags.fCheckForNewlyAddedItems = FALSE;
}
HandleShopKeeperInterface();
if( gubSkiDirtyLevel == SKI_DIRTY_LEVEL2 )
{
RenderShopKeeperInterface();
fInterfacePanelDirty = DIRTYLEVEL2;
gubSkiDirtyLevel = SKI_DIRTY_LEVEL0;
}
else if( gubSkiDirtyLevel == SKI_DIRTY_LEVEL1 )
{
fInterfacePanelDirty = DIRTYLEVEL2;
gubSkiDirtyLevel = SKI_DIRTY_LEVEL0;
}
// render buttons marked dirty
DisableSMPpanelButtonsWhenInShopKeeperInterface( FALSE );
RenderButtons( );
//CHRISL: If we put this function call here, we overwrite the bullet graphic. I don't know why. I just know it happens.
//RenderItemDescriptionBox( );
// render help
SaveBackgroundRects( );
RenderButtonsFastHelp( );
ExecuteBaseDirtyRectQueue();
EndFrameBufferRender();
if( gfSKIScreenExit )
{
ExitShopKeeperInterface();
gfSKIScreenExit = FALSE;
gfSKIScreenEntry = TRUE;
EnterTacticalScreen( );
UnPauseGame();
}
if ( gfDisplayNoRoomMsg )
{
// tell player there's not enough room in the player's offer area
// ARM: message is delayed because we need the mouse restriction to be in place BEFORE it comes up so it gets lifted/restored
DoSkiMessageBox( MSG_BOX_BASIC_STYLE, SKI_Text[ SKI_TEXT_NO_MORE_ROOM_IN_PLAYER_OFFER_AREA ], SHOPKEEPER_SCREEN, MSG_BOX_FLAG_OK, NULL );
gfDisplayNoRoomMsg = FALSE;
}
return( SHOPKEEPER_SCREEN );
}
UINT32 ShopKeeperScreenShutdown()
{
ShutDownArmsDealers();
return( TRUE );
}
BOOLEAN EnterShopKeeperInterface()
{
VOBJECT_DESC VObjectDesc;
CHAR8 zTemp[32];
VSURFACE_DESC vs_desc;
//ADB if we are here, we must be able to talk with an extended ear (CheckIfRadioIsEquipped())
//but if we are physically too far away, we don't have extended arms!
SOLDIERTYPE* pSoldier = gusSelectedSoldier;
SOLDIERTYPE* pShopkeeper = NULL;
if ( gusIDOfCivTrader != NOBODY )
pShopkeeper = gusIDOfCivTrader;
else
pShopkeeper = FindSoldierByProfileID( armsDealerInfo[gbSelectedArmsDealerID].ubShopKeeperID, FALSE );
if (PythSpacesAway( pSoldier->sGridNo, pShopkeeper->sGridNo ) > NPC_TALK_RADIUS )
{
//so now we know we are too far away to trade, so instead of just quitting,
//either post a message or run to the guy like HandleTalkInit does
if( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) )
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, L"Unable to interact with shopkeeper during combat" );
return( FALSE );
}
else
{
// First calculate APs and validate...
// Check AP cost...
if ( !EnoughPoints( pSoldier, APBPConstants[AP_TALK], 0, TRUE ) )
{
return( FALSE );
}
// First get an adjacent gridno....
UINT8 ubDirection;
INT32 sActionGridNo = FindAdjacentGridEx( pSoldier, pShopkeeper->sGridNo, &ubDirection, NULL, FALSE, TRUE );
if ( sActionGridNo == -1 )
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, TacticalStr[ NO_PATH ] );
return( FALSE );
}
if ( UIPlotPath( pSoldier, sActionGridNo, NO_COPYROUTE, FALSE, TEMPORARY, pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints ) == 0 )
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, TacticalStr[ NO_PATH ] );
return( FALSE );
}
// Walk up and talk to buddy....
gfNPCCircularDistLimit = TRUE;
INT32 sGoodGridNo = FindGridNoFromSweetSpotWithStructData( pSoldier, pSoldier->usUIMovementMode, pShopkeeper->sGridNo, (NPC_TALK_RADIUS-1), &ubDirection, TRUE );
gfNPCCircularDistLimit = FALSE;
// Now walkup to talk....
pSoldier->aiData.ubPendingAction = MERC_TALK;
pSoldier->aiData.uiPendingActionData1 = pShopkeeper->ubID;
pSoldier->aiData.ubPendingActionAnimCount = 0;
// WALK UP TO DEST FIRST
pSoldier->EVENT_InternalGetNewSoldierPath(sGoodGridNo, pSoldier->usUIMovementMode , TRUE , pSoldier->flags.fNoAPToFinishMove );
return( FALSE );
}
}
// make sure current merc is close enough and eligible to talk to the shopkeeper.
AssertMsg( CanMercInteractWithSelectedShopkeeper( gusSelectedSoldier ), "Selected merc can't interact with shopkeeper. Send save AM-1");
// Create a video surface to blt corner of the tactical screen that still shines through
vs_desc.fCreateFlags = VSURFACE_CREATE_DEFAULT | VSURFACE_SYSTEM_MEM_USAGE;
//Heinz: 22.02.09 BUGFIX: best way - to update whole tactical screen around SKI
//vs_desc.usWidth = SKI_TACTICAL_BACKGROUND_START_WIDTH;
//vs_desc.usHeight = SKI_TACTICAL_BACKGROUND_START_HEIGHT;
vs_desc.usWidth = SCREEN_WIDTH;
vs_desc.usHeight = SCREEN_HEIGHT;
vs_desc.ubBitDepth = 16;
if( !AddVideoSurface( &vs_desc, &guiCornerWhereTacticalIsStillSeenImage) )
{
#ifdef JA2BETAVERSION
ScreenMsg( FONT_MCOLOR_WHITE, MSG_BETAVERSION, L"Failed to create Surface where tactical map shows through" );
#endif
return( FALSE );
}
//Heinz: 22.02.09 shadowing of tactical screen for further using as a background
ShadowVideoSurfaceRect( guiCornerWhereTacticalIsStillSeenImage, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
//Clear out all the save background rects
EmptyBackgroundRects( );
ShadowVideoSurfaceRect( FRAME_BUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT - INV_INTERFACE_HEIGHT );
if( gfExitSKIDueToMessageBox )
{
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
gfExitSKIDueToMessageBox = FALSE;
}
// silversurfer: added this to prevent random crashes when the temp vector wasn't initialized properly.
// This is called in InitializeShopKeeper() a few lines below anyway so it shouldn't hurt here.
gpTempDealersInventory.clear();
//Check to make sure the inventory is null ( should always be null if we are just coming in to the SKI )
Assert( gpTempDealersInventory.empty() == true );
//Reinitialize the team panel to be the SM panel
SetCurrentInterfacePanel( SM_PANEL );
SetCurrentTacticalPanelCurrentMerc( gusSelectedSoldier );
SetSMPanelCurrentMerc( gusSelectedSoldier );
// load the Main trade screen background image
VObjectDesc.fCreateFlags=VOBJECT_CREATE_FROMFILE;
FilenameForBPP("InterFace\\itemcrossout.sti", VObjectDesc.ImageFile);
if( !AddVideoObject(&VObjectDesc, &guiItemCrossOut ))
{
#ifdef JA2BETAVERSION
ScreenMsg( FONT_MCOLOR_WHITE, MSG_BETAVERSION, L"Failed to load itemcrossout.sti" );
#endif
return( FALSE );
}
//Create an array of all mercs (anywhere!) currently in the player's employ, and load their small faces
// This is to support showing of repair item owner's faces even when they're not in the sector, as long as they still work for player
gubNumberMercsInArray = 0;
for( SoldierID ubCnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; ubCnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; ++ubCnt )
{
pSoldier = ubCnt;
if( pSoldier->bActive && ( pSoldier->ubProfile != NO_PROFILE ) &&
!(pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) && !AM_A_ROBOT( pSoldier ) )
{
// remember whose face is in this slot
gubArrayOfEmployedMercs[ gubNumberMercsInArray ] = pSoldier->ubProfile;
//Create the string for the face file name
if ( gMercProfiles[pSoldier->ubProfile].Type == PROFILETYPE_IMP )
{
sprintf( zTemp, "IMPFACES\\33FACE\\%02d.sti", gMercProfiles[ pSoldier->ubProfile ].ubFaceIndex );
}
else
{
sprintf( zTemp, "FACES\\33FACE\\%02d.sti", gMercProfiles[ pSoldier->ubProfile ].ubFaceIndex );
}
//While we are at it, add their small face
VObjectDesc.fCreateFlags=VOBJECT_CREATE_FROMFILE;
FilenameForBPP( zTemp, VObjectDesc.ImageFile);
if(!AddVideoObject(&VObjectDesc, &guiSmallSoldiersFace[ gubNumberMercsInArray ] ))
{
#ifdef JA2BETAVERSION
ScreenMsg( FONT_MCOLOR_WHITE, MSG_BETAVERSION, L"Failed to load %s", zTemp );
#endif
return( FALSE );
}
++gubNumberMercsInArray;
}
}
//Load the graphic for the arrow button
guiSKI_InvPageUpButtonImage = LoadButtonImage("INTERFACE\\TradeScrollArrows.sti", -1,0,-1,1,-1 );
guiSKI_InvPageDownButtonImage = UseLoadedButtonImage( guiSKI_InvPageUpButtonImage, -1,2,-1,3,-1 );
//Page up button for the merchant inventory
guiSKI_InvPageUpButton = QuickCreateButton( guiSKI_InvPageUpButtonImage, SKI_PAGE_UP_ARROWS_X, SKI_PAGE_UP_ARROWS_Y,
BUTTON_NEWTOGGLE, MSYS_PRIORITY_HIGHEST,
DEFAULT_MOVE_CALLBACK, BtnSKI_InvPageUpButtonCallback );
SpecifyDisabledButtonStyle( guiSKI_InvPageUpButton, DISABLED_STYLE_HATCHED );
//Page down button for the merchant inventory
guiSKI_InvPageDownButton = QuickCreateButton( guiSKI_InvPageDownButtonImage, SKI_PAGE_DOWN_ARROWS_X, SKI_PAGE_DOWN_ARROWS_Y,
BUTTON_NEWTOGGLE, MSYS_PRIORITY_HIGHEST,
DEFAULT_MOVE_CALLBACK, BtnSKI_InvPageDownButtonCallback );
SpecifyDisabledButtonStyle( guiSKI_InvPageDownButton, DISABLED_STYLE_HATCHED );
EnableDisableShopkeeperButtons(SHOPKEEPER_SCREEN, ACTIVATE_BUTTON);
//Blanket the entire screen
MSYS_DefineRegion( &gSKI_EntireScreenMouseRegions, 0, 0, (SCREEN_WIDTH - 1), (SCREEN_HEIGHT - INV_INTERFACE_HEIGHT), MSYS_PRIORITY_HIGH-2,
CURSOR_NORMAL, MSYS_NO_CALLBACK, MSYS_NO_CALLBACK);
MSYS_AddRegion( &gSKI_EntireScreenMouseRegions );
//Create the mouse regions for the inventory slot
CreateSkiInventorySlotMouseRegions( );
//Create the mouse region to limit the movement of the item cursos
MSYS_DefineRegion( &gSkiInventoryMovementAreaMouseRegions, SKI_ITEM_MOVEMENT_AREA_X, SKI_ITEM_MOVEMENT_AREA_Y, (UINT16)(SKI_ITEM_MOVEMENT_AREA_X+SKI_ITEM_MOVEMENT_AREA_WIDTH), (UINT16)(SKI_ITEM_MOVEMENT_AREA_Y+SKI_ITEM_MOVEMENT_AREA_HEIGHT), MSYS_PRIORITY_HIGH-1,
CURSOR_NORMAL, MSYS_NO_CALLBACK, MSYS_NO_CALLBACK ); //SelectSkiInventoryMovementAreaRegionCallBack
MSYS_AddRegion( &gSkiInventoryMovementAreaMouseRegions );
//Disable the region that limits the movement of the cursor with the item
MSYS_DisableRegion( &gSkiInventoryMovementAreaMouseRegions );
//Create the mouse region for the shopkeeper's face
MSYS_DefineRegion( &gArmsDealersFaceMouseRegions, SKI_FACE_X, SKI_FACE_Y, (UINT16)(SKI_FACE_X+SKI_FACE_WIDTH), (UINT16)(SKI_FACE_Y+SKI_FACE_HEIGHT), MSYS_PRIORITY_HIGH-1,
CURSOR_NORMAL, MSYS_NO_CALLBACK, SelectArmsDealersFaceRegionCallBack );
MSYS_AddRegion( &gArmsDealersFaceMouseRegions );
for (int x = 0; x < SKI_NUM_TRADING_INV_SLOTS; ++x)
{
ArmsDealerOfferArea[x].initialize();
PlayersOfferArea[x].initialize();
}
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
HandlePossibleRepairDelays();
}
//Setup the currently selected arms dealer
InitializeShopKeeper( TRUE );
//Set the flag indicating that we are in the shop keeper interface
guiTacticalInterfaceFlags |= INTERFACE_SHOPKEEP_INTERFACE;
gMoveingItem.initialize();
memset( &gfCommonQuoteUsedThisSession, FALSE, sizeof( gfCommonQuoteUsedThisSession ) );
//Init the shopkeepers face
InitShopKeepersFace( armsDealerInfo[ gbSelectedArmsDealerID ].ubShopKeeperID );
gfDoneBusinessThisSession = FALSE;
//Call this to set the fact that we just entered the screen
HandleShopKeeperDialog( 1 );
ResetAllQuoteSaidFlags();
//Reset the highlighted item pointer;
gpHighLightedItemObject = NULL;
//Reset
gfRemindedPlayerToPickUpHisStuff = FALSE;
gfUserHasRequestedToLeave = FALSE;
gfDisplayNoRoomMsg = FALSE;
//Disable the map radar region
MSYS_DisableRegion(&gRadarRegion);
gfDoEvaluationAfterOpening = FALSE;
if( gItemToAdd.fActive )
{
bool fAddedOK = false;
//if this is NOT a repair dealer or he is is but there is enough space in the player's offer area
// (you can't be out of space if it isn't a repairman, only they can fill it up with repaired items!)
if( ( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer != ARMS_DEALER_REPAIRS ) ||
(CountNumberOfItemsInThePlayersOfferArea( ) < (SKI_TRADER_INVENTORY_BOXES_PER_ROW * SKI_TRADER_INVENTORY_BOXES_PER_COL)) )
{
if ( OfferObjectToDealer( &(gItemToAdd.ItemObject), gpSMCurrentMerc->ubProfile, NO_SLOT ) )
{
fAddedOK = true;
}
}
if ( fAddedOK == true )
{
// evaluate it
gfDoEvaluationAfterOpening = TRUE;
}
else
{
//add the item back to the current PC into the slot it came from
//ADB screw slot, slot is (used to be, before it was deleted) only ever, so autoplace it
AutoPlaceObject(gpSMCurrentMerc->ubID, &gItemToAdd.ItemObject, FALSE);
}
//Clear the contents of the structure
gItemToAdd.initialize();
}
// Dirty the bottom panel
fInterfacePanelDirty = DIRTYLEVEL2;
gfDealerHasSaidTheEvaluateQuoteOnceThisSession = FALSE;
guiRandomQuoteDelayTime = SKI_DEALERS_RANDOM_QUOTE_DELAY;
pShopKeeperItemDescObject = NULL;
//Region to allow the user to drop items to the ground
MSYS_DefineRegion( &gArmsDealersDropItemToGroundMouseRegions, SKI_DROP_ITEM_TO_GROUND_START_X, SKI_DROP_ITEM_TO_GROUND_START_Y, SCREEN_WIDTH - 1, SCREEN_HEIGHT - INV_INTERFACE_HEIGHT, MSYS_PRIORITY_HIGH,
CURSOR_NORMAL, SelectArmsDealersDropItemToGroundMovementRegionCallBack, SelectArmsDealersDropItemToGroundRegionCallBack );
// CURSOR_NORMAL, MSYS_NO_CALLBACK, SelectArmsDealersDropItemToGroundRegionCallBack );
MSYS_AddRegion( &gArmsDealersDropItemToGroundMouseRegions );
gfSkiDisplayDropItemToGroundText = FALSE;
// by default re-enable calls to PerformTransaction()
gfPerformTransactionInProgress = FALSE;
#ifdef JA2UB
//JA25 UB
//if the dealer is RAUL
if( gbSelectedArmsDealerID == ARMS_DEALER_RAUL )
{
//set the fact the raul refreshed his inventory
SetFactFalse( FACT_RAULS_INVENTORY_CHANGED_SINCE_LAST_VISIT );
}
#endif
return( TRUE );
}
BOOLEAN InitShopKeepersFace( UINT8 usProfileID )
{
SOLDIERTYPE *pSoldier = NULL;
// if this is a non-NPC, don't set this up
if ( gusIDOfCivTrader == NOBODY )
pSoldier = FindSoldierByProfileID( armsDealerInfo[gbSelectedArmsDealerID].ubShopKeeperID, FALSE );
if( pSoldier == NULL )
{
//Create the facial index
giShopKeeperFaceIndex = InitFace( usProfileID, NOBODY, FACE_BIGFACE );
}
else
{
//Create the facial index
giShopKeeperFaceIndex = InitFace( usProfileID, pSoldier->ubID, FACE_BIGFACE );
}
SetAutoFaceActive( FRAME_BUFFER, FACE_AUTO_RESTORE_BUFFER, giShopKeeperFaceIndex, SKI_FACE_X, SKI_FACE_Y );
//Set it so the face cannot be set InActive
if ( giShopKeeperFaceIndex > -1 )
gFacesData[ giShopKeeperFaceIndex ].uiFlags |= FACE_INACTIVE_HANDLED_ELSEWHERE;
RenderAutoFace( giShopKeeperFaceIndex );
return(TRUE);
}
BOOLEAN ExitShopKeeperInterface()
{
if( gfExitSKIDueToMessageBox )
{
gfSKIScreenExit = FALSE;
// gfExitSKIDueToMessageBox = FALSE;
}
if( InItemDescriptionBox( ) && pShopKeeperItemDescObject != NULL )
{
DeleteItemDescriptionBox( );
}
FreeMouseCursor( TRUE );
//Delete the main shopkeep background
DeleteVideoObjectFromIndex( guiItemCrossOut );
DeleteVideoSurfaceFromIndex( guiCornerWhereTacticalIsStillSeenImage );
if ( guiMainTradeScreenImage != -1 )
{
DeleteVideoSurfaceFromIndex( guiMainTradeScreenImage );
guiMainTradeScreenImage = -1;
}
ShutUpShopKeeper();
UnloadButtonImage( guiSKI_InvPageUpButtonImage );
UnloadButtonImage( guiSKI_InvPageDownButtonImage );
//loop through the area and delete small faces
for (UINT16 ubCnt=0; ubCnt<gubNumberMercsInArray; ++ubCnt)
{
DeleteVideoObjectFromIndex( guiSmallSoldiersFace[ ubCnt ] );
}
RemoveButton( guiSKI_InvPageUpButton );
RemoveButton( guiSKI_InvPageDownButton );
EnableDisableShopkeeperButtons(SHOPKEEPER_SCREEN, DEACTIVATE_BUTTON);
MSYS_RemoveRegion( &gSKI_EntireScreenMouseRegions);
MSYS_RemoveRegion( &gSkiInventoryMovementAreaMouseRegions );
//Remove the region for the face
MSYS_RemoveRegion( &gArmsDealersFaceMouseRegions );
//Region to allow the user to drop items to the ground
MSYS_RemoveRegion( &gArmsDealersDropItemToGroundMouseRegions );
//Destroy the mouse regions for the inventory slots
DestroySkiInventorySlotMouseRegions( );
//if there is a temp inventory array, destroy it
gpTempDealersInventory.clear();
//Set the flag indicating that we are NOT in the shop keeper interface
guiTacticalInterfaceFlags &= ~INTERFACE_SHOPKEEP_INTERFACE;
//if there is a subtitles box up, remove it
RemoveShopKeeperSubTitledText();
//Get rid of the ShopKeeper face
DeleteFace( giShopKeeperFaceIndex );
//Enable the map region
MSYS_EnableRegion(&gRadarRegion);
gfSMDisableForItems = FALSE;
#ifdef JA2UB
//JA25 UB
//Check to see if a merc should say something
//CheckForValidQuotesWhenLeavingDealer( gTalkPanel.ubCharNum );
//if the laptop was just fixed
if( gubQuest[ QUEST_FIX_LAPTOP ] == QUESTDONE && !( gJa25SaveStruct.uiJa25GeneralFlags & JA_GF__PLAYER_SAID_LAPTOP_FIXED_QUOTE ) )
{
//Have a new merc say a quote
SoldierID bSoldierID = RandomSoldierIdFromNewMercsOnPlayerTeam();
if( bSoldierID != NOBODY )
{
TacticalCharacterDialogue( bSoldierID, QUOTE_JA2UB_LAPTOP_FIXED );
}
gJa25SaveStruct.uiJa25GeneralFlags |= JA_GF__PLAYER_SAID_LAPTOP_FIXED_QUOTE;
}
#endif
return( TRUE );
}
void HandleShopKeeperInterface()
{
UINT8 ubStatusOfSkiRenderDirtyFlag = gubSkiDirtyLevel;
INT32 iCounter = 0;
//if we are in the item desc panel, disable the buttons
if( InItemDescriptionBox( ) && pShopKeeperItemDescObject != NULL )
{
DisableSMPpanelButtonsWhenInShopKeeperInterface( FALSE );
DisableButton( guiSKI_InvPageUpButton );
DisableButton( guiSKI_InvPageDownButton );
EnableDisableShopkeeperButtons(SHOPKEEPER_SCREEN, DISABLE_BUTTON);
//DisableButton( guiSKI_TransactionButton );
//DisableButton( guiSKI_DoneButton );
//make sure the buttons dont render
// ButtonList[ guiSKI_InvPageUpButton ]->uiFlags |= BUTTON_FORCE_UNDIRTY;
// ButtonList[ guiSKI_InvPageDownButton ]->uiFlags |= BUTTON_FORCE_UNDIRTY;
//Heinz: 22.02.09 BUGFIX: buttons should render for items in inventory with EDB
//ButtonList[ guiSKI_TransactionButton ]->uiFlags |= BUTTON_FORCE_UNDIRTY;
//ButtonList[ guiSKI_DoneButton ]->uiFlags |= BUTTON_FORCE_UNDIRTY;
// make sure the shop keeper doesn't start talking ( reset the timing variable )
HandleShopKeeperDialog( 2 );
return;
}
if( gubSkiDirtyLevel == SKI_DIRTY_LEVEL2 )
{
fInterfacePanelDirty = DIRTYLEVEL2;
}
RenderTacticalInterface( );
if ( InItemStackPopup( ) )
{
RenderItemStackPopup( ( BOOLEAN )( fInterfacePanelDirty == DIRTYLEVEL2 ) );
}
// handle check if enough on the table
HandleCheckIfEnoughOnTheTable( );
// Render view window
fInterfacePanelDirty = DIRTYLEVEL2;
RenderRadarScreen( );
if( fInterfacePanelDirty == DIRTYLEVEL2 )
{
fInterfacePanelDirty = DIRTYLEVEL0;
}
DisplayTalkingArmsDealer();
MarkButtonsDirty( );
if( gfDoEvaluationAfterOpening )
{
BOOLEAN fFirstOne = TRUE;
gfDoEvaluationAfterOpening = FALSE;
for ( iCounter = 0; iCounter < gPlayersOfferActiveRegions; ++iCounter )
{
if( PlayersOfferArea[ iCounter ].fActive )
{
EvaluateItemAddedToPlayersOfferArea( ( INT8 ) iCounter, fFirstOne );
fFirstOne = FALSE;
}
}
gfAlreadySaidTooMuchToRepair = FALSE;
}
#ifdef JA2TESTVERSION
if ( gfTestDisplayDealerCash )
DisplayAllDealersCash();
#endif
//if the Ski dirty flag was changed to a lower value, make sure it is set properly
if( ubStatusOfSkiRenderDirtyFlag > gubSkiDirtyLevel )
gubSkiDirtyLevel = ubStatusOfSkiRenderDirtyFlag;
//if the merc is talking and there is an item currently being highlighted
// ( this gets rid of the item burning through the dealers text box )
if( gfIsTheShopKeeperTalking )
//Heinz: 23.02.09 BUGFIX: if shopkeeper is talking we should render popup in all cases
//if( gpHighLightedItemObject != NULL || gubSkiDirtyLevel != SKI_DIRTY_LEVEL0 )
RenderMercPopUpBoxFromIndex( giPopUpBoxId, gusPositionOfSubTitlesX, SKI_POSITION_SUBTITLES_Y, FRAME_BUFFER);
//if we are to display the drop item to ground text
if( gfSkiDisplayDropItemToGroundText )
{
DisplayTheSkiDropItemToGroundString();
}
#ifdef JA2UB
//ja25 UB
else if( gubDisplayMsgBoxAskingUserToAttachTransmitter != TRNSMTR_MSG_BOX__NONE )
{
if( gubDisplayMsgBoxAskingUserToAttachTransmitter == TRNSMTR_MSG_BOX__1_FRAME_WAIT )
{
gubDisplayMsgBoxAskingUserToAttachTransmitter = TRNSMTR_MSG_BOX__DISPLAY_BOX;
}
else if( gubDisplayMsgBoxAskingUserToAttachTransmitter == TRNSMTR_MSG_BOX__DISPLAY_BOX )
{
//Tell user that they are attaching the transmitter
AskUserToAttachTransmitterToLaptop();
//clear the flag
gubDisplayMsgBoxAskingUserToAttachTransmitter = TRNSMTR_MSG_BOX__NONE;
}
}
#endif
}
// Internal function to draw shop keeper inventory boxes
void DrawShopKeeperInventoryBox( INT32 usPosX, INT32 usPosY )
{
static const UINT16 col_itembox = Get16BPPColor( FROMRGB( 74, 65, 49 ) );
static const UINT16 col_itemdescription = Get16BPPColor( FROMRGB( 181, 150, 74 ) );
static const UINT16 col_itemstatusbar = Get16BPPColor( FROMRGB( 57, 56, 41 ) );
// draw background for status bar
ColorFillVideoSurfaceArea( FRAME_BUFFER, usPosX, usPosY, usPosX + 4, usPosY + 28, col_itemstatusbar );
// draw background for picture of item, etc.
ColorFillVideoSurfaceArea( FRAME_BUFFER, usPosX + 4, usPosY, usPosX + SKI_INV_SLOT_WIDTH, usPosY + 28, col_itembox );
// draw background for money area
ColorFillVideoSurfaceArea( FRAME_BUFFER, usPosX, usPosY + 28, usPosX + SKI_INV_SLOT_WIDTH, usPosY + SKI_INV_SLOT_HEIGHT, col_itemdescription );
}
BOOLEAN RenderShopKeeperInterface()
{
CHAR16 zMoney[128];
HVSURFACE hDestVSurface, hSrcVSurface;
SGPRect SrcRect;
if( InItemDescriptionBox( ) && pShopKeeperItemDescObject != NULL )
{
return( TRUE );
}
// wipe everything once and then redraw
if ( gubSkiDirtyLevel == SKI_DIRTY_LEVEL2 )
{
//Restore the area before blitting the new data on
RestoreExternBackgroundRect( SKI_TOTAL_BEGIN_X, SKI_TOTAL_BEGIN_Y,
SKI_TOTAL_END_X - SKI_TOTAL_BEGIN_X,
SKI_TOTAL_END_Y - SKI_TOTAL_BEGIN_Y );
}
UINT16 col_tradescreenmargin = Get16BPPColor( FROMRGB( 64, 64, 64 ) );
UINT16 col_tradescreen = Get16BPPColor( FROMRGB( 255, 0, 255 ) );
// we need to do the colouring twice to colour the margin differently
ColorFillVideoSurfaceArea( FRAME_BUFFER, SKI_TOTAL_BEGIN_X, SKI_TOTAL_BEGIN_Y,
SKI_TOTAL_END_X, SKI_TOTAL_END_Y,
col_tradescreenmargin );
// Flugente: because people find simple plane backgrounds to be offputting, we try for something more fancy
// we use a very huge background picture and then display parts of it.
// Given that the default picture has a size of 3088x2056, you would need a gigantic resolution for this to not work out.
// Flugente: guiMainTradeScreenImage is unsigned as the functions require UINT32. As it is unlikely we will ever use 2^32 pictures, this works well enough
if ( guiMainTradeScreenImage == -1 )
{
VSURFACE_DESC vs_desc;
vs_desc.fCreateFlags = VSURFACE_CREATE_FROMFILE | VSURFACE_SYSTEM_MEM_USAGE;
strcpy( vs_desc.ImageFile, "LAPTOP\\shopkeeperbackground.pcx" );
if ( FileExists( vs_desc.ImageFile ) )
{
AddVideoSurface( &vs_desc, &guiMainTradeScreenImage );
}
}
if ( guiMainTradeScreenImage != -1 )
{
HVSURFACE hSrcVSurface;
UINT32 uiDestPitchBYTES;
UINT32 uiSrcPitchBYTES;
UINT16 *pDestBuf;
UINT8 *pSrcBuf;
SGPRect clip;
// get surfaces
pDestBuf = (UINT16*)LockVideoSurface( FRAME_BUFFER, &uiDestPitchBYTES );
GetVideoSurface( &hSrcVSurface, guiMainTradeScreenImage );
pSrcBuf = LockVideoSurface( guiMainTradeScreenImage, &uiSrcPitchBYTES );
// for our 3 regions we display only parts of the picture
clip.iLeft = SKI_TRADER_INVENTORY_BEGIN_X + SKI_MARGIN;
clip.iRight = SKI_TRADER_INVENTORY_END_X - SKI_MARGIN;
clip.iTop = SKI_TRADER_INVENTORY_BEGIN_Y + SKI_MARGIN;
clip.iBottom = SKI_TRADER_INVENTORY_END_Y - SKI_MARGIN;
Blt8BPPDataSubTo16BPPBuffer( pDestBuf, uiDestPitchBYTES, hSrcVSurface, pSrcBuf, uiSrcPitchBYTES, SKI_TRADER_INVENTORY_BEGIN_X + SKI_MARGIN, SKI_TRADER_INVENTORY_BEGIN_Y + SKI_MARGIN, &clip );
clip.iLeft = SKI_TRADER_OFFER_BEGIN_X + SKI_MARGIN;
clip.iRight = SKI_TRADER_OFFER_END_X - SKI_MARGIN;
clip.iTop = SKI_TRADER_OFFER_BEGIN_Y + SKI_MARGIN;
clip.iBottom = SKI_TRADER_OFFER_END_Y - SKI_MARGIN;
Blt8BPPDataSubTo16BPPBuffer( pDestBuf, uiDestPitchBYTES, hSrcVSurface, pSrcBuf, uiSrcPitchBYTES, SKI_TRADER_OFFER_BEGIN_X + SKI_MARGIN, SKI_TRADER_OFFER_BEGIN_Y + SKI_MARGIN, &clip );
clip.iLeft = SKI_PLAYER_OFFER_BEGIN_X + SKI_MARGIN;
clip.iRight = SKI_PLAYER_OFFER_END_X - SKI_MARGIN;
clip.iTop = SKI_PLAYER_OFFER_BEGIN_Y + SKI_MARGIN;
clip.iBottom = SKI_PLAYER_OFFER_END_Y - SKI_MARGIN;
Blt8BPPDataSubTo16BPPBuffer( pDestBuf, uiDestPitchBYTES, hSrcVSurface, pSrcBuf, uiSrcPitchBYTES, SKI_PLAYER_OFFER_BEGIN_X + SKI_MARGIN, SKI_PLAYER_OFFER_BEGIN_Y + SKI_MARGIN, &clip );
// release surfaces
UnLockVideoSurface( guiMainTradeScreenImage );
UnLockVideoSurface( FRAME_BUFFER );
}
// if we cannot find the picture, use a fallback solution
else
{
ColorFillVideoSurfaceArea( FRAME_BUFFER, SKI_TRADER_INVENTORY_BEGIN_X + SKI_MARGIN, SKI_TRADER_INVENTORY_BEGIN_Y + SKI_MARGIN,
SKI_TRADER_INVENTORY_END_X - SKI_MARGIN, SKI_TRADER_INVENTORY_END_Y - SKI_MARGIN,
col_tradescreen );
ColorFillVideoSurfaceArea( FRAME_BUFFER, SKI_TRADER_OFFER_BEGIN_X + SKI_MARGIN, SKI_TRADER_OFFER_BEGIN_Y + SKI_MARGIN,
SKI_TRADER_OFFER_END_X - SKI_MARGIN, SKI_TRADER_OFFER_END_Y - SKI_MARGIN,
col_tradescreen );
ColorFillVideoSurfaceArea( FRAME_BUFFER, SKI_PLAYER_OFFER_BEGIN_X + SKI_MARGIN, SKI_PLAYER_OFFER_BEGIN_Y + SKI_MARGIN,
SKI_PLAYER_OFFER_END_X - SKI_MARGIN, SKI_PLAYER_OFFER_END_Y - SKI_MARGIN,
col_tradescreen );
}
{
int usPosY = SKI_TRADER_INVENTORY_BOXES_BEGIN_Y;
for ( int y = 0; y<SKI_TRADER_INVENTORY_BOXES_PER_COL; ++y )
{
int usPosX = SKI_TRADER_INVENTORY_BOXES_BEGIN_X;
for ( int x = 0; x<SKI_TRADER_INVENTORY_BOXES_PER_ROW; ++x )
{
DrawShopKeeperInventoryBox( usPosX, usPosY );
usPosX += SKI_INV_OFFSET_X;
}
usPosY += SKI_INV_OFFSET_Y;
}
}
{
int usPosY = SKI_TRADER_OFFER_BOXES_BEGIN_Y;
for ( int y = 0; y<SKI_TRADER_OFFER_BOXES_PER_COL; ++y )
{
int usPosX = SKI_TRADER_OFFER_BOXES_BEGIN_X;
for ( int x = 0; x<SKI_TRADER_OFFER_BOXES_PER_ROW; ++x )
{
DrawShopKeeperInventoryBox( usPosX, usPosY );
usPosX += SKI_INV_OFFSET_X;
}
usPosY += SKI_INV_OFFSET_Y;
}
}
{
int usPosY = SKI_PLAYER_OFFER_BOXES_BEGIN_Y;
for ( int y = 0; y<SKI_PLAYER_OFFER_BOXES_PER_COL; ++y )
{
int usPosX = SKI_PLAYER_OFFER_BOXES_BEGIN_X;
for ( int x = 0; x<SKI_PLAYER_OFFER_BOXES_PER_ROW; ++x )
{
DrawShopKeeperInventoryBox( usPosX, usPosY );
usPosX += SKI_INV_OFFSET_X;
}
usPosY += SKI_INV_OFFSET_Y;
}
}
if ( giShopKeeperFaceIndex > -1 )
RenderAutoFace( giShopKeeperFaceIndex );
// Flugente: if this is a non-NPC dealer, we instead show their body in their current animation
else if ( gusIDOfCivTrader != NOBODY )
{
SOLDIERTYPE* pSoldier = gusIDOfCivTrader;
if ( pSoldier )
{
UINT32 uiPitch;
// Lock video surface
UINT16* pBuffer = (UINT16*)LockVideoSurface( FRAME_BUFFER, &uiPitch );
if ( pBuffer != NULL )
{
UINT16 usAnimSurface = GetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );
HVOBJECT hVObject = gAnimSurfaceDatabase[usAnimSurface].hVideoObject;
ETRLEObject* pTrav = &(hVObject->pETRLEObject[pSoldier->usAniFrame]);
INT32 sCenX = SKI_FACE_X + (INT16)(abs( (long)(SKI_FACE_WIDTH - pTrav->usWidth) ) / 2) - pTrav->sOffsetX;
INT32 sCenY = SKI_FACE_Y + (INT16)(abs( (long)(SKI_FACE_HEIGHT - pTrav->usHeight) ) / 2) - pTrav->sOffsetY;
SGPRect rect;
rect.iLeft = SKI_FACE_X;
rect.iTop = SKI_FACE_Y;
rect.iRight = SKI_FACE_X + SKI_FACE_WIDTH;
rect.iBottom = SKI_FACE_Y + SKI_FACE_HEIGHT;
Blt8BPPDataTo16BPPBufferTransShadowClip( pBuffer, uiPitch,
hVObject,
sCenX, sCenY,
pSoldier->usAniFrame,
&rect,
pSoldier->pShades[pSoldier->ubFadeLevel],
TRUE
);
UnLockVideoSurface( FRAME_BUFFER );
}
}
}
// CHRISL: If in NIV mode, don't display the clock
if ( UsingNewInventorySystem( ) == false )
RenderClock( INTERFACE_CLOCK_X, INTERFACE_CLOCK_Y );
// CHRISL: Added X,Y parameters to allow control of TownID string placement.
RenderTownIDString( LOCATION_NAME_X, LOCATION_NAME_Y );
//Display the Title
DrawTextToScreen( SKI_Text[ SKI_TEXT_MERCHADISE_IN_STOCK ], SKI_MAIN_TITLE_X, SKI_MAIN_TITLE_Y, SKI_MAIN_TITLE_WIDTH, SKI_TITLE_FONT, SKI_TITLE_COLOR, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED );
// display shopkeeper budget
DisplayWrappedString( SKI_BUDGET_X, SKI_BUDGET_Y, SKI_BUDGET_WIDTH, 2, SKI_LABEL_FONT, SKI_TITLE_COLOR, SKI_Text[SKI_TEXT_BUDGET], FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED );
swprintf( zMoney, L"%s", FormatMoney(gArmsDealerStatus[gbSelectedArmsDealerID].uiArmsDealersCash).data() );
CHAR16 zTemp2[64];
if ( armsDealerInfo[gbSelectedArmsDealerID].uiFlags & ARMS_DEALER_DEALWITHINTEL )
swprintf( zTemp2, L"%s Intel", zMoney );
else
swprintf( zTemp2, L"%s", zMoney );
DrawTextToScreen( zTemp2, SKI_BUDGET_X, SKI_BUDGET_OFFSET_TO_VALUE, SKI_BUDGET_WIDTH, FONT10ARIAL, SKI_ITEM_PRICE_COLOR, FONT_MCOLOR_BLACK, TRUE, CENTER_JUSTIFIED );
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
//Display the Repair cost text
DisplayWrappedString( SKI_TOTAL_COST_X, SKI_TOTAL_COST_Y, SKI_TOTAL_COST_WIDTH, 2, SKI_LABEL_FONT, SKI_TITLE_COLOR, SKI_Text[ SKI_TEXT_REPAIR_COST ], FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED);
}
else
{
//Display the Total cost text
DisplayWrappedString( SKI_TOTAL_COST_X, SKI_TOTAL_COST_Y, SKI_TOTAL_COST_WIDTH, 2, SKI_LABEL_FONT, SKI_TITLE_COLOR, SKI_Text[ SKI_TEXT_TOTAL_COST ], FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED);
}
//Display the players current balance text
DisplayWrappedString( SKI_PLAYERS_CURRENT_BALANCE_X, SKI_PLAYERS_CURRENT_BALANCE_Y, SKI_PLAYERS_CURRENT_BALANCE_WIDTH, 2, SKI_LABEL_FONT, SKI_TITLE_COLOR, SkiMessageBoxText[ SKI_PLAYERS_CURRENT_BALANCE ], FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED );
//Display the players current balance value
if ( armsDealerInfo[gbSelectedArmsDealerID].uiFlags & ARMS_DEALER_DEALWITHINTEL )
{
swprintf( zMoney, L"%s", FormatMoney((int)(GetIntel()) ).data());
swprintf( zTemp2, L"%s Intel", zMoney );
}
else
{
swprintf( zMoney, L"%s", FormatMoney(LaptopSaveInfo.iCurrentBalance ).data());
swprintf( zTemp2, L"%s", zMoney );
}
DrawTextToScreen( zTemp2, SKI_PLAYERS_CURRENT_BALANCE_X, SKI_PLAYERS_CURRENT_BALANCE_OFFSET_TO_VALUE, SKI_PLAYERS_CURRENT_BALANCE_WIDTH, FONT10ARIAL, SKI_ITEM_PRICE_COLOR, FONT_MCOLOR_BLACK, TRUE, CENTER_JUSTIFIED );
//Display the total value text
DisplayWrappedString( SKI_TOTAL_VALUE_X, SKI_TOTAL_VALUE_Y, SKI_TOTAL_VALUE_WIDTH, 2, SKI_LABEL_FONT, SKI_TITLE_COLOR, SKI_Text[SKI_TEXT_TOTAL_VALUE], FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED );
//Heinz: 22.02.09 BUGFIX: best way - to update whole tactical screen around SKI
//BlitBufferToBuffer( guiRENDERBUFFER, guiSAVEBUFFER, 0, 0, SKI_TACTICAL_BACKGROUND_START_X, SKI_TACTICAL_BACKGROUND_START_HEIGHT );
BlitBufferToBuffer( guiRENDERBUFFER, guiSAVEBUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
//At this point the background is pure, copy it to the save buffer
if( gfRenderScreenOnNextLoop )
{
// BlitBufferToBuffer( guiRENDERBUFFER, guiCornerWhereTacticalIsStillSeenImage, SKI_TACTICAL_BACKGROUND_START_X, SKI_TACTICAL_BACKGROUND_START_Y, SKI_TACTICAL_BACKGROUND_START_WIDTH, SKI_TACTICAL_BACKGROUND_START_HEIGHT );
GetVideoSurface( &hDestVSurface, guiCornerWhereTacticalIsStillSeenImage );
GetVideoSurface( &hSrcVSurface, guiSAVEBUFFER );
//Heinz: 22.02.09 BUGFIX: best way - to update whole tactical screen around SKI
//SrcRect.iLeft = SKI_TACTICAL_BACKGROUND_START_X;
//SrcRect.iTop = SKI_TACTICAL_BACKGROUND_START_Y;
//SrcRect.iRight = SKI_TACTICAL_BACKGROUND_START_X + SKI_TACTICAL_BACKGROUND_START_WIDTH;
//SrcRect.iBottom = SKI_TACTICAL_BACKGROUND_START_Y + SKI_TACTICAL_BACKGROUND_START_HEIGHT;
//BltVSurfaceUsingDD( hDestVSurface, hSrcVSurface, VO_BLT_SRCTRANSPARENCY, SKI_TACTICAL_BACKGROUND_START_X, SKI_TACTICAL_BACKGROUND_START_Y, (RECT*)&SrcRect );
SrcRect.iLeft = 0;
SrcRect.iTop = 0;
SrcRect.iRight = SCREEN_WIDTH;
SrcRect.iBottom = SCREEN_HEIGHT;
BltVSurfaceUsingDD( hDestVSurface, hSrcVSurface, VO_BLT_SRCTRANSPARENCY, 0, 0, (RECT*)&SrcRect );
gfRenderScreenOnNextLoop = FALSE;
}
DisplayArmsDealerCurrentInventoryPage( );
DisplayArmsDealerOfferArea( );
DisplayPlayersOfferArea( );
EnableDisableEvaluateAndTransactionButtons( );
//if the merc is talking and the screen has been dirtied
if( gfIsTheShopKeeperTalking )
RenderMercPopUpBoxFromIndex( giPopUpBoxId, gusPositionOfSubTitlesX, SKI_POSITION_SUBTITLES_Y, FRAME_BUFFER);
//Restore the tactical background that is visble behind the SKI panel
RestoreTacticalBackGround();
InvalidateRegion( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
return( TRUE );
}
void RestoreTacticalBackGround()
{
HVSURFACE hDestVSurface, hSrcVSurface;
SGPRect SrcRect;
GetVideoSurface( &hDestVSurface, guiRENDERBUFFER );
GetVideoSurface( &hSrcVSurface, guiCornerWhereTacticalIsStillSeenImage );
// Top
SrcRect.iLeft = 0;
SrcRect.iTop = 0;
SrcRect.iRight = SCREEN_WIDTH;
SrcRect.iBottom = SKI_TOTAL_BEGIN_Y;
BltVSurfaceUsingDD( hDestVSurface, hSrcVSurface, VO_BLT_SRCTRANSPARENCY, SrcRect.iLeft, SrcRect.iTop, (RECT*)&SrcRect );
// Bottom
SrcRect.iLeft = 0;
SrcRect.iTop = SKI_TOTAL_END_Y;
SrcRect.iRight = SCREEN_WIDTH;
SrcRect.iBottom = SCREEN_HEIGHT - INV_INTERFACE_HEIGHT;
BltVSurfaceUsingDD( hDestVSurface, hSrcVSurface, VO_BLT_SRCTRANSPARENCY, SrcRect.iLeft, SrcRect.iTop, (RECT*)&SrcRect );
// Left
SrcRect.iLeft = 0;
SrcRect.iTop = SKI_TOTAL_BEGIN_Y;
SrcRect.iRight = SKI_TOTAL_BEGIN_X;
SrcRect.iBottom = SKI_TOTAL_END_Y;
BltVSurfaceUsingDD( hDestVSurface, hSrcVSurface, VO_BLT_SRCTRANSPARENCY, SrcRect.iLeft, SrcRect.iTop, (RECT*)&SrcRect );
// Right
SrcRect.iLeft = SKI_TOTAL_END_X;
SrcRect.iTop = SKI_TOTAL_BEGIN_Y;
SrcRect.iRight = SCREEN_WIDTH;
SrcRect.iBottom = SKI_TOTAL_END_Y;
BltVSurfaceUsingDD( hDestVSurface, hSrcVSurface, VO_BLT_SRCTRANSPARENCY, SrcRect.iLeft, SrcRect.iTop, (RECT*)&SrcRect );
}
void GetShopKeeperInterfaceUserInput()
{
InputAtom Event;
POINT MousePos;
GetCursorPos(&MousePos);
ScreenToClient(ghWindow, &MousePos); // In window coords!
while (DequeueSpecificEvent(&Event, KEY_DOWN|KEY_UP|KEY_REPEAT))
{
if( !HandleTextInput( &Event ) && Event.usEvent == KEY_DOWN )
{
switch( Event.usParam )
{
case ESC:
// clean exits - does quotes, shuts up shopkeeper, etc.
ExitSKIRequested();
#ifdef JA2TESTVERSION
//Instant exit - doesn't clean up much
// gfSKIScreenExit = TRUE;
#endif
break;
case 'x':
if( Event.usKeyState & ALT_DOWN )
{
HandleShortCutExitState( );
}
break;
case SPACE:
{
DeleteItemDescriptionBox( );
// skip Robot and EPCs
SoldierID ubID = FindNextActiveAndAliveMerc( gpSMCurrentMerc, FALSE, TRUE );
gubSelectSMPanelToMerc = ubID;
LocateSoldier( ubID, DONTSETLOCATOR );
// refresh background for player slots (in case item values change due to Flo's discount)
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
}
break;
#ifdef JA2TESTVERSION
case 'r':
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
break;
case 'i':
InvalidateRegion( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
break;
case 'd':
//Test key to toggle the display of the money each dealer has on hand
gfTestDisplayDealerCash ^= 1;
break;
#endif
}
}
}
}
#ifdef JA2TESTVERSION
void DisplayAllDealersCash()
{
UINT16 usPosY=0;
CHAR16 zTemp[512];
CHAR16 zTemp2[64];
UINT8 ubForeColor;
//loop through all the shopkeeper's and display their money
for ( INT8 bArmsDealer = 0; bArmsDealer<NUM_ARMS_DEALERS; ++bArmsDealer )
{
//Display the shopkeeper's name
DrawTextToScreen( gMercProfiles[ armsDealerInfo[ bArmsDealer ].ubShopKeeperID ].zNickname, SCREEN_X_OFFSET + 540, SCREEN_Y_OFFSET + usPosY, 0, FONT10ARIAL, SKI_TITLE_COLOR, FONT_MCOLOR_BLACK, TRUE, LEFT_JUSTIFIED );
//Display the arms dealer cash on hand
swprintf( zTemp, L"%s", FormatMoney(gArmsDealerStatus[ bArmsDealer ].uiArmsDealersCash ).data());
if ( armsDealerInfo[gbSelectedArmsDealerID].uiFlags & ARMS_DEALER_DEALWITHINTEL )
swprintf( zTemp2, L"%s Intel", zTemp );
else
swprintf( zTemp2, L"%s", zTemp );
ubForeColor = ( UINT8 ) ( ( bArmsDealer == gbSelectedArmsDealerID ) ? SKI_BUTTON_COLOR : SKI_TITLE_COLOR );
DrawTextToScreen( zTemp2, SCREEN_X_OFFSET + 590, SCREEN_Y_OFFSET + usPosY, 0, FONT10ARIAL, ubForeColor, FONT_MCOLOR_BLACK, TRUE, LEFT_JUSTIFIED );
usPosY += 17;
}
}
#endif
void BtnSKI_InvPageUpButtonCallback(GUI_BUTTON *btn,INT32 reason)
{
if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN )
{
btn->uiFlags |= BUTTON_CLICKED_ON;
InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY);
}
if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
{
btn->uiFlags &= (~BUTTON_CLICKED_ON );
if( gSelectArmsDealerInfo.ubCurrentPage > 1 )
{
gSelectArmsDealerInfo.ubCurrentPage--;
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
EnableDisableDealersInventoryPageButtons();
}
InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY);
}
}
void BtnSKI_InvPageDownButtonCallback(GUI_BUTTON *btn,INT32 reason)
{
if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN )
{
btn->uiFlags |= BUTTON_CLICKED_ON;
InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY);
}
if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
{
btn->uiFlags &= (~BUTTON_CLICKED_ON );
if( gSelectArmsDealerInfo.ubCurrentPage < gSelectArmsDealerInfo.ubNumberOfPages )
{
gSelectArmsDealerInfo.ubCurrentPage++;
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
EnableDisableDealersInventoryPageButtons();
}
InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY);
}
}
void BtnSKI_TransactionButtonCallback(GUI_BUTTON *btn,INT32 reason)
{
if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN )
{
btn->uiFlags |= BUTTON_CLICKED_ON;
InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY);
}
if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
{
btn->uiFlags &= (~BUTTON_CLICKED_ON );
InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY);
// if not already doing it (multiple clicks can be queued up while shopkeeper is still talking)
if ( !gfPerformTransactionInProgress )
{
// shut him up
ShutUpShopKeeper();
giShopKeepDialogueEventinProgress = -1;
PerformTransaction( 0 );
}
}
}
void BtnSKI_DoneButtonCallback(GUI_BUTTON *btn,INT32 reason)
{
if(reason & MSYS_CALLBACK_REASON_LBUTTON_DWN )
{
btn->uiFlags |= BUTTON_CLICKED_ON;
}
if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
{
btn->uiFlags &= (~BUTTON_CLICKED_ON );
ExitSKIRequested();
}
InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY);
}
void CreateSkiInventorySlotMouseRegions( )
{
int ubCnt, x,y;
UINT16 usPosX, usPosY;
// Create the mouse regions for the shopkeeper's inventory
usPosY = SKI_TRADER_INVENTORY_BOXES_BEGIN_Y;
ubCnt=0;
for ( y = 0; y<SKI_TRADER_INVENTORY_BOXES_PER_COL; ++y )
{
usPosX = SKI_TRADER_INVENTORY_BOXES_BEGIN_X;
for ( x = 0; x<SKI_TRADER_INVENTORY_BOXES_PER_ROW; ++x )
{
MSYS_DefineRegion( &gDealersInventoryMouseRegions[ ubCnt ], usPosX, usPosY, (INT16)(usPosX + SKI_INV_SLOT_WIDTH), (INT16)(usPosY + SKI_INV_SLOT_HEIGHT), MSYS_PRIORITY_HIGH,
CURSOR_NORMAL, SelectDealersInventoryMovementRegionCallBack, SelectDealersInventoryRegionCallBack );
MSYS_AddRegion( &gDealersInventoryMouseRegions[ubCnt] );
MSYS_SetRegionUserData( &gDealersInventoryMouseRegions[ ubCnt ], 0, ubCnt);
MSYS_SetRegionUserData( &gDealersInventoryMouseRegions[ ubCnt ], 1, ARMS_DEALER_INVENTORY );
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
//Small Faces
MSYS_DefineRegion( &gRepairmanInventorySmallFaceMouseRegions[ ubCnt ], (UINT16)(usPosX+SKI_SMALL_FACE_OFFSET_X), (UINT16)(usPosY), (INT16)(usPosX + SKI_SMALL_FACE_OFFSET_X + SKI_SMALL_FACE_WIDTH), (INT16)(usPosY+ SKI_SMALL_FACE_HEIGHT), MSYS_PRIORITY_HIGH+1,
CURSOR_NORMAL, NULL, NULL );
MSYS_AddRegion( &gRepairmanInventorySmallFaceMouseRegions[ ubCnt ] );
MSYS_SetRegionUserData( &gRepairmanInventorySmallFaceMouseRegions[ ubCnt ], 0, ubCnt );
MSYS_SetRegionUserData( &gRepairmanInventorySmallFaceMouseRegions[ ubCnt ], 1, ARMS_DEALER_INVENTORY );
}
usPosX += SKI_INV_OFFSET_X;
++ubCnt;
++gDealersInventoryActiveRegions;
}
usPosY += SKI_INV_OFFSET_Y;
}
// Create the mouse regions for the shopkeeper's trading slots
usPosY = SKI_TRADER_OFFER_BOXES_BEGIN_Y;
ubCnt=0;
for ( y = 0; y<SKI_TRADER_OFFER_BOXES_PER_COL; ++y )
{
usPosX = SKI_TRADER_OFFER_BOXES_BEGIN_X;
for ( x = 0; x<SKI_TRADER_OFFER_BOXES_PER_ROW; ++x )
{
MSYS_DefineRegion( &gDealersOfferSlotsMouseRegions[ ubCnt ], usPosX, usPosY, (INT16)(usPosX + SKI_INV_SLOT_WIDTH), (INT16)(usPosY + SKI_INV_SLOT_HEIGHT), MSYS_PRIORITY_HIGH,
CURSOR_NORMAL, SelectDealersOfferSlotsMovementRegionCallBack, SelectDealersOfferSlotsRegionCallBack );
MSYS_AddRegion( &gDealersOfferSlotsMouseRegions[ubCnt] );
MSYS_SetRegionUserData( &gDealersOfferSlotsMouseRegions[ ubCnt ], 0, ubCnt);
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
//Small Faces
MSYS_DefineRegion( &gDealersOfferSlotsSmallFaceMouseRegions[ ubCnt ], (UINT16)(usPosX+SKI_SMALL_FACE_OFFSET_X), (UINT16)(usPosY), (INT16)(usPosX + SKI_SMALL_FACE_OFFSET_X + SKI_SMALL_FACE_WIDTH), (INT16)(usPosY+ SKI_SMALL_FACE_HEIGHT), MSYS_PRIORITY_HIGH+1,
CURSOR_NORMAL, SelectDealersOfferSlotsMovementRegionCallBack, SelectDealersOfferSlotsRegionCallBack );
MSYS_AddRegion( &gDealersOfferSlotsSmallFaceMouseRegions[ubCnt] );
MSYS_SetRegionUserData( &gDealersOfferSlotsSmallFaceMouseRegions[ ubCnt ], 0, ubCnt);
MSYS_SetRegionUserData( &gDealersOfferSlotsSmallFaceMouseRegions[ ubCnt ], 1, ARMS_DEALER_OFFER_AREA );
}
usPosX += SKI_INV_OFFSET_X;
++ubCnt;
++gDealersOfferActiveRegions;
}
usPosY += SKI_INV_OFFSET_Y;
}
// Create the mouse regions for the Players trading slots
usPosY = SKI_PLAYER_OFFER_BOXES_BEGIN_Y;
ubCnt=0;
for ( y = 0; y<SKI_PLAYER_OFFER_BOXES_PER_COL; ++y )
{
usPosX = SKI_PLAYER_OFFER_BOXES_BEGIN_X;
for ( x = 0; x<SKI_PLAYER_OFFER_BOXES_PER_ROW; ++x )
{
//Trading Slots
MSYS_DefineRegion( &gPlayersOfferSlotsMouseRegions[ ubCnt ], usPosX, usPosY, (INT16)(usPosX + SKI_INV_SLOT_WIDTH), (INT16)(usPosY + SKI_INV_SLOT_HEIGHT), MSYS_PRIORITY_HIGH,
CURSOR_NORMAL, SelectPlayersOfferSlotsMovementRegionCallBack, SelectPlayersOfferSlotsRegionCallBack );
MSYS_AddRegion( &gPlayersOfferSlotsMouseRegions[ubCnt] );
MSYS_SetRegionUserData( &gPlayersOfferSlotsMouseRegions[ ubCnt ], 0, ubCnt);
//Small Faces
MSYS_DefineRegion( &gPlayersOfferSlotsSmallFaceMouseRegions[ ubCnt ], (UINT16)(usPosX+SKI_SMALL_FACE_OFFSET_X), (UINT16)(usPosY), (INT16)(usPosX + SKI_SMALL_FACE_OFFSET_X + SKI_SMALL_FACE_WIDTH), (INT16)(usPosY+ SKI_SMALL_FACE_HEIGHT), MSYS_PRIORITY_HIGH+1,
CURSOR_NORMAL, SelectPlayersOfferSlotsMovementRegionCallBack, SelectPlayersOfferSlotsRegionCallBack );
MSYS_AddRegion( &gPlayersOfferSlotsSmallFaceMouseRegions[ubCnt] );
MSYS_SetRegionUserData( &gPlayersOfferSlotsSmallFaceMouseRegions[ ubCnt ], 0, ubCnt);
MSYS_SetRegionUserData( &gPlayersOfferSlotsSmallFaceMouseRegions[ ubCnt ], 1, PLAYERS_OFFER_AREA );
usPosX += SKI_INV_OFFSET_X;
++ubCnt;
++gPlayersOfferActiveRegions;
}
usPosY += SKI_INV_OFFSET_Y;
}
}
void DestroySkiInventorySlotMouseRegions( )
{
for ( int i = 0; i<gDealersInventoryActiveRegions; ++i )
{
MSYS_RemoveRegion( &gDealersInventoryMouseRegions[ i ] );
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
MSYS_RemoveRegion( &gRepairmanInventorySmallFaceMouseRegions[ i ] );
}
}
gDealersInventoryActiveRegions = 0;
for ( int i = 0; i<gDealersOfferActiveRegions; ++i )
{
MSYS_RemoveRegion( &gDealersOfferSlotsMouseRegions[ i ] );
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
MSYS_RemoveRegion( &gDealersOfferSlotsSmallFaceMouseRegions[ i ] );
}
}
gDealersOfferActiveRegions = 0;
for ( int i = 0; i<gPlayersOfferActiveRegions; ++i )
{
MSYS_RemoveRegion( &gPlayersOfferSlotsMouseRegions[i] );
MSYS_RemoveRegion( &gPlayersOfferSlotsSmallFaceMouseRegions[i] );
}
gPlayersOfferActiveRegions = 0;
}
//Mouse Call back for the Arms traders inventory slot
void SelectDealersInventoryRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason )
{
if (iReason & MSYS_CALLBACK_REASON_INIT)
{
}
else if(iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
{
UINT16 ubSelectedInvSlot = MSYS_GetRegionUserData( pRegion, 0 );
INT16 ubLocation;
if( gpTempDealersInventory.empty() == true )
return;
ubSelectedInvSlot += gSelectArmsDealerInfo.ubFirstItemIndexOnPage;
//if the selected slot is above any inventory we have, return
if( ubSelectedInvSlot >= gpTempDealersInventory.size() )
return;
//if there are any items still there
if( gpTempDealersInventory[ ubSelectedInvSlot ].ItemObject.exists() == true )
{
//If the item type has not already been placed
if( !( gpTempDealersInventory[ ubSelectedInvSlot ].uiFlags & ARMS_INV_ITEM_SELECTED ) )
{
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
// ignore left clicks on items under repair. Fully repaired items are moved out to player's slots automatically
}
else // not a repairman
{
ubLocation = AddItemToArmsDealerOfferArea( &gpTempDealersInventory[ ubSelectedInvSlot ], ubSelectedInvSlot );
//if the item was added to the Dealer Offer Area correctly
if( ubLocation != -1 )
{
//Set the flag indicating the item has been selected
gpTempDealersInventory[ ubSelectedInvSlot ].uiFlags |= ARMS_INV_ITEM_SELECTED;
//Specify the location the items went to
gpTempDealersInventory[ ubSelectedInvSlot ].ubLocationOfObject = ARMS_DEALER_OFFER_AREA;
gpTempDealersInventory[ ubSelectedInvSlot ].bSlotIdInOtherLocation = ubLocation;
//if the shift key is being pressed, remove them all
if( gfKeyState[ SHIFT ] )
{
DeleteObj(&gpTempDealersInventory[ ubSelectedInvSlot ].ItemObject);
}
else
{
gpTempDealersInventory[ ubSelectedInvSlot ].ItemObject.RemoveObjectsFromStack(1);
}
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
}
}
}
else // some of this item are already in dealer's offer area
{
UINT8 ubNumToMove;
//if the shift key is being pressed, remove them all
if( gfKeyState[ SHIFT ] )
{
ubNumToMove = gpTempDealersInventory[ ubSelectedInvSlot ].ItemObject.ubNumberOfObjects;
}
else
{
ubNumToMove = 1;
}
//Reduce the number in dealer's inventory and Increase the number in dealer's offer area
ArmsDealerOfferArea[ gpTempDealersInventory[ ubSelectedInvSlot ].bSlotIdInOtherLocation ].ItemObject.ForceAddObjectsToStack(
gpTempDealersInventory[ ubSelectedInvSlot ].ItemObject, ubNumToMove);
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
}
}
}
else if (iReason & MSYS_CALLBACK_REASON_RBUTTON_UP)
{
UINT16 ubSelectedInvSlot = MSYS_GetRegionUserData( pRegion, 0 );
if( gpTempDealersInventory.empty() == true )
return;
ubSelectedInvSlot += gSelectArmsDealerInfo.ubFirstItemIndexOnPage;
//if the selected slot is above any inventory we have, return
if( ubSelectedInvSlot >= gpTempDealersInventory.size() )
return;
if(gpTempDealersInventory[ ubSelectedInvSlot ].ItemObject.exists() == false)
return;
//DEF:
//bring up the item description box
if ( !InItemDescriptionBox( ) )
{
InitShopKeeperItemDescBox( &gpTempDealersInventory[ ubSelectedInvSlot ].ItemObject, ubSelectedInvSlot, ARMS_DEALER_INVENTORY );
}
else
{
DeleteItemDescriptionBox( );
}
//if the item has been seleceted
if( gpTempDealersInventory[ ubSelectedInvSlot ].uiFlags & ARMS_INV_ITEM_SELECTED )
{
//Check to see it there is more then 1 item in the location
if( ArmsDealerOfferArea[ gpTempDealersInventory[ ubSelectedInvSlot ].bSlotIdInOtherLocation ].ItemObject.exists() == true )
{
//Decrease the number in the dealer offer area
ArmsDealerOfferArea[ gpTempDealersInventory[ ubSelectedInvSlot ].bSlotIdInOtherLocation ].ItemObject.MoveThisObjectTo(gTempObject, 1);
//Increase the number in the dealer inventory
gpTempDealersInventory[ ubSelectedInvSlot ].ItemObject.AddObjectsToStack(gTempObject);
//if there is nothing left in the arms dealer offer area
if( ArmsDealerOfferArea[ gpTempDealersInventory[ ubSelectedInvSlot ].bSlotIdInOtherLocation ].ItemObject.exists() == false )
{
RemoveItemFromArmsDealerOfferArea( gpTempDealersInventory[ ubSelectedInvSlot ].bSlotIdInOtherLocation, FALSE );
}
//redraw the screen
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
}
}
}
}
void SelectDealersInventoryMovementRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason )
{
UINT16 ubSelectedInvSlot = MSYS_GetRegionUserData( pRegion, 0 );
ubSelectedInvSlot += gSelectArmsDealerInfo.ubFirstItemIndexOnPage;
if( gpTempDealersInventory.empty() == true )
return;
if( ubSelectedInvSlot >= gpTempDealersInventory.size() )
return;
if (iReason & MSYS_CALLBACK_REASON_INIT)
{
}
else if(iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE )
{
//if there is nothing in the slot, exit
if( gpTempDealersInventory[ ubSelectedInvSlot ].fActive == FALSE )
return;
gpHighLightedItemObject = &gpTempDealersInventory[ ubSelectedInvSlot ].ItemObject;
HandleCompatibleAmmoUI( gpSMCurrentMerc, -1, TRUE );
}
else if(iReason & MSYS_CALLBACK_REASON_LOST_MOUSE )
{
//if there is nothing in the slot, exit
if( gpTempDealersInventory[ ubSelectedInvSlot ].fActive == FALSE )
return;
gpHighLightedItemObject = NULL;
gubSkiDirtyLevel = SKI_DIRTY_LEVEL1; //ddd
HandleCompatibleAmmoUI( gpSMCurrentMerc, -1, FALSE );
}
}
void SelectDealersOfferSlotsMovementRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason )
{
UINT16 ubSelectedInvSlot = MSYS_GetRegionUserData( pRegion, 0 );
if (iReason & MSYS_CALLBACK_REASON_INIT)
{
}
else if(iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE )
{
//if there is nothing in the slot, exit
if( ArmsDealerOfferArea[ ubSelectedInvSlot ].fActive == FALSE )
return;
gpHighLightedItemObject = &ArmsDealerOfferArea[ ubSelectedInvSlot ].ItemObject;
HandleCompatibleAmmoUI( gpSMCurrentMerc, -1, TRUE );
}
else if(iReason & MSYS_CALLBACK_REASON_LOST_MOUSE )
{
//if there is nothing in the slot, exit
if( ArmsDealerOfferArea[ ubSelectedInvSlot ].fActive == FALSE )
return;
gpHighLightedItemObject = NULL;
gubSkiDirtyLevel = SKI_DIRTY_LEVEL1;//ddd
HandleCompatibleAmmoUI( gpSMCurrentMerc, -1, FALSE );
}
}
void SelectPlayersOfferSlotsMovementRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason )
{
UINT16 ubSelectedInvSlot = MSYS_GetRegionUserData( pRegion, 0 );
if (iReason & MSYS_CALLBACK_REASON_INIT)
{
}
else if(iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE )
{
//if there is nothing in the slot, exit
if( PlayersOfferArea[ ubSelectedInvSlot ].fActive == FALSE )
return;
gpHighLightedItemObject = &PlayersOfferArea[ ubSelectedInvSlot ].ItemObject;
HandleCompatibleAmmoUI( gpSMCurrentMerc, -1, TRUE );
}
else if(iReason & MSYS_CALLBACK_REASON_LOST_MOUSE )
{
//if there is nothing in the slot, exit
if( PlayersOfferArea[ ubSelectedInvSlot ].fActive == FALSE )
return;
gpHighLightedItemObject = NULL;
gubSkiDirtyLevel = SKI_DIRTY_LEVEL1;//ddd
HandleCompatibleAmmoUI( gpSMCurrentMerc, -1, FALSE );
}
}
//Mouse Call back for the dealer's OFFER slot
void SelectDealersOfferSlotsRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason )
{
UINT16 ubSelectedInvSlot = MSYS_GetRegionUserData( pRegion, 0 );
if (iReason & MSYS_CALLBACK_REASON_INIT)
{
}
else if( iReason & MSYS_CALLBACK_REASON_RBUTTON_UP )
{
//if there is something here
if( ArmsDealerOfferArea[ ubSelectedInvSlot ].fActive )
{
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
// return item to player
RemoveRepairItemFromDealersOfferArea( ubSelectedInvSlot );
}
else
{
//bring up the item description box
if ( !InItemDescriptionBox( ) )
{
InitShopKeeperItemDescBox( &ArmsDealerOfferArea[ ubSelectedInvSlot ].ItemObject, ubSelectedInvSlot, ARMS_DEALER_OFFER_AREA );
}
else
{
DeleteItemDescriptionBox( );
}
}
}
}
else if( iReason & MSYS_CALLBACK_REASON_LBUTTON_UP ) //MSYS_CALLBACK_REASON_LBUTTON_UP)
{
/*
//if the current merc is disabled for whatever reason
if( gfSMDisableForItems )
// the
return;
*/
//if there is something here
if( ArmsDealerOfferArea[ ubSelectedInvSlot ].fActive )
{
//if this is a repair dealer
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
//if we don't have an item, pick one up
if( gMoveingItem.sItemIndex == 0 )
{
//if the dealer is a repair dealer, allow the player to pick up the item
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
BeginSkiItemPointer( ARMS_DEALER_OFFER_AREA, ubSelectedInvSlot, FALSE );
}
}
else
{
//swap what is in the cursor with what is in the player offer slot
INVENTORY_IN_SLOT TempSlot;
// if the slot is overloaded (holds more objects than we have room for valid statuses of)
if ( ArmsDealerOfferArea[ ubSelectedInvSlot ].ItemObject.ubNumberOfObjects > MAX_OBJECTS_PER_SLOT )
{
// then ignore the click - we can't do the swap, or anything very useful, because we can't allow overloaded
// items into the player's cursor - if he put them into a merc's inventory, the extra statuses are missing!
// At best, we could do some sort of message here.
return;
}
IfMercOwnedCopyItemToMercInv( &gMoveingItem );
TempSlot = ArmsDealerOfferArea[ ubSelectedInvSlot ];
ArmsDealerOfferArea[ ubSelectedInvSlot ] = gMoveingItem;
gMoveingItem = TempSlot;
IfMercOwnedRemoveItemFromMercInv( &gMoveingItem );
// Change mouse cursor
gpItemPointer = &gMoveingItem.ItemObject;
SetSkiCursor( EXTERN_CURSOR );
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
}
}
else //non-repairman
{
//if the shift key is being pressed, remove them all
if( gfKeyState[ SHIFT ] || ( ArmsDealerOfferArea[ ubSelectedInvSlot ].ItemObject.ubNumberOfObjects == 1 ) )
{
RemoveItemFromArmsDealerOfferArea( ubSelectedInvSlot, TRUE );//ArmsDealerOfferArea[ ubSelectedInvSlot ].bSlotIdInOtherLocation
}
else // multiple items there, SHIFT isn't being pressed
{
// remove only one at a time
gpTempDealersInventory[ ArmsDealerOfferArea[ ubSelectedInvSlot ].bSlotIdInOtherLocation ].ItemObject.ForceAddObjectsToStack(ArmsDealerOfferArea[ ubSelectedInvSlot ].ItemObject, 1);
}
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
}
}
else // empty slot
{
//if the cursor has something in it
if( gMoveingItem.sItemIndex > 0 )
{
// we'd better talking to a repairman, cursor is locked out of this area while full for non-repairmen!
Assert( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS );
//Drop the item into the current slot
AddInventoryToSkiLocation( &gMoveingItem, ubSelectedInvSlot, ARMS_DEALER_OFFER_AREA );
//Reset the cursor
SetSkiCursor( CURSOR_NORMAL );
//refresh the screen
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
}
}
}
}
//Mouse Call back for the Players OFFER slot
void SelectPlayersOfferSlotsRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason )
{
UINT16 ubSelectedInvSlot = MSYS_GetRegionUserData( pRegion, 0 );
INT16 bAddedToSlotID = -1;
if (iReason & MSYS_CALLBACK_REASON_INIT)
{
}
else if( iReason & MSYS_CALLBACK_REASON_LBUTTON_UP )
{
//if the cursor has no item in it
if( gMoveingItem.sItemIndex == 0 )
{
//if there is nothing here, return
if( PlayersOfferArea[ ubSelectedInvSlot ].fActive == FALSE )
return;
// pick it up into the cursor
BeginSkiItemPointer( PLAYERS_OFFER_AREA, ubSelectedInvSlot, FALSE );
}
else // we have something in the cursor
{
//Drop the item into the current slot
// AddObjectToSkiLocation( &gpSkiItemPointer, ubSelectedInvSlot, PLAYERS_OFFER_AREA );
//if there is something already there
if( PlayersOfferArea[ ubSelectedInvSlot ].fActive )
{
//swap what is in the cursor with what is in the player offer slot
INVENTORY_IN_SLOT TempSlot;
// if the slot is overloaded (holds more objects than we have room for valid statuses of)
if ( PlayersOfferArea[ ubSelectedInvSlot ].ItemObject.ubNumberOfObjects > MAX_OBJECTS_PER_SLOT )
{
// then ignore the click - we can't do the swap, or anything very useful, because we can't allow overloaded
// items into the player's cursor - if he put them into a merc's inventory, the extra statuses are missing!
// At best, we could do some sort of message here.
return;
}
IfMercOwnedCopyItemToMercInv( &gMoveingItem );
TempSlot = PlayersOfferArea[ ubSelectedInvSlot ];
PlayersOfferArea[ ubSelectedInvSlot ] = gMoveingItem;
gMoveingItem = TempSlot;
IfMercOwnedRemoveItemFromMercInv( &gMoveingItem );
// Change mouse cursor
gpItemPointer = &gMoveingItem.ItemObject;
SetSkiCursor( EXTERN_CURSOR );
//if the item we are adding is money
if( Item[ PlayersOfferArea[ ubSelectedInvSlot ].sItemIndex ].usItemClass == IC_MONEY && !( armsDealerInfo[gbSelectedArmsDealerID].uiFlags & ARMS_DEALER_DEALWITHINTEL ) )
{
//Since money is always evaluated
PlayersOfferArea[ ubSelectedInvSlot ].uiFlags |= ARMS_INV_PLAYERS_ITEM_HAS_VALUE;
PlayersOfferArea[ ubSelectedInvSlot ].uiItemPrice = PlayersOfferArea[ ubSelectedInvSlot ].ItemObject[0]->data.money.uiMoneyAmount;
}
}
else // slot is empty
{
// if the item has already been evaluated, or has just been purchased
if ( ( gMoveingItem.uiFlags & ARMS_INV_PLAYERS_ITEM_HAS_BEEN_EVALUATED ) ||
( gMoveingItem.uiFlags & ARMS_INV_JUST_PURCHASED ) )
{
//place the item that is in the player's hand into this player offer area slot
bAddedToSlotID = AddInventoryToSkiLocation( &gMoveingItem, ubSelectedInvSlot, PLAYERS_OFFER_AREA );
Assert ( bAddedToSlotID != -1 );
}
else
{
// this splits complex items for repairs. Also puts things into the first free POA slot
OfferObjectToDealer( &gMoveingItem.ItemObject, gMoveingItem.ubIdOfMercWhoOwnsTheItem, gMoveingItem.bSlotIdInOtherLocation );
}
//Reset the cursor
SetSkiCursor( CURSOR_NORMAL );
}
//Dirty the interface
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
}
}
else if(iReason & MSYS_CALLBACK_REASON_RBUTTON_UP ) //MSYS_CALLBACK_REASON_LBUTTON_UP)
{
//if the box is active
if( PlayersOfferArea[ ubSelectedInvSlot ].fActive )
{
RemoveItemFromPlayersOfferArea( ubSelectedInvSlot );
/*
item description
else
{
if ( !InItemDescriptionBox( ) )
{
InitItemDescriptionBox( gpSMCurrentMerc, (UINT8)PlayersOfferArea[ ubSelectedInvSlot ].bSlotIdInOtherLocation, 214, 1 + INV_INTERFACE_START_Y, 0 );
}
else
{
DeleteItemDescriptionBox( );
}
}
*/
}
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
}
}
void SelectSkiInventoryMovementAreaRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason )
{
if (iReason & MSYS_CALLBACK_REASON_INIT)
{
}
else if( iReason & MSYS_CALLBACK_REASON_LOST_MOUSE )
{
}
}
void EnterShopKeeperInterfaceScreen( UINT8 ubArmsDealer )
{
gusIDOfCivTrader = NOBODY;
//Get Dealer ID from from merc Id
gbSelectedArmsDealerID = GetArmsDealerIDFromMercID( ubArmsDealer );
if( gbSelectedArmsDealerID <= -1 )
{
#ifdef JA2BETAVERSION
ScreenMsg( FONT_MCOLOR_WHITE, MSG_BETAVERSION, L"Failed to find Arms Dealer ID From Merc ID #%d", ubArmsDealer );
#endif
gfSKIScreenExit = TRUE;
}
LeaveTacticalScreen( SHOPKEEPER_SCREEN );
}
// Flugente: set up shopkeeper with a non-NPC
void EnterShopKeeperInterfaceScreen_NonNPC( INT8 ubArmsDealerID, SoldierID aMercID )
{
gusIDOfCivTrader = aMercID;
//Get Dealer ID from from merc Id
gbSelectedArmsDealerID = ubArmsDealerID;
if ( gbSelectedArmsDealerID <= -1 )
{
#ifdef JA2BETAVERSION
ScreenMsg( FONT_MCOLOR_WHITE, MSG_BETAVERSION, L"Failed to find Arms Dealer ID From Merc ID #%d", ubArmsDealerID );
#endif
gfSKIScreenExit = TRUE;
}
// Flugente: additional dialogue
if ( gusSelectedSoldier != NOBODY )
AdditionalTacticalCharacterDialogue_CallsLua( gusSelectedSoldier, ADE_MERCHANT_CHAT, ubArmsDealerID >= 0 ? (UINT32)ubArmsDealerID : 0 );
LeaveTacticalScreen( SHOPKEEPER_SCREEN );
}
void InitializeShopKeeper( BOOLEAN fResetPage )
{
// update time player last dealt with him
gArmsDealerStatus[ gbSelectedArmsDealerID ].uiTimePlayerLastInSKI = GetWorldTotalMin();
gpTempDealersInventory.clear();
HandlePossibleArmsDealerIntelRefresh(FALSE);
//Get the number of distinct items in the inventory
//Create the shopkeeper's temp inventory
DetermineArmsDealersSellingInventory( );
gSelectArmsDealerInfo.ubNumberOfPages = (UINT8)(gpTempDealersInventory.size( ) / (SKI_TRADER_INVENTORY_BOXES_PER_ROW * SKI_TRADER_INVENTORY_BOXES_PER_COL));
if( gpTempDealersInventory.size() % 15 != 0 )
gSelectArmsDealerInfo.ubNumberOfPages += 1;
//Should we reset the current inventory page being displayed
if( gpTempDealersInventory.empty() == true )
gSelectArmsDealerInfo.ubCurrentPage = 0;
else
{
if( fResetPage )
{
if( gpTempDealersInventory.empty() == true )
gSelectArmsDealerInfo.ubCurrentPage = 0;
else
gSelectArmsDealerInfo.ubCurrentPage = 1;
}
//or if the current page will be an invalid page ( before the first, and after the last )
else if( gSelectArmsDealerInfo.ubCurrentPage < 0 || gSelectArmsDealerInfo.ubCurrentPage > gSelectArmsDealerInfo.ubNumberOfPages )
{
gSelectArmsDealerInfo.ubCurrentPage = 1;
}
else if( gpTempDealersInventory.empty() == false )
gSelectArmsDealerInfo.ubCurrentPage = 1;
}
//if there is no inventory
if( gpTempDealersInventory.empty() == true )
{
gSelectArmsDealerInfo.ubCurrentPage = 0;
gSelectArmsDealerInfo.ubNumberOfPages = 0;
//disable the page up/down buttons
DisableButton( guiSKI_InvPageUpButton );
DisableButton( guiSKI_InvPageDownButton );
return;
}
else
{
//enable the page up/down buttons
EnableButton( guiSKI_InvPageUpButton );
EnableButton( guiSKI_InvPageDownButton );
}
EnableDisableDealersInventoryPageButtons();
}
void CalculateFirstItemIndexOnPage( )
{
gSelectArmsDealerInfo.ubFirstItemIndexOnPage = (gSelectArmsDealerInfo.ubCurrentPage - 1) * (SKI_TRADER_INVENTORY_BOXES_PER_ROW * SKI_TRADER_INVENTORY_BOXES_PER_COL);
}
void DisplayArmsDealerCurrentInventoryPage( )
{
CHAR16 zTemp[32];
UINT16 uiFontHeight;
UINT16 usCnt=0;
UINT16 usPosX, usPosY;
int sItemCount=0;
BOOLEAN fDisplayHatchOnItem=FALSE;
usPosX = SKI_TRADER_INVENTORY_BOXES_BEGIN_X;
usPosY = SKI_TRADER_INVENTORY_BOXES_BEGIN_Y;
//if there is any inventory
if( gpTempDealersInventory.empty() == false )
{
if( gubSkiDirtyLevel != SKI_DIRTY_LEVEL0 )
{
//Calculate the item that is at the top of the list
CalculateFirstItemIndexOnPage( );
//Display the current inventory page
DrawTextToScreen( SKI_Text[ SKI_TEXT_PAGE ], SKI_PAGE_X, (UINT16)(SKI_PAGE_Y+3), SKI_PAGE_WIDTH, SKI_LABEL_FONT, SKI_TITLE_COLOR, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED );
//Display the Current Page number
uiFontHeight = GetFontHeight( SKI_LABEL_FONT );
swprintf( zTemp, L"%d/%d", gSelectArmsDealerInfo.ubCurrentPage, gSelectArmsDealerInfo.ubNumberOfPages );
DrawTextToScreen( zTemp, SKI_PAGE_X, (UINT16)(SKI_PAGE_Y+uiFontHeight+6), SKI_PAGE_WIDTH, SKI_LABEL_FONT, SKI_TITLE_COLOR, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED );
}
//Display all the items for the current page
for ( usCnt = gSelectArmsDealerInfo.ubFirstItemIndexOnPage; ((usCnt<gpTempDealersInventory.size( )) && (sItemCount < gDealersInventoryActiveRegions)); ++usCnt )
{
//if the item is still in the inventory
if( gpTempDealersInventory[ usCnt ].sItemIndex != 0 )
{
// hatch it out if region is disabled
if ( !( gDealersInventoryMouseRegions[ sItemCount ].uiFlags & MSYS_REGION_ENABLED ) )
{
fDisplayHatchOnItem = TRUE;
}
//if the dealer repairs
else if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
//if the item is damaged
if( armsDealerInfo[ gbSelectedArmsDealerID ].uiFlags & ARMS_INV_ITEM_NOT_REPAIRED_YET )
fDisplayHatchOnItem = TRUE;
else
fDisplayHatchOnItem = FALSE;
}
else // non-repairman
{
// check if none left
if ( gpTempDealersInventory[ usCnt ].ItemObject.exists() == false )
{
fDisplayHatchOnItem = TRUE;
}
else
{
fDisplayHatchOnItem = FALSE;
}
}
// Display the inventory slot
DisplayInvSlot( usCnt, gpTempDealersInventory[ usCnt ].sItemIndex, usPosX, usPosY,
&gpTempDealersInventory[ usCnt ].ItemObject,
fDisplayHatchOnItem,
ARMS_DEALER_INVENTORY );
if( gubSkiDirtyLevel == SKI_DIRTY_LEVEL2 )
{
SetSkiRegionHelpText( &gpTempDealersInventory[ usCnt ], &gDealersInventoryMouseRegions[sItemCount], ARMS_DEALER_INVENTORY );
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
SetSkiFaceRegionHelpText( &gpTempDealersInventory[ usCnt ], &gRepairmanInventorySmallFaceMouseRegions[sItemCount], ARMS_DEALER_INVENTORY );
}
}
++sItemCount;
usPosX += SKI_INV_OFFSET_X;
//if we are on to the next row
if ( !(sItemCount % SKI_TRADER_INVENTORY_BOXES_PER_ROW) )
{
usPosX = SKI_TRADER_INVENTORY_BOXES_BEGIN_X;
usPosY += SKI_INV_OFFSET_Y;
}
}
}
}
if( gubSkiDirtyLevel == SKI_DIRTY_LEVEL2 )
{
//This handles the remaining (empty) slots, resetting Fast Help text, and hatching out disabled ones
while ( sItemCount < gDealersInventoryActiveRegions )
{
SetSkiRegionHelpText( NULL, &gDealersInventoryMouseRegions[ sItemCount ], ARMS_DEALER_INVENTORY );
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
SetSkiFaceRegionHelpText( NULL, &gRepairmanInventorySmallFaceMouseRegions[ sItemCount ], ARMS_DEALER_INVENTORY );
}
// hatch it out if region is disabled
if ( !( gDealersInventoryMouseRegions[ sItemCount ].uiFlags & MSYS_REGION_ENABLED ) )
{
HatchOutInvSlot( usPosX, usPosY );
}
++sItemCount;
usPosX += SKI_INV_OFFSET_X;
//if we are on to the next row
if ( !(sItemCount % SKI_TRADER_INVENTORY_BOXES_PER_ROW) )
{
usPosX = SKI_TRADER_INVENTORY_BOXES_BEGIN_X;
usPosY += SKI_INV_OFFSET_Y;
}
}
}
}
UINT32 DisplayInvSlot( UINT16 ubSlotNum, UINT16 usItemIndex, UINT16 usPosX, UINT16 usPosY, OBJECTTYPE *pItemObject, BOOLEAN fHatchedOut, UINT8 ubItemArea )
{
CHAR16 zTemp[64];
HVOBJECT hVObject;
HVOBJECT hPixHandle;
INVTYPE *pItem;
ETRLEObject *pTrav;
UINT32 usHeight, usWidth;
INT16 sCenX, sCenY;
BOOLEAN fHighlighted = IsGunOrAmmoOfSameTypeSelected( pItemObject );
BOOLEAN fDisplayMercFace=FALSE;
UINT8 ubMercID=0;
UINT32 uiItemCost=0;
BOOLEAN fPrintRepaired = FALSE;
//if the item is not highlighted, and we are not rerendering the screen
if( !( fHighlighted || gubSkiDirtyLevel != SKI_DIRTY_LEVEL0 ) )
return( 0 );
//Display the item graphic, and price
pItem = &Item[ usItemIndex ];
GetVideoObject( &hVObject, GetInterfaceGraphicForItem( pItem ) );
UINT16 usGraphicNum = g_bUsePngItemImages ? 0 : pItem->ubGraphicNum;
pTrav = &(hVObject->pETRLEObject[ usGraphicNum ] );
usHeight = (UINT32)pTrav->usHeight;
usWidth = (UINT32)pTrav->usWidth;
sCenX = usPosX + 7 + (INT16)( abs( (long)(SKI_INV_WIDTH - 3 - usWidth) ) / 2 ) - pTrav->sOffsetX;
sCenY = usPosY + (INT16)( abs( (long) (SKI_INV_HEIGHT - usHeight) ) / 2 ) - pTrav->sOffsetY;
//blt the shadow of the item
if(gGameSettings.fOptions[ TOPTION_SHOW_ITEM_SHADOW ]) BltVideoObjectOutlineShadowFromIndex( FRAME_BUFFER, GetInterfaceGraphicForItem( pItem ), g_bUsePngItemImages ? 0 : pItem->ubGraphicNum, sCenX-2, sCenY+2);
//blt the item
BltVideoObjectOutlineFromIndex( FRAME_BUFFER, GetInterfaceGraphicForItem( pItem ), g_bUsePngItemImages ? 0 : pItem->ubGraphicNum, sCenX, sCenY, Get16BPPColor( FROMRGB( 255, 255, 255 ) ), fHighlighted );
#ifdef ENCYCLOPEDIA_WORKS
//Moa: encyclopedia item visibility
EncyclopediaSetItemAsVisible( usItemIndex, ENC_ITEM_DISCOVERED_INSPECTABLE );
#endif
//Display the status of the item
//bfant: adjusted size of status bar for item text
DrawItemUIBarEx( pItemObject, 0, (INT16)(usPosX+2), (INT16)(usPosY+2+24), 2, 24, Get16BPPColor( FROMRGB( 140, 136, 119 ) ), Get16BPPColor( FROMRGB( 140, 136, 119 ) ), TRUE, guiRENDERBUFFER );//guiSAVEBUFFER
//Display the Items Cost
if( ubItemArea == PLAYERS_OFFER_AREA )
{
//if the item has value
if( PlayersOfferArea[ ubSlotNum ].uiFlags & ARMS_INV_PLAYERS_ITEM_HAS_VALUE )
{
uiItemCost = PlayersOfferArea[ ubSlotNum ].uiItemPrice;
}
//if the item belongs to a merc
if( PlayersOfferArea[ ubSlotNum ].ubIdOfMercWhoOwnsTheItem != NO_PROFILE )
{
//Display the face of the merc
fDisplayMercFace = TRUE;
ubMercID = PlayersOfferArea[ ubSlotNum ].ubIdOfMercWhoOwnsTheItem;
}
// if the item has just been repaired
if ( PlayersOfferArea[ ubSlotNum ].uiFlags & ARMS_INV_ITEM_REPAIRED )
{
fPrintRepaired = TRUE;
}
}
else if( ubItemArea == ARMS_DEALER_INVENTORY )
{
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer != ARMS_DEALER_REPAIRS )
{
if( fHatchedOut && pItemObject->exists() == false )
{
uiItemCost = 0;
}
else
{
// show the UNIT price, not the total value of all if stacked
uiItemCost = CalcShopKeeperItemPrice( DEALER_SELLING, TRUE, usItemIndex, armsDealerInfo[ gbSelectedArmsDealerID ].dSellModifier, pItemObject );
}
}
else // UNDER REPAIR
{
//display the length of time needed to repair the item
uiItemCost = 0;
//Get the length of time needed to fix the item
BuildDoneWhenTimeString( zTemp, gbSelectedArmsDealerID, &(gpTempDealersInventory[ ubSlotNum ]) );
DrawTextToScreen( zTemp, (UINT16)(usPosX+SKI_INV_PRICE_OFFSET_X), (UINT16)(usPosY+SKI_INV_PRICE_OFFSET_Y), SKI_INV_SLOT_WIDTH, SKI_ITEM_DESC_FONT, SKI_ITEM_PRICE_COLOR, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED );
//if the item belongs to a merc
if( gpTempDealersInventory[ ubSlotNum ].ubIdOfMercWhoOwnsTheItem != NO_PROFILE )
{
//Display the face of the merc
fDisplayMercFace = TRUE;
ubMercID = gpTempDealersInventory[ ubSlotNum ].ubIdOfMercWhoOwnsTheItem;
}
}
}
else // DEALER'S OFFER AREA
{
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
//if the dealer repairs, there is an item here, therefore display the items owner's face
fDisplayMercFace = TRUE;
ubMercID = ArmsDealerOfferArea[ ubSlotNum ].ubIdOfMercWhoOwnsTheItem;
uiItemCost = CalculateObjectItemRepairCost( gbSelectedArmsDealerID, pItemObject );
}
else
uiItemCost = CalcShopKeeperItemPrice( DEALER_SELLING, FALSE, usItemIndex, armsDealerInfo[ gbSelectedArmsDealerID ].dSellModifier, pItemObject );
}
//if the item has a price, display it
if( uiItemCost != 0 )
{
swprintf( zTemp, L"%s", FormatMoney(uiItemCost).data() );
CHAR16 zTemp2[64];
if ( armsDealerInfo[gbSelectedArmsDealerID].uiFlags & ARMS_DEALER_DEALWITHINTEL )
swprintf( zTemp2, L"%s Intel", zTemp );
else
swprintf( zTemp2, L"%s", zTemp );
DrawTextToScreen( zTemp2, (UINT16)(usPosX+SKI_INV_PRICE_OFFSET_X), (UINT16)(usPosY+SKI_INV_PRICE_OFFSET_Y+4), SKI_INV_SLOT_WIDTH, SKI_ITEM_DESC_FONT, SKI_ITEM_PRICE_COLOR, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED );
}
// bfant: Display item name, limited to displayable characters (space constraints)
if ( pItem->szItemName != 0 )
{
wcsncpy( zTemp, pItem->szItemName, sizeof( zTemp ) / 2 );
zTemp[63] = '\0'; // make sure it is null-terminated
ReduceStringLength( zTemp, SKI_INV_SLOT_WIDTH - 2, BLOCKFONTNARROW );
DrawTextToScreen( zTemp, usPosX, usPosY + 1, SKI_INV_SLOT_WIDTH, BLOCKFONTNARROW, SKI_ITEM_PRICE_COLOR, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED );
}
//if the there is more then 1 or if the item is stackable and some of it has been bought and only 1 remains
if( ( pItemObject->ubNumberOfObjects > 1 ) ||
( ( pItemObject->ubNumberOfObjects == 1 ) &&
( ubItemArea == ARMS_DEALER_INVENTORY ) && ( gpTempDealersInventory[ ubSlotNum ].uiFlags & ARMS_INV_ITEM_SELECTED ) ) )
{
swprintf( zTemp, L"x%d", pItemObject->ubNumberOfObjects );
DrawTextToScreen( zTemp, (UINT16)(usPosX+SKI_ITEM_NUMBER_TEXT_OFFSET_X), (UINT16)(usPosY+SKI_ITEM_NUMBER_TEXT_OFFSET_Y), SKI_ITEM_NUMBER_TEXT_WIDTH, SKIT_NUMBER_FONT, SKI_ITEM_PRICE_COLOR, FONT_MCOLOR_BLACK, FALSE, RIGHT_JUSTIFIED );
}
//if we are to display the face
if( fDisplayMercFace )
{
INT8 iFaceSlot = GetSlotNumberForMerc( ubMercID );
// if still in player's employ
if ( iFaceSlot != -1 )
{
//Get and blit the face
GetVideoObject(&hPixHandle, guiSmallSoldiersFace[ iFaceSlot ] );
BltVideoObject(FRAME_BUFFER, hPixHandle, 0,(UINT16)(usPosX+SKI_SMALL_FACE_OFFSET_X), (UINT16)(usPosY), VO_BLT_SRCTRANSPARENCY,NULL);//SKI_SMALL_FACE_OFFSET_Y
}
}
//if the item has attachments
if( ItemHasAttachments( pItemObject ) )
{
//Display the '*' in the bottom right corner of the square
swprintf( zTemp, L"*" );
DrawTextToScreen( zTemp, (UINT16)(usPosX+SKI_ATTACHMENT_SYMBOL_X_OFFSET), (UINT16)(usPosY+SKI_ATTACHMENT_SYMBOL_Y_OFFSET), 0, TINYFONT1, FONT_GREEN, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED );
}
// CHRISL: if item is LBENODE
else if( (UsingNewInventorySystem() == true) && pItemObject->HasAnyActiveLBEs() == true)
{
//Display the '*' in the bottom right corner of the square
swprintf( zTemp, L"*" );
DrawTextToScreen( zTemp, (UINT16)(usPosX+SKI_ATTACHMENT_SYMBOL_X_OFFSET), (UINT16)(usPosY+SKI_ATTACHMENT_SYMBOL_Y_OFFSET), 0, TINYFONT1, FONT_BLUE, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED );
}
// Display 'JAMMED' if it's jammed
if ( (*pItemObject)[0]->data.gun.bGunAmmoStatus < 0 )
{
swprintf( zTemp, TacticalStr[ JAMMED_ITEM_STR ] );
VarFindFontCenterCoordinates( usPosX, usPosY, SKI_INV_SLOT_WIDTH, SKI_INV_HEIGHT, TINYFONT1, &sCenX, &sCenY, zTemp );
DrawTextToScreen( zTemp, sCenX, sCenY, SKI_INV_SLOT_WIDTH, TINYFONT1, FONT_RED, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED );
}
else if ( fPrintRepaired )
{
swprintf( zTemp, SKI_Text[ SKI_TEXT_REPAIRED ] );
VarFindFontCenterCoordinates( usPosX, usPosY, SKI_INV_SLOT_WIDTH, SKI_INV_HEIGHT, TINYFONT1, &sCenX, &sCenY, zTemp );
DrawTextToScreen( zTemp, sCenX, sCenY, SKI_INV_SLOT_WIDTH, TINYFONT1, FONT_RED, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED );
}
if( fHatchedOut )
{
HatchOutInvSlot( usPosX, usPosY );
}
InvalidateRegion( usPosX, usPosY, usPosX+SKI_INV_SLOT_WIDTH, usPosY+SKI_INV_SLOT_HEIGHT );
return( uiItemCost );
}
BOOLEAN DetermineArmsDealersSellingInventory( )
{
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("DEF: DetermineArmsDealersSellingInventory") );
//allocate memory to hold the inventory in memory
gpTempDealersInventory.clear();
guiNextFreeInvSlot = 0;
//loop through the dealer's permanent inventory items, adding them all to the temp inventory list
for (DealerItemList::iterator iter = gArmsDealersInventory[ gbSelectedArmsDealerID ].begin();
iter != gArmsDealersInventory[ gbSelectedArmsDealerID ].end();) {
if ( iter->ItemIsInInventory() == false) {
++iter;
continue;
}
bool increment = true;
if (ItemIsSpecial(*iter) == false) {
StoreObjectsInNextFreeDealerInvSlot( &(*iter), gpTempDealersInventory, gbSelectedArmsDealerID );
}
else
{
//if the object has attachments, or is damaged, or otherwise special, then it is not stacked
bool fAddSpecialItem = true;
bool underRepair = false;
bool repaired = false;
if( iter->IsUnderRepair() == true )
{
underRepair = true;
//if the repairs are done
if( iter->uiRepairDoneTime <= GetWorldTotalMin() )
{
if (RepairIsDone( &(*iter) ))
{
//ADB moved from RepairIsDone so iterator remains ok
// Remove the repaired item from the dealer's permanent inventory list
iter = gArmsDealersInventory[ gbSelectedArmsDealerID ].erase(iter);
if (iter == gArmsDealersInventory[ gbSelectedArmsDealerID ].end()) {
break;
}
increment = false;
// don't add it here, it was put in the player's area
fAddSpecialItem = false;
}
else
{
repaired = true;
}
}
}
if ( fAddSpecialItem == true )
{
UINT8 ubOwner;
if ( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer != ARMS_DEALER_REPAIRS )
{
// no merc is the owner
ubOwner = NO_PROFILE;
}
else
{
// retain owner so we can display this
ubOwner = iter->ubOwnerProfileId;
}
StoreObjectsInNextFreeDealerInvSlot( &(*iter), gpTempDealersInventory, ubOwner );
if (underRepair == true) {
if (repaired == true) {
gpTempDealersInventory.back().uiFlags |= ARMS_INV_ITEM_REPAIRED;
}
else {
gpTempDealersInventory.back().uiFlags |= ARMS_INV_ITEM_NOT_REPAIRED_YET;
}
}
}
}
if (increment == true) {
++iter;
}
}
// repairmen sort differently from merchants
// sort this list by object category, and by ascending price within each category
if ( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
std::sort(gpTempDealersInventory.begin(), gpTempDealersInventory.end(), RepairmanItemQsortCompare);//RepairmanItemQsortCompare
}
else
{
std::sort(gpTempDealersInventory.begin(), gpTempDealersInventory.end(), ArmsDealerItemQsortCompare);//ArmsDealerItemQsortCompare
}
return( TRUE );
}
bool ArmsDealerItemQsortCompare(INVENTORY_IN_SLOT& pInvSlot1, INVENTORY_IN_SLOT& pInvSlot2)
{
int retVal = ( CompareItemsForSorting( pInvSlot1.sItemIndex, pInvSlot2.sItemIndex,
pInvSlot1.ItemObject[0]->data.objectStatus, pInvSlot2.ItemObject[0]->data.objectStatus ) );
if (retVal == -1) {
return true;
}
return false;
}
bool RepairmanItemQsortCompare(INVENTORY_IN_SLOT& pInvSlot1, INVENTORY_IN_SLOT& pInvSlot2)
{
// lower repair time first
if ( pInvSlot1.uiRepairDoneTime < pInvSlot2.uiRepairDoneTime )
{
return true;
}
return false;
}
void StoreObjectsInNextFreeDealerInvSlot( DEALER_SPECIAL_ITEM *pSpclItemInfo, std::vector<INVENTORY_IN_SLOT>& pInventory, UINT8 ubOwner )
{
pInventory.push_back(INVENTORY_IN_SLOT());
INVENTORY_IN_SLOT* pDealerInvSlot = &(pInventory.back());
pDealerInvSlot->fActive = TRUE;
pDealerInvSlot->sItemIndex = pSpclItemInfo->object.usItem;
pDealerInvSlot->uiRepairDoneTime = pSpclItemInfo->uiRepairDoneTime;
pDealerInvSlot->ubIdOfMercWhoOwnsTheItem = ubOwner;
pDealerInvSlot->bSlotIdInOtherLocation = -1;
// Create the item object ( with no more than MAX_OBJECTS_PER_SLOT )
// can't use the real #, because CreateItems() will blindly set the bStatus for however many we tell it, beyond 8
MakeObjectOutOfDealerItems( pSpclItemInfo, &(pDealerInvSlot->ItemObject) );
//ADB no longer necessary
/*
if ( ubHowMany > MAX_OBJECTS_PER_SLOT )
{
// HACK: Override ItemObject->ubNumberOfObjects (1-8) with the REAL # of items in this box.
// Note that this makes it an illegal OBJECTTYPE, since there ubHowMany can easily be more than MAX_OBJECTS_PER_SLOT,
// so there's no room to store the status of all of them one. But we only so this for perfect items, so
// we don't care, it works & saves us a lot hassles here. Just be careful using the damn things!!! You can't just
// pass them off the most functions in Items.C(), use ShopkeeperAutoPlaceObject() and ShopkeeperAddItemToPool() instead.
pDealerInvSlot->ItemObject.ubNumberOfObjects = ubHowMany;
}
*/
}
BOOLEAN RepairIsDone(DEALER_SPECIAL_ITEM* pSpecial)
{
// make a new shopkeeper invslot item out of it
INVENTORY_IN_SLOT RepairItem;
RepairItem.fActive = TRUE;
RepairItem.sItemIndex = pSpecial->object.usItem;
// set the owner of the item. Slot is always -1 of course.
RepairItem.ubIdOfMercWhoOwnsTheItem = pSpecial->ubOwnerProfileId;
RepairItem.bSlotIdInOtherLocation = -1;
// Create the item object
MakeObjectOutOfDealerItems( pSpecial, &RepairItem.ItemObject );
if ( CanDealerRepairItem( gbSelectedArmsDealerID, RepairItem.ItemObject.usItem ) )
{
// make its condition 100%
RepairItem.ItemObject[0]->data.objectStatus = 100;
// Flugente: shopkeepers will also restore an item's repair threshold
RepairItem.ItemObject[0]->data.sRepairThreshold = 100;
}
// max condition of all permanent attachments on it
for (attachmentList::iterator iter = RepairItem.ItemObject[0]->attachments.begin(); iter != RepairItem.ItemObject[0]->attachments.end(); ++iter) {
if ( iter->exists() && CanDealerRepairItem( gbSelectedArmsDealerID, iter->usItem ) )
{
// fix it up
(*iter)[0]->data.objectStatus = 100;
// Flugente: shopkeepers will also restore an item's repair threshold and clean it
(*iter)[0]->data.sRepairThreshold = 100;
}
}
#ifdef JA2UB
//no UB
#else
// if the item is imprinted (by anyone, even player's mercs), and it's Fredo repairing it
if ( ( gbSelectedArmsDealerID == ARMS_DEALER_FREDO ) )
{
// then reset the imprinting!
RepairItem.ItemObject[0]->data.ubImprintID = NO_PROFILE;
}
#endif
//try to add the item to the players offer area
INT8 bSlotNum;
bSlotNum = AddItemToPlayersOfferArea( RepairItem.ubIdOfMercWhoOwnsTheItem, &RepairItem, -1 );
// if there wasn't room for it
if( bSlotNum == -1 )
{
// then we have to treat it like it's not done yet!
return(FALSE);
}
PlayersOfferArea[ bSlotNum ].uiFlags |= ARMS_INV_ITEM_REPAIRED;
// there was room to add it to player's area
return(TRUE);
}
void DrawHatchOnInventory( UINT32 uiSurface, UINT16 usPosX, UINT16 usPosY, UINT16 usWidth, UINT16 usHeight, UINT16 usColor )
{
UINT8 *pDestBuf;
UINT32 uiDestPitchBYTES;
SGPRect ClipRect;
static UINT8 Pattern[8][8] =
{
1,0,1,0,1,0,1,0,
0,1,0,1,0,1,0,1,
1,0,1,0,1,0,1,0,
0,1,0,1,0,1,0,1,
1,0,1,0,1,0,1,0,
0,1,0,1,0,1,0,1,
1,0,1,0,1,0,1,0,
0,1,0,1,0,1,0,1
};
// CHRISL:
ClipRect.iLeft = usPosX-1;
ClipRect.iRight = usPosX + usWidth-1;
ClipRect.iTop = usPosY;
ClipRect.iBottom = usPosY + usHeight;
pDestBuf = LockVideoSurface( uiSurface, &uiDestPitchBYTES );
if(usColor == 0){
Blt16BPPBufferPixelateRect( (UINT16*)pDestBuf, uiDestPitchBYTES, &ClipRect, Pattern );
}
else{
Blt16BPPBufferPixelateRectWithColor( (UINT16*)pDestBuf, uiDestPitchBYTES, &ClipRect, Pattern, usColor );
}
UnLockVideoSurface( uiSurface );
}
void DrawHatchOnInventory_MilitiaAccess( UINT32 uiSurface, UINT16 usPosX, UINT16 usPosY, UINT16 usWidth, UINT16 usHeight, UINT16 usColor )
{
UINT8 *pDestBuf;
UINT32 uiDestPitchBYTES;
SGPRect ClipRect;
static UINT8 Pattern[8][8] =
{
1,0,0,0,0,0,0,0,
0,0,0,0,0,1,0,0,
0,0,1,0,0,0,0,0,
0,0,0,0,0,0,0,1,
0,0,0,0,1,0,0,0,
0,1,0,0,0,0,0,0,
0,0,0,0,0,0,1,0,
0,0,0,1,0,0,0,0
};
// CHRISL:
ClipRect.iLeft = usPosX-1;
ClipRect.iRight = usPosX + usWidth-1;
ClipRect.iTop = usPosY;
ClipRect.iBottom = usPosY + usHeight;
pDestBuf = LockVideoSurface( uiSurface, &uiDestPitchBYTES );
if(usColor == 0){
Blt16BPPBufferPixelateRect( (UINT16*)pDestBuf, uiDestPitchBYTES, &ClipRect, Pattern );
}
else{
Blt16BPPBufferPixelateRectWithColor( (UINT16*)pDestBuf, uiDestPitchBYTES, &ClipRect, Pattern, usColor );
}
UnLockVideoSurface( uiSurface );
}
UINT32 CalcShopKeeperItemPrice( BOOLEAN fDealerSelling, BOOLEAN fUnitPriceOnly, UINT16 usItemID, FLOAT dModifier, OBJECTTYPE *pItemObject )
{
UINT8 ubCnt;
UINT32 uiUnitPrice = 0;
UINT32 uiTotalPrice = 0;
// UINT32 uiDifFrom10 = 0;
#ifdef JA2UB
UINT8 ubItemsNotCounted = 0; //ja25 UB
#endif
std::vector<UINT32> uiItemPrice;
uiItemPrice.resize(pItemObject->ubNumberOfObjects);
for (ubCnt = 0; ubCnt < pItemObject->ubNumberOfObjects; ++ubCnt )
{
uiItemPrice[ubCnt] = 0;
// add up value of the main item(s), exact procedure depends on its item class
switch ( Item [ usItemID ].usItemClass )
{
case IC_GUN:
// add value of the gun
uiItemPrice[ubCnt] += (UINT32)( CalcValueOfItemToDealer( gbSelectedArmsDealerID, usItemID, fDealerSelling ) *
ItemConditionModifier(usItemID, (*pItemObject)[ubCnt]->data.gun.bGunStatus) *
dModifier );
// if any ammo is loaded
if( (*pItemObject)[ubCnt]->data.gun.usGunAmmoItem != NONE)
{
// if it's regular ammo
if( Item[ (*pItemObject)[ubCnt]->data.gun.usGunAmmoItem ].usItemClass == IC_AMMO )
{
// add value of its remaining ammo
uiItemPrice[ubCnt] += (UINT32)( CalcValueOfItemToDealer( gbSelectedArmsDealerID, (*pItemObject)[ubCnt]->data.gun.usGunAmmoItem, fDealerSelling ) *
ItemConditionModifier((*pItemObject)[ubCnt]->data.gun.usGunAmmoItem, (*pItemObject)[ubCnt]->data.gun.ubGunShotsLeft) *
dModifier );
}
else // assume it's attached ammo (mortar shells, grenades)
{
// add its value (uses normal status 0-100)
uiItemPrice[ubCnt] += (UINT32)( CalcValueOfItemToDealer( gbSelectedArmsDealerID, (*pItemObject)[ubCnt]->data.gun.usGunAmmoItem, fDealerSelling ) *
ItemConditionModifier((*pItemObject)[ubCnt]->data.gun.usGunAmmoItem, (*pItemObject)[ubCnt]->data.gun.bGunAmmoStatus) *
dModifier );
}
}
break;
case IC_AMMO:
// add the value of each magazine (multiple mags may have vastly different #bullets left)
// for bullets, ItemConditionModifier will convert the #ShotsLeft into a percentage
uiItemPrice[ubCnt] += (UINT32)( CalcValueOfItemToDealer( gbSelectedArmsDealerID, usItemID, fDealerSelling ) *
ItemConditionModifier(usItemID, (*pItemObject)[ubCnt]->data.ubShotsLeft) *
dModifier );
break;
default:
// add the value of each magazine (multiple mags may have vastly different #bullets left)
// for bullets, ItemConditionModifier will convert the #ShotsLeft into a percentage
uiItemPrice[ubCnt] += (UINT32)( CalcValueOfItemToDealer( gbSelectedArmsDealerID, usItemID, fDealerSelling ) *
ItemConditionModifier(usItemID, (*pItemObject)[ ubCnt ]->data.objectStatus) *
dModifier );
break;
}
// loop through any attachments and add in their price
for (attachmentList::iterator iter = (*pItemObject)[ubCnt]->attachments.begin(); iter != (*pItemObject)[ubCnt]->attachments.end(); ++iter) {
// add value of this particular attachment
if(iter->exists())
uiItemPrice[ubCnt] += CalcShopKeeperItemPrice( fDealerSelling, fUnitPriceOnly, iter->usItem, dModifier, &(*iter)) ;
}
// Flugente: used in backgrounds now, old code in comments for historical reasons
/*// if Flo is doing the dealin' and wheelin'
if ( gpSMCurrentMerc->ubProfile == FLO )
{
// if it's a GUN or AMMO (but not Launchers, and all attachments and payload is included)
switch ( Item [ usItemID ].usItemClass )
{
case IC_GUN:
case IC_AMMO:
uiDiscountValue = ( uiItemPrice[ubCnt] * FLO_DISCOUNT_PERCENTAGE ) / 100;
// she gets a discount! Read her M.E.R.C. profile to understand why
if ( fDealerSelling )
{
// she buys for less...
uiItemPrice[ubCnt] -= uiDiscountValue;
}
else
{
// and sells for more!
uiItemPrice[ubCnt] += uiDiscountValue;
}
break;
}
}*/
// Flugente: backgrounds can alter prices
INT16 pricepercentage = 100;
// if it's a GUN or AMMO (but not Launchers, and all attachments and payload is included)
if ( Item [ usItemID ].usItemClass & (IC_GUN|IC_AMMO) )
{
pricepercentage = max( 0, 100 + (fDealerSelling ? -1 : 1) * gpSMCurrentMerc->GetBackgroundValue( BG_PERC_PRICES_GUNS ) );
// Even without backgrounds, Flo gets a discount. Read her M.E.R.C. profile to understand why
if ( !UsingBackGroundSystem() && gpSMCurrentMerc->ubProfile == FLO )
pricepercentage = max( 0, 100 + (fDealerSelling ? -1 : 1) * FLO_DISCOUNT_PERCENTAGE );
}
else if ( Item [ usItemID ].usItemClass & (IC_MAPFILTER_MELEE|IC_MAPFILTER_KIT|IC_MAPFILTER_LBE|IC_MAPFILTER_ARMOR|IC_MAPFILTER_MISC) )
{
pricepercentage = max( 0, 100 + (fDealerSelling ? -1 : 1) * gpSMCurrentMerc->GetBackgroundValue( BG_PERC_PRICES ) );
}
uiItemPrice[ubCnt] = (uiItemPrice[ubCnt] * pricepercentage) / 100;
// if it's the dealer selling this, make sure the item is worth at least $1
// if he is buying this from a player, then we allow a value of 0, since that has a special "worthless" quote #18
if( fDealerSelling && ( uiItemPrice[ubCnt] == 0 ) )
{
uiItemPrice[ubCnt] = 1;
}
/*
//if the price is not diviseble by 10, make it so
uiDifFrom10 = 10 - uiItemPrice[ubCnt] % 10;
if( uiDifFrom10 != 0 && uiDifFrom10 != 10 )
{
uiItemPrice[ubCnt] += uiDifFrom10;
}
*/
}
// if we want the unit price, find the highest price in the stack
for (ubCnt = 0; ubCnt < pItemObject->ubNumberOfObjects; ++ubCnt )
{
if(fUnitPriceOnly == TRUE)
{
uiUnitPrice = __max(uiUnitPrice, uiItemPrice[ubCnt]);
}
else
{
uiUnitPrice += uiItemPrice[ubCnt];
}
}
// we're always count the first one
uiTotalPrice = uiUnitPrice;
#ifdef JA2UB
//---------------------------JA25 UB---------------
// if NOT pricing just one
if ( !fUnitPriceOnly )
{
// add value of all that weren't already counted
uiTotalPrice += ( ubItemsNotCounted * uiUnitPrice );
}
//if the dealer is Raul
if( gbSelectedArmsDealerID == ARMS_DEALER_RAUL )
{
//if the item is the antique musket
if( usItemID == HAND_CANNON ) //4498
{
//if the "Find anitque musket" quest is active
if( gubQuest[ QUEST_FIND_ANTIQUE_MUSKET_FOR_RAUL ] == QUESTINPROGRESS )
{
//the price should be.... nothin ( reward for the quest )
uiTotalPrice = 1;
//if the player hasnt said the quote before
if( !( gArmsDealerStatus[ gbSelectedArmsDealerID ].ubSpecificDealerFlags & ARMS_DEALER_FLAG__RAUL_SAID_QUOTE_48 ) )
{
StartShopKeeperTalking( SK_QUOTES_NPC_SPECIFIC_48 );
gArmsDealerStatus[ gbSelectedArmsDealerID ].ubSpecificDealerFlags |= ARMS_DEALER_FLAG__RAUL_SAID_QUOTE_48;
gfCanSayMakeTransactionQuote = FALSE;
}
//else if
if( gArmsDealerStatus[ gbSelectedArmsDealerID ].ubSpecificDealerFlags & ARMS_DEALER_FLAG__RAUL_SAID_QUOTE_49 )
{
//if the player hasnt said it before
if( !( gArmsDealerStatus[ gbSelectedArmsDealerID ].ubSpecificDealerFlags & ARMS_DEALER_FLAG__RAUL_SAID_QUOTE_50 ) )
{
StartShopKeeperTalking( SK_QUOTES_NPC_SPECIFIC_50 );
gArmsDealerStatus[ gbSelectedArmsDealerID ].ubSpecificDealerFlags |= ARMS_DEALER_FLAG__RAUL_SAID_QUOTE_50;
}
//the price should be
uiTotalPrice = 100;
}
}
}
//if the item is the barrat
if( usItemID == BARRETT_UB )
{
//and if the player found the antique musket for Raul
if( IsJa25GeneralFlagSet( JA_GF__BARRETT_IS_HALF_PRICE ) )
{
//the barrett is half off
uiTotalPrice /= 2;
}
}
}
//JA25UB
//if the dealer is Raul
if( gbSelectedArmsDealerID == ARMS_DEALER_BETTY )
{
//if the item is the antique musket
if( usItemID == TEX_MOVIE_ATTACK_CLYDESDALES || usItemID == TEX_MOVIE_WILD_EAST || usItemID == TEX_MOVIE_HAVE_HONDA )
// if( usItemID == 1356 || usItemID == 1357 || usItemID == 1358 )
{
//Make the item price top be what is listed in the item table ( 20 )
uiTotalPrice = Item[ usItemID ].usPrice;
}
}
#endif
return( uiTotalPrice );
}
FLOAT ItemConditionModifier(UINT16 usItemIndex, INT16 bStatus)
{
FLOAT dConditionModifier = 1.0f;
//if the item is ammo, the condition modifier is based on how many shots are left
if( Item[ usItemIndex ].usItemClass == IC_AMMO )
{
// # bullets left / max magazine capacity
dConditionModifier = ( bStatus / (FLOAT) Magazine[ Item[ usItemIndex ].ubClassIndex ].ubMagSize );
}
else // non-ammo
{
// handle non-ammo payloads in jammed guns as if they weren't
if ( bStatus < 0 )
{
bStatus *= -1;
}
// an item at 100% is worth full price...
if (ItemIsRepairable(usItemIndex))
{
// a REPAIRABLE item at 0% is still worth 50% of its full price, not 0%
dConditionModifier = 0.5f + ( bStatus / (FLOAT)200 );
}
else
{
// an UNREPAIRABLE item is worth precisely its condition percentage
dConditionModifier = bStatus / (FLOAT)100;
}
}
return( dConditionModifier );
}
void DisplayArmsDealerOfferArea()
{
INT16 sCnt, sCount;
CHAR16 zTemp[32];
UINT32 uiTotalCost;
UINT16 usPosX, usPosY;
BOOLEAN fDisplayHatchOnItem;
usPosX = SKI_TRADER_OFFER_BOXES_BEGIN_X;
usPosY = SKI_TRADER_OFFER_BOXES_BEGIN_Y;
sCount = 0;
//Display all the items that are in the Arms traders offered area
uiTotalCost = 0;
for ( sCnt = 0; sCnt<gDealersOfferActiveRegions; ++sCnt )
{
if( ArmsDealerOfferArea[ sCnt ].fActive )
{
// hatch it out if region is disabled
if ( !( gDealersOfferSlotsMouseRegions[ sCnt ].uiFlags & MSYS_REGION_ENABLED ) )
{
fDisplayHatchOnItem = TRUE;
}
else if( ArmsDealerOfferArea[ sCnt ].uiFlags & ARMS_INV_ITEM_SELECTED )
{
fDisplayHatchOnItem = TRUE;
}
else
fDisplayHatchOnItem = FALSE;
// Display the inventory slot
uiTotalCost += DisplayInvSlot( (UINT16)sCnt, ArmsDealerOfferArea[ sCnt ].sItemIndex, usPosX, usPosY,
&ArmsDealerOfferArea[ sCnt ].ItemObject,
fDisplayHatchOnItem,
ARMS_DEALER_OFFER_AREA );
}
else // empty
{
// hatch it out if region is disabled
if ( !( gDealersOfferSlotsMouseRegions[ sCnt ].uiFlags & MSYS_REGION_ENABLED ) )
{
HatchOutInvSlot( usPosX, usPosY );
}
}
usPosX += SKI_INV_OFFSET_X;
++sCount;
//if we are on to the next row
if ( !(sCount % SKI_TRADER_OFFER_BOXES_PER_ROW) )
{
usPosX = SKI_TRADER_OFFER_BOXES_BEGIN_X;
usPosY += SKI_INV_OFFSET_Y;
}
}
if( gubSkiDirtyLevel == SKI_DIRTY_LEVEL2 )
{
//Display the total cost text
swprintf( zTemp, L"%s", FormatMoney(uiTotalCost).data() );
CHAR16 zTemp2[64];
if ( armsDealerInfo[gbSelectedArmsDealerID].uiFlags & ARMS_DEALER_DEALWITHINTEL )
swprintf( zTemp2, L"%s Intel", zTemp );
else
swprintf( zTemp2, L"%s", zTemp );
DrawTextToScreen( zTemp2, SKI_ARMS_DEALER_TOTAL_COST_X, (UINT16)(SKI_ARMS_DEALER_TOTAL_COST_Y+5), SKI_INV_SLOT_WIDTH, SKI_LABEL_FONT, SKI_ITEM_PRICE_COLOR, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED );
}
}
INT16 AddItemToArmsDealerOfferArea( INVENTORY_IN_SLOT* pInvSlot, INT16 bSlotIdInOtherLocation )
{
for ( INT16 bCnt = 0; bCnt<gDealersOfferActiveRegions; ++bCnt )
{
//if there are no items here, copy the data in
if( ArmsDealerOfferArea[bCnt].fActive == FALSE )
{
//Copy the inventory items
ArmsDealerOfferArea[bCnt] = *pInvSlot;
//if the shift key is being pressed, add them all
if( gfKeyState[ SHIFT ] ) {
//nothing needed
}
//If there was more then 1 item, reduce it to only 1 item moved
else if( pInvSlot->ItemObject.ubNumberOfObjects > 1 ) {
ArmsDealerOfferArea[bCnt].ItemObject.RemoveObjectsFromStack(ArmsDealerOfferArea[bCnt].ItemObject.ubNumberOfObjects - 1);
}
//Remember where the item came from
ArmsDealerOfferArea[bCnt].bSlotIdInOtherLocation = bSlotIdInOtherLocation;
ArmsDealerOfferArea[bCnt].ubIdOfMercWhoOwnsTheItem = pInvSlot->ubIdOfMercWhoOwnsTheItem;
ArmsDealerOfferArea[bCnt].fActive = TRUE;
SetSkiRegionHelpText( &ArmsDealerOfferArea[bCnt], &gDealersOfferSlotsMouseRegions[ bCnt ], ARMS_DEALER_OFFER_AREA );
SetSkiFaceRegionHelpText( &ArmsDealerOfferArea[bCnt], &gDealersOfferSlotsSmallFaceMouseRegions[ bCnt ], ARMS_DEALER_OFFER_AREA );
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
return( bCnt );
}
}
return( -1 );
}
BOOLEAN RemoveItemFromArmsDealerOfferArea( INT16 bSlotId, BOOLEAN fKeepItem )
{
//if this is the requested slot
if( ArmsDealerOfferArea[bSlotId].fActive )
{
if( fKeepItem )
{
gpTempDealersInventory[ ArmsDealerOfferArea[bSlotId].bSlotIdInOtherLocation ].ItemObject.ForceAddObjectsToStack(ArmsDealerOfferArea[ bSlotId ].ItemObject);
}
//Clear the flag that hatches out the item
gpTempDealersInventory[ ArmsDealerOfferArea[bSlotId].bSlotIdInOtherLocation ].uiFlags &= ~ARMS_INV_ITEM_SELECTED;
ClearArmsDealerOfferSlot( bSlotId );
return( TRUE );
}
return( FALSE );
}
void SetSkiRegionHelpText( INVENTORY_IN_SLOT *pInv, MOUSE_REGION *pRegion, UINT8 ubScreenArea )
{
CHAR16 zHelpText[ 512 ];
Assert( pRegion );
BuildItemHelpTextString( zHelpText, pInv, ubScreenArea );
SetRegionFastHelpText( pRegion, zHelpText );
SetRegionHelpEndCallback( pRegion, SkiHelpTextDoneCallBack );
}
void SetSkiFaceRegionHelpText( INVENTORY_IN_SLOT *pInv, MOUSE_REGION *pRegion, UINT8 ubScreenArea )
{
CHAR16 zTempText[ 512 ];
CHAR16 zHelpText[ 512 ];
Assert( pRegion );
//if the item isn't NULL, and is owned by a merc
if( ( pInv != NULL ) && ( pInv->ubIdOfMercWhoOwnsTheItem != NO_PROFILE ) )
{
BuildItemHelpTextString( zTempText, pInv, ubScreenArea );
// add who owns it
swprintf( zHelpText, L"%s%s %s", gMercProfiles[ pInv->ubIdOfMercWhoOwnsTheItem ].zNickname, pMessageStrings[ MSG_DASH_S ], zTempText );
}
else
{
zHelpText[ 0 ] = '\0';
}
SetRegionFastHelpText( pRegion, zHelpText );
SetRegionHelpEndCallback( pRegion, SkiHelpTextDoneCallBack );
}
void SkiHelpTextDoneCallBack( void )
{
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
}
INT8 AddItemToPlayersOfferArea( UINT8 ubProfileID, INVENTORY_IN_SLOT* pInvSlot, INT16 bSlotIdInOtherLocation )
{
//if we are to check for a previous slot
if( bSlotIdInOtherLocation != -1 )
{
//if the item has already been added somewhere. don't add it again
if( GetPtrToOfferSlotWhereThisItemIs( ubProfileID, bSlotIdInOtherLocation ) != NULL )
return( -1 );
}
//look for the first free slot
for ( int bCnt = 0; bCnt<gPlayersOfferActiveRegions; ++bCnt )
{
//if there are no items here, copy the data in
if( PlayersOfferArea[bCnt].fActive == FALSE )
{
PlayersOfferArea[bCnt] = *pInvSlot;
PlayersOfferArea[bCnt].fActive = TRUE;
//Specify the owner of the merc
PlayersOfferArea[bCnt].ubIdOfMercWhoOwnsTheItem = ubProfileID;
PlayersOfferArea[bCnt].bSlotIdInOtherLocation = bSlotIdInOtherLocation;
IfMercOwnedCopyItemToMercInv( &(PlayersOfferArea[bCnt]) );
SetSkiRegionHelpText( &PlayersOfferArea[bCnt], &gPlayersOfferSlotsMouseRegions[ bCnt ], PLAYERS_OFFER_AREA );
SetSkiFaceRegionHelpText( &PlayersOfferArea[bCnt], &gPlayersOfferSlotsSmallFaceMouseRegions[ bCnt ], PLAYERS_OFFER_AREA );
//if the item we are adding is money
if( Item[ PlayersOfferArea[ bCnt ].sItemIndex ].usItemClass == IC_MONEY && !( armsDealerInfo[gbSelectedArmsDealerID].uiFlags & ARMS_DEALER_DEALWITHINTEL ) )
{
//Since money is always evaluated
PlayersOfferArea[ bCnt ].uiFlags |= ARMS_INV_PLAYERS_ITEM_HAS_VALUE;
PlayersOfferArea[ bCnt ].uiItemPrice = PlayersOfferArea[ bCnt ].ItemObject[0]->data.money.uiMoneyAmount;
}
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
return( bCnt );
}
}
return( -1 );
}
BOOLEAN RemoveItemFromPlayersOfferArea( INT16 bSlot )
{
//if the item doesn't have a duplicate copy in its owner merc's inventory slot
if( PlayersOfferArea[ bSlot ].bSlotIdInOtherLocation == -1 )
{
if ( !SKITryToReturnInvToOwnerOrCurrentMerc( &( PlayersOfferArea[ bSlot ] ) ) )
{
//failed to add item, inventory probably filled up or item is unowned and current merc ineligible
return( FALSE );
}
}
// Clear the contents
ClearPlayersOfferSlot( bSlot );
// Dirty
fInterfacePanelDirty = DIRTYLEVEL2;
gubSkiDirtyLevel = SKI_DIRTY_LEVEL1;
return( TRUE );
}
void DisplayPlayersOfferArea()
{
INT16 sCount = 0;
CHAR16 zTemp[32];
UINT32 uiTotalCost;
UINT16 usPosX, usPosY;
BOOLEAN fDisplayHatchOnItem=FALSE;
HVOBJECT hHandle;
// load the "cross out" graphic
GetVideoObject( &hHandle, guiItemCrossOut );
usPosX = SKI_PLAYER_OFFER_BOXES_BEGIN_X;
usPosY = SKI_PLAYER_OFFER_BOXES_BEGIN_Y;
//Display all the items that are in the Players offered area
uiTotalCost = 0;
for ( int sCnt = 0; sCnt < gPlayersOfferActiveRegions; ++sCnt )
{
if( PlayersOfferArea[ sCnt ].fActive )
{
//if the item is money
if( Item[ PlayersOfferArea[ sCnt ].sItemIndex ].usItemClass == IC_MONEY )
{
//get an updated status from the amount in the pocket
if( PlayersOfferArea[ sCnt ].bSlotIdInOtherLocation != -1 && PlayersOfferArea[ sCnt ].ubIdOfMercWhoOwnsTheItem != NO_PROFILE )
{
SoldierID sSoldierID = GetSoldierIDFromMercID( PlayersOfferArea[ sCnt ].ubIdOfMercWhoOwnsTheItem );
Assert(sSoldierID != NOBODY);
PlayersOfferArea[ sCnt ].ItemObject[0]->data.money.uiMoneyAmount = sSoldierID->inv[ PlayersOfferArea[ sCnt ].bSlotIdInOtherLocation ][0]->data.money.uiMoneyAmount;
PlayersOfferArea[ sCnt ].uiItemPrice = PlayersOfferArea[ sCnt ].ItemObject[0]->data.money.uiMoneyAmount;
}
}
else // not money
{
//if non-repairman
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer != ARMS_DEALER_REPAIRS )
{
// don't evaluate anything he wouldn't buy!
if( WillShopKeeperRejectObjectsFromPlayer( gbSelectedArmsDealerID, ( INT8 ) sCnt ) == FALSE )
{
// skip purchased items!
if ( !( PlayersOfferArea[ sCnt ].uiFlags & ARMS_INV_JUST_PURCHASED ) )
{
// re-evaluate the value of the item (needed for Flo's discount handling)
EvaluateInvSlot( &( PlayersOfferArea[ sCnt ] ) );
}
}
}
}
// Display the inventory slot
DisplayInvSlot( (UINT16)sCnt, PlayersOfferArea[ sCnt ].sItemIndex, usPosX, usPosY,
&PlayersOfferArea[ sCnt ].ItemObject,
fDisplayHatchOnItem,
PLAYERS_OFFER_AREA );
if( PlayersOfferArea[ sCnt ].uiFlags & ARMS_INV_PLAYERS_ITEM_HAS_VALUE )
uiTotalCost += PlayersOfferArea[ sCnt ].uiItemPrice;
// cross out unwanted items
if ( !(PlayersOfferArea[sCnt].uiFlags & ARMS_INV_JUST_PURCHASED) )
{
//If item can't be sold here, or it's completely worthless (very cheap / very broken)
if ( (WillShopKeeperRejectObjectsFromPlayer( gbSelectedArmsDealerID, sCnt ) == TRUE) || !(PlayersOfferArea[sCnt].uiFlags & ARMS_INV_PLAYERS_ITEM_HAS_VALUE) )
{
BltVideoObject( FRAME_BUFFER, hHandle, 0, (usPosX + 22), (usPosY), VO_BLT_SRCTRANSPARENCY, NULL );
// invalidate the region
InvalidateRegion( usPosX - 1, usPosY - 1, usPosX + SKI_INV_SLOT_WIDTH + 1, usPosY + SKI_INV_SLOT_HEIGHT + 1 );
}
}
}
usPosX += SKI_INV_OFFSET_X;
++sCount;
//if we are on to the next row
if ( !(sCount % SKI_TRADER_OFFER_BOXES_PER_ROW) )
{
usPosX = SKI_PLAYER_OFFER_BOXES_BEGIN_X;
usPosY += SKI_INV_OFFSET_Y;
}
}
if( gubSkiDirtyLevel == SKI_DIRTY_LEVEL2 )
{
//Display the total cost text
swprintf( zTemp, L"%s", FormatMoney(uiTotalCost).data() );
CHAR16 zTemp2[64];
if ( armsDealerInfo[gbSelectedArmsDealerID].uiFlags & ARMS_DEALER_DEALWITHINTEL )
swprintf( zTemp2, L"%s Intel", zTemp );
else
swprintf( zTemp2, L"%s", zTemp );
DrawTextToScreen( zTemp2, SKI_TOTAL_VALUE_X, SKI_TOTAL_VALUE_OFFSET_TO_VALUE, SKI_INV_SLOT_WIDTH, SKI_LABEL_FONT, SKI_ITEM_PRICE_COLOR, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED );
}
}
INVENTORY_IN_SLOT *GetPtrToOfferSlotWhereThisItemIs( UINT8 ubProfileID, INT16 bInvPocket )
{
for ( int ubCnt = 0; ubCnt < gPlayersOfferActiveRegions; ++ubCnt )
{
if( ( PlayersOfferArea[ ubCnt ].bSlotIdInOtherLocation == bInvPocket ) &&
( PlayersOfferArea[ ubCnt ].ubIdOfMercWhoOwnsTheItem == ubProfileID ) &&
( PlayersOfferArea[ ubCnt ].ItemObject.exists() == true ) )
{
return( &( PlayersOfferArea[ ubCnt ] ) );
}
}
for ( int ubCnt = 0; ubCnt < gDealersOfferActiveRegions; ++ubCnt )
{
if ( (ArmsDealerOfferArea[ubCnt].bSlotIdInOtherLocation == bInvPocket) &&
(ArmsDealerOfferArea[ubCnt].ubIdOfMercWhoOwnsTheItem == ubProfileID) &&
(ArmsDealerOfferArea[ubCnt].ItemObject.exists( ) == true) )
{
return(&(ArmsDealerOfferArea[ubCnt]));
}
}
// not found (perfectly valid result)
return( NULL );
}
BOOLEAN ShouldSoldierDisplayHatchOnItem( UINT8 ubProfileID, INT16 sSlotNum )
{
INVENTORY_IN_SLOT *pInvSlot = GetPtrToOfferSlotWhereThisItemIs( ubProfileID, sSlotNum );
if( pInvSlot == NULL )
// not found in either offer area
return( FALSE );
// found it, display it hatched out
return( TRUE );
}
UINT32 CalculateTotalArmsDealerCost()
{
UINT32 uiTotal=0;
for ( int uiCnt = 0; uiCnt< gDealersOfferActiveRegions; ++uiCnt )
{
if( ArmsDealerOfferArea[ uiCnt ].fActive )
{
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
uiTotal += CalculateObjectItemRepairCost( gbSelectedArmsDealerID, &( ArmsDealerOfferArea[ uiCnt ].ItemObject ) );
else
uiTotal += CalcShopKeeperItemPrice( DEALER_SELLING, FALSE, ArmsDealerOfferArea[ uiCnt ].sItemIndex, armsDealerInfo[ gbSelectedArmsDealerID ].dSellModifier, &ArmsDealerOfferArea[uiCnt].ItemObject );
}
}
return( uiTotal );
}
UINT32 CalculateTotalPlayersValue()
{
// we can't substitute money for intel
if ( armsDealerInfo[gbSelectedArmsDealerID].uiFlags & ARMS_DEALER_DEALWITHINTEL )
return 0;
UINT32 uiTotal = 0;
for ( int ubCnt = 0; ubCnt < gPlayersOfferActiveRegions; ++ubCnt )
{
if( ( PlayersOfferArea[ ubCnt ].fActive ) && ( PlayersOfferArea[ ubCnt ].uiFlags & ARMS_INV_PLAYERS_ITEM_HAS_VALUE ) )
{
//Calculate a price for the item
if( Item[ PlayersOfferArea[ ubCnt ].sItemIndex ].usItemClass == IC_MONEY )
uiTotal += PlayersOfferArea[ ubCnt ].ItemObject[0]->data.money.uiMoneyAmount;
else
uiTotal += PlayersOfferArea[ ubCnt ].uiItemPrice;
}
}
return( uiTotal );
}
void PerformTransaction( UINT32 uiMoneyFromPlayersAccount )
{
UINT32 uiPlayersTotalMoneyValue = CalculateTotalPlayersValue() + uiMoneyFromPlayersAccount;
UINT32 uiArmsDealersItemsCost = CalculateTotalArmsDealerCost();
UINT32 uiMoneyInPlayersOfferArea = CalculateHowMuchMoneyIsInPlayersOfferArea( );
INT32 iChangeToGiveToPlayer = 0;
int iAvailablePlayerOfferSlots;
//if the player has already requested to leave, get out
if( gfUserHasRequestedToLeave )
return;
// handle bribing... if the player is giving the dealer money, without buying anything
if( IsMoneyTheOnlyItemInThePlayersOfferArea( ) && CountNumberOfItemsInTheArmsDealersOfferArea( ) == 0 )
{
// accept/refuse money (varies by dealer according to ACCEPTS_GIFTS flag)
StartShopKeeperTalking( SK_QUOTES_DEALER_OFFERED_MONEY_AS_A_GIFT );
// if the arms dealer is the kind of person who accepts gifts
if( armsDealerInfo[ gbSelectedArmsDealerID ].uiFlags & ARMS_DEALER_ACCEPTS_GIFTS )
{
//Move all the players evaluated items to the dealer (also adds it to dealer's cash)
MovePlayerOfferedItemsOfValueToArmsDealersInventory();
DealerGetsBribed( armsDealerInfo[ gbSelectedArmsDealerID ].ubShopKeeperID, uiMoneyInPlayersOfferArea );
}
}
else // not a bribe
{
//if the dealer is not a repairman
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer != ARMS_DEALER_REPAIRS )
{
iAvailablePlayerOfferSlots = (gPlayersOfferActiveRegions - CountNumberOfValuelessItemsInThePlayersOfferArea( ));
// always reserve an empty slot for change. We can't tell for sure whether player is getting change because that
// could depend on how many items he's trying to buy won't fit - he could only be getting change because of that!
if ( iAvailablePlayerOfferSlots > 0 )
{
--iAvailablePlayerOfferSlots;
}
//if there is NOT enough room in the players offer area
if( CountNumberOfItemsInTheArmsDealersOfferArea( ) > iAvailablePlayerOfferSlots )
{
// tell player there's not enough room in the player's offer area
DoSkiMessageBox( MSG_BOX_BASIC_STYLE, SKI_Text[ SKI_TEXT_NO_MORE_ROOM_IN_PLAYER_OFFER_AREA ], SHOPKEEPER_SCREEN, MSG_BOX_FLAG_OK, NULL );
return;
}
}
//if the player doesn't have enough money to pay for what he's buying
if( uiArmsDealersItemsCost > uiPlayersTotalMoneyValue )
{
INT32 balance = LaptopSaveInfo.iCurrentBalance;
if ( armsDealerInfo[gbSelectedArmsDealerID].uiFlags & ARMS_DEALER_DEALWITHINTEL )
balance = (INT32)GetIntel();
//if the player doesn't have enough money in his account to pay the rest
if( uiArmsDealersItemsCost > uiPlayersTotalMoneyValue + balance )
{
// tell player he can't possibly afford this
SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_SHOPKEEPER, 6,0, 0, giShopKeeperFaceIndex, DIALOGUE_SHOPKEEPER_UI );
SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_SKIP_A_FRAME, 0,0, 0, giShopKeeperFaceIndex, DIALOGUE_SHOPKEEPER_UI );
SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_SHOPKEEPER, 0, ( uiArmsDealersItemsCost - ( balance + uiPlayersTotalMoneyValue ) ), 0, giShopKeeperFaceIndex, DIALOGUE_SHOPKEEPER_UI );
}
else
{
// player doesn't have enough on the table, but can pay for it from his balance
/// ask player if wants to subtract the shortfall directly from his balance
SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_SKIP_A_FRAME, 0,0, 0, giShopKeeperFaceIndex, DIALOGUE_SHOPKEEPER_UI );
SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_SHOPKEEPER, 6,0, 0, giShopKeeperFaceIndex, DIALOGUE_SHOPKEEPER_UI );
if ( armsDealerInfo[gbSelectedArmsDealerID].uiFlags & ARMS_DEALER_DEALWITHINTEL )
{
if ( uiPlayersTotalMoneyValue )
SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_SHOPKEEPER, 8, ( uiArmsDealersItemsCost - uiPlayersTotalMoneyValue ), 0, giShopKeeperFaceIndex, DIALOGUE_SHOPKEEPER_UI );
else
SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_SHOPKEEPER, 9, ( uiArmsDealersItemsCost - uiPlayersTotalMoneyValue ), 0, giShopKeeperFaceIndex, DIALOGUE_SHOPKEEPER_UI );
}
else
{
if ( uiPlayersTotalMoneyValue )
SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_SHOPKEEPER, 1, ( uiArmsDealersItemsCost - uiPlayersTotalMoneyValue ), 0, giShopKeeperFaceIndex, DIALOGUE_SHOPKEEPER_UI );
else
SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_SHOPKEEPER, 2, ( uiArmsDealersItemsCost - uiPlayersTotalMoneyValue ), 0, giShopKeeperFaceIndex, DIALOGUE_SHOPKEEPER_UI );
}
}
SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_SHOPKEEPER, 7,0, 0, giShopKeeperFaceIndex, DIALOGUE_SHOPKEEPER_UI );
gfResetShopKeepIdleQuote = TRUE;
// disable further calls to PerformTransaction() until callback calls us again to free this flag
gfPerformTransactionInProgress = TRUE;
return;
}
// to get this far, player should have the money needed!
Assert( uiPlayersTotalMoneyValue >= uiArmsDealersItemsCost );
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
//Make sure there is enough money in the Player's offer area to cover the repair charge
if( ( uiMoneyFromPlayersAccount + uiMoneyInPlayersOfferArea ) >= uiArmsDealersItemsCost )
{
//Move all the players evaluated items to the dealer ( For a repairman, this means just: get rid of the money )
MovePlayerOfferedItemsOfValueToArmsDealersInventory();
//if the arms dealer is the type of person to give change
if( armsDealerInfo[ gbSelectedArmsDealerID ].uiFlags & ARMS_DEALER_GIVES_CHANGE )
{
//determine the amount of change to give
iChangeToGiveToPlayer = ( uiMoneyFromPlayersAccount + uiMoneyInPlayersOfferArea ) - uiArmsDealersItemsCost;
//if there is change
if( iChangeToGiveToPlayer > 0 )
{
GivePlayerSomeChange( iChangeToGiveToPlayer );
if( uiArmsDealersItemsCost > 0 )
{
StartShopKeeperTalking( SK_QUOTES_PLAYER_HAS_TOO_MUCH_MONEY_FOR_TRANSACTION );
}
}
else
StartShopKeeperTalking( SK_QUOTES_PLAYER_HAS_EXACTLY_ENOUGH_MONEY_FOR_TRANSACTION );
}
// move the offered items to the repairman's inventory
MovePlayersItemsToBeRepairedToArmsDealersInventory();
}
else
{
//the player doesn't have enough money
return;
}
}
else // non-repairman
{
// DON'T include uiMoneyFromPlayersAccount in change given to player. The only way this can happen is if he agrees
// to subtract from his balance, but there isn't enough room. In that case, the cost of items not transfered is
// used to reduce his balance deduction instead (not here, in the calling function).
iChangeToGiveToPlayer = CalculateTotalPlayersValue() - uiArmsDealersItemsCost;
//if the arms dealer buys stuff
if( ( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_BUYS_ONLY ) ||
( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_BUYS_SELLS ) )
{
// but the dealer can't afford this transaction
if( iChangeToGiveToPlayer > ( INT32 ) gArmsDealerStatus[ gbSelectedArmsDealerID ].uiArmsDealersCash )
{
// HACK HACK HACK: We forgot to write/record quote 27 for Jake, so he ALWAYS must have enough money!
if ( gbSelectedArmsDealerID != ARMS_DEALER_JAKE )
{
// no deal - dealer can't afford it
StartShopKeeperTalking( SK_QUOTES_CANT_AFFORD_TO_BUY_OR_TOO_MUCH_TO_REPAIR );
gfResetShopKeepIdleQuote = TRUE;
return;
}
}
}
//Move all the player's evaluated items to the dealer
MovePlayerOfferedItemsOfValueToArmsDealersInventory();
//Move all dealers offered items to the player
MoveAllArmsDealersItemsInOfferAreaToPlayersOfferArea( );
#ifdef JA2UB
//JA25 UB
//if the arms dealer is Raul
if( gbSelectedArmsDealerID == ARMS_DEALER_RAUL )
{
//if this is the first time that the player purchased somehting form raul
if( gArmsDealerStatus[ gbSelectedArmsDealerID ].fHasSoldSomethingToPlayer == FALSE )
{
//if RAUL doesnt have the BARRETT anymore ie the player already bought it
//if( gArmsDealersInventory[ ARMS_DEALER_PERKO ][ BARRETT ].ubTotalItems != 0 )
//{
if( gubQuest[ QUEST_FIND_ANTIQUE_MUSKET_FOR_RAUL ] == QUESTNOTSTARTED )
{
//Raul Start the quest and have Raul say somehitng
StartShopKeeperTalking( SK_QUOTES_NPC_SPECIFIC_46 );
StartShopKeeperTalking( SK_QUOTES_NPC_SPECIFIC_47 );
StartQuest( QUEST_FIND_ANTIQUE_MUSKET_FOR_RAUL, -1, -1 );
}
//}
}
}
#endif
//if the arms dealer is the type of person to give change
if( armsDealerInfo[ gbSelectedArmsDealerID ].uiFlags & ARMS_DEALER_GIVES_CHANGE )
{
if( iChangeToGiveToPlayer > 0 )
{
GivePlayerSomeChange( iChangeToGiveToPlayer );
/*
//Remove the change the arms dealer is returning to the player
if( ( gArmsDealerStatus[ gbSelectedArmsDealerID ].uiArmsDealersCash - uiChangeToGiveToPlayer ) >= 0 )
gArmsDealerStatus[ gbSelectedArmsDealerID ].uiArmsDealersCash -= uiChangeToGiveToPlayer;
else
gArmsDealerStatus[ gbSelectedArmsDealerID ].uiArmsDealersCash = 0;
*/
if( uiArmsDealersItemsCost == 0 )
{
StartShopKeeperTalking( SK_QUOTES_PLAYER_HAS_EXACTLY_ENOUGH_MONEY_FOR_TRANSACTION );
}
else
{
StartShopKeeperTalking( SK_QUOTES_PLAYER_HAS_TOO_MUCH_MONEY_FOR_TRANSACTION );
}
}
else if( iChangeToGiveToPlayer == 0 )
StartShopKeeperTalking( SK_QUOTES_PLAYER_HAS_EXACTLY_ENOUGH_MONEY_FOR_TRANSACTION );
}
#ifdef JA2UB
//Bought from dealer specific stuff
//if the arms dealer is Betty, set fact 403
if( gbSelectedArmsDealerID == ARMS_DEALER_BETTY )
{
//if the player spent more the X
if( uiMoneyFromPlayersAccount >= SKI_BETTY_MINIMUM_AMOUNT_TO_SET_FACT )
{
//set the fact
SetFactTrue( FACT_PLAYER_PAID_BETTY_MORE_THEN_X_FOR_ITEM );
}
}
//if the arms dealer is Raul, set fact 407
if( gbSelectedArmsDealerID == ARMS_DEALER_RAUL )
{
//if the player spent more the X
if( uiMoneyFromPlayersAccount >= SKI_RAUL_MINIMUM_AMOUNT_TO_SET_FACT )
{
//set the fact
SetFactTrue( FACT_PLAYER_BOUGHT_SOMETHING_FROM_RAUL );
}
}
//Remeber the fact that the player has sold somehting to the player
gArmsDealerStatus[ gbSelectedArmsDealerID ].fHasSoldSomethingToPlayer = TRUE;
#endif
//if the arms dealer is tony
if( gbSelectedArmsDealerID == ARMS_DEALER_TONY )
gfHavePurchasedItemsFromTony = TRUE;
//if the arms dealer is Howard( 125), set fact 222
if( gbSelectedArmsDealerID == ARMS_DEALER_HOWARD )
SetFactTrue( 222 );
}
// add cash transfered from player's account directly to dealer's cash balance
if( uiMoneyFromPlayersAccount > 0 )
{
gArmsDealerStatus[ gbSelectedArmsDealerID ].uiArmsDealersCash += uiMoneyFromPlayersAccount;
}
//Redraw the screen
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
gfDoneBusinessThisSession = TRUE;
//The shopkeeper's inventory has changed, reinitialize
InitializeShopKeeper( FALSE );
}
gfResetShopKeepIdleQuote = TRUE;
}
void MoveAllArmsDealersItemsInOfferAreaToPlayersOfferArea( )
{
//for all items in the dealers items offer area
INT16 bSlotID=0;
#ifdef JA2UB
BOOLEAN fAddItemToPlayer=TRUE;
#endif
//loop through all the slots in the shopkeeper's offer area
for ( int uiCnt = 0; uiCnt<gDealersOfferActiveRegions; ++uiCnt )
{
//if there is an item here
if( ArmsDealerOfferArea[ uiCnt ].fActive )
{
#ifdef JA2UB
//JA25 UB
fAddItemToPlayer = TRUE;
//if the dealer is betty
if( gbSelectedArmsDealerID == ARMS_DEALER_BETTY )
{
//if the item is the laptop transmitter
if( ArmsDealerOfferArea[ uiCnt ].sItemIndex == LAPTOP_TRANSMITTER ) //4500
{
//Dont transfer the item
fAddItemToPlayer = FALSE;
//Tell the user we are going to attach the transmitter to the laptop
//( need to wait 1 frame caus ewe are currently in a msg box )
gubDisplayMsgBoxAskingUserToAttachTransmitter = TRNSMTR_MSG_BOX__1_FRAME_WAIT;
}
//if the item is the porno mag
if( ArmsDealerOfferArea[ uiCnt ].sItemIndex == PORNOS )
{
StartShopKeeperTalking( SK_QUOTES_WHEN_PLAYER_BUYS_PORNO );
}
//if the item is Tex's videos
if( ArmsDealerOfferArea[ uiCnt ].sItemIndex == TEX_MOVIE_ATTACK_CLYDESDALES ||//1356 ||
ArmsDealerOfferArea[ uiCnt ].sItemIndex == TEX_MOVIE_WILD_EAST ||//1357 ||
ArmsDealerOfferArea[ uiCnt ].sItemIndex == TEX_MOVIE_HAVE_HONDA )//1358 )
{
SetFactTrue( FACT_PLAYER_BOUGHT_A_TEX_VIDEO_FROM_BETTY );
}
}
if( fAddItemToPlayer ) //JA25 UB
{
#endif
bSlotID = AddItemToPlayersOfferArea( NO_PROFILE, &ArmsDealerOfferArea[ uiCnt ], -1 );
if( bSlotID != -1 )
{
PlayersOfferArea[ bSlotID ].uiFlags |= ARMS_INV_JUST_PURCHASED;
#ifdef JA2UB
//if the player has just purchased the BArrett form Raul
if( ( gbSelectedArmsDealerID == ARMS_DEALER_RAUL ) && ( ArmsDealerOfferArea[ uiCnt ].sItemIndex == BARRETT_UB ) )
{
// set a special flag
gArmsDealerStatus[ gbSelectedArmsDealerID ].ubSpecificDealerFlags |= ARMS_DEALER_FLAG__RAUL_HAS_SOLD_BARRETT_TO_PLAYER;
}
#endif
//if the player has just purchased a VIDEO_CAMERA from Franz Hinkle
if( ( gbSelectedArmsDealerID == ARMS_DEALER_FRANZ ) && ( ArmsDealerOfferArea[ uiCnt ].sItemIndex == VIDEO_CAMERA ) )
{
// set a special flag
gArmsDealerStatus[ gbSelectedArmsDealerID ].ubSpecificDealerFlags |= ARMS_DEALER_FLAG__FRANZ_HAS_SOLD_VIDEO_CAMERA_TO_PLAYER;
}
}
#ifdef JA2UB
}
#endif
//Remove the items out of the dealers inventory
RemoveItemFromDealersInventory( &ArmsDealerOfferArea[ uiCnt ], (UINT16)ArmsDealerOfferArea[ uiCnt ].bSlotIdInOtherLocation );
//Remove the items from the Shopkeepers Offer area
if( !RemoveItemFromArmsDealerOfferArea( (UINT16)uiCnt, FALSE ) )//ArmsDealerOfferArea[uiCnt].bSlotIdInOtherLocation
Assert( 0 );
if( ArmsDealerOfferArea[uiCnt].fActive )
Assert(0);
}
}
//loop through all the slots in the shopkeeper's offer area
for ( int uiCnt = 0; uiCnt < gDealersOfferActiveRegions; ++uiCnt )
{
if( ArmsDealerOfferArea[uiCnt].fActive )
Assert(0);
}
}
BOOLEAN RemoveItemFromDealersInventory( INVENTORY_IN_SLOT* pInvSlot, UINT16 ubSlot )
{
//Remove all of this item out of the specified inventory slot
RemoveItemFromArmsDealerInventory( gbSelectedArmsDealerID, gpTempDealersInventory[ubSlot].sItemIndex, pInvSlot->ItemObject.ubNumberOfObjects, &pInvSlot->ItemObject );
gfResetShopKeepIdleQuote = TRUE;
return( TRUE );
}
void MovePlayerOfferedItemsOfValueToArmsDealersInventory()
{
#ifdef JA2UB
BOOLEAN fAddItemToDealer=TRUE;
#endif
//loop through all the slots in the players offer area
for ( int uiCnt = 0; uiCnt < gPlayersOfferActiveRegions; ++uiCnt )
{
//if there is an item here
if( PlayersOfferArea[ uiCnt ].fActive )
{
// and it has value
if( PlayersOfferArea[ uiCnt ].uiFlags & ARMS_INV_PLAYERS_ITEM_HAS_VALUE )
{
//Remove the item from the owner merc's inventory
IfMercOwnedRemoveItemFromMercInv( &(PlayersOfferArea[ uiCnt ]) );
//if the item is money
if( Item[ PlayersOfferArea[ uiCnt ].sItemIndex ].usItemClass == IC_MONEY )
{
//add the money to the dealers 'cash'
gArmsDealerStatus[ gbSelectedArmsDealerID ].uiArmsDealersCash += PlayersOfferArea[ uiCnt ].ItemObject[0]->data.money.uiMoneyAmount;
}
#ifdef JA2UB
else
{
fAddItemToDealer = TRUE;
//if the dealer is raul
if( gbSelectedArmsDealerID == ARMS_DEALER_RAUL )
{
//if the item is the cannon balls
if( PlayersOfferArea[ uiCnt ].sItemIndex == CLIP_CANNON_BALL )// 4499 )
{
fAddItemToDealer = FALSE;
}
//if the item is the antique musket
if( PlayersOfferArea[ uiCnt ].sItemIndex == HAND_CANNON )// 4498 )
{
//Only do this if the player knew about the cannon offer
if( gubQuest[ QUEST_FIND_ANTIQUE_MUSKET_FOR_RAUL ] == QUESTINPROGRESS )
{
//Have raul say a quote
StartShopKeeperTalking( SK_QUOTES_NPC_SPECIFIC_51 );
//Trigger Rauls action #52
TriggerNPCRecord( RAUL_UB, 52 );
//Remeber to discount the barrett
SetJa25GeneralFlag( JA_GF__BARRETT_IS_HALF_PRICE );
}
//Mark the quest done
EndQuest( QUEST_FIND_ANTIQUE_MUSKET_FOR_RAUL, -1, -1 );
fAddItemToDealer = FALSE;
}
}
//if we ARE to transfer the item
if( fAddItemToDealer )
{
//if the dealer doesn't strictly buy items from the player, give the item to the dealer
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer != ARMS_DEALER_BUYS_ONLY )
{
// item cease to be merc-owned during this operation
AddObjectToArmsDealerInventory( gbSelectedArmsDealerID, &( PlayersOfferArea[ uiCnt ].ItemObject ) );
PlayersOfferArea[ uiCnt ].ItemObject.initialize();
}
}
}
#else
else
{
//if the dealer doesn't strictly buy items from the player, give the item to the dealer
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer != ARMS_DEALER_BUYS_ONLY )
{
// item cease to be merc-owned during this operation
AddObjectToArmsDealerInventory( gbSelectedArmsDealerID, &( PlayersOfferArea[ uiCnt ].ItemObject ) );
PlayersOfferArea[ uiCnt ].ItemObject.initialize();
}
}
#endif
//erase the item from the player's offer area
ClearPlayersOfferSlot( uiCnt );
}
}
}
gfResetShopKeepIdleQuote = TRUE;
}
void BeginSkiItemPointer( UINT8 ubSource, INT16 bSlotNum, BOOLEAN fOfferToDealerFirst )
{
SGPRect Rect;
/*
// If we are already moving an item
if ( gMoveingItem.sItemIndex != 0 )
{
return;
}
*/
switch( ubSource )
{
case ARMS_DEALER_INVENTORY:
//Should never get in here
Assert( 0 );
return;
break;
case ARMS_DEALER_OFFER_AREA:
//Get the item from the slot.
gMoveingItem = ArmsDealerOfferArea[ bSlotNum ];
IfMercOwnedRemoveItemFromMercInv( &gMoveingItem );
//remove the item from the slot
ClearArmsDealerOfferSlot( bSlotNum );
//Restrict the cursor to the arms dealer offer area, players offer area and the players inventory
Rect.iLeft = 0;//SKI_ITEM_MOVEMENT_AREA_X;
Rect.iTop = SKI_DEALER_OFFER_AREA_Y;
Rect.iRight = SKI_ITEM_MOVEMENT_AREA_X + SKI_ITEM_MOVEMENT_AREA_WIDTH;
Rect.iBottom = SKI_ITEM_MOVEMENT_AREA_Y + SKI_ITEM_MOVEMENT_AREA_HEIGHT;
if((UsingNewInventorySystem() == true)){
Rect.iBottom += 60;
}
gpItemPointer = &gMoveingItem.ItemObject;
//if there is an owner of the item
if( gMoveingItem.ubIdOfMercWhoOwnsTheItem != -1 )
{
gpItemPointerSoldier = FindSoldierByProfileID( gMoveingItem.ubIdOfMercWhoOwnsTheItem, TRUE );
//make sure the soldier is not null
if( gpItemPointerSoldier == NULL )
{
gpItemPointerSoldier = gpSMCurrentMerc;
}
}
else
gpItemPointerSoldier = gpSMCurrentMerc;
break;
case PLAYERS_OFFER_AREA:
//Get the item from the slot.
gMoveingItem = PlayersOfferArea[ bSlotNum ];
// if the slot is overloaded (holds more objects than we have room for valid statuses of)
if ( PlayersOfferArea[ bSlotNum ].ItemObject.ubNumberOfObjects > MAX_OBJECTS_PER_SLOT )
{
// allow only MAX_OBJECTS_PER_SLOT of those objects to be picked up at a time
// (sure it kind of sucks, but it's a lot easier than handling overloaded cursor objects in Interface Items!
PlayersOfferArea[ bSlotNum ].ItemObject.MoveThisObjectTo(gMoveingItem.ItemObject, MAX_OBJECTS_PER_SLOT);
}
else // completely legal object
{
// NOTE: a merc-owned object can never be "overloaded", so no problem here
IfMercOwnedRemoveItemFromMercInv( &gMoveingItem );
//remove the item from the slot
ClearPlayersOfferSlot( bSlotNum );
}
//Restrict the cursor to the players offer area and the players inventory
Rect.iLeft = 0;//SKI_ITEM_MOVEMENT_AREA_X;
Rect.iTop = SKI_ITEM_MOVEMENT_AREA_Y;
Rect.iRight = SKI_ITEM_MOVEMENT_AREA_X + SKI_ITEM_MOVEMENT_AREA_WIDTH;
Rect.iBottom = SKI_ITEM_MOVEMENT_AREA_Y + SKI_ITEM_MOVEMENT_AREA_HEIGHT;
if((UsingNewInventorySystem() == true)){
Rect.iBottom += 60;
}
gpItemPointer = &gMoveingItem.ItemObject;
//if there is an owner of the item
if( gMoveingItem.ubIdOfMercWhoOwnsTheItem != -1 )
{
gpItemPointerSoldier = FindSoldierByProfileID( gMoveingItem.ubIdOfMercWhoOwnsTheItem, TRUE );
//make sure the soldier is not null
if( gpItemPointerSoldier == NULL )
{
gpItemPointerSoldier = gpSMCurrentMerc;
}
}
else
gpItemPointerSoldier = gpSMCurrentMerc;
#ifdef JA2UB
//ja25 ub
//if the dealer is Raul
if( gbSelectedArmsDealerID == ARMS_DEALER_RAUL )
{
//if the item is the antique musket
if( gMoveingItem.ItemObject.usItem == HAND_CANNON ) //4498 )
{
if( gubQuest[ QUEST_FIND_ANTIQUE_MUSKET_FOR_RAUL ] == QUESTINPROGRESS )
{
//if the player hasnt said the quote before
if( !( gArmsDealerStatus[ gbSelectedArmsDealerID ].ubSpecificDealerFlags & ARMS_DEALER_FLAG__RAUL_SAID_QUOTE_49 ) )
{
StartShopKeeperTalking( SK_QUOTES_NPC_SPECIFIC_49 );
gArmsDealerStatus[ gbSelectedArmsDealerID ].ubSpecificDealerFlags |= ARMS_DEALER_FLAG__RAUL_SAID_QUOTE_49;
}
}
}
}
#endif
break;
case PLAYERS_INVENTORY:
// better be a valid merc pocket index, or -1
Assert( ( bSlotNum >= -1 ) && ( bSlotNum < (INT8)gpSMCurrentMerc->inv.size() ) );
// if we're supposed to store the original pocket #, but that pocket still holds more of these
if ( ( bSlotNum != -1 ) && ( gpSMCurrentMerc->inv[ bSlotNum ].exists() == true ) )
{
// then we can't store the pocket #, because our system can't return stacked objects
bSlotNum = -1;
}
// First try to evaluate the item immediately like any other left-click on a merc's inventory slot would be. Only
// if that doesn't work (because there isn't enough room in the player's offer area), the item is picked up into
// the cursor, and may then get placed into the player's offer area directly, but it will NOT get evaluated that
// way, and so has no possibility of entering the dealer's inventory (where complex items aren't permitted).
if ( fOfferToDealerFirst && OfferObjectToDealer( gpItemPointer, gpSMCurrentMerc->ubProfile, bSlotNum ) )
{
//Reset the cursor
SetSkiCursor( CURSOR_NORMAL );
}
else // not supposed to offer it, or dealer has no more room
{
// store the current contents of the cursor in a temporary object structure.
// We have to do this before memsetting gMoveingItem, 'cause during swaps, gpItemPointer == &gMoveingItem.ItemObject!
gTempObject = *gpItemPointer;
//ARM: The memset was previously commented out, in order to preserve the owning merc's inv slot # during a swap of
// items in an inventory slot. However, that leads to other bugs: if you picked the thing you're swapping in from
// a restricted inv slot (headgear, vest, etc.), the item swapped out will end up belonging to an illegal slot, and
// return there with a right click on it in the player's offer area. So now ALL items picked up here are unowned.
gMoveingItem.initialize();
//Get the item from the pointer
gMoveingItem.ItemObject = gTempObject;
gMoveingItem.fActive = TRUE;
gMoveingItem.sItemIndex = gTempObject.usItem;
gMoveingItem.ubLocationOfObject = ubSource;
// By necessity, these items don't belong to a slot (so you can't return them via a right click),
// because it would be too much work to handle attachments, members of a stack, or even items swapped out of slots.
gMoveingItem.ubIdOfMercWhoOwnsTheItem = gpSMCurrentMerc->ubProfile;
gMoveingItem.bSlotIdInOtherLocation = bSlotNum;
//Restrict the cursor to the players offer area and the players inventory
Rect.iLeft = 0;//SKI_ITEM_MOVEMENT_AREA_X;
Rect.iTop = SKI_ITEM_MOVEMENT_AREA_Y;
Rect.iRight = SKI_ITEM_MOVEMENT_AREA_X + SKI_ITEM_MOVEMENT_AREA_WIDTH;
Rect.iBottom = SKI_ITEM_MOVEMENT_AREA_Y + SKI_ITEM_MOVEMENT_AREA_HEIGHT;
if((UsingNewInventorySystem() == true)){
Rect.iBottom += 60;
}
gpItemPointer = &gMoveingItem.ItemObject;
gpItemPointerSoldier = gpSMCurrentMerc;
}
break;
}
// if we have something in hand
if ( gpItemPointer != NULL )
{
//make sure the soldier is not null
Assert( gpItemPointerSoldier != NULL );
// Set mouse
SetSkiCursor( EXTERN_CURSOR );
//Enable the region that limits the movement of the cursor with the item
MSYS_EnableRegion( &gSkiInventoryMovementAreaMouseRegions );
RestrictMouseCursor( &Rect );
DisableAllDealersInventorySlots();
if ( ubSource != ARMS_DEALER_OFFER_AREA )
{
DisableAllDealersOfferSlots();
}
gfResetShopKeepIdleQuote = TRUE;
}
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
}
void RestrictSkiMouseCursor()
{
SGPRect Rect;
Rect.iLeft = 0;//SKI_ITEM_MOVEMENT_AREA_X;
Rect.iTop = SKI_ITEM_MOVEMENT_AREA_Y;
Rect.iRight = SKI_ITEM_MOVEMENT_AREA_X + SKI_ITEM_MOVEMENT_AREA_WIDTH;
Rect.iBottom = SKI_ITEM_MOVEMENT_AREA_Y + SKI_ITEM_MOVEMENT_AREA_HEIGHT;
RestrictMouseCursor( &Rect );
}
void SetSkiCursor( UINT16 usCursor )
{
int ubCnt;
//if we are setting up an item as a cursor
if( usCursor == EXTERN_CURSOR )
{
// EnableSMPanelButtons( FALSE, FALSE );
// if the current merc is in range
if( !gfSMDisableForItems )
{
// hatch out unavailable merc inventory slots
ReevaluateItemHatches( gpSMCurrentMerc, FALSE );
}
// Set mouse
guiExternVo = GetInterfaceGraphicForItem( &(Item[ gMoveingItem.sItemIndex ]) );
gusExternVoSubIndex = g_bUsePngItemImages ? 0 : Item[ gMoveingItem.sItemIndex ].ubGraphicNum;
SetCurrentCursorFromDatabase( EXTERN_CURSOR );
MSYS_ChangeRegionCursor( &gSMPanelRegion, usCursor );
MSYS_ChangeRegionCursor( &gSKI_EntireScreenMouseRegions, usCursor );
MSYS_ChangeRegionCursor( &gArmsDealersDropItemToGroundMouseRegions, usCursor );
MSYS_SetCurrentCursor( usCursor );
//if the item desc window is up
if( gInvDesc.uiFlags & MSYS_REGION_EXISTS )
MSYS_ChangeRegionCursor( &gInvDesc, usCursor );
for( ubCnt = 0; ubCnt < gMoveingItem.ItemObject[0]->attachments.size(); ++ubCnt)
{
if( gItemDescAttachmentRegions[ubCnt].uiFlags & MSYS_REGION_EXISTS )
MSYS_ChangeRegionCursor( &gItemDescAttachmentRegions[ubCnt], usCursor );
}
for ( ubCnt = 0; ubCnt<gDealersOfferActiveRegions; ++ubCnt )
{
MSYS_ChangeRegionCursor( &gPlayersOfferSlotsMouseRegions[ ubCnt], usCursor );
MSYS_ChangeRegionCursor( &gPlayersOfferSlotsSmallFaceMouseRegions[ ubCnt], usCursor );
MSYS_ChangeRegionCursor( &gDealersOfferSlotsMouseRegions[ ubCnt], usCursor );
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
MSYS_ChangeRegionCursor( &gDealersOfferSlotsSmallFaceMouseRegions[ ubCnt], usCursor );
}
}
MSYS_ChangeRegionCursor( &gSkiInventoryMovementAreaMouseRegions, usCursor );
}
//else we are restoring the old cursor
else
{
gMoveingItem.initialize();
// gpSkiItemPointer = NULL;
gpItemPointer = NULL;
DisableTacticalTeamPanelButtons( FALSE );
// EnableSMPanelButtons( TRUE, FALSE );
// CheckForDisabledForGiveItem( );
// if the current merc is in range
if( !gfSMDisableForItems )
{
// make all merc inventory slots available again
ReevaluateItemHatches( gpSMCurrentMerc, TRUE );
}
MSYS_ChangeRegionCursor( &gSMPanelRegion, usCursor );
MSYS_ChangeRegionCursor( &gSKI_EntireScreenMouseRegions, usCursor );
MSYS_ChangeRegionCursor( &gArmsDealersDropItemToGroundMouseRegions, usCursor );
for ( ubCnt = 0; ubCnt<gDealersOfferActiveRegions; ++ubCnt )
{
MSYS_ChangeRegionCursor( &gDealersOfferSlotsMouseRegions[ ubCnt ], usCursor );
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
MSYS_ChangeRegionCursor( &gDealersOfferSlotsSmallFaceMouseRegions[ ubCnt ], usCursor );
}
}
for ( ubCnt = 0; ubCnt<gPlayersOfferActiveRegions; ++ubCnt )
{
MSYS_ChangeRegionCursor( &gPlayersOfferSlotsMouseRegions[ubCnt], usCursor );
MSYS_ChangeRegionCursor( &gPlayersOfferSlotsSmallFaceMouseRegions[ubCnt], usCursor );
}
//if the item desc window is up
if( gInvDesc.uiFlags & MSYS_REGION_EXISTS )
MSYS_ChangeRegionCursor( &gInvDesc, usCursor );
for( ubCnt = 0; ubCnt < gMoveingItem.ItemObject[0]->attachments.size(); ++ubCnt)
{
if( gItemDescAttachmentRegions[ubCnt].uiFlags & MSYS_REGION_EXISTS )
MSYS_ChangeRegionCursor( &gItemDescAttachmentRegions[ubCnt], usCursor );
}
MSYS_ChangeRegionCursor( &gSkiInventoryMovementAreaMouseRegions, usCursor );
MSYS_SetCurrentCursor( usCursor );
SetCurrentCursorFromDatabase( usCursor );
FreeMouseCursor( TRUE );
EnableAllDealersInventorySlots();
EnableAllDealersOfferSlots();
//Disable the region that limits the movement of the cursor with the item
MSYS_DisableRegion( &gSkiInventoryMovementAreaMouseRegions );
}
SetCurrentCursorFromDatabase( usCursor );
// make sure disabled slot hatching gets updated when items picked up / dropped
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
}
INT16 AddInventoryToSkiLocation( INVENTORY_IN_SLOT *pInv, UINT16 ubSpotLocation, UINT8 ubWhere )
{
INT16 bSlotAddedTo = -1;
switch( ubWhere )
{
case ARMS_DEALER_INVENTORY:
case PLAYERS_INVENTORY:
// not used this way
Assert( 0 );
return( bSlotAddedTo );
break;
case ARMS_DEALER_OFFER_AREA:
//If we can add the item into the slot that was clicked on
if( ArmsDealerOfferArea[ ubSpotLocation ].fActive == FALSE )
{
ArmsDealerOfferArea[ ubSpotLocation ] = *pInv;
IfMercOwnedCopyItemToMercInv( pInv );
SetSkiRegionHelpText( &ArmsDealerOfferArea[ ubSpotLocation ], &gDealersOfferSlotsMouseRegions[ ubSpotLocation ], ARMS_DEALER_OFFER_AREA );
SetSkiFaceRegionHelpText( &ArmsDealerOfferArea[ ubSpotLocation ], &gDealersOfferSlotsSmallFaceMouseRegions[ ubSpotLocation ], ARMS_DEALER_OFFER_AREA );
bSlotAddedTo = ubSpotLocation;
}
else
{
bSlotAddedTo = AddItemToArmsDealerOfferArea( pInv, -1 );
}
break;
case PLAYERS_OFFER_AREA:
//If we can add the item into the slot that was clicked on
if( PlayersOfferArea[ ubSpotLocation ].fActive == FALSE )
{
// put it down in that player offer area slot
PlayersOfferArea[ ubSpotLocation ] = *pInv;
IfMercOwnedCopyItemToMercInv( pInv );
//if the item is money
if( Item[ PlayersOfferArea[ ubSpotLocation ].sItemIndex ].usItemClass == IC_MONEY && !(armsDealerInfo[gbSelectedArmsDealerID].uiFlags & ARMS_DEALER_DEALWITHINTEL) )
{
//Since money is always evaluated
PlayersOfferArea[ ubSpotLocation ].uiFlags |= ARMS_INV_PLAYERS_ITEM_HAS_VALUE;
PlayersOfferArea[ ubSpotLocation ].uiItemPrice = PlayersOfferArea[ ubSpotLocation ].ItemObject[0]->data.money.uiMoneyAmount;
}
SetSkiRegionHelpText( &PlayersOfferArea[ ubSpotLocation ], &gPlayersOfferSlotsMouseRegions[ ubSpotLocation ], PLAYERS_OFFER_AREA );
SetSkiFaceRegionHelpText( &PlayersOfferArea[ ubSpotLocation ], &gPlayersOfferSlotsSmallFaceMouseRegions[ ubSpotLocation ], PLAYERS_OFFER_AREA );
bSlotAddedTo = ubSpotLocation;
}
// else if( )
// {
//check to see the type we are adding is the same as the type that is already there
// }
else // that slot is full
{
// add it elsewhere
bSlotAddedTo = AddItemToPlayersOfferArea( pInv->ubIdOfMercWhoOwnsTheItem, pInv, pInv->bSlotIdInOtherLocation );
}
break;
}
//Redraw the screen
gubSkiDirtyLevel = SKI_DIRTY_LEVEL1;
gfResetShopKeepIdleQuote = TRUE;
fInterfacePanelDirty = DIRTYLEVEL2;
return( bSlotAddedTo );
}
void DisplayTalkingArmsDealer()
{
static BOOLEAN fWasTheMercTalking= FALSE;
// static UINT32 uiLastTime=0;
// UINT32 uiCurTime = GetJA2Clock();
// static UINT32 uiMinimumLengthForTalkingText;
//Make sure the Dealers doesn't get disabled
if ( giShopKeeperFaceIndex > -1 )
gFacesData[ giShopKeeperFaceIndex ].fDisabled = FALSE;
HandleDialogue();
//Gets handled when we render the tactical interface
HandleAutoFaces( );
HandleTalkingAutoFaces( );
HandleShopKeeperDialog( 0 );
gfIsTheShopKeeperTalking = FALSE;
if ( giShopKeeperFaceIndex > -1 )
gfIsTheShopKeeperTalking = gFacesData[ giShopKeeperFaceIndex ].fTalking;
//if the merc just started talking
// if( gfIsTheShopKeeperTalking && !fWasTheMercTalking )
{
}
//if the merc is talking
if( gfIsTheShopKeeperTalking )
fWasTheMercTalking = TRUE;
//if the merc just finished talking
if( !gfIsTheShopKeeperTalking && fWasTheMercTalking )
{
RemoveShopKeeperSubTitledText();
fWasTheMercTalking = FALSE;
}
}
void HandleShopKeeperDialog( UINT8 ubInit )
{
UINT32 uiCurTime = GetJA2Clock();
static UINT32 uiLastTime = 0;
static UINT32 uiLastTimeSpoke = 0;
static INT8 bSpeech = -1;
if( ubInit >= 1 )
{
uiLastTime = GetJA2Clock();
}
if( ubInit == 1 )
{
// special: if it's Arnie, and we have stuff in for repairs, but it's not fixed yet, use a different opening quote!
if ( ( gbSelectedArmsDealerID == ARMS_DEALER_ARNIE ) && RepairmanIsFixingItemsButNoneAreDoneYet( ARNIE ) )
{
bSpeech = ARNIE_QUOTE_NOT_REPAIRED_YET;
}
// if repairs were delayed
if ( gfStartWithRepairsDelayedQuote )
{
bSpeech = FREDO_PERKO_SORRY_REPAIR_DELAYED;
gfStartWithRepairsDelayedQuote = FALSE;
// treat this as having done business - the player WAS here for a good reason, and it's the dealers fault...
gfDoneBusinessThisSession = TRUE;
}
else
{
bSpeech = SK_QUOTES_PLAYER_FIRST_ENTERS_SKI;
}
return;
}
//if its the first time in
if( bSpeech != -1 )
{
if( ( uiCurTime - uiLastTime ) > 800 )
{
uiLastTimeSpoke = GetJA2Clock();
StartShopKeeperTalking( bSpeech );
bSpeech = -1;
uiLastTime = GetJA2Clock();
}
}
//Handle Idle converstions
if( gfIsTheShopKeeperTalking )
{
uiLastTime = uiCurTime;
}
//if the player has requested to leave
else if( gfUserHasRequestedToLeave && ( giShopKeepDialogueEventinProgress == -1 ) )
{
// to see if the player has finished talking
if( !gFacesData[ giShopKeeperFaceIndex ].fTalking )
{
SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_SHOPKEEPER, 5, 0, 0, giShopKeeperFaceIndex, DIALOGUE_SHOPKEEPER_UI );
}
DealWithItemsStillOnTheTable();
}
else
{
// Determine if the shopkeeper should say a quote
if( ( uiCurTime - uiLastTime ) > ( guiRandomQuoteDelayTime + Random( guiRandomQuoteDelayTime ) ) )
{
//Only say it occasionally
if( Chance ( 35 ) )
{
INT16 sRandomQuoteToUse = -1;
// first check if one of the situation warrants one of the more precise quotes
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
// if there are items in the arms dealer's offer area (must be awaiting repairs)
if ( CountNumberOfItemsInTheArmsDealersOfferArea() > 0)
{
sRandomQuoteToUse = SK_QUOTES_RANDOM_QUOTE_WHILE_ITEMS_CHOSEN_TO_SELL_OR_REPAIR;
}
}
else // non-repairman
{
// if there is any items in the player's area with value to this dealer other than just money
if ( ( CalculateTotalPlayersValue() - CalculateHowMuchMoneyIsInPlayersOfferArea( ) ) > 0 )
{
// if the player has also selected some items for purchase
if ( CountNumberOfItemsInTheArmsDealersOfferArea() > 0 )
{
sRandomQuoteToUse = SK_QUOTES_RANDOM_QUOTE_WHILE_ITEMS_CHOSEN_TO_TRADE;
}
else
{
sRandomQuoteToUse = SK_QUOTES_RANDOM_QUOTE_WHILE_ITEMS_CHOSEN_TO_SELL_OR_REPAIR;
}
}
}
#ifdef JA2UB
//ja25 UB
// if neither of the more precise quotes fit, or 33 percent of the time anyways
if ( ( sRandomQuoteToUse == -1 ) || Chance( 33 ) )
{
//If the dealer is Betty,
if( gbSelectedArmsDealerID == ARMS_DEALER_BETTY )
{
//she has 4 random quotes
UINT8 ubRandom = Random( 100 );
if( ubRandom > 75 )
sRandomQuoteToUse = SK_QUOTES_RANDOM_QUOTE_WHILE_PLAYER_DECIDING_1;
else if( ubRandom > 50 )
sRandomQuoteToUse = SK_QUOTES_RANDOM_QUOTE_WHILE_PLAYER_DECIDING_2;
else if( ubRandom > 25 )
sRandomQuoteToUse = SK_QUOTES_RANDOM_QUOTE_WHILE_PLAYER_DECIDING_3;
else
sRandomQuoteToUse = SK_QUOTES_RANDOM_QUOTE_WHILE_PLAYER_DECIDING_4;
}
else
{
if( Chance ( 50 ) )
sRandomQuoteToUse = SK_QUOTES_RANDOM_QUOTE_WHILE_PLAYER_DECIDING_1;
else
sRandomQuoteToUse = SK_QUOTES_RANDOM_QUOTE_WHILE_PLAYER_DECIDING_2;
}
}
Assert( sRandomQuoteToUse != -1 );
Assert( sRandomQuoteToUse < NUM_COMMON_SK_QUOTES );
if ( !gfCommonQuoteUsedThisSession[ sRandomQuoteToUse ] )
{
StartShopKeeperTalking( (UINT16) sRandomQuoteToUse );
gfCommonQuoteUsedThisSession[ sRandomQuoteToUse ] = TRUE;
//increase the random quote delay
guiRandomQuoteDelayTime += SKI_DEALERS_RANDOM_QUOTE_DELAY_INCREASE_RATE;
}
#else
// if neither of the more precise quotes fit, or 33 percent of the time anyways
if ( ( sRandomQuoteToUse == -1 ) || Chance( 33 ) )
{
if( Chance ( 50 ) )
sRandomQuoteToUse = SK_QUOTES_RANDOM_QUOTE_WHILE_PLAYER_DECIDING_1;
else
sRandomQuoteToUse = SK_QUOTES_RANDOM_QUOTE_WHILE_PLAYER_DECIDING_2;
}
Assert( sRandomQuoteToUse != -1 );
Assert( sRandomQuoteToUse < NUM_COMMON_SK_QUOTES );
if ( !gfCommonQuoteUsedThisSession[ sRandomQuoteToUse ] )
{
StartShopKeeperTalking( (UINT16) sRandomQuoteToUse );
gfCommonQuoteUsedThisSession[ sRandomQuoteToUse ] = TRUE;
//increase the random quote delay
guiRandomQuoteDelayTime += SKI_DEALERS_RANDOM_QUOTE_DELAY_INCREASE_RATE;
}
#endif
}
uiLastTime = GetJA2Clock();
}
}
}
BOOLEAN StartShopKeeperTalking( UINT16 usQuoteNum )
{
BOOLEAN fSuccess;
// if already in the process of leaving, don't start any additional quotes
if ( gfSKIScreenExit || gfRemindedPlayerToPickUpHisStuff || gfUserHasRequestedToLeave )
{
return( FALSE );
}
// post event to mark shopkeeper dialogue in progress
SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_SHOPKEEPER, 3, usQuoteNum, 0, giShopKeeperFaceIndex, DIALOGUE_SHOPKEEPER_UI );
// post quote dialogue
fSuccess = CharacterDialogue( armsDealerInfo[ gbSelectedArmsDealerID ].ubShopKeeperID, usQuoteNum, giShopKeeperFaceIndex, DIALOGUE_SHOPKEEPER_UI, FALSE, FALSE );
// post event to mark shopkeeper dialogue as ended
SpecialCharacterDialogueEvent( DIALOGUE_SPECIAL_EVENT_SHOPKEEPER, 4, usQuoteNum, 0, giShopKeeperFaceIndex, DIALOGUE_SHOPKEEPER_UI );
if ( !fSuccess )
{
return( FALSE );
}
gfResetShopKeepIdleQuote = TRUE;
return( TRUE );
}
BOOLEAN IsGunOrAmmoOfSameTypeSelected( OBJECTTYPE *pItemObject )
{
//if there is no item selected, return
if( gpHighLightedItemObject == NULL )
return( FALSE );
//if the item is ammo
if( Item[ gpHighLightedItemObject->usItem ].usItemClass == IC_AMMO )
{
//if there is a gun
if( Item[ pItemObject->usItem ].usItemClass == IC_GUN )
{
//of the same caliber
if( Weapon[ pItemObject->usItem ].ubCalibre == Magazine[ Item[ gpHighLightedItemObject->usItem ].ubClassIndex ].ubCalibre )
{
return( TRUE );
}
}
}
//else if the item is a gun
else if( Item[ gpHighLightedItemObject->usItem ].usItemClass == IC_GUN )
{
//if there is a gun
if( Item[ pItemObject->usItem ].usItemClass == IC_AMMO )
{
//of the same caliber
if( Weapon[ gpHighLightedItemObject->usItem ].ubCalibre == Magazine[ Item[ pItemObject->usItem ].ubClassIndex ].ubCalibre )
{
return( TRUE );
}
}
}
//if the highlighted object is an attachment
if(ItemIsAttachment(pItemObject->usItem))
{
if( ValidAttachment( pItemObject->usItem, gpHighLightedItemObject ) )
return( TRUE );
}
else
{
if( ValidAttachment( gpHighLightedItemObject->usItem, pItemObject ) )
return( TRUE );
}
return( FALSE );
}
void InitShopKeeperSubTitledText( STR16 pString )
{
//Clear the contents of the subtitle text
memset(gsShopKeeperTalkingText, 0, sizeof(CHAR16)* SKI_SUBTITLE_TEXT_SIZE);
swprintf( gsShopKeeperTalkingText, L"\"%s\"", pString );
//Now setup the popup box
if( gGameSettings.fOptions[ TOPTION_SUBTITLES ] )
{
UINT16 usActualWidth=0;
UINT16 usActualHeight=0;
giPopUpBoxId = PrepareMercPopupBox( giPopUpBoxId, BASIC_MERC_POPUP_BACKGROUND, BASIC_MERC_POPUP_BORDER, gsShopKeeperTalkingText, 300, 0, 0, 0, &usActualWidth, &usActualHeight);
// gusPositionOfSubTitlesX = ( 640 - usActualWidth ) / 2 ;
//position it to start under the guys face
gusPositionOfSubTitlesX = 13 + SCREEN_X_OFFSET;
RenderMercPopUpBoxFromIndex( giPopUpBoxId, gusPositionOfSubTitlesX, SKI_POSITION_SUBTITLES_Y, FRAME_BUFFER);
//check to make sure the region is not already initialized
if( !( gShopKeeperSubTitleMouseRegion.uiFlags & MSYS_REGION_EXISTS ) )
{
MSYS_DefineRegion( &gShopKeeperSubTitleMouseRegion, gusPositionOfSubTitlesX, SKI_POSITION_SUBTITLES_Y, (INT16)(gusPositionOfSubTitlesX + usActualWidth), (INT16)(SKI_POSITION_SUBTITLES_Y + usActualHeight), MSYS_PRIORITY_HIGH,
CURSOR_NORMAL, MSYS_NO_CALLBACK, ShopKeeperSubTitleRegionCallBack );
MSYS_AddRegion( &gShopKeeperSubTitleMouseRegion );
}
//redraw the screen
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
}
}
void RemoveShopKeeperSubTitledText()
{
if( giPopUpBoxId == -1 )
return;
if( RemoveMercPopupBoxFromIndex( giPopUpBoxId ) )
{
giPopUpBoxId = -1;
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
//Get rid of the subtitles region
if( gGameSettings.fOptions[ TOPTION_SUBTITLES ] )
MSYS_RemoveRegion( &gShopKeeperSubTitleMouseRegion );
}
}
BOOLEAN AreThereItemsInTheArmsDealersOfferArea( )
{
//loop through the players offer area and see if there are any items there
for ( int ubCnt = 0; ubCnt < gDealersOfferActiveRegions; ++ubCnt )
{
//if is an item here
if( ArmsDealerOfferArea[ ubCnt ].fActive )
{
return( TRUE );
}
}
return( FALSE );
}
BOOLEAN AreThereItemsInThePlayersOfferArea( )
{
//loop through the players offer area and see if there are any items there
for ( int ubCnt = 0; ubCnt < gPlayersOfferActiveRegions; ++ubCnt )
{
//if is an item here
if( PlayersOfferArea[ ubCnt ].fActive )
{
return( TRUE );
}
}
return( FALSE );
}
//Mouse Call back for the Arms traders inventory slot
void ShopKeeperSubTitleRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason )
{
if (iReason & MSYS_CALLBACK_REASON_INIT)
{
}
else if(iReason & MSYS_CALLBACK_REASON_LBUTTON_UP || iReason & MSYS_CALLBACK_REASON_RBUTTON_UP)
{
ShutUpShopKeeper();
}
}
//Mouse Call back for the Arms delaers face
void SelectArmsDealersFaceRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason )
{
if (iReason & MSYS_CALLBACK_REASON_INIT)
{
}
else if(iReason & MSYS_CALLBACK_REASON_LBUTTON_UP || iReason & MSYS_CALLBACK_REASON_RBUTTON_UP)
{
ShutUpShopKeeper();
}
}
void ShutUpShopKeeper()
{
// RemoveShopKeeperSubTitledText();
ShutupaYoFace( giShopKeeperFaceIndex );
gfIsTheShopKeeperTalking = FALSE;
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
}
int CountNumberOfValuelessItemsInThePlayersOfferArea( )
{
int count = 0;
//loop through the players offer area and see if there are any items there
for ( int ubCnt = 0; ubCnt<gPlayersOfferActiveRegions; ++ubCnt )
{
//if is an item here
if( PlayersOfferArea[ubCnt].fActive )
{
//and if it has no value
if( !( PlayersOfferArea[ubCnt].uiFlags & ARMS_INV_PLAYERS_ITEM_HAS_VALUE ) )
{
++count;
}
}
}
return( count );
}
int CountNumberOfItemsInThePlayersOfferArea( )
{
int count = 0;
//loop through the player's offer area and see if there are any items there
for ( int ubCnt = 0; ubCnt < gPlayersOfferActiveRegions; ++ubCnt )
{
//if is an item here
if( PlayersOfferArea[ubCnt].fActive )
{
++count;
}
}
return( count );
}
int CountNumberOfItemsInTheArmsDealersOfferArea( )
{
int count = 0;
//loop through the dealer's offer area and see if there are any items there
for ( int ubCnt = 0; ubCnt < gDealersOfferActiveRegions; ++ubCnt )
{
//if is an item here
if( ArmsDealerOfferArea[ubCnt].fActive )
{
++count;
}
}
return( count );
}
INT8 GetSlotNumberForMerc( UINT8 ubProfile )
{
for( UINT16 bCnt = 0; bCnt < gubNumberMercsInArray; ++bCnt )
{
if( gubArrayOfEmployedMercs[ bCnt ] == ubProfile )
return( bCnt );
}
// not found - not currently working for the player
return( -1 );
}
void EnableDisableDealersInventoryPageButtons()
{
//if we are on the first page, disable the page up arrow
if( gSelectArmsDealerInfo.ubCurrentPage <= 1 )
{
DisableButton( guiSKI_InvPageUpButton );
}
else
{
EnableButton( guiSKI_InvPageUpButton );
}
//if we are on the last page, disable the page down button
if( gSelectArmsDealerInfo.ubCurrentPage == gSelectArmsDealerInfo.ubNumberOfPages )
{
DisableButton( guiSKI_InvPageDownButton );
}
else
{
EnableButton( guiSKI_InvPageDownButton );
}
}
void EnableDisableShopkeeperButtons(UINT32 uiCurrentItemDescriptionScreen, int bpAction)
{
if(uiCurrentItemDescriptionScreen != SHOPKEEPER_SCREEN)
return;
switch (bpAction)
{
case ACTIVATE_BUTTON:
if(guiSKI_TransactionButton != 0)
RemoveButton( guiSKI_TransactionButton );
if(guiSKI_TransactionButtonImage != 0)
UnloadButtonImage( guiSKI_TransactionButtonImage );
if(guiSKI_DoneButton != 0)
RemoveButton( guiSKI_DoneButton );
if(guiSKI_DoneButtonImage != 0)
UnloadButtonImage( guiSKI_DoneButtonImage );
guiSKI_TransactionButtonImage = LoadButtonImage("INTERFACE\\TradeButtons.sti", -1,0,-1,1,-1 );
guiSKI_DoneButtonImage = UseLoadedButtonImage( guiSKI_TransactionButtonImage, -1,0,-1,1,-1 );
guiSKI_TransactionButton = CreateIconAndTextButton( guiSKI_TransactionButtonImage, SKI_Text[SKI_TEXT_TRANSACTION], SKI_BUTTON_FONT,
SKI_BUTTON_COLOR, DEFAULT_SHADOW, SKI_BUTTON_COLOR, DEFAULT_SHADOW, TEXT_CJUSTIFIED,
SKI_TRANSACTION_BUTTON_X, SKI_TRANSACTION_BUTTON_Y, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH,
DEFAULT_MOVE_CALLBACK, BtnSKI_TransactionButtonCallback);
SpecifyDisabledButtonStyle( guiSKI_TransactionButton, DISABLED_STYLE_HATCHED );
//if the dealer repairs, use the repair fast help text for the transaction button
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
SetButtonFastHelpText( guiSKI_TransactionButton, SkiMessageBoxText[ SKI_REPAIR_TRANSACTION_BUTTON_HELP_TEXT ] );
else
SetButtonFastHelpText( guiSKI_TransactionButton, SkiMessageBoxText[ SKI_TRANSACTION_BUTTON_HELP_TEXT ] );
//Done button
guiSKI_DoneButton = CreateIconAndTextButton( guiSKI_DoneButtonImage, SKI_Text[SKI_TEXT_DONE], SKI_BUTTON_FONT,
SKI_BUTTON_COLOR, DEFAULT_SHADOW, SKI_BUTTON_COLOR, DEFAULT_SHADOW, TEXT_CJUSTIFIED,
SKI_DONE_BUTTON_X, SKI_DONE_BUTTON_Y, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH+10,
DEFAULT_MOVE_CALLBACK, BtnSKI_DoneButtonCallback);
SpecifyDisabledButtonStyle( guiSKI_DoneButton, DISABLED_STYLE_HATCHED );
SetButtonFastHelpText( guiSKI_DoneButton, SkiMessageBoxText[ SKI_DONE_BUTTON_HELP_TEXT ] );
break;
case DEACTIVATE_BUTTON:
if(guiSKI_TransactionButton != 0)
RemoveButton( guiSKI_TransactionButton );
if(guiSKI_DoneButton != 0)
RemoveButton( guiSKI_DoneButton );
EnableDisableShopkeeperButtons(uiCurrentItemDescriptionScreen, UNLOAD_BUTTON);
guiSKI_TransactionButtonImage = 0;
guiSKI_TransactionButton = 0;
guiSKI_DoneButtonImage = 0;
guiSKI_DoneButton = 0;
break;
case ENABLE_BUTTON:
if(guiSKI_TransactionButton == 0 || guiSKI_DoneButton == 0)
{
EnableDisableShopkeeperButtons(uiCurrentItemDescriptionScreen, DEACTIVATE_BUTTON);
EnableDisableShopkeeperButtons(uiCurrentItemDescriptionScreen, ACTIVATE_BUTTON);
}
if(guiSKI_TransactionButton != 0)
EnableButton( guiSKI_TransactionButton );
if(guiSKI_DoneButton != 0)
EnableButton( guiSKI_DoneButton );
break;
case DISABLE_BUTTON:
if(guiSKI_TransactionButton != 0)
DisableButton( guiSKI_TransactionButton );
if(guiSKI_DoneButton != 0)
DisableButton( guiSKI_DoneButton );
break;
case UNLOAD_BUTTON:
if(guiSKI_TransactionButtonImage != 0)
UnloadButtonImage( guiSKI_TransactionButtonImage );
if(guiSKI_DoneButtonImage != 0)
UnloadButtonImage( guiSKI_DoneButtonImage );
break;
}
}
void EnableDisableEvaluateAndTransactionButtons()
{
BOOLEAN fItemsHere=FALSE;
BOOLEAN fItemEvaluated=FALSE;
UINT32 uiArmsDealerTotalCost = CalculateTotalArmsDealerCost();
UINT32 uiPlayersOfferAreaTotalCost = CalculateTotalPlayersValue();
UINT32 uiPlayersOfferAreaTotalMoney = CalculateHowMuchMoneyIsInPlayersOfferArea( );
//loop through the players offer area
//loop through all the items in the players offer area and determine if they can be sold here.
for ( int ubCnt = 0; ubCnt < gPlayersOfferActiveRegions; ++ubCnt )
{
//if there is an item here
if( PlayersOfferArea[ ubCnt ].fActive )
{
fItemsHere = TRUE;
//if the item has value
if( PlayersOfferArea[ ubCnt ].uiFlags & ARMS_INV_PLAYERS_ITEM_HAS_VALUE )
{
//if the item isnt money ( which is always evaluated )
if( Item[ PlayersOfferArea[ ubCnt ].sItemIndex ].usItemClass != IC_MONEY )
fItemEvaluated = TRUE;
//else if it is not a repair dealer, and the item is money
else if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer != ARMS_DEALER_REPAIRS &&
Item[ PlayersOfferArea[ ubCnt ].sItemIndex ].usItemClass == IC_MONEY &&
!( armsDealerInfo[gbSelectedArmsDealerID].uiFlags & ARMS_DEALER_DEALWITHINTEL ) )
fItemEvaluated = TRUE;
}
}
}
for ( int ubCnt = 0; ubCnt < gDealersOfferActiveRegions; ++ubCnt )
{
//if the dealer is a repair dealer
if ( armsDealerInfo[gbSelectedArmsDealerID].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
//if there is an item here, the item has to have been evaluated
if ( ArmsDealerOfferArea[ubCnt].fActive )
{
fItemEvaluated = TRUE;
}
}
}
//if there are evaluated items here
if( fItemEvaluated )
{
// //and if the players offer area value exceeds the shopkeeper's area
// if( uiPlayersOfferAreaTotalCost >= uiArmsDealerTotalCost )
EnableButton( guiSKI_TransactionButton );
// else
// DisableButton( guiSKI_TransactionButton );
/*
//if the only item in the players offer area is money, and there is nothing in the dealers offer area
if( IsMoneyTheOnlyItemInThePlayersOfferArea( ) && uiArmsDealerTotalCost == 0 )
DisableButton( guiSKI_TransactionButton );
*/
//If its a repair dealer, and there is no items in the Dealer Offer area,
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS &&
CountNumberOfItemsInTheArmsDealersOfferArea( ) == 0 &&
uiPlayersOfferAreaTotalMoney < uiArmsDealerTotalCost
)
DisableButton( guiSKI_TransactionButton );
}
//else if there is
else if( uiArmsDealerTotalCost != 0 )
{
EnableButton( guiSKI_TransactionButton );
}
else
{
DisableButton( guiSKI_TransactionButton );
}
INT32 balance = LaptopSaveInfo.iCurrentBalance;
if ( armsDealerInfo[gbSelectedArmsDealerID].uiFlags & ARMS_DEALER_DEALWITHINTEL )
balance = (INT32)GetIntel();
if( uiArmsDealerTotalCost > uiPlayersOfferAreaTotalCost + balance )
{
DisableButton( guiSKI_TransactionButton );
}
/* Allow transaction attempt when dealer can't afford to buy that much - he'll reject it with a special quote!
else if( ( gArmsDealerStatus[ gbSelectedArmsDealerID ].uiArmsDealersCash + uiArmsDealerTotalCost ) < ( uiPlayersOfferAreaTotalCost - uiPlayersOfferAreaTotalMoney ) )
{
DisableButton( guiSKI_TransactionButton );
}
*/
else if( ( uiPlayersOfferAreaTotalCost == uiPlayersOfferAreaTotalMoney ) && ( uiPlayersOfferAreaTotalMoney > 0 ) && ( uiArmsDealerTotalCost == 0 ) )
{
DisableButton( guiSKI_TransactionButton );
}
//if the player is currently moving an item, disable the transaction button
if( gMoveingItem.sItemIndex != 0 )
{
EnableDisableShopkeeperButtons(SHOPKEEPER_SCREEN, DISABLE_BUTTON);
}
else
{
EnableButton( guiSKI_DoneButton );
}
//ARM: Always permit trying bribes, even if they don't work on a given dealer!
// if the arms dealer is the kind of person who accepts gifts, and there is stuff to take
// if( armsDealerInfo[ gbSelectedArmsDealerID ].uiFlags & ARMS_DEALER_ACCEPTS_GIFTS )
{
//if the player is giving the dealer money, without buying anything
if( IsMoneyTheOnlyItemInThePlayersOfferArea( ) && CountNumberOfItemsInTheArmsDealersOfferArea( ) == 0 )
{
EnableButton( guiSKI_TransactionButton );
}
}
if( gfUserHasRequestedToLeave )
DisableButton( guiSKI_TransactionButton );
//if the player is in the item desc for the arms dealer items
if( InItemDescriptionBox( ) && pShopKeeperItemDescObject != NULL )
{
ButtonList[ guiSKI_TransactionButton ]->uiFlags |= BUTTON_FORCE_UNDIRTY;
ButtonList[ guiSKI_DoneButton ]->uiFlags |= BUTTON_FORCE_UNDIRTY;
}
else
{
ButtonList[ guiSKI_TransactionButton ]->uiFlags &= ~BUTTON_FORCE_UNDIRTY;
ButtonList[ guiSKI_DoneButton ]->uiFlags &= ~BUTTON_FORCE_UNDIRTY;
}
}
void AddItemToPlayersOfferAreaAfterShopKeeperOpen( OBJECTTYPE *pItemObject)
{
gItemToAdd.fActive = TRUE;
gItemToAdd.ItemObject = *pItemObject;
}
BOOLEAN IsMoneyTheOnlyItemInThePlayersOfferArea( )
{
// we can't substitute money for intel
if ( armsDealerInfo[gbSelectedArmsDealerID].uiFlags & ARMS_DEALER_DEALWITHINTEL )
return FALSE;
BOOLEAN fFoundMoney = FALSE;
for ( int ubCnt = 0; ubCnt < gPlayersOfferActiveRegions; ++ubCnt )
{
//if there is an item here
if( PlayersOfferArea[ ubCnt ].fActive )
{
if( Item[ PlayersOfferArea[ ubCnt ].sItemIndex ].usItemClass != IC_MONEY )
return( FALSE );
else
fFoundMoney = TRUE;
}
}
if ( fFoundMoney )
Assert( CalculateHowMuchMoneyIsInPlayersOfferArea() > 0 );
// only return TRUE if there IS money in the POA
return( fFoundMoney );
}
UINT32 CalculateHowMuchMoneyIsInPlayersOfferArea( )
{
// we can't substitute money for intel
if ( armsDealerInfo[gbSelectedArmsDealerID].uiFlags & ARMS_DEALER_DEALWITHINTEL )
return 0;
UINT32 uiTotalMoneyValue=0;
for ( int ubCnt = 0; ubCnt < gPlayersOfferActiveRegions; ++ubCnt )
{
//if there is an item here
if( PlayersOfferArea[ ubCnt ].fActive )
{
if( Item[ PlayersOfferArea[ ubCnt ].sItemIndex ].usItemClass == IC_MONEY )
{
uiTotalMoneyValue += PlayersOfferArea[ ubCnt ].ItemObject[0]->data.money.uiMoneyAmount;
}
}
}
return( uiTotalMoneyValue );
}
void MovePlayersItemsToBeRepairedToArmsDealersInventory()
{
//loop through all the slots in the shopkeeper's offer area
for ( int uiCnt = 0; uiCnt<gDealersOfferActiveRegions; ++uiCnt )
{
//if there is a item here
if( ArmsDealerOfferArea[ uiCnt ].fActive )
{
// NOTE: Any items that make it into a repairman's dealer offer area are guaranteed to be:
// a) Repairable
// b) Actually damaged
// c) Already stripped of all attachments
// d) If a gun, stripped of any non-ammo-class GunAmmoItems, and bullets
// add it to the arms dealer's inventory
GiveObjectToArmsDealerForRepair( gbSelectedArmsDealerID, &( ArmsDealerOfferArea[ uiCnt ].ItemObject ), ArmsDealerOfferArea[ uiCnt ].ubIdOfMercWhoOwnsTheItem );
//Remove the item from the owner merc's inventory
IfMercOwnedRemoveItemFromMercInv( &( ArmsDealerOfferArea[ uiCnt ]) );
//erase the item from the dealer's offer area
ClearArmsDealerOfferSlot( uiCnt );
}
}
gfResetShopKeepIdleQuote = TRUE;
}
BOOLEAN RemoveRepairItemFromDealersOfferArea( INT16 bSlot )
{
//if the item doesn't have a duplicate copy in its owner merc's inventory slot
if( ArmsDealerOfferArea[ bSlot ].bSlotIdInOtherLocation == -1 )
{
if ( !SKITryToReturnInvToOwnerOrCurrentMerc( &( ArmsDealerOfferArea[ bSlot ] ) ) )
{
//failed to add item, inventory probably filled up or item is unowned and current merc ineligible
return( FALSE );
}
}
// Clear the contents of the dealer's offer slot and its help text
ClearArmsDealerOfferSlot( bSlot );
// Dirty
fInterfacePanelDirty = DIRTYLEVEL2;
gubSkiDirtyLevel = SKI_DIRTY_LEVEL1;
return( TRUE );
}
INT8 GetInvSlotOfUnfullMoneyInMercInventory( SOLDIERTYPE *pSoldier )
{
//loop through the soldier's inventory
INT8 invsize = (INT8)pSoldier->inv.size();
for( INT8 bCnt=0; bCnt < invsize; ++bCnt)
{
// Look for MONEY only, not Gold or Silver!!! And look for a slot not already full
if( ( pSoldier->inv[ bCnt ].usItem == MONEY ) && ( pSoldier->inv[ bCnt ][0]->data.money.uiMoneyAmount < MoneySlotLimit( bCnt ) ) )
{
return( bCnt );
}
}
return( NO_SLOT );
}
void ClearArmsDealerOfferSlot( INT32 ubSlotToClear )
{
// Clear the contents
ArmsDealerOfferArea[ ubSlotToClear ].initialize();
//Remove the mouse help text from the region
SetRegionFastHelpText( &gDealersOfferSlotsMouseRegions[ ubSlotToClear ], L"" );
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
SetRegionFastHelpText( &gDealersOfferSlotsSmallFaceMouseRegions[ ubSlotToClear ], L"" );
}
}
void ClearPlayersOfferSlot( INT32 ubSlotToClear )
{
// Clear the contents
PlayersOfferArea[ ubSlotToClear ].initialize();
//Clear the text for the item
SetRegionFastHelpText( &gPlayersOfferSlotsMouseRegions[ ubSlotToClear ], L"" );
SetRegionFastHelpText( &gPlayersOfferSlotsSmallFaceMouseRegions[ ubSlotToClear ], L"" );
// if the player offer area is clear, reset flags for transaction
CheckAndHandleClearingOfPlayerOfferArea( );
}
void EvaluateItemAddedToPlayersOfferArea( INT8 bSlotID, BOOLEAN fFirstOne )
{
UINT32 uiEvalResult = EVAL_RESULT_NORMAL;
BOOLEAN fRocketRifleWasEvaluated = FALSE;
UINT8 ubNumberOfItemsAddedToRepairDuringThisEvaluation=0;
// there better be an item there
Assert ( PlayersOfferArea[ bSlotID ].fActive );
//if money is the item being evaluated, leave
if( Item[ PlayersOfferArea[ bSlotID ].sItemIndex ].usItemClass == IC_MONEY )
return;
// if already evaluated, don't do it again
if( PlayersOfferArea[ bSlotID ].uiFlags & ARMS_INV_PLAYERS_ITEM_HAS_BEEN_EVALUATED )
{
return;
}
// say "Hmm... Let's see" once per trading session to start evaluation
// SPECIAL: Devin doesn't have this quote (he's the only one)
#ifdef JA2UB
//no UB
#else
if( !gfDealerHasSaidTheEvaluateQuoteOnceThisSession && ( gbSelectedArmsDealerID != ARMS_DEALER_DEVIN ) )
{
gfDealerHasSaidTheEvaluateQuoteOnceThisSession = TRUE;
StartShopKeeperTalking( SK_QUOTES_PLAYER_REQUESTED_EVALUATION );
}
#endif
//Can this particular kind of item be sold/repaired here
if( WillShopKeeperRejectObjectsFromPlayer( gbSelectedArmsDealerID, bSlotID ) == FALSE )
{
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
int uiNumberOfItemsInForRepairs = CountTotalItemsRepairDealerHasInForRepairs( gbSelectedArmsDealerID );
int uiNumberOfItemsAlreadyEvaluated = CountNumberOfItemsInTheArmsDealersOfferArea();
//Get the number of items being evaluated
ubNumberOfItemsAddedToRepairDuringThisEvaluation = PlayersOfferArea[ bSlotID ].ItemObject.ubNumberOfObjects;
//if there is already enough items in for repairs, complain about it and DON'T accept the item for repairs
if( ( uiNumberOfItemsAlreadyEvaluated + ubNumberOfItemsAddedToRepairDuringThisEvaluation + uiNumberOfItemsInForRepairs ) > SKI_MAX_AMOUNT_OF_ITEMS_DEALER_CAN_REPAIR_AT_A_TIME )
{
if ( !gfAlreadySaidTooMuchToRepair )
{
//Start the dealer talking
StartShopKeeperTalking( SK_QUOTES_CANT_AFFORD_TO_BUY_OR_TOO_MUCH_TO_REPAIR );
gfAlreadySaidTooMuchToRepair = TRUE;
}
return;
}
//if the item is a rocket rifle
if(ItemHasFingerPrintID( PlayersOfferArea[ bSlotID ].sItemIndex ) )
{
fRocketRifleWasEvaluated = TRUE;
}
//if the item is damaged, or is a rocket rifle (which always "need repairing" even at 100%, to reset imprinting)
if( ( PlayersOfferArea[ bSlotID ].ItemObject[0]->data.objectStatus < 100 ) || fRocketRifleWasEvaluated )
{
// Move the item to the Dealer's Offer Area
INT16 bSlotAddedTo = AddItemToArmsDealerOfferArea( &PlayersOfferArea[ bSlotID ], PlayersOfferArea[ bSlotID ].bSlotIdInOtherLocation );
if( bSlotAddedTo != -1 )
{
// Clear the contents
ClearPlayersOfferSlot( bSlotID );
/* ARM: Leave it there, until transaction occurs it should be recallable
//Remove the item from the owner merc's inventory
IfMercOwnedRemoveItemFromMercInv( &(ArmsDealerOfferArea[ bSlotAddedTo ]) );
*/
//Mark the item as unselected, signifying that it can be moved
ArmsDealerOfferArea[ bSlotAddedTo ].uiFlags &= ~ARMS_INV_ITEM_SELECTED;
//increment the number of items being added
++ubNumberOfItemsAddedToRepairDuringThisEvaluation;
// check if the item is really badly damaged
if( Item[ ArmsDealerOfferArea[ bSlotAddedTo ].sItemIndex ].usItemClass != IC_AMMO )
{
if( ArmsDealerOfferArea[ bSlotAddedTo ].ItemObject[0]->data.objectStatus < REALLY_BADLY_DAMAGED_THRESHOLD )
{
uiEvalResult = EVAL_RESULT_OK_BUT_REALLY_DAMAGED;
}
}
// check if it's the first time a rocket rifle is being submitted to Fredo
if( fRocketRifleWasEvaluated && ( gbSelectedArmsDealerID == ARMS_DEALER_FREDO ) )
{
//if he hasn't yet said his quote
if( !( gArmsDealerStatus[ gbSelectedArmsDealerID ].ubSpecificDealerFlags & ARMS_DEALER_FLAG__FREDO_HAS_SAID_ROCKET_RIFLE_QUOTE ) )
{
// use this special evaluation result instead (has a unique quote)
uiEvalResult = EVAL_RESULT_ROCKET_RIFLE;
}
}
}
else
{
#ifdef JA2BETAVERSION
ScreenMsg( FONT_MCOLOR_WHITE, MSG_BETAVERSION, L"Failed to add repair item to ArmsDealerOfferArea. AM-0");
#endif
return;
}
}
else
{
uiEvalResult = EVAL_RESULT_NOT_DAMAGED;
}
}
else // not a repairman
{
uiEvalResult = EvaluateInvSlot( & ( PlayersOfferArea[ bSlotID ] ) );
}
}
else // dealer doesn't handle this type of object
{
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
// only otherwise repairable items count as actual rejections
if (ItemIsRepairable(PlayersOfferArea[bSlotID].sItemIndex))
{
uiEvalResult = EVAL_RESULT_DONT_HANDLE;
}
else
{
uiEvalResult = EVAL_RESULT_NON_REPAIRABLE;
}
}
else
{
uiEvalResult = EVAL_RESULT_DONT_HANDLE;
}
}
// mark this item as having been evaluated
PlayersOfferArea[ bSlotID ].uiFlags |= ARMS_INV_PLAYERS_ITEM_HAS_BEEN_EVALUATED;
// when evaluating complex items that get split into multiple subobjects, dealer will talk only about the first one!
// don't bother with any of this if shopkeeper can't talk right now
if ( fFirstOne && CanShopkeeperOverrideDialogue( ) )
{
INT16 sQuoteNum = -1;
UINT8 ubChanceToSayNormalQuote;
switch ( uiEvalResult )
{
case EVAL_RESULT_DONT_HANDLE:
#ifdef JA2UB
//JA25 UB
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_SELLS_ONLY )
{
// then he doesn't have quotes 17, 19, or 20, always use 4. Devin doesn't have 18 either,
// while the text of 18 seems wrong for Sam & Howard if offered something they should consider valuable.
sQuoteNum = SK_QUOTES_NOT_INTERESTED_IN_THIS_ITEM;
}
else
{
//if the dealer is BETTY
if( gbSelectedArmsDealerID == ARMS_DEALER_BETTY &&
( Item[ PlayersOfferArea[ bSlotID ].sItemIndex ].usItemClass == IC_AMMO ||
Item[ PlayersOfferArea[ bSlotID ].sItemIndex ].usItemClass == IC_GUN
)
)
{
// she accepts items, but not this one
sQuoteNum = SK_QUOTES_NPC_SPECIFIC_40;
}
else
{
// he accepts items, but not this one
sQuoteNum = SK_QUOTES_DURING_EVALUATION_STUFF_REJECTED;
}
}
#endif
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_SELLS_ONLY )
{
// then he doesn't have quotes 17, 19, or 20, always use 4. Devin doesn't have 18 either,
// while the text of 18 seems wrong for Sam & Howard if offered something they should consider valuable.
sQuoteNum = SK_QUOTES_NOT_INTERESTED_IN_THIS_ITEM;
}
else
{
// he accepts items, but not this one
sQuoteNum = SK_QUOTES_DURING_EVALUATION_STUFF_REJECTED;
}
break;
case EVAL_RESULT_NON_REPAIRABLE:
// use quote 4 for this situation... It's a bit weird but better than "that looks fine / ain't broke"
sQuoteNum = SK_QUOTES_NOT_INTERESTED_IN_THIS_ITEM;
break;
case EVAL_RESULT_NOT_DAMAGED:
case EVAL_RESULT_WORTHLESS:
sQuoteNum = SK_QUOTES_EVALUATION_RESULT_VALUE_OF_ZERO;
break;
case EVAL_RESULT_OK_BUT_REALLY_DAMAGED:
sQuoteNum = SK_QUOTES_EVALUATION_RESULT_SOME_REALLY_DAMAGED_ITEMS;
break;
case EVAL_RESULT_ROCKET_RIFLE:
sQuoteNum = FREDO_QUOTE_CAN_RESET_IMPRINTING;
break;
case EVAL_RESULT_NORMAL:
//if it has been a long time since saying the last quote
if( guiLastTimeDealerSaidNormalEvaluationQuote == 0 )
ubChanceToSayNormalQuote = 100;
else if( ( GetJA2Clock() - guiLastTimeDealerSaidNormalEvaluationQuote ) > 25000 )
ubChanceToSayNormalQuote = 60;
else if( ( GetJA2Clock() - guiLastTimeDealerSaidNormalEvaluationQuote ) > 12000 )
ubChanceToSayNormalQuote = 25;
else if( ( GetJA2Clock() - guiLastTimeDealerSaidNormalEvaluationQuote ) > 7000 )
ubChanceToSayNormalQuote = 10;
else
ubChanceToSayNormalQuote = 0;
if( Chance( ubChanceToSayNormalQuote ) )
{
sQuoteNum = SK_QUOTES_EVALUATION_RESULT_NORMAL;
}
break;
default:
#ifdef JA2BETAVERSION
ScreenMsg( FONT_MCOLOR_WHITE, MSG_BETAVERSION, L"Invalid evaluation result of %d. AM-0", uiEvalResult );
#endif
break;
}
// if a valid quote was selected
if ( sQuoteNum != -1 )
{
// if the appropriate quote hasn't already been used during this evaluation cycle
if ( !gfEvalResultQuoteSaid[ uiEvalResult ] )
{
// try to say it
if ( StartShopKeeperTalking( ( UINT16 ) sQuoteNum ) )
{
// set flag to keep from repeating it
gfEvalResultQuoteSaid[ uiEvalResult ] = TRUE;
// if it's the normal result
if ( uiEvalResult == EVAL_RESULT_NORMAL )
{
// set new time of last usage
guiLastTimeDealerSaidNormalEvaluationQuote = GetJA2Clock();
}
if ( sQuoteNum == FREDO_QUOTE_CAN_RESET_IMPRINTING )
{
//Set the fact that we have said this (to prevent that quote from coming up again)
gArmsDealerStatus[ gbSelectedArmsDealerID ].ubSpecificDealerFlags |= ARMS_DEALER_FLAG__FREDO_HAS_SAID_ROCKET_RIFLE_QUOTE;
}
}
}
}
}
}
BOOLEAN DoSkiMessageBox( UINT8 ubStyle, STR16 zString, UINT32 uiExitScreen, UINT8 ubFlags, MSGBOX_CALLBACK ReturnCallback )
{
SGPRect pCenteringRect= {0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - INV_INTERFACE_HEIGHT };
// reset exit mode
gfExitSKIDueToMessageBox = TRUE;
// do message box and return
giSKIMessageBox = DoMessageBox( ubStyle, zString, uiExitScreen, ( UINT8 ) ( ubFlags| MSG_BOX_FLAG_USE_CENTERING_RECT ), ReturnCallback, &pCenteringRect );
// send back return state
return( ( giSKIMessageBox != -1 ) );
}
void ConfirmDontHaveEnoughForTheDealerMessageBoxCallBack( UINT8 bExitValue )
{
// simply redraw the panel
if( bExitValue == MSG_BOX_RETURN_OK )
{
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
}
// done, re-enable calls to PerformTransaction()
gfPerformTransactionInProgress = FALSE;
}
void ConfirmToDeductMoneyFromPlayersAccountMessageBoxCallBack( UINT8 bExitValue )
{
// yes, deduct the money
if( bExitValue == MSG_BOX_RETURN_YES )
{
UINT32 uiPlayersOfferAreaValue = CalculateTotalPlayersValue();
UINT32 uiArmsDealersItemsCost = CalculateTotalArmsDealerCost();
INT32 iMoneyToDeduct = (INT32)( uiArmsDealersItemsCost - uiPlayersOfferAreaValue );
//Perform the transaction with the extra money from the players account
PerformTransaction( iMoneyToDeduct );
AddTransactionToPlayersBook( PURCHASED_ITEM_FROM_DEALER, armsDealerInfo[ gbSelectedArmsDealerID ].ubShopKeeperID, GetWorldTotalMin(), -iMoneyToDeduct );
}
// done, re-enable calls to PerformTransaction()
gfPerformTransactionInProgress = FALSE;
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
}
void ConfirmToDeductIntelFromPlayersAccountMessageBoxCallBack( UINT8 bExitValue )
{
// yes, deduct the money
if ( bExitValue == MSG_BOX_RETURN_YES )
{
UINT32 uiPlayersOfferAreaValue = CalculateTotalPlayersValue();
UINT32 uiArmsDealersItemsCost = CalculateTotalArmsDealerCost();
INT32 iMoneyToDeduct = (INT32)( uiArmsDealersItemsCost - uiPlayersOfferAreaValue );
//Perform the transaction with the extra money from the players account
PerformTransaction( iMoneyToDeduct );
AddIntel( (FLOAT)-iMoneyToDeduct, TRUE );
}
// done, re-enable calls to PerformTransaction()
gfPerformTransactionInProgress = FALSE;
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
}
// run through what the player has on the table and see if the shop keep will aceept it or not
BOOLEAN WillShopKeeperRejectItemFromPlayer( INT8 bDealerId, UINT16 usItem )
{
BOOLEAN fRejected = TRUE;
if ( Item[usItem].usItemClass == IC_MONEY )
{
// we can't substitute money for intel
if ( armsDealerInfo[gbSelectedArmsDealerID].uiFlags & ARMS_DEALER_DEALWITHINTEL )
fRejected = TRUE;
else
fRejected = FALSE;
}
else if ( CanDealerTransactItem( gbSelectedArmsDealerID, usItem, TRUE ) )
{
fRejected = FALSE;
}
else
{
fRejected = TRUE;
}
return fRejected;
}
BOOLEAN DoesCurrentDealerRefuseToTradeItem( UINT16 usItem )
{
//if non-repairman
if ( armsDealerInfo[gbSelectedArmsDealerID].ubTypeOfArmsDealer != ARMS_DEALER_REPAIRS )
{
// don't evaluate anything he wouldn't buy!
if ( WillShopKeeperRejectItemFromPlayer( gbSelectedArmsDealerID, usItem ) )
{
return TRUE;
}
}
return FALSE;
}
// run through what the player has on the table and see if the shop keep will aceept it or not
BOOLEAN WillShopKeeperRejectObjectsFromPlayer( INT8 bDealerId, INT8 bSlotId )
{
return WillShopKeeperRejectItemFromPlayer( bDealerId, PlayersOfferArea[bSlotId].sItemIndex );
}
void CheckAndHandleClearingOfPlayerOfferArea( void )
{
BOOLEAN fActiveSlot = FALSE;
// find out if all the player trade slots/offer area is empty
for ( int iCounter = 0; iCounter<gPlayersOfferActiveRegions; ++iCounter )
{
if( PlayersOfferArea[ iCounter ].fActive == TRUE )
{
// nope, there is an active slot
fActiveSlot = TRUE;
break;
}
}
// if all player's offer area slots are empty
if( fActiveSlot == FALSE )
{
ResetAllQuoteSaidFlags();
gfRemindedPlayerToPickUpHisStuff = FALSE;
}
}
BOOLEAN CanShopkeeperOverrideDialogue( void )
{
// if he's not currently saying anything
if( !gfIsTheShopKeeperTalking && ( giShopKeepDialogueEventinProgress == -1 ) )
{
return( TRUE );
}
// if the quote he is currently saying is unimportant (fluff),
// then shut him up & allow the override
if( giShopKeepDialogueEventinProgress < SK_QUOTES_PLAYER_REQUESTED_EVALUATION )
{
// shutup the shop keep and do this quote
ShutUpShopKeeper( );
return( TRUE );
}
// he's currently saying something important
return( FALSE );
}
INT16 GetNumberOfItemsInPlayerOfferArea( void )
{
INT16 sCounter = 0;
// find number of active slot in player offer area
for ( int iCounter = 0; iCounter < gPlayersOfferActiveRegions; ++iCounter )
{
if( PlayersOfferArea[ iCounter ].fActive )
{
++sCounter;
}
}
return( sCounter );
}
void HandleCheckIfEnoughOnTheTable( void )
{
static INT32 iLastTime = 0;
INT32 iDifference = 0, iRand = 0;
UINT32 uiPlayersOfferAreaValue = CalculateTotalPlayersValue();
UINT32 uiArmsDealersItemsCost = CalculateTotalArmsDealerCost();
if( ( iLastTime == 0 ) || gfResetShopKeepIdleQuote )
{
iLastTime = GetJA2Clock();
gfResetShopKeepIdleQuote = FALSE;
}
iDifference = GetJA2Clock() - iLastTime;
iRand = Random( 100 );
// delay for shopkeeper passed?
if( iDifference > DELAY_FOR_SHOPKEEPER_IDLE_QUOTE )
{
// random chance enough?
if( iRand > CHANCE_FOR_SHOPKEEPER_IDLE_QUOTE )
{
// is there enough on the table
if( ( uiArmsDealersItemsCost > uiPlayersOfferAreaValue ) && ( uiPlayersOfferAreaValue ) )
{
StartShopKeeperTalking( SK_QUOTES_EVAULATION_PLAYER_DOESNT_HAVE_ENOUGH_VALUE );
}
}
gfResetShopKeepIdleQuote = TRUE;
}
}
void InitShopKeeperItemDescBox( OBJECTTYPE *pObject, UINT16 ubPocket, UINT8 ubFromLocation )
{
INT16 sPosX, sPosY;
bool showItemDescriptionBox = false;
switch( ubFromLocation )
{
case ARMS_DEALER_INVENTORY:
{
showItemDescriptionBox = true;
UINT16 ubSelectedInvSlot = ubPocket - gSelectArmsDealerInfo.ubFirstItemIndexOnPage;
if( gGameSettings.fOptions[ TOPTION_ENHANCED_DESC_BOX ] == FALSE)
{
sPosX = SKI_TRADER_INVENTORY_BOXES_BEGIN_X;
sPosY = SKI_TRADER_INVENTORY_BOXES_BEGIN_Y;
}
else
{
sPosX = SCREEN_X_OFFSET + 2;
sPosY = SCREEN_Y_OFFSET + 146;
}
//if the start position + the height of the box is off the screen, reposition
if( sPosY < (0 + SCREEN_Y_OFFSET) )
sPosY = 0 + SCREEN_Y_OFFSET;
//if the start position + the width of the box is off the screen, reposition
if( ( sPosX + 358 ) > SCREEN_WIDTH )
sPosX = sPosX - ( ( sPosX + 358 ) - SCREEN_WIDTH ) - 5;
//if it is starting to far to the left
else if( sPosX < (0 + SCREEN_X_OFFSET) )
sPosX = 0 + SCREEN_X_OFFSET;
//if the box will appear over the mercs face, move the box over so it doesn't obstruct the face
if( sPosY < SKI_FACE_Y + SKI_FACE_HEIGHT + 20 )
if( sPosX < (SCREEN_X_OFFSET + 160) )
sPosX = SCREEN_X_OFFSET + 160;
}
break;
case ARMS_DEALER_OFFER_AREA:
{
showItemDescriptionBox = false;
sPosX = SKI_TRADER_OFFER_BOXES_BEGIN_X;
sPosY = SKI_TRADER_OFFER_BOXES_BEGIN_Y;
//if the start position + the height of the box is off the screen, reposition
if( sPosY < (SCREEN_Y_OFFSET + 0) )
sPosY = 0 + SCREEN_Y_OFFSET;
//if the start position + the width of the box is off the screen, reposition
if( ( sPosX + 358 ) > SCREEN_WIDTH )
sPosX = sPosX - ( ( sPosX + 358 ) - SCREEN_WIDTH ) - 5;
//if it is starting to far to the left
else if( sPosX < (0 + SCREEN_X_OFFSET) )
sPosX = 10 + SCREEN_X_OFFSET;
//if the box will appear over the mercs face, move the box over so it doesn't obstruct the face
if( sPosY < SKI_FACE_Y + SKI_FACE_HEIGHT + 20 )
if( sPosX < (160 + SCREEN_X_OFFSET) )
sPosY = SCREEN_Y_OFFSET + INV_INTERFACE_HEIGHT;
}
break;
default:
Assert( 0 );
return;
}
// WANNE: Only allow right click (item desription box) in the arms dealer inventory
if (showItemDescriptionBox)
{
pShopKeeperItemDescObject = pObject;
InitItemDescriptionBox( gpSMCurrentMerc, 255, sPosX, sPosY, 0 );
StartSKIDescriptionBox( );
}
}
void StartSKIDescriptionBox( void )
{
//shadow the entire screen
DrawHatchOnInventory( FRAME_BUFFER, SKI_TOTAL_BEGIN_X, SKI_TOTAL_BEGIN_Y, SKI_TOTAL_END_X - SKI_TOTAL_BEGIN_X, SKI_TOTAL_END_Y - SKI_TOTAL_BEGIN_Y );
InvalidateRegion( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
// disable almost everything!
gfSMDisableForItems = TRUE;
DisableInvRegions( gfSMDisableForItems );
DisableButton( guiSKI_InvPageUpButton );
DisableButton( guiSKI_InvPageDownButton );
EnableDisableShopkeeperButtons(SHOPKEEPER_SCREEN, DISABLE_BUTTON);
DisableAllDealersInventorySlots();
DisableAllDealersOfferSlots();
for ( int iCnt = 0; iCnt < gPlayersOfferActiveRegions; ++iCnt )
{
MSYS_DisableRegion( &gPlayersOfferSlotsMouseRegions[ iCnt ] );
MSYS_DisableRegion( &gPlayersOfferSlotsSmallFaceMouseRegions[ iCnt ] );
}
if( gShopKeeperSubTitleMouseRegion.uiFlags & MSYS_REGION_EXISTS )
{
MSYS_DisableRegion( &gShopKeeperSubTitleMouseRegion );
}
RenderItemDescriptionBox( );
}
void DeleteShopKeeperItemDescBox()
{
pShopKeeperItemDescObject = NULL;
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
//Redraw the face
if ( giShopKeeperFaceIndex > -1 )
gFacesData[ giShopKeeperFaceIndex ].uiFlags |= FACE_REDRAW_WHOLE_FACE_NEXT_FRAME;
// enable almost everything!
CheckForDisabledForGiveItem( );
DisableInvRegions( gfSMDisableForItems ); //actually does an enable if current merc is in range
EnableButton( guiSKI_InvPageUpButton );
EnableButton( guiSKI_InvPageDownButton );
EnableDisableShopkeeperButtons(SHOPKEEPER_SCREEN, ENABLE_BUTTON);
EnableDisableDealersInventoryPageButtons();
EnableDisableEvaluateAndTransactionButtons();
EnableAllDealersInventorySlots();
EnableAllDealersOfferSlots();
for ( int iCnt = 0; iCnt < gPlayersOfferActiveRegions; ++iCnt )
{
MSYS_EnableRegion( &gPlayersOfferSlotsMouseRegions[ iCnt ] );
MSYS_EnableRegion( &gPlayersOfferSlotsSmallFaceMouseRegions[ iCnt ] );
}
if( gShopKeeperSubTitleMouseRegion.uiFlags & MSYS_REGION_EXISTS )
{
MSYS_EnableRegion( &gShopKeeperSubTitleMouseRegion );
}
}
BOOLEAN OfferObjectToDealer( OBJECTTYPE *pComplexObject, UINT8 ubOwnerProfileId, INT16 bOwnerSlotId )
{
UINT8 ubRepairableSubObjects;
UINT8 ubNonRepairableSubObjects;
int ubDealerOfferAreaSlotsNeeded;
int ubPlayerOfferAreaSlotsNeeded;
int ubDiff;
int ubHowManyMoreItemsCanDealerTake;
BOOLEAN fFirstOne = TRUE;
BOOLEAN fSuccess = FALSE;
if ( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer != ARMS_DEALER_REPAIRS )
{
// if not actually doing repairs, there's no need to split objects up at all
if ( !AddObjectForEvaluation( pComplexObject, ubOwnerProfileId, bOwnerSlotId, TRUE ) )
{
gfDisplayNoRoomMsg = TRUE;
return(FALSE); // no room
}
}
else // dealing with a repairman
{
// split the object into its components
SplitComplexObjectIntoSubObjects( pComplexObject );
// determine how many subobjects of each category this complex object will have to be broken up into
CountSubObjectsInObject( pComplexObject, &ubRepairableSubObjects, &ubNonRepairableSubObjects );
// in the simplest situation, the # subobjects of each type gives us how many slots of each type are needed
ubDealerOfferAreaSlotsNeeded = ubRepairableSubObjects;
ubPlayerOfferAreaSlotsNeeded = ubNonRepairableSubObjects;
// consider that if dealer is at or will reach his max # of items repaired limit, not everything repairable will move
ubHowManyMoreItemsCanDealerTake = SKI_MAX_AMOUNT_OF_ITEMS_DEALER_CAN_REPAIR_AT_A_TIME -
CountTotalItemsRepairDealerHasInForRepairs( gbSelectedArmsDealerID ) -
CountNumberOfItemsInTheArmsDealersOfferArea();
// if he can't repair everything repairable that we're about to submit, the space we'll need changes
if ( ubDealerOfferAreaSlotsNeeded > ubHowManyMoreItemsCanDealerTake )
{
ubDiff = ubDealerOfferAreaSlotsNeeded - ubHowManyMoreItemsCanDealerTake;
ubDealerOfferAreaSlotsNeeded -= ubDiff;
ubPlayerOfferAreaSlotsNeeded += ubDiff;
}
// if there is anything repairable that dealer will accept
if ( ubDealerOfferAreaSlotsNeeded > 0 )
{
// we need at least one EXTRA empty slot in player's area to pass repairable objects through for their evaluation
++ubPlayerOfferAreaSlotsNeeded;
}
//this is a silly assert, was it meant to be something else?
Assert( SKI_MAX_AMOUNT_OF_ITEMS_DEALER_CAN_REPAIR_AT_A_TIME < (SKI_PLAYER_OFFER_BOXES_PER_ROW * SKI_PLAYER_OFFER_BOXES_PER_COL) );
/*
This code is commented out because a repair dealer will never be allowed to repair over more then
SKI_MAX_AMOUNT_OF_ITEMS_DEALER_CAN_REPAIR_AT_A_TIME ( 4 ) items at a time, therefore, cant fill up the dealer offer area
// check if we have room for all of each type
if ( ( ubDealerOfferAreaSlotsNeeded + CountNumberOfItemsInTheArmsDealersOfferArea() ) > SKI_NUM_TRADING_INV_SLOTS )
{
// tell player there's not enough room in the dealer's offer area
DoSkiMessageBox( MSG_BOX_BASIC_STYLE, L"There is not enough room in the dealers repair area.", SHOPKEEPER_SCREEN, MSG_BOX_FLAG_OK, NULL );
return(FALSE); // no room
}
*/
if ( (ubPlayerOfferAreaSlotsNeeded + CountNumberOfItemsInThePlayersOfferArea( )) > (SKI_PLAYER_OFFER_BOXES_PER_ROW * SKI_PLAYER_OFFER_BOXES_PER_COL) )
{
gfDisplayNoRoomMsg = TRUE;
return(FALSE); // no room
}
// we have room, so move them all to the appropriate slots
for ( size_t ubSubObject = 0; ubSubObject < subObjects.size(); ++ubSubObject )
{
// if it's the main item itself (always in the very first subobject), and it has no other subobjects
if ( ( ubSubObject == 0 ) && ( subObjects.size() == 1) )
{
// store its owner merc as the owner, and store the correct slot
fSuccess = AddObjectForEvaluation( &subObjects[ ubSubObject ], ubOwnerProfileId, bOwnerSlotId, fFirstOne );
}
else // attachments, bullets/payload
{
// store it with a valid owner, but an invalid slot, so it still shows who owns it, but can't return to its slot
// ARM: New code will be needed here if we add parent/child item support & interface
fSuccess = AddObjectForEvaluation( &subObjects[ ubSubObject ], ubOwnerProfileId, -1, fFirstOne );
}
// it has to succeed, or we have a bug in our earlier check for sufficient room
Assert( fSuccess );
fFirstOne = FALSE;
}
gfAlreadySaidTooMuchToRepair = FALSE;
}
//ARM: This comment isn't true unless parent/child support is added. Right now repairmen don't do this!
// NOTE that, either way, if owned, the item remains in the merc's inventory (hatched out) until the transaction is completed
// Dirty
fInterfacePanelDirty = DIRTYLEVEL2;
return(TRUE); // it worked
}
void SplitComplexObjectIntoSubObjects( OBJECTTYPE *pComplexObject )
{
Assert( pComplexObject );
Assert( pComplexObject->exists() == true );
subObjects.clear();
int usItem = pComplexObject->usItem;
//CHRISL: This won't work. As we his MoveThisObjectTo, we reduce ubNumberOfObjects. If we started with ubNumberOfObjects=4 & x=0,
// our first iteration would be ubNumberOfObjects=3 & x=1. Then ubNumberOfObjects=2 & x=2, which ends the loop. We want to
// run the loop ubNumberOfObjects number of times regardless of whether ubNumberOfObjects is reduced. Use a while loop.
//for (int x = 0; x < pComplexObject->ubNumberOfObjects; ++x) {
while(pComplexObject->ubNumberOfObjects > 0) {
//we need not worry about attachments!!!!
if (pComplexObject->MoveThisObjectTo(gTempObject, 1) == 0) {
subObjects.push_back(gTempObject);
StackedObjectData* pData = subObjects.back()[0];
// if it's a gun
if ( Item [ usItem ].usItemClass == IC_GUN )
{
int usGunAmmoItem = pData->data.gun.usGunAmmoItem;
int ubGunShotsLeft = pData->data.gun.ubGunShotsLeft;
int bGunAmmoStatus = pData->data.gun.bGunAmmoStatus;
// strip off any loaded ammo/payload
// Exception: don't do this with rocket launchers, their "shots left" are fake and this screws 'em up!
if ( !ItemIsSingleShotRocketLauncher(usItem)) // Madd rpg - still do this
{
pData->data.gun.usGunAmmoItem = NONE;
pData->data.gun.ubGunShotsLeft = 0;
pData->data.gun.ubGunState = 0;
pData->data.gun.ubGunAmmoType = 0;
pData->data.gun.bGunAmmoStatus = 0;
}
// and it has ammo/payload
if ( usGunAmmoItem != NONE )
{
// if it's bullets
if ( Item[ usGunAmmoItem ].usItemClass == IC_AMMO )
{
// and there are some left
if ( ubGunShotsLeft > 0 )
{
// make the bullets into another subobject
CreateAmmo( usGunAmmoItem, &gTempObject, ubGunShotsLeft );
subObjects.push_back(gTempObject);
}
// ignore this if it's out of bullets
}
else // non-ammo payload
{
// if the gun was jammed, fix up the payload's status
if ( bGunAmmoStatus < 0 )
{
bGunAmmoStatus *= -1;
}
// make the payload into another subobject
CreateItem( usGunAmmoItem, bGunAmmoStatus, &gTempObject );
subObjects.push_back(gTempObject);
}
}
}
}
}
}
void CountSubObjectsInObject( OBJECTTYPE *pComplexObject, UINT8 *pubRepairableSubObjects, UINT8 *pubNonRepairableSubObjects )
{
UINT8 ubSubObject;
*pubRepairableSubObjects = 0;
*pubNonRepairableSubObjects = 0;
// check every subobject and count it as either repairable or non-
for ( ubSubObject = 0; ubSubObject < subObjects.size(); ubSubObject++ )
{
// is it in need of fixing, and also repairable by this dealer?
// A jammed gun with a 100% status is NOT repairable - shouldn't ever happen
if ( ( subObjects[ ubSubObject ][0]->data.objectStatus != 100 ) &&
CanDealerRepairItem( gbSelectedArmsDealerID, subObjects[ ubSubObject ].usItem ) )
{
( *pubRepairableSubObjects )++;
}
else
{
( *pubNonRepairableSubObjects )++;
}
}
}
BOOLEAN AddObjectForEvaluation(OBJECTTYPE *pObject, UINT8 ubOwnerProfileId, INT16 bOwnerSlotId, BOOLEAN fFirstOne )
{
INVENTORY_IN_SLOT InvSlot;
INT8 bAddedToSlotID;
// Make a new inv slot out of the subobject
InvSlot.ItemObject = *pObject;
InvSlot.sItemIndex = pObject->usItem;
InvSlot.ubLocationOfObject = PLAYERS_INVENTORY;
InvSlot.ubIdOfMercWhoOwnsTheItem = ubOwnerProfileId;
//Add the item to the Players Offer Area
bAddedToSlotID = AddItemToPlayersOfferArea( ubOwnerProfileId, &InvSlot, bOwnerSlotId );
//Do the evaluation for the item if it was added correctly
if( bAddedToSlotID != -1 )
{
// This will move any repairable (sub)objects into the dealer's offer area immediately
EvaluateItemAddedToPlayersOfferArea( bAddedToSlotID, fFirstOne );
return( TRUE );
}
else
{
return( FALSE );
}
}
// The Shopkeeper interface *MUST* use this intermediary function instead of calling AutoPlaceObject() directly!
// This is because the OBJECTTYPEs used within Shopkeeper may contain an illegal ubNumberOfObjects
BOOLEAN ShopkeeperAutoPlaceObject( SOLDIERTYPE * pSoldier, OBJECTTYPE * pObject, BOOLEAN fNewItem )
{
// the entire pObj will get memset to 0 by RemoveObjs() if all the items are successfully placed,
// so we have to keep a copy to retrieve with every iteration of the loop
OBJECTTYPE movingObject;
while ( pObject->exists() == true )
{
// figure out how many to place during this loop iteration. Can't do more than MAX_OBJECTS_PER_SLOT at a time
int numToMove = min( MAX_OBJECTS_PER_SLOT, pObject->ubNumberOfObjects);
pObject->MoveThisObjectTo(movingObject, numToMove);
if (!AutoPlaceObject( pSoldier, &movingObject, fNewItem ))
//if (!AutoPlaceObject( pSoldier, pObject, fNewItem ))
{
//CHRISL: When we call this, we don't care about cap limits so stack up to max
// no more room, didn't all fit - add back in any that we didn't even get to yet
pObject->AddObjectsToStack(movingObject, -1, 0, NUM_INV_SLOTS, MAX_OBJECTS_PER_SLOT);
return( FALSE );
}
}
return( TRUE );
}
// The Shopkeeper interface *MUST* use this intermediary function instead of calling AddItemToPool() directly!
// This is because the OBJECTTYPEs used within Shopkeeper may contain an illegal ubNumberOfObjects
void ShopkeeperAddItemToPool( INT32 sGridNo, OBJECTTYPE *pObject, INT8 bVisible, UINT8 ubLevel, UINT16 usFlags, INT8 bRenderZHeightAboveLevel )
{
// the entire pObj will get memset to 0 by RemoveObjs() if all the items are successfully placed,
// so we have to keep a copy to retrieve with every iteration of the loop
OBJECTTYPE movingObject;
while ( pObject->exists() == true )
{
// figure out how many to place during this loop iteration. Can't do more than MAX_OBJECTS_PER_SLOT at a time
int numToMove = min( MAX_OBJECTS_PER_SLOT, pObject->ubNumberOfObjects);
pObject->MoveThisObjectTo(movingObject, numToMove);
AddItemToPool( sGridNo, &movingObject, bVisible, ubLevel, usFlags, bRenderZHeightAboveLevel );
}
}
void IfMercOwnedCopyItemToMercInv( INVENTORY_IN_SLOT *pInv )
{
//if the item picked up was in a previous location, and that location is on a merc's inventory
if ( ( pInv->bSlotIdInOtherLocation != -1 ) && ( pInv->ubIdOfMercWhoOwnsTheItem != NO_PROFILE ) )
{
// get soldier
SoldierID sSoldierID = GetSoldierIDFromMercID( pInv->ubIdOfMercWhoOwnsTheItem );
Assert( sSoldierID != NOBODY );
Assert( CanMercInteractWithSelectedShopkeeper( sSoldierID ) );
// then it better be a valid slot #
Assert( pInv->bSlotIdInOtherLocation < (INT8)(sSoldierID->inv.size()) );
// and it better have a valid merc who owned it
Assert( pInv->ubIdOfMercWhoOwnsTheItem != NO_PROFILE );
//Copy the object back into that merc's original inventory slot
sSoldierID->inv[ pInv->bSlotIdInOtherLocation ] = pInv->ItemObject;
}
}
void IfMercOwnedRemoveItemFromMercInv( INVENTORY_IN_SLOT *pInv )
{
IfMercOwnedRemoveItemFromMercInv2( pInv->ubIdOfMercWhoOwnsTheItem, pInv->bSlotIdInOtherLocation );
}
void IfMercOwnedRemoveItemFromMercInv2( UINT8 ubOwnerProfileId, INT16 bOwnerSlotId )
{
//if this item was in a previous location, and that location is on a merc's inventory
if ( ( bOwnerSlotId != -1 ) && ( ubOwnerProfileId != NO_PROFILE ) )
{
// and it better have a valid merc who owned it
Assert( ubOwnerProfileId != NO_PROFILE );
SoldierID sSoldierID = GetSoldierIDFromMercID( ubOwnerProfileId );
Assert( sSoldierID != NOBODY );
// then it better be a valid slot #
Assert( bOwnerSlotId < (INT8)(sSoldierID->inv.size() ));
Assert( CanMercInteractWithSelectedShopkeeper( sSoldierID ) );
//remove the object from that merc's original inventory slot
DeleteObj(&(sSoldierID->inv[bOwnerSlotId]));
}
}
BOOLEAN SKITryToReturnInvToOwnerOrCurrentMerc( INVENTORY_IN_SLOT *pInv )
{
// don't use this if the item has a copy in merc's inventory!! It would create a duplicate!!
Assert( pInv->bSlotIdInOtherLocation == -1 );
// if it does have an owner
if( pInv->ubIdOfMercWhoOwnsTheItem != NO_PROFILE )
{
SoldierID sSoldierID = GetSoldierIDFromMercID( pInv->ubIdOfMercWhoOwnsTheItem );
// if that soldier is not in player's hire any longer
if ( sSoldierID >= NOBODY )
{
return(FALSE);
}
// For owners of repaired items, this checks that owner is still hired, in sector,
// on current squad, close enough to the shopkeeper, etc.
if ( !CanMercInteractWithSelectedShopkeeper( sSoldierID ) )
{
return(FALSE);
}
// Try to find a place to put in its owner's inventory (regardless of which merc is currently displayed!)
if ( SKITryToAddInvToMercsInventory( pInv, sSoldierID ) )
{
return( TRUE );
}
// owner's inventory is full, so we'll try to give it to the current merc instead
}
//if the current merc is not disabled
if( !gfSMDisableForItems )
{
// Try to find a place to put in current merc's inventory
if ( SKITryToAddInvToMercsInventory( pInv, gpSMCurrentMerc ) )
{
return( TRUE );
}
}
//failed to add item, either inventory was filled up, or nobody was even eligible to receive it
return( FALSE );
}
BOOLEAN SKITryToAddInvToMercsInventory( INVENTORY_IN_SLOT *pInv, SOLDIERTYPE *pSoldier )
{
INT8 bMoneyInvPos;
BOOLEAN fNewItem = FALSE;
//if the item is money
if( Item[ pInv->sItemIndex ].usItemClass == IC_MONEY )
{
// search through the merc's inventory for a pocket of money which isn't full already
bMoneyInvPos = GetInvSlotOfUnfullMoneyInMercInventory( pSoldier );
// if he has a money pocket going
if ( bMoneyInvPos != -1 )
{
// try to add to it. If successful, it will delete the object... It returns TRUE even if not all would fit!
PlaceObject( pSoldier, bMoneyInvPos, &( pInv->ItemObject ) );
// if the money is all gone
if ( pInv->ItemObject[0]->data.money.uiMoneyAmount == 0 )
{
// we've been succesful!
return(TRUE);
}
// otherwise we'll try to place the rest seperately
}
}
// If it's just been purchased or repaired, mark it as a "new item"
fNewItem = ( BOOLEAN ) ( pInv->uiFlags & ( ARMS_INV_JUST_PURCHASED | ARMS_INV_ITEM_REPAIRED ) );
//try autoplacing the item in this soldier's inventory.
if( !ShopkeeperAutoPlaceObject( pSoldier, &pInv->ItemObject, fNewItem ) )
{
//failed to add item, inventory probably filled up
return( FALSE );
}
return(TRUE);
}
BOOLEAN CanMercInteractWithSelectedShopkeeper( SOLDIERTYPE *pSoldier )
{
SOLDIERTYPE *pShopkeeper;
INT32 sDestGridNo;
INT8 bDestLevel;
UINT32 uiRange;
Assert( pSoldier!= NULL );
Assert( gbSelectedArmsDealerID != -1 );
if ( gusIDOfCivTrader != NOBODY )
pShopkeeper = gusIDOfCivTrader;
else
pShopkeeper = FindSoldierByProfileID( armsDealerInfo[gbSelectedArmsDealerID].ubShopKeeperID, FALSE );
Assert( pShopkeeper != NULL );
Assert( pShopkeeper->bActive );
Assert( pShopkeeper->bInSector );
if ( pShopkeeper->stats.bLife < OKLIFE )
{
return( FALSE );
}
if ( pSoldier->bActive && pSoldier->bInSector && IsMercOnCurrentSquad( pSoldier ) && ( pSoldier->stats.bLife >= OKLIFE ) &&
!( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) && !AM_A_ROBOT( pSoldier ) )
{
sDestGridNo = pShopkeeper->sGridNo;
bDestLevel = pShopkeeper->pathing.bLevel;
// If he has LOS...
if ( SoldierTo3DLocationLineOfSightTest( pSoldier, sDestGridNo, bDestLevel, 3, TRUE, CALC_FROM_ALL_DIRS ) )
{
// Get range to shopkeeper
uiRange = PythSpacesAway( pSoldier->sGridNo, sDestGridNo );
// and is close enough to talk to the shopkeeper (use this define INSTEAD of PASSING_ITEM_DISTANCE_OKLIFE!)
if ( uiRange <= NPC_TALK_RADIUS )
{
return( TRUE );
}
}
}
return( FALSE );
}
#ifdef JA2TESTVERSION
void AddShopkeeperToGridNo( UINT8 ubProfile, INT32 sGridNo )
{
SOLDIERCREATE_STRUCT MercCreateStruct;
INT16 sSectorX, sSectorY;
SoldierID ubID;
GetCurrentWorldSector( &sSectorX, &sSectorY );
MercCreateStruct.initialize();
MercCreateStruct.bTeam = CIV_TEAM;
MercCreateStruct.ubProfile = ubProfile;
MercCreateStruct.sSectorX = sSectorX;
MercCreateStruct.sSectorY = sSectorY;
MercCreateStruct.bSectorZ = gbWorldSectorZ;
MercCreateStruct.sInsertionGridNo = sGridNo;
if ( TacticalCreateSoldier( &MercCreateStruct, &ubID ) )
{
AddSoldierToSector( ubID );
// So we can see them!
AllTeamsLookForAll(NO_INTERRUPTS);
}
}
#endif
void ExitSKIRequested()
{
BOOLEAN fPlayerOwnedStuffOnTable = FALSE;
ShutUpShopKeeper();
if( !gfRemindedPlayerToPickUpHisStuff )
{
if( AreThereItemsInThePlayersOfferArea( ) )
{
fPlayerOwnedStuffOnTable = TRUE;
}
if( ( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS ) &&
AreThereItemsInTheArmsDealersOfferArea( ) )
{
fPlayerOwnedStuffOnTable = TRUE;
}
//if there are any items belonging to player still on the table
if( fPlayerOwnedStuffOnTable )
{
// remind player to pick them up
ShutUpShopKeeper( );
StartShopKeeperTalking( SK_QUOTES_PRESSED_DONE_STILL_HAS_STUFF_IN_OFFER_AREA );
gfRemindedPlayerToPickUpHisStuff = TRUE;
return;
}
else
{
//if the player has already requested to leave
if( gfUserHasRequestedToLeave )
{
// has already asked to leave, shut the dealer up and allow the player to leave
ShutUpShopKeeper();
return;
}
}
}
// if the player hasn't already requested to leave
if( !gfUserHasRequestedToLeave )
{
UINT16 usQuoteNum;
if( gfDoneBusinessThisSession )
usQuoteNum = SK_QUOTES_PRESSES_DONE_HAS_AT_LEAST_1_TRANSACTION;
else
usQuoteNum = SK_QUOTES_PRESSED_DONE_HASNT_MADE_TRANSACTION;
StartShopKeeperTalking( usQuoteNum );
//Set the fact that the player wants to leave
gfUserHasRequestedToLeave = TRUE;
}
}
void ResetAllQuoteSaidFlags()
{
// reset flags for quotes said
for ( UINT8 uiCnt = 0; uiCnt < NUM_EVAL_RESULTS; ++uiCnt )
{
gfEvalResultQuoteSaid[ uiCnt ] = FALSE;
}
guiLastTimeDealerSaidNormalEvaluationQuote = 0;
}
void DealWithItemsStillOnTheTable()
{
SOLDIERTYPE *pDropSoldier;
// in case we have have to drop stuff off at someone's feet, figure out where it's all gonna go
// use the current merc, unless he's ineligible, then use the selected merc instead.
if( !gfSMDisableForItems )
{
pDropSoldier = gpSMCurrentMerc;
}
else
{
pDropSoldier = gusSelectedSoldier;
}
// this guy HAS to be valid!
Assert( CanMercInteractWithSelectedShopkeeper( pDropSoldier ) );
//loop through the players offer area and add return any items there to the player
for ( int ubCnt = 0; ubCnt < (SKI_PLAYER_OFFER_BOXES_PER_ROW * SKI_PLAYER_OFFER_BOXES_PER_COL); ++ubCnt )
{
//if there is an item here, give it back somehow
if( PlayersOfferArea[ ubCnt ].fActive )
{
ReturnItemToPlayerSomehow( &( PlayersOfferArea[ ubCnt ] ), pDropSoldier );
ClearPlayersOfferSlot( ubCnt );
}
}
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
//loop through the arms dealers' offer area and return any items there to the player
for ( int ubCnt = 0; ubCnt < (SKI_TRADER_OFFER_BOXES_PER_ROW * SKI_TRADER_OFFER_BOXES_PER_COL); ++ubCnt)
{
//if there is an item here, give it back somehow
if( ArmsDealerOfferArea[ ubCnt ].fActive )
{
ReturnItemToPlayerSomehow( &( ArmsDealerOfferArea[ ubCnt ] ), pDropSoldier );
ClearArmsDealerOfferSlot( ubCnt );
}
}
}
}
void ReturnItemToPlayerSomehow( INVENTORY_IN_SLOT *pInvSlot, SOLDIERTYPE *pDropSoldier )
{
//if the item doesn't have a duplicate copy in its owner merc's inventory slot
if( pInvSlot->bSlotIdInOtherLocation == -1 )
{
// first try to give it to its owner, or if he's unavailable, the current merc
if ( !SKITryToReturnInvToOwnerOrCurrentMerc( pInvSlot ) )
{
// failed to add item, inventory probably filled up or item is unowned and current merc ineligible.
// drop it at the specified guy's feet instead
ShopkeeperAddItemToPool( pDropSoldier->sGridNo, &pInvSlot->ItemObject, VISIBLE, pDropSoldier->pathing.bLevel, 0, 0 );
}
}
//CHRISL: We need to handle LBENODE items a little differently so we don't lose anything stored in them. Instead of
// trying to return the item, simply drop the item to the ground.
if(UsingNewInventorySystem() == true && pInvSlot->ItemObject.IsActiveLBE(0) == true)
{
pDropSoldier->inv[pInvSlot->bSlotIdInOtherLocation].initialize();
ShopkeeperAddItemToPool( pDropSoldier->sGridNo, &pInvSlot->ItemObject, VISIBLE, pDropSoldier->pathing.bLevel, 0, 0 );
}
}
void GivePlayerSomeChange( UINT32 uiAmount )
{
INVENTORY_IN_SLOT MoneyInvSlot;
CreateMoney( uiAmount, &MoneyInvSlot.ItemObject );
MoneyInvSlot.sItemIndex = MoneyInvSlot.ItemObject.usItem;
MoneyInvSlot.ubLocationOfObject = PLAYERS_INVENTORY;
MoneyInvSlot.ubIdOfMercWhoOwnsTheItem = NO_PROFILE;
AddItemToPlayersOfferArea( NO_PROFILE, &MoneyInvSlot, -1 );
if ( ( gbSelectedArmsDealerID == ARMS_DEALER_JAKE ) &&
( gArmsDealerStatus[ gbSelectedArmsDealerID ].uiArmsDealersCash < uiAmount ) )
{
// HACK HACK HACK: We forgot to write/record quote 27 for Jake, so he ALWAYS must have enough money!
gArmsDealerStatus[ gbSelectedArmsDealerID ].uiArmsDealersCash += ( uiAmount - gArmsDealerStatus[ gbSelectedArmsDealerID ].uiArmsDealersCash );
}
// he must have enough cash left to give change
Assert( gArmsDealerStatus[ gbSelectedArmsDealerID ].uiArmsDealersCash >= uiAmount );
// subtract change from dealer's cash
gArmsDealerStatus[ gbSelectedArmsDealerID ].uiArmsDealersCash -= uiAmount;
}
void DealerGetsBribed( UINT8 ubProfileId, UINT32 uiMoneyAmount )
{
BOOLEAN fBribable = FALSE;
UINT32 uiMinBribe = 0;
UINT16 usFact = 0;
// this is only for dealers
if (!IsMercADealer( ubProfileId ) )
{
return;
}
switch ( ubProfileId )
{
case FRANK:
fBribable = TRUE;
uiMinBribe = 10; // This should match the handling of a bribe submitted outside the SKI, using the GIVE system
usFact = FACT_FRANK_HAS_BEEN_BRIBED;
break;
}
// if this guy can be bribed, and we gave enough to bribe him
if ( fBribable && ( uiMoneyAmount >= uiMinBribe ) )
{
// set the appropriate fact
SetFactTrue( usFact );
}
}
void HandlePossibleRepairDelays()
{
UINT32 uiHoursSinceAnyItemsShouldHaveBeenRepaired = 0;
// assume there won't be a delay
gfStartWithRepairsDelayedQuote = FALSE;
// if it's Fredo or Perko
#ifdef JA2UB
if ( ( gbSelectedArmsDealerID == ARMS_DEALER_FREDO ) || ( gbSelectedArmsDealerID == ARMS_DEALER_RAUL ) )
#else
if ( ( gbSelectedArmsDealerID == ARMS_DEALER_FREDO ) || ( gbSelectedArmsDealerID == ARMS_DEALER_PERKO ) )
#endif
{
// because the quotes are so specific, we'll only use them once per game per repairman
if( !gArmsDealerStatus[ gbSelectedArmsDealerID ].fRepairDelayBeenUsed )
{
// and it's been a while since the player last dealt with this repairman (within SKI that is)
// this serves 2 purposes:
// a) reduces delays being much more likely if player checks time remaining very frequently, AND
// b) gives time for the events described in the text of the dealers' excuses to happen (e.g. scouting trip)
if ( ( GetWorldTotalMin() - gArmsDealerStatus[ gbSelectedArmsDealerID ].uiTimePlayerLastInSKI ) >= ( 3 * 60 ) )
{
// if he should have been finished, but it's only been a few hours since then (not days!)
if ( RepairmanFixingAnyItemsThatShouldBeDoneNow( &uiHoursSinceAnyItemsShouldHaveBeenRepaired ) &&
( uiHoursSinceAnyItemsShouldHaveBeenRepaired < REPAIR_DELAY_IN_HOURS ) )
{
// then there's a fair chance he'll be delayed. Use pre-chance to hopefully preserve across reloads
if ( PreChance( 50 ) )
{
DelayRepairsInProgressBy( ( REPAIR_DELAY_IN_HOURS + Random( REPAIR_DELAY_IN_HOURS ) ) * 60 );
// this triggers a different opening quote
gfStartWithRepairsDelayedQuote = TRUE;
// set flag so it doesn't happen again
gArmsDealerStatus[ gbSelectedArmsDealerID ].fRepairDelayBeenUsed = TRUE;
}
}
}
}
}
}
BOOLEAN RepairmanFixingAnyItemsThatShouldBeDoneNow( UINT32 *puiHoursSinceOldestItemRepaired )
{
//if the dealer is not a repair dealer, return
if( !DoesDealerDoRepairs( gbSelectedArmsDealerID ) )
return( FALSE );
BOOLEAN fFoundOne = FALSE;
UINT32 uiMinutesSinceItWasDone;
UINT32 uiMinutesShopClosedSinceItWasDone;
UINT32 uiWorkingHoursSinceThisItemRepaired;
*puiHoursSinceOldestItemRepaired = 0;
UINT32 currentTime = GetWorldTotalMin();
//loop through the dealers inventory and check if there are only unrepaired items
for (DealerItemList::iterator iter = gArmsDealersInventory[gbSelectedArmsDealerID].begin();
iter != gArmsDealersInventory[gbSelectedArmsDealerID].end(); ++iter) {
if ( iter->ItemIsInInventory() == true )
{
if( iter->IsUnderRepair() == true )
{
//if the repairs are done
if( iter->uiRepairDoneTime <= currentTime )
{
// at least one item is supposed to be done by now
fFoundOne = TRUE;
uiMinutesSinceItWasDone = currentTime - iter->uiRepairDoneTime;
uiMinutesShopClosedSinceItWasDone = CalculateMinutesClosedBetween( gbSelectedArmsDealerID, iter->uiRepairDoneTime, currentTime );
// calculate how many WORKING hours since this item's been repaired
uiWorkingHoursSinceThisItemRepaired = ( uiMinutesSinceItWasDone - uiMinutesShopClosedSinceItWasDone ) / 60;
// we need to determine how long it's been since the item that's been repaired for the longest time was done
if ( uiWorkingHoursSinceThisItemRepaired > *puiHoursSinceOldestItemRepaired )
{
*puiHoursSinceOldestItemRepaired = uiWorkingHoursSinceThisItemRepaired;
}
}
}
}
}
// if FALSE returned here, he's either not repairing anything, or none of it's done yet
return( fFoundOne );
}
void DelayRepairsInProgressBy( UINT32 uiMinutesDelayed )
{
UINT32 uiMinutesShopClosedBeforeItsDone;
//if the dealer is not a repair dealer, return
if( !DoesDealerDoRepairs( gbSelectedArmsDealerID ) )
return;
//loop through the dealers inventory and check if there are only unrepaired items
for (DealerItemList::iterator iter = gArmsDealersInventory[gbSelectedArmsDealerID].begin();
iter != gArmsDealersInventory[gbSelectedArmsDealerID].end(); ++iter) {
if ( iter->ItemIsInInventory() == true )
{
if( iter->IsUnderRepair() == true )
{
uiMinutesShopClosedBeforeItsDone = CalculateOvernightRepairDelay( gbSelectedArmsDealerID, iter->uiRepairDoneTime, uiMinutesDelayed );
// add this many minutes to the repair time estimate
iter->uiRepairDoneTime += ( uiMinutesShopClosedBeforeItsDone + uiMinutesDelayed );
}
}
}
}
//Mouse Call back for the Arms delaers face
void SelectArmsDealersDropItemToGroundRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason )
{
// WANNE 2: not needed to drop the item to the ground->on the right side of the shopkeeper screen!
/*
if (iReason & MSYS_CALLBACK_REASON_INIT)
{
}
else if(iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
{
SOLDIERTYPE *pDropSoldier;
// use the current merc, unless he's ineligible, then use the selected merc instead.
if( !gfSMDisableForItems )
{
pDropSoldier = gpSMCurrentMerc;
}
else
{
pDropSoldier = gusSelectedSoldier;
}
//if we don't have an item, pick one up
if( gMoveingItem.sItemIndex != 0 )
{
//add the item to the ground
ShopkeeperAddItemToPool( pDropSoldier->sGridNo, &gMoveingItem.ItemObject, VISIBLE, pDropSoldier->pathing.bLevel, 0, 0 );
//Reset the cursor
SetSkiCursor( CURSOR_NORMAL );
//refresh the screen
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
}
}
*/
}
void SelectArmsDealersDropItemToGroundMovementRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason )
{
if( iReason & MSYS_CALLBACK_REASON_LOST_MOUSE )
{
gfSkiDisplayDropItemToGroundText = FALSE;
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
}
else if( iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE )
{
if( CanTheDropItemToGroundStringBeDisplayed() )
{
gfSkiDisplayDropItemToGroundText = TRUE;
}
else
{
gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
gfSkiDisplayDropItemToGroundText = FALSE;
}
}
else if( iReason & MSYS_CALLBACK_REASON_MOVE )
{
}
}
BOOLEAN CanTheDropItemToGroundStringBeDisplayed()
{
//if we don't have an item, pick one up
if( gMoveingItem.sItemIndex != 0 )
return( TRUE );
else
return( FALSE );
}
void DisplayTheSkiDropItemToGroundString()
{
DisplayWrappedString( SKI_DROP_ITEM_TO_GROUND_START_X, SKI_DROP_ITEM_TO_GROUND_TEXT_START_Y, (UINT16)(SCREEN_WIDTH-SKI_DROP_ITEM_TO_GROUND_START_X), 2, SKI_LABEL_FONT, SKI_TITLE_COLOR, SKI_Text[ SKI_TEXT_DROP_ITEM_TO_GROUND ], FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED | DONT_DISPLAY_TEXT );
}
UINT32 EvaluateInvSlot( INVENTORY_IN_SLOT *pInvSlot )
{
UINT32 uiEvalResult = EVAL_RESULT_NORMAL;
FLOAT dPriceModifier;
UINT32 uiBuyingPrice;
//if the dealer is Micky
if( gbSelectedArmsDealerID == ARMS_DEALER_MICKY )
{
SoldierID sSoldierID = GetSoldierIDFromMercID( armsDealerInfo[ gbSelectedArmsDealerID ].ubShopKeeperID );
if( ( sSoldierID != NOBODY ) && ( GetDrunkLevel( sSoldierID ) == DRUNK ) )
{
//Micky is DRUNK, pays more!
dPriceModifier = armsDealerInfo[ gbSelectedArmsDealerID ].dSellModifier;
}
else
{
// Micky isn't drunk, charge regular price
dPriceModifier = armsDealerInfo[ gbSelectedArmsDealerID ].dBuyModifier;
}
}
else
{
dPriceModifier = armsDealerInfo[ gbSelectedArmsDealerID ].dBuyModifier;
}
// Calculate dealer's buying price for the item
uiBuyingPrice = CalcShopKeeperItemPrice( DEALER_BUYING, FALSE, pInvSlot->sItemIndex, dPriceModifier, &( pInvSlot->ItemObject ) );
pInvSlot->uiItemPrice = uiBuyingPrice;
if ( uiBuyingPrice > 0 )
{
// check if the item is really badly damaged
if( Item[ pInvSlot->sItemIndex ].usItemClass != IC_AMMO )
{
if( pInvSlot->ItemObject[0]->data.objectStatus < REALLY_BADLY_DAMAGED_THRESHOLD )
{
uiEvalResult = EVAL_RESULT_OK_BUT_REALLY_DAMAGED;
}
}
//the item can be sold here, so mark it as such
pInvSlot->uiFlags |= ARMS_INV_PLAYERS_ITEM_HAS_VALUE;
}
else
{
// he normally buys this, but it's in such bad shape that he won't, it's worthless (different quote)
uiEvalResult = EVAL_RESULT_WORTHLESS;
pInvSlot->uiFlags &= ~ARMS_INV_PLAYERS_ITEM_HAS_VALUE;
}
return( uiEvalResult );
}
void BuildRepairTimeString( CHAR16 sString[], UINT32 uiTimeInMinutesToFixItem )
{
UINT16 usNumberOfHoursToFixItem = 0;
// if it's 0, it shouldn't be up here any more!
Assert ( uiTimeInMinutesToFixItem > 0 );
// round off to next higher 15 minutes
if ( ( uiTimeInMinutesToFixItem % REPAIR_MINUTES_INTERVAL ) > 0 )
{
uiTimeInMinutesToFixItem += REPAIR_MINUTES_INTERVAL - ( uiTimeInMinutesToFixItem % REPAIR_MINUTES_INTERVAL );
}
if ( uiTimeInMinutesToFixItem < REPAIR_MINUTES_INTERVAL )
{
uiTimeInMinutesToFixItem = REPAIR_MINUTES_INTERVAL;
}
// show up to 1.5 hrs in minutes
if ( uiTimeInMinutesToFixItem <= 90 )
{
// show minutes
swprintf( sString, SKI_Text[ SKI_TEXT_MINUTES ], uiTimeInMinutesToFixItem );
}
else
{
// show hours
// round fractions of 15+ minutes up to next full hour
usNumberOfHoursToFixItem = (UINT16) ( ( uiTimeInMinutesToFixItem + 45 ) / 60 );
if ( usNumberOfHoursToFixItem > 1 )
{
swprintf( sString, SKI_Text[ SKI_TEXT_PLURAL_HOURS ], usNumberOfHoursToFixItem );
}
else
{
wcscpy( sString, SKI_Text[ SKI_TEXT_ONE_HOUR ] );
}
}
}
void BuildDoneWhenTimeString( CHAR16 sString[], UINT8 ubArmsDealer, INVENTORY_IN_SLOT* pObject )
{
UINT32 uiDoneTime;
UINT32 uiDay, uiHour, uiMin;
//dealer must be a repair dealer
Assert( DoesDealerDoRepairs( ubArmsDealer ) );
DEALER_SPECIAL_ITEM* pSpecial = 0;
for (DealerItemList::iterator iter = gArmsDealersInventory[ubArmsDealer].begin();
iter != gArmsDealersInventory[ubArmsDealer].end(); ++iter) {
if (iter->ItemIsInInventory() == true && iter->IsUnderRepair() == true) {
if (pObject->ItemObject == iter->object && pObject->uiRepairDoneTime == iter->uiRepairDoneTime) {
pSpecial = &(*iter);
break;
}
}
}
Assert(pSpecial);
// that item must be in repair
Assert( pSpecial->IsUnderRepair() == true );
//if the item has already been repaired
uiDoneTime = pSpecial->uiRepairDoneTime;
if( uiDoneTime <= GetWorldTotalMin() )
{
wcscpy( sString, L"" );
return;
}
// round off to next higher 15 minutes
if ( ( uiDoneTime % REPAIR_MINUTES_INTERVAL ) > 0 )
{
uiDoneTime += REPAIR_MINUTES_INTERVAL - ( uiDoneTime % REPAIR_MINUTES_INTERVAL );
}
// uiDoneTime is in minutes...
uiDay = ( uiDoneTime / NUM_MIN_IN_DAY );
uiHour = ( uiDoneTime - ( uiDay * NUM_MIN_IN_DAY ) ) / NUM_MIN_IN_HOUR;
uiMin = uiDoneTime - ( ( uiDay * NUM_MIN_IN_DAY ) + ( uiHour * NUM_MIN_IN_HOUR ) );
// only show day if it's gonna take overnight
if ( GetWorldDay() != uiDay )
{
swprintf( sString, L"%s %d %02d:%02d", pDayStrings[ 0 ], uiDay, uiHour, uiMin );
}
else
{
swprintf( sString, L"%02d:%02d", uiHour, uiMin );
}
}
void BuildItemHelpTextString( CHAR16 sString[], INVENTORY_IN_SLOT *pInv, UINT8 ubScreenArea )
{
CHAR16 zHelpText[ 512 ];
CHAR16 zRepairTime[ 64 ];
if( pInv != NULL )
{
GetHelpTextForItem( zHelpText, &( pInv->ItemObject ), NULL );
// add repair time for items in a repairman's offer area
if ( ( ubScreenArea == ARMS_DEALER_OFFER_AREA ) &&
( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS ) )
{
BuildRepairTimeString( zRepairTime, CalculateObjectItemRepairTime( gbSelectedArmsDealerID, &( pInv->ItemObject ) ) );
swprintf( sString, L"%s\n(%s: %s)", zHelpText, gzLateLocalizedString[ 44 ], zRepairTime );
}
else
{
wcscpy( sString, zHelpText );
}
}
else
{
sString[ 0 ] = '\0';
}
}
void DisableAllDealersInventorySlots( void )
{
for ( int iCnt = 0; iCnt < gDealersInventoryActiveRegions; ++iCnt )
{
MSYS_DisableRegion( &gDealersInventoryMouseRegions[ iCnt ] );
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
MSYS_DisableRegion( &gRepairmanInventorySmallFaceMouseRegions[ iCnt ] );
}
}
}
void EnableAllDealersInventorySlots( void )
{
for ( int iCnt = 0; iCnt < gDealersInventoryActiveRegions; ++iCnt )
{
MSYS_EnableRegion( &gDealersInventoryMouseRegions[ iCnt ] );
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
MSYS_EnableRegion( &gRepairmanInventorySmallFaceMouseRegions[ iCnt ] );
}
}
}
void DisableAllDealersOfferSlots( void )
{
for ( int iCnt = 0; iCnt < gDealersOfferActiveRegions; ++iCnt )
{
MSYS_DisableRegion( &gDealersOfferSlotsMouseRegions[ iCnt ] );
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
MSYS_DisableRegion( &gDealersOfferSlotsSmallFaceMouseRegions[ iCnt ] );
}
}
}
void EnableAllDealersOfferSlots( void )
{
for ( int iCnt = 0; iCnt < gDealersOfferActiveRegions; ++iCnt )
{
MSYS_EnableRegion( &gDealersOfferSlotsMouseRegions[ iCnt ] );
//if the dealer repairs
if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS )
{
MSYS_EnableRegion( &gDealersOfferSlotsSmallFaceMouseRegions[ iCnt ] );
}
}
}
void HatchOutInvSlot( UINT16 usPosX, UINT16 usPosY )
{
UINT16 usSlotX, usSlotY;
UINT16 usSlotWidth, usSlotHeight;
//if we are in the item desc panel
if( InItemDescriptionBox( ) && pShopKeeperItemDescObject != NULL )
{
// do nothing, don't wanna shade things twice!
return;
}
usSlotX = ( UINT16 ) ( usPosX - 1 );
usSlotY = ( UINT16 ) ( usPosY - 1 );
usSlotWidth = SKI_INV_SLOT_WIDTH;
usSlotHeight = SKI_INV_SLOT_HEIGHT;
//Hatch it out
DrawHatchOnInventory( guiRENDERBUFFER, usSlotX, usSlotY, usSlotWidth, usSlotHeight );
InvalidateRegion( usSlotX, usSlotY, usSlotX + usSlotWidth, usSlotY + usSlotHeight );
}
#ifdef JA2UB
//ja25 UB
void AskUserToAttachTransmitterToLaptop()
{
DoSkiMessageBox( MSG_BOX_BASIC_STYLE, zNewTacticalMessages[ TCTL_MSG__ATTACH_TRANSMITTER_TO_LAPTOP ], SHOPKEEPER_SCREEN, MSG_BOX_FLAG_OK, AttachLaptopTransmitterToLaptop );
}
void AttachLaptopTransmitterToLaptop( UINT8 ubExitValue )
{
//
// Get the laptop working again
//
//Mark the quest done
if ( gGameUBOptions.LaptopQuestEnabled == TRUE )
EndQuest( QUEST_FIX_LAPTOP, -1, -1 );
}
#endif