Files
source/Tactical/ShopKeeper Interface.cpp
T

7367 lines
250 KiB
C++

#ifdef PRECOMPILEDHEADERS
#include "Tactical All.h"
#else
#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 "Personnel.h"
#include "LaptopSave.h"
#include "Quests.h"
#include "Weapons.h"
#include "MessageBoxScreen.h"
#include "LINE.H"
#include "Drugs and Alcohol.h"
#include "Map Screen Interface.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 "Encyclopedia_new.h"
#include "Animation Control.h" // added by Flugente
#include "Town Militia.h" // added by Flugente
#endif
#ifdef JA2UB
#include "Explosion Control.h"
#include "Ja25_Tactical.h"
#include "Ja25 Strategic Ai.h"
#include "MapScreen Quotes.h"
#include "email.h"
#include "interface Dialogue.h"
#include "Ja25_Tactical.h"
#include "ub_config.h"
#endif
#include "BuildDefines.h"
#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
UINT8 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 ];
UINT8 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 UINT8 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 );
extern BOOLEAN ItemIsARocketRifle( INT16 sItemIndex );
#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;
UINT16 ubCnt;
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 = MercPtrs[ gusSelectedSoldier ];
SOLDIERTYPE* pShopkeeper = NULL;
if ( gusIDOfCivTrader != NOBODY )
pShopkeeper = MercPtrs[gusIDOfCivTrader];
else
pShopkeeper = FindSoldierByProfileID( armsDealerInfo[gbSelectedArmsDealerID].ubShopKeeperID, FALSE );
if ( GetRangeFromGridNoDiff( 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( MercPtrs[ 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( (UINT8)gusSelectedSoldier );
SetSMPanelCurrentMerc( (UINT8)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( ubCnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; ubCnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; ++ubCnt )
{
pSoldier = MercPtrs[ 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(&Menptr[ 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 (UINT8 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
INT16 bSoldierID = RandomSoldierIdFromNewMercsOnPlayerTeam();
if( bSoldierID > -1 )
{
TacticalCharacterDialogue( &Menptr[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 = MercPtrs[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"%d", gArmsDealerStatus[gbSelectedArmsDealerID].uiArmsDealersCash );
InsertCommasForDollarFigure( zMoney );
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"%d", (int)(GetIntel()) );
InsertCommasForDollarFigure( zMoney );
swprintf( zTemp2, L"%s Intel", zMoney );
}
else
{
swprintf( zMoney, L"%d", LaptopSaveInfo.iCurrentBalance );
InsertCommasForDollarFigure( zMoney );
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:
{
UINT8 ubID;
DeleteItemDescriptionBox( );
// skip Robot and EPCs
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"%d", gArmsDealerStatus[ bArmsDealer ].uiArmsDealersCash );
InsertCommasForDollarFigure( zTemp );
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, UINT8 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( MercPtrs[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"%d", uiItemCost );
InsertCommasForDollarFigure( zTemp );
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 ( CanDealerRepairItem( gbSelectedArmsDealerID, iter->usItem ) && iter->exists())
{
// 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 ( Item[ usItemIndex ].fFlags & ITEM_REPAIRABLE )
if ( Item[ usItemIndex ].repairable )
{
// 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"%d", uiTotalCost );
InsertCommasForDollarFigure( zTemp );
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 )
{
INT16 sSoldierID;
sSoldierID = GetSoldierIDFromMercID( PlayersOfferArea[ sCnt ].ubIdOfMercWhoOwnsTheItem );
Assert(sSoldierID != -1);
PlayersOfferArea[ sCnt ].ItemObject[0]->data.money.uiMoneyAmount = Menptr[ 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"%d", uiTotalCost );
InsertCommasForDollarFigure( zTemp );
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( Item[ pItemObject->usItem ].fFlags & ITEM_ATTACHMENT )
if( Item[ pItemObject->usItem ].attachment )
{
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( INT8 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( ItemIsARocketRifle( 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 ( Item[ PlayersOfferArea[ bSlotID ].sItemIndex ].fFlags & ITEM_REPAIRABLE )
if ( Item[ PlayersOfferArea[ bSlotID ].sItemIndex ].repairable )
{
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 ( !Item[usItem].singleshotrocketlauncher ) // 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 )
{
INT16 sSoldierID;
//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
sSoldierID = GetSoldierIDFromMercID( pInv->ubIdOfMercWhoOwnsTheItem );
Assert( sSoldierID != -1 );
Assert( CanMercInteractWithSelectedShopkeeper( MercPtrs[ sSoldierID ] ) );
// then it better be a valid slot #
Assert( pInv->bSlotIdInOtherLocation < (INT8)Menptr[ 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
Menptr[ 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 );
INT16 sSoldierID = GetSoldierIDFromMercID( ubOwnerProfileId );
Assert( sSoldierID != -1 );
// then it better be a valid slot #
Assert( bOwnerSlotId < (INT8)Menptr[ sSoldierID ].inv.size() );
Assert( CanMercInteractWithSelectedShopkeeper( MercPtrs[ sSoldierID ] ) );
//remove the object from that merc's original inventory slot
DeleteObj(&(Menptr[ 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 )
{
INT16 sSoldierID;
sSoldierID = GetSoldierIDFromMercID( pInv->ubIdOfMercWhoOwnsTheItem );
// if that soldier is not in player's hire any longer
if ( sSoldierID == -1 )
{
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( MercPtrs[ 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, MercPtrs[ 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 = MercPtrs[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 = GetRangeFromGridNoDiff( 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;
UINT8 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 = MercPtrs[ 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 = MercPtrs[ 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 )
{
INT16 sSoldierID;
sSoldierID = GetSoldierIDFromMercID( armsDealerInfo[ gbSelectedArmsDealerID ].ubShopKeeperID );
if( ( sSoldierID != -1 ) && ( GetDrunkLevel( &Menptr[ 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