#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 "Store Inventory.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" #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 // temp struct skirgbcolor { UINT8 ubRed; UINT8 ubGreen; UINT8 ubBlue; }; typedef struct skirgbcolor SKIRGBCOLOR; SKIRGBCOLOR SkiGlowColorsA[]={ {0,0,0}, {25,0,0}, {50,0,0}, {75,0,0}, {100,0,0}, {125,0,0}, {150,0,0}, {175,0,0}, {200,0,0}, {225,0,0}, {250,0,0}, }; //////////////////////////////////////////// // // 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_ATM_BUTTON_FONT FONT10ARIAL #define SKI_ATM_BUTTON_COLOR FONT_MCOLOR_BLACK #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 #define SKI_MAIN_BACKGROUND_X SCREEN_X_OFFSET #define SKI_MAIN_BACKGROUND_Y SCREEN_Y_OFFSET #define SKI_FACE_X (SCREEN_X_OFFSET + 13) #define SKI_FACE_Y (SCREEN_Y_OFFSET + 13) #define SKI_FACE_WIDTH 90 #define SKI_FACE_HEIGHT 100 #define SKI_PAGE_UP_ARROWS_X (SCREEN_X_OFFSET + 121) #define SKI_PAGE_UP_ARROWS_Y (SCREEN_Y_OFFSET + 35) #define SKI_PAGE_DOWN_ARROWS_X SKI_PAGE_UP_ARROWS_X #define SKI_PAGE_DOWN_ARROWS_Y (SCREEN_Y_OFFSET + 102) //Evaluate: //#define SKI_EVALUATE_BUTTON_X 15 //#define SKI_EVALUATE_BUTTON_Y 233 #define SKI_TRANSACTION_BUTTON_X (SCREEN_X_OFFSET + 147)//214 #define SKI_TRANSACTION_BUTTON_Y (SCREEN_Y_OFFSET +233)//SKI_EVALUATE_BUTTON_Y #define SKI_DONE_BUTTON_X (SCREEN_X_OFFSET + 292)//414 #define SKI_DONE_BUTTON_Y (SCREEN_Y_OFFSET + 233)//SKI_EVALUATE_BUTTON_Y #define SKI_MAIN_TITLE_X (SCREEN_X_OFFSET + 112) #define SKI_MAIN_TITLE_Y (SCREEN_Y_OFFSET + 12) #define SKI_MAIN_TITLE_WIDTH 420 #define SKI_TOTAL_COST_X (SCREEN_X_OFFSET + 9) #define SKI_TOTAL_COST_Y (SCREEN_Y_OFFSET + 162)//159 #define SKI_TOTAL_COST_WIDTH 73 #define SKI_TOTAL_VALUE_X SKI_TOTAL_COST_X #define SKI_TOTAL_VALUE_Y (SCREEN_Y_OFFSET + 291)//268 #define SKI_TOTAL_VALUE_WIDTH SKI_TOTAL_COST_WIDTH #define SKI_PLAYERS_CURRENT_BALANCE_X SKI_TOTAL_COST_X #define SKI_PLAYERS_CURRENT_BALANCE_Y (SCREEN_Y_OFFSET + 235) #define SKI_PLAYERS_CURRENT_BALANCE_WIDTH SKI_TOTAL_COST_WIDTH #define SKI_PLAYERS_CURRENT_BALANCE_OFFSET_TO_VALUE SCREEN_Y_OFFSET + 265 #define SKI_PAGE_X (SCREEN_X_OFFSET + 112) #define SKI_PAGE_Y (SCREEN_Y_OFFSET + 70) #define SKI_PAGE_WIDTH 45 #define SKI_PAGE_HEIGHT 27 //Number of Inventory slots #define SKI_NUM_ARMS_DEALERS_INV_SLOTS 15 #define SKI_NUM_ARMS_DEALERS_INV_ROWS 3 #define SKI_NUM_ARMS_DEALERS_INV_COLS 5 //Inventory Slots size and offsets #define SKI_INV_SLOT_WIDTH 67 #define SKI_INV_SLOT_HEIGHT 31 #define SKI_INV_HEIGHT SKI_INV_SLOT_HEIGHT - 7 #define SKI_INV_WIDTH 60 #define SKI_INV_PRICE_OFFSET_X 1 #define SKI_INV_PRICE_OFFSET_Y 24 #define SKI_INV_OFFSET_X 74 #define SKI_INV_OFFSET_Y 36 // the delay for glow cycling in glow areas, in millisecs #define SKI_GLOW_DELAY 70 //Start Locations for the inventory boxes #define SKI_ARMS_DEALERS_INV_START_X (SCREEN_X_OFFSET + 165) #define SKI_ARMS_DEALERS_INV_START_Y (SCREEN_Y_OFFSET + 30) #define SKI_ARMS_DEALERS_TRADING_INV_X (SCREEN_X_OFFSET + 91) #define SKI_ARMS_DEALERS_TRADING_INV_Y (SCREEN_Y_OFFSET + 151) #define SKI_ARMS_DEALERS_TRADING_INV_WIDTH 436 #define SKI_ARMS_DEALERS_TRADING_INV_HEIGHT 67 #define SKI_PLAYERS_TRADING_INV_X (SCREEN_X_OFFSET + 91) #define SKI_PLAYERS_TRADING_INV_Y (SCREEN_Y_OFFSET + 266) #define SKI_PLAYERS_TRADING_INV_HEIGHT 70 #define SKI_PLAYERS_TRADING_INV_WIDTH 440 #define SKI_ARMS_DEALER_TOTAL_COST_X (SCREEN_X_OFFSET + 16) #define SKI_ARMS_DEALER_TOTAL_COST_Y (SCREEN_Y_OFFSET + 194)//191 #define SKI_ARMS_DEALER_TOTAL_COST_WIDTH 59 #define SKI_ARMS_DEALER_TOTAL_COST_HEIGHT 20 #define SKI_PLAYERS_TOTAL_VALUE_X (SCREEN_X_OFFSET + 16) #define SKI_PLAYERS_TOTAL_VALUE_Y (SCREEN_Y_OFFSET + 310)//308 #define SKI_PLAYERS_TOTAL_VALUE_WIDTH 59 #define SKI_PLAYERS_TOTAL_VALUE_HEIGHT 20 #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_TACTICAL_BACKGROUND_START_WIDTH (SCREEN_WIDTH - SKI_TACTICAL_BACKGROUND_START_X) #define SKI_TACTICAL_BACKGROUND_START_HEIGHT (SCREEN_HEIGHT - INV_INTERFACE_HEIGHT) #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 15 #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_ATM_PANEL_X (SCREEN_X_OFFSET + 87) #define SKI_ATM_PANEL_Y (SCREEN_Y_OFFSET + SKI_INTERFACE_HEIGHT) #define SKI_ATM_PANEL_WIDTH 127 #define SKI_ATM_PANEL_HEIGHT 135 #define SKI_ATM_BUTTON_X SKI_ATM_PANEL_X + 23 #define SKI_ATM_BUTTON_Y SKI_ATM_PANEL_Y + 64 #define SKI_ATM_BUTTON_HEIGHT 15 #define SKI_ATM_NUM_BUTTON_WIDTH 15 #define SKI_ATM_SIDE_MENU_PANEL_START_X (SCREEN_X_OFFSET + 48) #define SKI_TRANSFER_STRING_X SKI_ATM_PANEL_X + 22 #define SKI_TRANSFER_STRING_Y SKI_ATM_PANEL_Y + 50 #define SKI_TRANSFER_STRING_WIDTH 83 #define SKI_TRANSFER_STRING_HEIGHT 10 #define SKI_MERCS_MONEY_Y SKI_ATM_PANEL_Y + 11 #define SKI_MODE_TEXT_X SKI_TRANSFER_STRING_X #define SKI_MODE_TEXT_Y SKI_ATM_PANEL_Y + 27 #define SKI_MODE_TEXT_WIDTH SKI_TRANSFER_STRING_WIDTH #define SKI_MAX_AMOUNT_OF_ITEMS_DEALER_CAN_REPAIR_AT_A_TIME 4 #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 //////////////////////////////////////////// // // 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 guiMainTradeScreenImage; UINT32 guiCornerWhereTacticalIsStillSeenImage; //This image is for where the corner of tactical is still seen through the shop keeper interface //ATM: //UINT32 guiSkiAtmImage; BOOLEAN gfSKIScreenEntry = TRUE; BOOLEAN gfSKIScreenExit = FALSE; BOOLEAN gfUserHasRequestedToLeave = FALSE; //BOOLEAN gfRedrawSkiScreen = TRUE; BOOLEAN gfRenderScreenOnNextLoop = FALSE; UINT8 gubSkiDirtyLevel = SKI_DIRTY_LEVEL0; INT32 giSKIMessageBox=-1; INT8 gbSelectedArmsDealerID = -1; //Contains the enum value for the currently selected arms dealer //the quote that is in progress, in certain circumstances, we don't want queuing of related but different quotes INT32 giShopKeepDialogueEventinProgress = - 1; //OBJECTTYPE gpSkiItemPointer; 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 gpTempDealersInventory; INVENTORY_IN_SLOT ArmsDealerOfferArea[ SKI_NUM_TRADING_INV_SLOTS ]; INVENTORY_IN_SLOT PlayersOfferArea[ SKI_NUM_TRADING_INV_SLOTS ]; std::vector 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; // the transfer funds string CHAR16 gzSkiAtmTransferString[ 32 ]; 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 the various Atm modes enum { SKI_ATM_DISABLED_MODE, SKI_ATM_TAKE_MODE, SKI_ATM_GIVE_MODE, SKI_ATM_ERR_TAKE_MODE, SKI_ATM_ERR_GIVE_MODE, SKI_ATM_DISPLAY_PLAYERS_BALANCE, }; UINT8 gubCurrentSkiAtmMode = SKI_ATM_DISABLED_MODE; // 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; /* //Evaluate: //Evaluate buttons void BtnSKI_EvaluateButtonCallback(GUI_BUTTON *btn,INT32 reason); UINT32 guiSKI_EvaluateButton; INT32 guiSKI_EvaluateButtonImage; */ //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; //Atm buttons void BtnSKI_AtmButtonCallback(GUI_BUTTON *btn,INT32 reason); UINT32 guiSKI_AtmButton[ NUM_SKI_ATM_BUTTONS ]; INT32 guiSKI_AtmNumButtonImage; INT32 guiSKI_AtmOkButtonImage; INT32 guiSKI_AtmSideMenuButtonImage; UINT32 guiItemCrossOut; 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 ]; void SelectDealersInventoryRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason ); void SelectDealersInventoryMovementRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason ); MOUSE_REGION gDealersOfferSlotsMouseRegions[ SKI_NUM_TRADING_INV_SLOTS ]; void SelectDealersOfferSlotsRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason ); void SelectDealersOfferSlotsMovementRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason ); MOUSE_REGION gPlayersOfferSlotsMouseRegions[ SKI_NUM_TRADING_INV_SLOTS ]; void SelectPlayersOfferSlotsRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason ); void SelectPlayersOfferSlotsMovementRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason ); MOUSE_REGION gDealersOfferSlotsSmallFaceMouseRegions[ SKI_NUM_TRADING_INV_SLOTS ]; MOUSE_REGION gPlayersOfferSlotsSmallFaceMouseRegions[ SKI_NUM_TRADING_INV_SLOTS ]; MOUSE_REGION gSkiInventoryMovementAreaMouseRegions; //void SelectSkiInventoryMovementAreaRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason ); //Mouse region for the subtitles region when the merc is talking MOUSE_REGION gShopKeeperSubTitleMouseRegion; void ShopKeeperSubTitleRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason ); //ATM: //MOUSE_REGION gShopKeeperCoverTacticalButtonMouseRegion; 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& 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 ); //Evaluate: //void EvaluateAllItemsInPlayersOfferArea( ); 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 ubMercID ); void DisplayTalkingArmsDealer(); BOOLEAN StartShopKeeperTalking( UINT16 usQuoteNum ); void HandleShopKeeperDialog( UINT8 ubInit ); BOOLEAN IsGunOrAmmoOfSameTypeSelected( OBJECTTYPE *pItemObject ); void RemoveShopKeeperSubTitledText(); BOOLEAN AreThereItemsInTheArmsDealersOfferArea( ); BOOLEAN AreThereItemsInThePlayersOfferArea( ); void ShutUpShopKeeper(); UINT8 CountNumberOfValuelessItemsInThePlayersOfferArea( ); UINT8 CountNumberOfItemsOfValueInThePlayersOfferArea( ); UINT8 CountNumberOfItemsInThePlayersOfferArea( ); UINT8 CountNumberOfItemsInTheArmsDealersOfferArea( ); INT8 GetSlotNumberForMerc( UINT8 ubProfile ); void RenderSkiAtmPanel(); void RemoveSkiAtmButtons(); void CreateSkiAtmButtons(); void AddNumberToSkiAtm( UINT8 ubNumber ); void HandleSkiAtmPanel( ); void DisplaySkiAtmTransferString(); void HandleSkiAtmPanelInput( UINT8 ubButtonPress ); void EnableDisableSkiAtmButtons(); void HandleCurrentModeText( UINT8 ubMode ); void ToggleSkiAtmButtons(); void HandleAtmOK(); 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 ); void ConfirmToDeductMoneyFromPlayersAccountMessageBoxCallBack( UINT8 bExitValue ); BOOLEAN DoSkiMessageBox( UINT8 ubStyle, STR16 zString, UINT32 uiExitScreen, UINT8 ubFlags, MSGBOX_CALLBACK ReturnCallback ); BOOLEAN WillShopKeeperRejectObjectsFromPlayer( INT8 bDealerId, INT8 bSlotId ); void CheckAndHandleClearingOfPlayerOfferArea( void ); void CrossOutUnwantedItems( 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 //ATM: DisableSMPpanelButtonsWhenInShopKeeperInterface( FALSE ); //Heinz: 22.02.09 BUGFIX: buttons should not render upon shopkeeper's message if( gfIsTheShopKeeperTalking && !gfInItemDescBox ) { ButtonList[ guiSKI_TransactionButton ]->uiFlags |= BUTTON_FORCE_UNDIRTY; ButtonList[ guiSKI_DoneButton ]->uiFlags |= BUTTON_FORCE_UNDIRTY; } 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; UINT8 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 = 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; } //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 backgroiund image VObjectDesc.fCreateFlags=VOBJECT_CREATE_FROMFILE; FilenameForBPP("InterFace\\TradeScreen.sti", VObjectDesc.ImageFile); if( !AddVideoObject(&VObjectDesc, &guiMainTradeScreenImage )) { #ifdef JA2BETAVERSION ScreenMsg( FONT_MCOLOR_WHITE, MSG_BETAVERSION, L"Failed to load TradeScreen.sti" ); #endif return( FALSE ); } // 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 ); } /* ATM: // load the Main trade screen backgroiund image VObjectDesc.fCreateFlags=VOBJECT_CREATE_FROMFILE; FilenameForBPP("InterFace\\TradeScreenAtm.sti", VObjectDesc.ImageFile); CHECKF(AddVideoObject(&VObjectDesc, &guiSkiAtmImage )); */ //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 //sprintf( zTemp, "FACES\\33FACE\\%02d.sti", gMercProfiles[ pSoldier->ubProfile ].ubFaceIndex ); if ( gProfilesIMP[ pSoldier->ubProfile ].ProfilId == pSoldier->ubProfile ) { 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 ); //Evaluate: EnableDisableShopkeeperButtons(SHOPKEEPER_SCREEN, ACTIVATE_BUTTON); // guiSKI_EvaluateButtonImage = LoadButtonImage("INTERFACE\\TradeButtons.sti", -1,0,-1,1,-1 ); // guiSKI_TransactionButtonImage = UseLoadedButtonImage( guiSKI_EvaluateButtonImage, -1,0,-1,1,-1 ); //guiSKI_TransactionButtonImage = LoadButtonImage("INTERFACE\\TradeButtons.sti", -1,0,-1,1,-1 ); //guiSKI_DoneButtonImage = UseLoadedButtonImage( guiSKI_TransactionButtonImage, -1,0,-1,1,-1 ); /* //Evaluate: //Evaluate button guiSKI_EvaluateButton = CreateIconAndTextButton( guiSKI_EvaluateButtonImage, SKI_Text[SKI_TEXT_EVALUATE], SKI_BUTTON_FONT, SKI_BUTTON_COLOR, DEFAULT_SHADOW, SKI_BUTTON_COLOR, DEFAULT_SHADOW, TEXT_CJUSTIFIED, SKI_EVALUATE_BUTTON_X, SKI_EVALUATE_BUTTON_Y, BUTTON_TOGGLE, MSYS_PRIORITY_HIGH, DEFAULT_MOVE_CALLBACK, BtnSKI_EvaluateButtonCallback); SpecifyDisabledButtonStyle( guiSKI_EvaluateButton, DISABLED_STYLE_HATCHED ); */ //Transaction button //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 ] ); //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 ); /* //ATM: //Blanket the tactical buttons where the ATM will go MSYS_DefineRegion( &gShopKeeperCoverTacticalButtonMouseRegion, SKI_ATM_PANEL_X, SKI_ATM_PANEL_Y, (UINT16)(SKI_ATM_PANEL_X+SKI_ATM_PANEL_WIDTH), (UINT16)(SKI_ATM_PANEL_Y+SKI_ATM_PANEL_HEIGHT), MSYS_PRIORITY_HIGH-1, 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 ); //Create the atm button //ATM: // CreateSkiAtmButtons(); 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; gubCurrentSkiAtmMode = SKI_ATM_DISABLED_MODE; 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_NUM_ARMS_DEALERS_INV_SLOTS ) ) { 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 ubMercID ) { SOLDIERTYPE *pSoldier = FindSoldierByProfileID( armsDealerInfo[ gbSelectedArmsDealerID ].ubShopKeeperID, FALSE ); if( pSoldier == NULL ) { #ifdef JA2TESTVERSION //Create the facial index giShopKeeperFaceIndex = InitFace( ubMercID, NOBODY, FACE_BIGFACE ); #else return( FALSE ); #endif } else { //Create the facial index giShopKeeperFaceIndex = InitFace( ubMercID, 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 gFacesData[ giShopKeeperFaceIndex ].uiFlags |= FACE_INACTIVE_HANDLED_ELSEWHERE; RenderAutoFace( giShopKeeperFaceIndex ); return(TRUE); } BOOLEAN ExitShopKeeperInterface() { UINT8 ubCnt; if( gfExitSKIDueToMessageBox ) { gfSKIScreenExit = FALSE; // gfExitSKIDueToMessageBox = FALSE; } if( InItemDescriptionBox( ) && pShopKeeperItemDescObject != NULL ) { DeleteItemDescriptionBox( ); } FreeMouseCursor( TRUE ); //Delete the main shopkeep background DeleteVideoObjectFromIndex( guiMainTradeScreenImage ); DeleteVideoObjectFromIndex( guiItemCrossOut ); DeleteVideoSurfaceFromIndex( guiCornerWhereTacticalIsStillSeenImage ); ShutUpShopKeeper(); //Delete the Atm backgorund //ATM: // DeleteVideoObjectFromIndex( guiSkiAtmImage ); UnloadButtonImage( guiSKI_InvPageUpButtonImage ); UnloadButtonImage( guiSKI_InvPageDownButtonImage ); //UnloadButtonImage( guiSKI_TransactionButtonImage ); // UnloadButtonImage( guiSKI_EvaluateButtonImage ); //UnloadButtonImage( guiSKI_DoneButtonImage ); //loop through the area and delete small faces for(ubCnt=0; ubCntuiFlags |= 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; } // 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( CLOCK_X, (CLOCK_Y - 29) ); RenderTownIDString( LOCATION_NAME_X, LOCATION_NAME_Y ); //ATM: // RenderSkiAtmPanel(); DisplayTalkingArmsDealer(); DisplayArmsDealerCurrentInventoryPage( ); DisplayArmsDealerOfferArea(); DisplayPlayersOfferArea(); EnableDisableEvaluateAndTransactionButtons(); MarkButtonsDirty( ); if( gfDoEvaluationAfterOpening ) { BOOLEAN fFirstOne = TRUE; gfDoEvaluationAfterOpening = FALSE; for( iCounter = 0; iCounter < SKI_NUM_TRADING_INV_SLOTS; 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 } BOOLEAN RenderShopKeeperInterface() { HVOBJECT hPixHandle; CHAR16 zMoney[128]; HVSURFACE hDestVSurface, hSrcVSurface; SGPRect SrcRect; if( InItemDescriptionBox( ) && pShopKeeperItemDescObject != NULL ) { return( TRUE ); } // RenderSMPanel( &fDirty ); // RenderTacticalInterface( ); // Render view window // RenderRadarScreen( ); //Get the main background screen graphic and blt it GetVideoObject(&hPixHandle, guiMainTradeScreenImage ); BltVideoObject(FRAME_BUFFER, hPixHandle, 0,SKI_MAIN_BACKGROUND_X, SKI_MAIN_BACKGROUND_Y, VO_BLT_SRCTRANSPARENCY,NULL); //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 ); //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 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 ); //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 swprintf( zMoney, L"%d", LaptopSaveInfo.iCurrentBalance ); InsertCommasForDollarFigure( zMoney ); InsertDollarSignInToString( zMoney ); DrawTextToScreen( zMoney, 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 ); //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(); //if the merc is talking and the screen has been dirtied if( gfIsTheShopKeeperTalking ) RenderMercPopUpBoxFromIndex( giPopUpBoxId, gusPositionOfSubTitlesX, SKI_POSITION_SUBTITLES_Y, FRAME_BUFFER); CrossOutUnwantedItems( ); //Render the clock and the town name // 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 ); //RenderTownIDString( CLOCK_X, (CLOCK_Y - 29) ); // RenderTacticalInterface( ); // RenderSMPanel( &fDirty ); //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 ); //Heinz: 22.02.09 BUGFIX: best way - to update whole tactical screen around SKI //SrcRect.iLeft = SKI_TACTICAL_BACKGROUND_START_X; //0; //SrcRect.iTop = SKI_TACTICAL_BACKGROUND_START_Y; //0; //SrcRect.iRight = SKI_TACTICAL_BACKGROUND_START_WIDTH; //SKI_TACTICAL_BACKGROUND_START_WIDTH; //SrcRect.iBottom = SKI_TACTICAL_BACKGROUND_START_HEIGHT; //SKI_TACTICAL_BACKGROUND_START_HEIGHT; //BltVSurfaceUsingDD( hDestVSurface, hSrcVSurface, VO_BLT_SRCTRANSPARENCY, SKI_TACTICAL_BACKGROUND_START_X, SKI_TACTICAL_BACKGROUND_START_Y, (RECT*)&SrcRect ); // Top SrcRect.iLeft = 0; SrcRect.iTop = 0; SrcRect.iRight = SCREEN_WIDTH; SrcRect.iBottom = SCREEN_Y_OFFSET; BltVSurfaceUsingDD( hDestVSurface, hSrcVSurface, VO_BLT_SRCTRANSPARENCY, SrcRect.iLeft, SrcRect.iTop, (RECT*)&SrcRect ); // Bottom SrcRect.iLeft = 0; SrcRect.iTop = SCREEN_Y_OFFSET + SKI_INTERFACE_HEIGHT; 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 = SCREEN_Y_OFFSET; SrcRect.iRight = SCREEN_X_OFFSET; SrcRect.iBottom = SCREEN_Y_OFFSET + SKI_INTERFACE_HEIGHT; BltVSurfaceUsingDD( hDestVSurface, hSrcVSurface, VO_BLT_SRCTRANSPARENCY, SrcRect.iLeft, SrcRect.iTop, (RECT*)&SrcRect ); // Right SrcRect.iLeft = SCREEN_X_OFFSET + SKI_INTERFACE_WIDTH; SrcRect.iTop = SCREEN_Y_OFFSET; SrcRect.iRight = SCREEN_WIDTH; SrcRect.iBottom = SCREEN_Y_OFFSET + SKI_INTERFACE_HEIGHT; BltVSurfaceUsingDD( hDestVSurface, hSrcVSurface, VO_BLT_SRCTRANSPARENCY, SrcRect.iLeft, SrcRect.iTop, (RECT*)&SrcRect ); // WANNE: I think it is not used. //InvalidateRegion( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT ); } void GetShopKeeperInterfaceUserInput() { InputAtom Event; POINT MousePos; GetCursorPos(&MousePos); ScreenToClient(ghWindow, &MousePos); // In window coords! while (DequeueSpecificEvent(&Event, KEY_DOWN|KEY_UP|KEY_REPEAT)) { // HOOK INTO MOUSE HOOKS /* removed warning condition C4060 (jonathanl) switch( Event.usEvent) { } */ if( !HandleTextInput( &Event ) && Event.usEvent == KEY_DOWN ) { /* //ATM: //if the number entered was a number if( (Event.usParam >= '0' ) && ( Event.usParam <= '9') ) { UINT8 ubValue = 0; ubValue = ( INT8 )( Event.usParam - '0' ); AddNumberToSkiAtm( ubValue ); } */ 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() { INT8 bArmsDealer; UINT16 usPosY=0; CHAR16 zTemp[512]; UINT8 ubForeColor; //loop through all the shopkeeper's and display their money for( bArmsDealer=0; bArmsDealeruiFlags |= 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); } } /* //Evaluate: void BtnSKI_EvaluateButtonCallback(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 ); ShutUpShopKeeper(); EvaluateAllItemsInPlayersOfferArea( ); 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( ) { UINT8 x,y,ubCnt; UINT16 usPosX, usPosY; // Create the mouse regions for the shopkeeper's inventory usPosY = SKI_ARMS_DEALERS_INV_START_Y; ubCnt=0; for(y=0; y= 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 ) { //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 ) { //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 ); } void InitializeShopKeeper( BOOLEAN fResetPage ) { // update time player last dealt with him gArmsDealerStatus[ gbSelectedArmsDealerID ].uiTimePlayerLastInSKI = GetWorldTotalMin(); gpTempDealersInventory.clear(); //Get the number of distinct items in the inventory //Create the shopkeeper's temp inventory DetermineArmsDealersSellingInventory( ); gSelectArmsDealerInfo.ubNumberOfPages = (UINT8)( gpTempDealersInventory.size() / SKI_NUM_ARMS_DEALERS_INV_SLOTS ); 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_NUM_ARMS_DEALERS_INV_SLOTS; } void DisplayArmsDealerCurrentInventoryPage( ) { CHAR16 zTemp[32]; UINT16 uiFontHeight; UINT16 usCnt=0; UINT16 usPosX, usPosY; UINT8 sItemCount=0; BOOLEAN fDisplayHatchOnItem=FALSE; usPosX = SKI_ARMS_DEALERS_INV_START_X; usPosY = SKI_ARMS_DEALERS_INV_START_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( ); //Restore the background before blitting the text back on RestoreExternBackgroundRect( SKI_PAGE_X, SKI_PAGE_Y, SKI_PAGE_WIDTH, SKI_PAGE_HEIGHT ); //Restore the pristine region RestoreExternBackgroundRect( SKI_ARMS_DEALERS_INV_START_X, SKI_ARMS_DEALERS_INV_START_Y, 370, 107 ); //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; ( ( usCntubGraphicNum; 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; //Restore the background region RestoreExternBackgroundRect( usPosX, usPosY, SKI_INV_SLOT_WIDTH, SKI_INV_HEIGHT ); //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 ); //Display the status of the item DrawItemUIBarEx( pItemObject, 0, (INT16)(usPosX+2), (INT16)(usPosY+2+20), 2, 20, 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 ); InsertDollarSignInToString( zTemp ); 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 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& 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; RepairItem.ItemObject[0]->data.bDirtLevel = 0.0f; } // 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; (*iter)[0]->data.bDirtLevel = 0.0f; } } #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 uiDiscountValue = 0; // UINT32 uiDifFrom10 = 0; #ifdef JA2UB UINT8 ubItemsNotCounted = 0; //ja25 UB #endif std::vector 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; } }*/ // 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) ) { if ( gpSMCurrentMerc->GetBackgroundValue(BG_PERC_PRICES_GUNS) ) uiDiscountValue = ( uiItemPrice[ubCnt] * gpSMCurrentMerc->GetBackgroundValue(BG_PERC_PRICES_GUNS) ) / 100; // if we play without backgrounds, Flo gets the hardcoded bonus else if ( gpSMCurrentMerc->ubProfile == FLO ) 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; } } // Flugente: backgrounds else if ( Item [ usItemID ].usItemClass & (IC_MAPFILTER_MELEE|IC_MAPFILTER_KIT|IC_MAPFILTER_LBE|IC_MAPFILTER_ARMOR|IC_MAPFILTER_MISC) ) { uiDiscountValue = ( uiItemPrice[ubCnt] * gpSMCurrentMerc->GetBackgroundValue(BG_PERC_PRICES) ) / 100; if ( fDealerSelling ) { // buy for less... uiItemPrice[ubCnt] -= uiDiscountValue; } else { // and sell for more! uiItemPrice[ubCnt] += uiDiscountValue; } } // 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_ARMS_DEALERS_TRADING_INV_X; usPosY = SKI_ARMS_DEALERS_TRADING_INV_Y; if( gubSkiDirtyLevel != SKI_DIRTY_LEVEL0 ) { //Restore the area before blitting the new data on RestoreExternBackgroundRect( usPosX, usPosY, SKI_ARMS_DEALERS_TRADING_INV_WIDTH, SKI_ARMS_DEALERS_TRADING_INV_HEIGHT ); } sCount = 0; //Display all the items that are in the Arms traders offered area uiTotalCost = 0; for( sCnt=0; sCntItemObject.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 ) { // UINT16 usCnt; //Loop through all the slot to see if the requested one is here // for( usCnt=0; usCnt 1 ) 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 ) { INT8 bCnt; //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( bCnt=0; bCntdata.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 sCnt, sCount; CHAR16 zTemp[32]; UINT32 uiTotalCost; UINT16 usPosX, usPosY; BOOLEAN fDisplayHatchOnItem=FALSE; usPosX = SKI_PLAYERS_TRADING_INV_X; usPosY = SKI_PLAYERS_TRADING_INV_Y; if( gubSkiDirtyLevel == SKI_DIRTY_LEVEL2 ) { //Restore the area before blitting the new data on RestoreExternBackgroundRect( SKI_PLAYERS_TRADING_INV_X, SKI_PLAYERS_TRADING_INV_Y, SKI_PLAYERS_TRADING_INV_WIDTH, SKI_PLAYERS_TRADING_INV_HEIGHT ); } sCount = 0; //Display all the items that are in the Players offered area uiTotalCost = 0; for( sCnt=0; sCntdata.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 ] ) ); } } } } // hatch it out if it hasn't been evaluated or just purchased // fDisplayHatchOnItem = ( PlayersOfferArea[ sCnt ].uiFlags & ( ARMS_INV_PLAYERS_ITEM_HAS_BEEN_EVALUATED | ARMS_INV_JUST_PURCHASED ) ) == 0; // 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; } usPosX += SKI_INV_OFFSET_X; sCount++; //if we are on to the next row if( !( sCount % SKI_NUM_TRADING_INV_COLS ) ) { usPosX = SKI_ARMS_DEALERS_TRADING_INV_X; usPosY += SKI_INV_OFFSET_Y; } } if( gubSkiDirtyLevel == SKI_DIRTY_LEVEL2 ) { // //Display the Total value text and number // //Restore the previous area RestoreExternBackgroundRect( SKI_PLAYERS_TOTAL_VALUE_X, SKI_PLAYERS_TOTAL_VALUE_Y, SKI_PLAYERS_TOTAL_VALUE_WIDTH, SKI_PLAYERS_TOTAL_VALUE_HEIGHT ); //Display the total cost text swprintf( zTemp, L"%d", uiTotalCost ); InsertCommasForDollarFigure( zTemp ); InsertDollarSignInToString( zTemp ); DrawTextToScreen( zTemp, SKI_PLAYERS_TOTAL_VALUE_X, (UINT16)(SKI_PLAYERS_TOTAL_VALUE_Y+5), SKI_INV_SLOT_WIDTH, SKI_LABEL_FONT, SKI_ITEM_PRICE_COLOR, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED ); } CrossOutUnwantedItems( ); } INVENTORY_IN_SLOT *GetPtrToOfferSlotWhereThisItemIs( UINT8 ubProfileID, INT16 bInvPocket ) { UINT8 ubCnt = 0; for( ubCnt = 0; ubCnt < SKI_NUM_TRADING_INV_SLOTS; ubCnt++ ) { if( ( PlayersOfferArea[ ubCnt ].bSlotIdInOtherLocation == bInvPocket ) && ( PlayersOfferArea[ ubCnt ].ubIdOfMercWhoOwnsTheItem == ubProfileID ) && ( PlayersOfferArea[ ubCnt ].ItemObject.exists() == true ) ) { return( &( PlayersOfferArea[ 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 = NULL; pInvSlot = GetPtrToOfferSlotWhereThisItemIs( ubProfileID, sSlotNum ); if( pInvSlot == NULL ) // not found in either offer area return( FALSE ); else // found it, display it hatched out return( TRUE ); } UINT32 CalculateTotalArmsDealerCost() { UINT32 uiCnt; UINT32 uiTotal=0; for( uiCnt=0; uiCntdata.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; UINT32 uiAvailablePlayerOfferSlots; //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 ) { uiAvailablePlayerOfferSlots = ( SKI_NUM_TRADING_INV_SLOTS - 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 ( uiAvailablePlayerOfferSlots > 0 ) { uiAvailablePlayerOfferSlots--; } //if there is NOT enough room in the players offer area if( CountNumberOfItemsInTheArmsDealersOfferArea( ) > uiAvailablePlayerOfferSlots ) { // 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 ) { //if the player doesn't have enough money in his account to pay the rest if( uiArmsDealersItemsCost > uiPlayersTotalMoneyValue + LaptopSaveInfo.iCurrentBalance ) { // 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 - ( LaptopSaveInfo.iCurrentBalance + 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( 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; return; } void MoveAllArmsDealersItemsInOfferAreaToPlayersOfferArea( ) { //for all items in the dealers items offer area UINT32 uiCnt; INT16 bSlotID=0; #ifdef JA2UB BOOLEAN fAddItemToPlayer=TRUE; #endif //loop through all the slots in the shopkeeper's offer area for( uiCnt=0; uiCntItemObject.ubNumberOfObjects, &pInvSlot->ItemObject ); gfResetShopKeepIdleQuote = TRUE; return( TRUE ); } void MovePlayerOfferedItemsOfValueToArmsDealersInventory() { UINT32 uiCnt; #ifdef JA2UB BOOLEAN fAddItemToDealer=TRUE; #endif //loop through all the slots in the players offer area for( uiCnt=0; uiCntdata.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 ) { UINT8 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; ubCntattachments.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 ) { //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 gFacesData[ giShopKeeperFaceIndex ].fDisabled = FALSE; HandleDialogue(); //Gets handled when we render the tactical interface HandleAutoFaces( ); HandleTalkingAutoFaces( ); HandleShopKeeperDialog( 0 ); 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, SKI_SUBTITLE_TEXT_SIZE ); #ifdef TAIWANESE swprintf( gsShopKeeperTalkingText, L"%s", pString ); #else swprintf( gsShopKeeperTalkingText, L"\"%s\"", pString ); #endif //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( ) { UINT8 ubCnt; //loop through the players offer area and see if there are any items there for( ubCnt = 0; ubCnt < SKI_NUM_TRADING_INV_SLOTS; ubCnt++) { //if is an item here if( ArmsDealerOfferArea[ ubCnt ].fActive ) { return( TRUE ); } } return( FALSE ); } BOOLEAN AreThereItemsInThePlayersOfferArea( ) { UINT8 ubCnt; //loop through the players offer area and see if there are any items there for( ubCnt = 0; ubCnt < SKI_NUM_TRADING_INV_SLOTS; 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; } UINT8 CountNumberOfValuelessItemsInThePlayersOfferArea( ) { UINT8 ubCnt; UINT8 ubCount=0; //loop through the players offer area and see if there are any items there for( ubCnt=0; ubCntuiFlags |= BUTTON_CLICKED_ON; InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); } if(reason & MSYS_CALLBACK_REASON_LBUTTON_UP ) { /*UINT8 ubButton = (UINT8) */MSYS_GetBtnUserData( btn, 0 ); btn->uiFlags &= (~BUTTON_CLICKED_ON ); //ATM: // HandleSkiAtmPanelInput( ubButton ); InvalidateRegion(btn->Area.RegionTopLeftX, btn->Area.RegionTopLeftY, btn->Area.RegionBottomRightX, btn->Area.RegionBottomRightY); } } void HandleSkiAtmPanel( ) { //Dispaly the appropriate text for the the curret atm mode HandleCurrentModeText( gubCurrentSkiAtmMode ); //Display Atm transfer string DisplaySkiAtmTransferString(); ToggleSkiAtmButtons(); } void HandleSkiAtmPanelInput( UINT8 ubButtonPress ) { //switch on the current mode switch( ubButtonPress ) { case SKI_ATM_1: case SKI_ATM_2: case SKI_ATM_3: case SKI_ATM_4: case SKI_ATM_5: case SKI_ATM_6: case SKI_ATM_7: case SKI_ATM_8: case SKI_ATM_9: case SKI_ATM_0: AddNumberToSkiAtm( ubButtonPress ); break; case SKI_ATM_OK: { HandleAtmOK(); break; } case SKI_ATM_TAKE: gubCurrentSkiAtmMode = SKI_ATM_TAKE_MODE; EnableDisableSkiAtmButtons(); break; case SKI_ATM_GIVE: gubCurrentSkiAtmMode = SKI_ATM_GIVE_MODE; EnableDisableSkiAtmButtons(); break; case SKI_ATM_CANCEL: memset( gzSkiAtmTransferString, 0, 32 ); gubCurrentSkiAtmMode = SKI_ATM_DISABLED_MODE; EnableDisableSkiAtmButtons(); break; case SKI_ATM_CLEAR: memset( gzSkiAtmTransferString, 0, 32 ); break; default: Assert( 0 ); } } void HandleAtmOK() { INT32 iAmountToTransfer; BOOLEAN fOkToClear=FALSE; //Get the amount to transfer swscanf( gzSkiAtmTransferString, L"%d", &iAmountToTransfer ); if( iAmountToTransfer == 0 ) return; //if we are in an error mode if( gubCurrentSkiAtmMode == SKI_ATM_ERR_TAKE_MODE ) gubCurrentSkiAtmMode = SKI_ATM_TAKE_MODE; else if( gubCurrentSkiAtmMode == SKI_ATM_ERR_GIVE_MODE ) gubCurrentSkiAtmMode = SKI_ATM_GIVE_MODE; //if we are in either the give or take mode, exit if( gubCurrentSkiAtmMode != SKI_ATM_TAKE_MODE && gubCurrentSkiAtmMode != SKI_ATM_GIVE_MODE ) return; //if we are taking money if( gubCurrentSkiAtmMode == SKI_ATM_TAKE_MODE ) { //are we trying to take more then the merc has if( iAmountToTransfer > GetFundsOnMerc( gpSMCurrentMerc ) ) { UINT32 uiFundsOnCurrentMerc = GetFundsOnMerc( gpSMCurrentMerc ); if( uiFundsOnCurrentMerc == 0 ) memset( gzSkiAtmTransferString, 0, 32 ); else { //Set the amount to transfer swprintf( gzSkiAtmTransferString, L"%d", uiFundsOnCurrentMerc ); } gubCurrentSkiAtmMode = SKI_ATM_ERR_TAKE_MODE; HandleCurrentModeText( gubCurrentSkiAtmMode ); } else { TransferFundsFromMercToBank( gpSMCurrentMerc, iAmountToTransfer ); fOkToClear=TRUE; } } else if( gubCurrentSkiAtmMode == SKI_ATM_GIVE_MODE ) { //are we tring to take more then we have? if( iAmountToTransfer > LaptopSaveInfo.iCurrentBalance ) { if( LaptopSaveInfo.iCurrentBalance == 0 ) memset( gzSkiAtmTransferString, 0, 32 ); else { //Set the amount to transfer swprintf( gzSkiAtmTransferString, L"%d", LaptopSaveInfo.iCurrentBalance ); } gubCurrentSkiAtmMode = SKI_ATM_ERR_GIVE_MODE; HandleCurrentModeText( gubCurrentSkiAtmMode ); } else { TransferFundsFromBankToMerc( gpSMCurrentMerc, iAmountToTransfer ); fOkToClear=TRUE; } } if( fOkToClear ) { gubCurrentSkiAtmMode = SKI_ATM_DISABLED_MODE; EnableDisableSkiAtmButtons(); memset( gzSkiAtmTransferString, 0, 32 ); } //Redraw the screen gubSkiDirtyLevel = SKI_DIRTY_LEVEL2; } void AddNumberToSkiAtm( UINT8 ubNumber ) { CHAR16 zTemp[16]; //make sure to durt the panel gubSkiDirtyLevel = SKI_DIRTY_LEVEL2; //if the mode is an error mode if( gubCurrentSkiAtmMode == SKI_ATM_ERR_TAKE_MODE ) { gubCurrentSkiAtmMode = SKI_ATM_TAKE_MODE; HandleCurrentModeText( gubCurrentSkiAtmMode ); } //else if the mode was else if( gubCurrentSkiAtmMode == SKI_ATM_ERR_GIVE_MODE ) { gubCurrentSkiAtmMode = SKI_ATM_GIVE_MODE; HandleCurrentModeText( gubCurrentSkiAtmMode ); } //if we are not in the correct mode, return if( gubCurrentSkiAtmMode != SKI_ATM_TAKE_MODE && gubCurrentSkiAtmMode != SKI_ATM_GIVE_MODE ) return; //make sure we arent going over the numbers max size if( wcslen( gzSkiAtmTransferString ) >= 9 ) return; //if its the first number being added and the number is a zero if( gzSkiAtmTransferString[0] == L'\0' && ubNumber == SKI_ATM_0 ) return; swprintf( zTemp, L"%d", ubNumber ); //add the number to the current amount wcscat( gzSkiAtmTransferString, zTemp ); } void DisplaySkiAtmTransferString() { CHAR16 zSkiAtmTransferString[ 32 ]; UINT32 uiMoney; //Erase the background behind the string ColorFillVideoSurfaceArea( FRAME_BUFFER, SKI_TRANSFER_STRING_X, SKI_TRANSFER_STRING_Y, SKI_TRANSFER_STRING_X+SKI_TRANSFER_STRING_WIDTH, SKI_TRANSFER_STRING_Y+SKI_TRANSFER_STRING_HEIGHT, Get16BPPColor( FROMRGB( 0, 0, 0 ) ) ); wcscpy( zSkiAtmTransferString, gzSkiAtmTransferString ); InsertCommasForDollarFigure( zSkiAtmTransferString ); InsertDollarSignInToString( zSkiAtmTransferString ); //Display the transfer string DrawTextToScreen( zSkiAtmTransferString, SKI_TRANSFER_STRING_X, SKI_TRANSFER_STRING_Y, SKI_TRANSFER_STRING_WIDTH, SKI_ATM_BUTTON_FONT, FONT_MCOLOR_WHITE, FONT_MCOLOR_BLACK, FALSE, RIGHT_JUSTIFIED ); // //Get and Display the money on the merc // uiMoney = GetFundsOnMerc( gpSMCurrentMerc ); swprintf( zSkiAtmTransferString, L"%d", uiMoney ); InsertCommasForDollarFigure( zSkiAtmTransferString ); InsertDollarSignInToString( zSkiAtmTransferString ); DrawTextToScreen( zSkiAtmTransferString, SKI_TRANSFER_STRING_X, SKI_MERCS_MONEY_Y, SKI_TRANSFER_STRING_WIDTH, SKI_ATM_BUTTON_FONT, FONT_MCOLOR_WHITE, FONT_MCOLOR_BLACK, FALSE, RIGHT_JUSTIFIED ); } void EnableDisableSkiAtmButtons() { UINT8 ubCnt; switch( gubCurrentSkiAtmMode ) { case SKI_ATM_DISABLED_MODE: //Disable number positions for( ubCnt=SKI_ATM_0; ubCnt<=SKI_ATM_9; ubCnt++) DisableButton( guiSKI_AtmButton[ubCnt] ); DisableButton( guiSKI_AtmButton[SKI_ATM_OK] ); DisableButton( guiSKI_AtmButton[SKI_ATM_CANCEL] ); DisableButton( guiSKI_AtmButton[SKI_ATM_CLEAR] ); EnableButton( guiSKI_AtmButton[ SKI_ATM_TAKE ] ); EnableButton( guiSKI_AtmButton[ SKI_ATM_GIVE ] ); break; case SKI_ATM_TAKE_MODE: //Make sure the numbers are not disbaled for( ubCnt=SKI_ATM_0; ubCnt<=SKI_ATM_9; ubCnt++) EnableButton( guiSKI_AtmButton[ubCnt] ); //enable other EnableButton( guiSKI_AtmButton[SKI_ATM_OK] ); EnableButton( guiSKI_AtmButton[SKI_ATM_CANCEL] ); EnableButton( guiSKI_AtmButton[SKI_ATM_CLEAR] ); break; case SKI_ATM_GIVE_MODE: //Make sure the numbers are not disbaled for( ubCnt=SKI_ATM_0; ubCnt<=SKI_ATM_9; ubCnt++) EnableButton( guiSKI_AtmButton[ubCnt] ); EnableButton( guiSKI_AtmButton[SKI_ATM_OK] ); EnableButton( guiSKI_AtmButton[SKI_ATM_CANCEL] ); EnableButton( guiSKI_AtmButton[SKI_ATM_CLEAR] ); break; default: Assert( 0 ); break; } } void HandleCurrentModeText( UINT8 ubMode ) { CHAR16 zTemp[128]; CHAR16 zMoney[128]; static UINT32 uiLastTime=0; UINT32 uiCurTime=GetJA2Clock(); static UINT8 ubLastMode=0; static UINT8 ubDisplayMode=0; if( ubMode != ubLastMode ) { uiLastTime = uiCurTime; ubLastMode = ubMode; ubDisplayMode = ubMode; } else { //if the current mode is an error mode if( ubLastMode != SKI_ATM_ERR_TAKE_MODE && ubLastMode != SKI_ATM_ERR_GIVE_MODE ) { //has the timer gone off? if( ( uiCurTime - uiLastTime ) > DELAY_PER_MODE_CHANGE_IN_ATM ) { //display the players current balance //if the modes are the same, change to display the players balance if( ubLastMode == ubMode ) if( ubMode == ubDisplayMode ) ubMode = SKI_ATM_DISPLAY_PLAYERS_BALANCE; ubDisplayMode = ubMode; uiLastTime = GetJA2Clock(); } else ubMode = ubDisplayMode; } } //if the current mode is an error mode if( ubLastMode == SKI_ATM_ERR_TAKE_MODE || ubLastMode == SKI_ATM_ERR_GIVE_MODE ) { //if it is time to get rid of the error message if( ( uiCurTime - uiLastTime ) > DELAY_PER_MODE_CHANGE_IN_ATM ) { if( ubLastMode == SKI_ATM_ERR_TAKE_MODE ) { ubLastMode = SKI_ATM_TAKE_MODE; gubCurrentSkiAtmMode = ubLastMode; } else { ubLastMode = SKI_ATM_GIVE_MODE; gubCurrentSkiAtmMode = ubLastMode; } } } switch( ubMode ) { case SKI_ATM_DISABLED_MODE: wcscpy( zTemp, gzSkiAtmText[ SKI_ATM_MODE_TEXT_SELECT_MODE ] ); break; case SKI_ATM_TAKE_MODE: //if the player has selected any money yet if( gzSkiAtmTransferString[0] == L'\0' ) wcscpy( zTemp, gzSkiAtmText[ SKI_ATM_MODE_TEXT_ENTER_AMOUNT ] ); else wcscpy( zTemp, gzSkiAtmText[ SKI_ATM_MODE_TEXT_SELECT_FROM_MERC ] ); break; case SKI_ATM_GIVE_MODE: //if the player has selected any money yet if( gzSkiAtmTransferString[0] == L'\0' ) wcscpy( zTemp, gzSkiAtmText[ SKI_ATM_MODE_TEXT_ENTER_AMOUNT ] ); else wcscpy( zTemp, gzSkiAtmText[ SKI_ATM_MODE_TEXT_SELECT_TO_MERC ] ); break; case SKI_ATM_ERR_TAKE_MODE: case SKI_ATM_ERR_GIVE_MODE: wcscpy( zTemp, gzSkiAtmText[ SKI_ATM_MODE_TEXT_SELECT_INUSUFFICIENT_FUNDS ] ); break; case SKI_ATM_DISPLAY_PLAYERS_BALANCE: swprintf( zMoney, L"%d", LaptopSaveInfo.iCurrentBalance ); InsertCommasForDollarFigure( zMoney ); InsertDollarSignInToString( zMoney ); swprintf( zTemp, L"%s: %s", gzSkiAtmText[ SKI_ATM_MODE_TEXT_BALANCE ], zMoney ); break; } DisplayWrappedString( SKI_MODE_TEXT_X, SKI_MODE_TEXT_Y, SKI_MODE_TEXT_WIDTH, 2, SKI_ATM_BUTTON_FONT, FONT_MCOLOR_WHITE, zTemp, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED); //invalidate the atm panel area InvalidateRegion( SKI_ATM_PANEL_X, SKI_ATM_PANEL_Y, SKI_ATM_PANEL_X+SKI_ATM_PANEL_WIDTH, SKI_ATM_PANEL_Y+SKI_ATM_PANEL_HEIGHT ); } void ToggleSkiAtmButtons() { switch( gubCurrentSkiAtmMode ) { case SKI_ATM_DISABLED_MODE: ButtonList[ guiSKI_AtmButton[SKI_ATM_TAKE] ]->uiFlags &= ~BUTTON_CLICKED_ON; ButtonList[ guiSKI_AtmButton[SKI_ATM_GIVE] ]->uiFlags &= ~BUTTON_CLICKED_ON; ButtonList[ guiSKI_AtmButton[SKI_ATM_CANCEL] ]->uiFlags &= ~BUTTON_CLICKED_ON; break; case SKI_ATM_TAKE_MODE: case SKI_ATM_ERR_TAKE_MODE: ButtonList[ guiSKI_AtmButton[SKI_ATM_GIVE] ]->uiFlags &= ~BUTTON_CLICKED_ON; ButtonList[ guiSKI_AtmButton[SKI_ATM_CANCEL] ]->uiFlags &= ~BUTTON_CLICKED_ON; ButtonList[ guiSKI_AtmButton[SKI_ATM_TAKE] ]->uiFlags |= BUTTON_CLICKED_ON; break; case SKI_ATM_GIVE_MODE: case SKI_ATM_ERR_GIVE_MODE: ButtonList[ guiSKI_AtmButton[SKI_ATM_TAKE] ]->uiFlags &= ~BUTTON_CLICKED_ON; ButtonList[ guiSKI_AtmButton[SKI_ATM_CANCEL] ]->uiFlags &= ~BUTTON_CLICKED_ON; ButtonList[ guiSKI_AtmButton[SKI_ATM_GIVE] ]->uiFlags |= BUTTON_CLICKED_ON; break; } } 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() { UINT8 ubCnt; 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( ubCnt=0; ubCnt= 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 ); } if( uiArmsDealerTotalCost > uiPlayersOfferAreaTotalCost + LaptopSaveInfo.iCurrentBalance ) { 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 ) { //Evaluate: // DisableButton( guiSKI_EvaluateButton ); EnableDisableShopkeeperButtons(SHOPKEEPER_SCREEN, DISABLE_BUTTON); //DisableButton( guiSKI_TransactionButton ); //DisableButton( guiSKI_DoneButton ); } 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( ) { UINT8 ubCnt; BOOLEAN fFoundMoney = FALSE; for( ubCnt=0; ubCnt 0 ); // only return TRUE if there IS money in the POA return( fFoundMoney ); } /* void MoveRepairEvaluatedPlayerOfferedItemsToArmsDealersOfferArea() { UINT32 uiCnt; //loop through all the slots in the players offer area for( uiCnt=0; uiCntdata.money.uiMoneyAmount; } } } return( uiTotalMoneyValue ); } void MovePlayersItemsToBeRepairedToArmsDealersInventory() { //for all items in the dealers items offer area UINT32 uiCnt; //loop through all the slots in the shopkeeper's offer area for( uiCnt=0; uiCntinv.size(); ubCnt++) { // Look for MONEY only, not Gold or Silver!!! And look for a slot not already full if( ( pSoldier->inv[ ubCnt ].usItem == MONEY ) && ( pSoldier->inv[ ubCnt ][0]->data.money.uiMoneyAmount < MoneySlotLimit( ubCnt ) ) ) { return( ubCnt ); } } return( - 1 ); } 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 ) { UINT32 uiNumberOfItemsInForRepairs = CountTotalItemsRepairDealerHasInForRepairs( gbSelectedArmsDealerID ); UINT32 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 ) { INT16 bSlotAddedTo; // Move the item to the Dealer's Offer Area 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; return; } 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; } // 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 ) { BOOLEAN fRejected = TRUE; if( Item[ PlayersOfferArea[ bSlotId ].sItemIndex ].usItemClass == IC_MONEY ) { fRejected = FALSE; } else if( CanDealerTransactItem( gbSelectedArmsDealerID, PlayersOfferArea[ bSlotId ].sItemIndex, TRUE ) ) { fRejected = FALSE; } else { fRejected = TRUE; } return( fRejected ); } void CheckAndHandleClearingOfPlayerOfferArea( void ) { INT32 iCounter = 0; BOOLEAN fActiveSlot = FALSE; // find out if all the player trade slots/offer area is empty for( iCounter=0; iCounter 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; } return; } 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_ARMS_DEALERS_INV_START_X; // + ( SKI_INV_OFFSET_X * ( ubSelectedInvSlot % SKI_NUM_ARMS_DEALERS_INV_COLS ) - ( 358 / 2 ) ) + SKI_INV_SLOT_WIDTH / 2; sPosY = SKI_ARMS_DEALERS_INV_START_Y; // + ( ( SKI_INV_OFFSET_Y * ubSelectedInvSlot / SKI_NUM_ARMS_DEALERS_INV_COLS ) + 1 ) - ( 128 / 2 ) + SKI_INV_SLOT_HEIGHT / 2; } 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_ARMS_DEALERS_TRADING_INV_X; // + ( SKI_INV_OFFSET_X * ( ubPocket % ( SKI_NUM_TRADING_INV_SLOTS/2) ) - ( 358 / 2 ) ) + SKI_INV_SLOT_WIDTH / 2; sPosY = SKI_ARMS_DEALERS_TRADING_INV_Y; // + ( ( SKI_INV_OFFSET_Y * ubPocket / ( SKI_NUM_TRADING_INV_SLOTS/2) ) + 1 ) - ( 128 / 2 ) + SKI_INV_SLOT_HEIGHT / 2; //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 ) { INT32 iCnt; //shadow the entire screen // ShadowVideoSurfaceRect( FRAME_BUFFER, 0, 0, 640, 480 ); //if the current merc is too far away, dont shade the SM panel because it is already shaded // WANNE: Do not shade the background if( gfSMDisableForItems ) DrawHatchOnInventory( FRAME_BUFFER, SCREEN_X_OFFSET, SCREEN_Y_OFFSET, SKI_INTERFACE_WIDTH, SKI_INTERFACE_HEIGHT ); else { DrawHatchOnInventory( FRAME_BUFFER, SCREEN_X_OFFSET, SCREEN_Y_OFFSET, SKI_INTERFACE_WIDTH, SKI_INTERFACE_HEIGHT ); } InvalidateRegion( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT ); // disable almost everything! gfSMDisableForItems = TRUE; DisableInvRegions( gfSMDisableForItems ); DisableButton( guiSKI_InvPageUpButton ); DisableButton( guiSKI_InvPageDownButton ); // DisableButton( guiSKI_EvaluateButton ); EnableDisableShopkeeperButtons(SHOPKEEPER_SCREEN, DISABLE_BUTTON); //DisableButton( guiSKI_TransactionButton ); //DisableButton( guiSKI_DoneButton ); /* for (iCnt = 0; iCnt < NUM_SKI_ATM_BUTTONS; iCnt++) DisableButton( guiSKI_AtmButton[ iCnt ] ); */ DisableAllDealersInventorySlots(); DisableAllDealersOfferSlots(); for (iCnt = 0; iCnt < SKI_NUM_TRADING_INV_SLOTS; iCnt++) { MSYS_DisableRegion( &gPlayersOfferSlotsMouseRegions[ iCnt ] ); MSYS_DisableRegion( &gPlayersOfferSlotsSmallFaceMouseRegions[ iCnt ] ); } if( gShopKeeperSubTitleMouseRegion.uiFlags & MSYS_REGION_EXISTS ) { MSYS_DisableRegion( &gShopKeeperSubTitleMouseRegion ); } // MSYS_DisableRegion( &gShopKeeperCoverTacticalButtonMouseRegion ); // if( giItemDescAmmoButton >= 0 && ButtonList[ giItemDescAmmoButton ]. // DisableButton( giItemDescAmmoButton ); RenderItemDescriptionBox( ); } void DeleteShopKeeperItemDescBox() { INT32 iCnt; pShopKeeperItemDescObject = NULL; gubSkiDirtyLevel = SKI_DIRTY_LEVEL2; //Redraw the face 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); //EnableButton( guiSKI_TransactionButton ); //EnableButton( guiSKI_DoneButton ); EnableDisableDealersInventoryPageButtons(); EnableDisableEvaluateAndTransactionButtons(); // EnableButton( guiSKI_EvaluateButton ); /* for (iCnt = 0; iCnt < NUM_SKI_ATM_BUTTONS; iCnt++) EnableButton( guiSKI_AtmButton[ iCnt ] ); */ EnableAllDealersInventorySlots(); EnableAllDealersOfferSlots(); for (iCnt = 0; iCnt < SKI_NUM_TRADING_INV_SLOTS; iCnt++) { MSYS_EnableRegion( &gPlayersOfferSlotsMouseRegions[ iCnt ] ); MSYS_EnableRegion( &gPlayersOfferSlotsSmallFaceMouseRegions[ iCnt ] ); } if( gShopKeeperSubTitleMouseRegion.uiFlags & MSYS_REGION_EXISTS ) { MSYS_EnableRegion( &gShopKeeperSubTitleMouseRegion ); } // MSYS_EnableRegion( &gShopKeeperCoverTacticalButtonMouseRegion ); // if( giItemDescAmmoButton >= 0 && ButtonList[ giItemDescAmmoButton ]. // EnableButton( giItemDescAmmoButton ); } BOOLEAN OfferObjectToDealer( OBJECTTYPE *pComplexObject, UINT8 ubOwnerProfileId, INT16 bOwnerSlotId ) { UINT8 ubRepairableSubObjects; UINT8 ubNonRepairableSubObjects; UINT8 ubDealerOfferAreaSlotsNeeded; UINT8 ubPlayerOfferAreaSlotsNeeded; UINT8 ubDiff; UINT8 ubHowManyMoreItemsCanDealerTake; UINT8 ubSubObject; 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_NUM_TRADING_INV_SLOTS ); /* 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_NUM_TRADING_INV_SLOTS ) { gfDisplayNoRoomMsg = TRUE; return(FALSE); // no room } // we have room, so move them all to the appropriate slots for ( 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 ); 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() { UINT8 uiCnt; // reset flags for quotes said for ( uiCnt = 0; uiCnt < NUM_EVAL_RESULTS; uiCnt++ ) { gfEvalResultQuoteSaid[ uiCnt ] = FALSE; } guiLastTimeDealerSaidNormalEvaluationQuote = 0; } void DealWithItemsStillOnTheTable() { UINT8 ubCnt; 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( ubCnt=0; ubCntbSlotIdInOtherLocation == -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() { UINT16 usHeight; //get the height of the displayed text usHeight = 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 ); // WANNE: Not needed to show the string //display the 'drop item to ground' text //DisplayWrappedString( SKI_DROP_ITEM_TO_GROUND_START_X, (UINT16)(SKI_DROP_ITEM_TO_GROUND_TEXT_START_Y-usHeight), (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 | INVALIDATE_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 ); } // round off reapir times shown to the near quarter-hour #define REPAIR_MINUTES_INTERVAL 15 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 ) { INT32 iCnt; for (iCnt = 0; iCnt < SKI_NUM_ARMS_DEALERS_INV_SLOTS; iCnt++) { MSYS_DisableRegion( &gDealersInventoryMouseRegions[ iCnt ] ); //if the dealer repairs if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS ) { MSYS_DisableRegion( &gRepairmanInventorySmallFaceMouseRegions[ iCnt ] ); } } } void EnableAllDealersInventorySlots( void ) { INT32 iCnt; for (iCnt = 0; iCnt < SKI_NUM_ARMS_DEALERS_INV_SLOTS; iCnt++) { MSYS_EnableRegion( &gDealersInventoryMouseRegions[ iCnt ] ); //if the dealer repairs if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS ) { MSYS_EnableRegion( &gRepairmanInventorySmallFaceMouseRegions[ iCnt ] ); } } } void DisableAllDealersOfferSlots( void ) { INT32 iCnt; for (iCnt = 0; iCnt < SKI_NUM_TRADING_INV_SLOTS; iCnt++) { MSYS_DisableRegion( &gDealersOfferSlotsMouseRegions[ iCnt ] ); //if the dealer repairs if( armsDealerInfo[ gbSelectedArmsDealerID ].ubTypeOfArmsDealer == ARMS_DEALER_REPAIRS ) { MSYS_DisableRegion( &gDealersOfferSlotsSmallFaceMouseRegions[ iCnt ] ); } } } void EnableAllDealersOfferSlots( void ) { INT32 iCnt; for (iCnt = 0; iCnt < SKI_NUM_TRADING_INV_SLOTS; 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