From 7bb401b1fb5d5a9e16bdf4e43abb4819867ae89e Mon Sep 17 00:00:00 2001 From: Flugente Date: Sun, 15 May 2016 17:11:29 +0000 Subject: [PATCH] New feature: We can now create non-profile-based merchants via lua.scripts. Requires GameDir >= r2320. For more info, see http://thepit.ja-galaxy-forum.com/index.php?t=msg&th=23061&goto=345526&#msg_345526 git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8212 3b4a5df2-a311-0410-b5c6-a8a6f20db521 --- GameSettings.cpp | 1 + GameSettings.h | 1 + GameVersion.cpp | 18 +-- GameVersion.h | 3 +- Init.cpp | 84 +---------- SaveLoadGame.cpp | 18 ++- Strategic/LuaInitNPCs.cpp | 23 +-- Tactical/Arms Dealer Init.cpp | 45 +++--- Tactical/Arms Dealer Init.h | 5 +- Tactical/Overhead.cpp | 2 +- Tactical/ShopKeeper Interface.cpp | 233 ++++++++++++------------------ Tactical/ShopKeeper Interface.h | 7 +- Tactical/Soldier Control.cpp | 35 +++++ Tactical/Soldier Control.h | 2 +- Tactical/Soldier Create.cpp | 17 ++- Tactical/Soldier Create.h | 4 +- Tactical/XML.h | 24 +-- Tactical/XML_Merchants.cpp | 9 +- Utils/Text.h | 3 + Utils/_ChineseText.cpp | 7 + Utils/_DutchText.cpp | 7 + Utils/_EnglishText.cpp | 8 + Utils/_FrenchText.cpp | 7 + Utils/_GermanText.cpp | 7 + Utils/_ItalianText.cpp | 7 + Utils/_PolishText.cpp | 7 + Utils/_RussianText.cpp | 7 + 27 files changed, 295 insertions(+), 296 deletions(-) diff --git a/GameSettings.cpp b/GameSettings.cpp index 7ddbed49..743748ee 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -1460,6 +1460,7 @@ void LoadGameExternalOptions() // can extra civilians be created via LUA? gGameExternalOptions.bExtraCivilians = iniReader.ReadBoolean( "Tactical Interface Settings", "ALLOW_EXTRA_CIVILIANS", FALSE ); + gGameExternalOptions.bExtraMerchants = iniReader.ReadBoolean( "Tactical Interface Settings", "ALLOW_EXTRA_MERCHANTS", FALSE ); // Add smoke after regular explosions gGameExternalOptions.bAddSmokeAfterExplosion = iniReader.ReadBoolean("Tactical Interface Settings","ADD_SMOKE_AFTER_EXPLOSION",FALSE); diff --git a/GameSettings.h b/GameSettings.h index ad532e5e..e25a3334 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -800,6 +800,7 @@ typedef struct BOOLEAN bLazyCivilians; BOOLEAN bNeutralCiviliansAvoidPlayerMines; //sevenfm: Neutral civilians can detect mines with MAPELEMENT_PLAYER_MINE_PRESENT flag set BOOLEAN bExtraCivilians; // Flugente: add civilians via LUA + BOOLEAN bExtraMerchants; // Flugente: add non-profile-based merchants via LUA BOOLEAN bAddSmokeAfterExplosion; BOOLEAN bAllowExplosiveAttachments; BOOLEAN bAllowSpecialExplosiveAttachments; diff --git a/GameVersion.cpp b/GameVersion.cpp index 54f7e01d..7877dcdc 100644 --- a/GameVersion.cpp +++ b/GameVersion.cpp @@ -15,9 +15,9 @@ #ifdef JA2EDITOR #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Unfinished Business - Map Editor v1.13.8207 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Unfinished Business - Map Editor v1.13.8212 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Map Editor v1.13.8207 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Map Editor v1.13.8212 (Development Build)" }; #endif // ------------------------------ @@ -27,11 +27,11 @@ //DEBUG BUILD VERSION #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Debug: Unfinished Business - v1.13.8207 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: Unfinished Business - v1.13.8212 (Development Build)" }; #elif defined (JA113DEMO) - CHAR16 zVersionLabel[256] = { L"Debug: JA2 Demo - v1.13.8207 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: JA2 Demo - v1.13.8212 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Debug: v1.13.8207 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: v1.13.8212 (Development Build)" }; #endif #elif defined CRIPPLED_VERSION @@ -46,16 +46,16 @@ //RELEASE BUILD VERSION #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Release Unfinished Business - v1.13.8207 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release Unfinished Business - v1.13.8212 (Development Build)" }; #elif defined (JA113DEMO) - CHAR16 zVersionLabel[256] = { L"Release JA2 Demo - v1.13.8207 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release JA2 Demo - v1.13.8212 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Release v1.13.8207 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release v1.13.8212 (Development Build)" }; #endif #endif -CHAR8 czVersionNumber[16] = { "Build 16.05.08" }; //YY.MM.DD +CHAR8 czVersionNumber[16] = { "Build 16.05.15" }; //YY.MM.DD CHAR16 zTrackingNumber[16] = { L"Z" }; // SAVE_GAME_VERSION is defined in header, change it there diff --git a/GameVersion.h b/GameVersion.h index ceccc42f..a39730d0 100644 --- a/GameVersion.h +++ b/GameVersion.h @@ -21,6 +21,7 @@ extern CHAR16 zTrackingNumber[16]; // Keeps track of the saved game version. Increment the saved game version whenever // you will invalidate the saved game file +#define NONPROFILE_MERCHANTS 167 // Flugente: merchants can be created from non-profile NPCs #define INDIVIDUAL_MILITIA 166 // Flugente: individual militia #define ENEMY_JEEPS 165 // Flugente: enemy jeeps #define ENEMY_HELICOPTERS 164 // Flugente: enemy helicopters @@ -85,7 +86,7 @@ extern CHAR16 zTrackingNumber[16]; #define AP100_SAVEGAME_DATATYPE_CHANGE 105 // Before this, we didn't have the 100AP structure changes #define NIV_SAVEGAME_DATATYPE_CHANGE 102 // Before this, we used the old structure system -#define SAVE_GAME_VERSION INDIVIDUAL_MILITIA +#define SAVE_GAME_VERSION NONPROFILE_MERCHANTS //#define RUSSIANGOLD #ifdef __cplusplus diff --git a/Init.cpp b/Init.cpp index 1745ebca..5a21aec0 100644 --- a/Init.cpp +++ b/Init.cpp @@ -678,86 +678,12 @@ BOOLEAN LoadExternalGameplayData(STR directoryName) strcpy(fileName, directoryName); strcat(fileName, TINAINVENTORYFILENAME); SGP_THROW_IFFALSE(ReadInInventoryStats(gTinaInventory,fileName),TINAINVENTORYFILENAME); - - strcpy(fileName, directoryName); - strcat(fileName, ADITIONALDEALER_1_INVENTORYFILENAME); - SGP_THROW_IFFALSE(ReadInInventoryStats(gArmsDealerAdditional[0],fileName),ADITIONALDEALER_1_INVENTORYFILENAME); - - strcpy(fileName, directoryName); - strcat(fileName, ADITIONALDEALER_2_INVENTORYFILENAME); - SGP_THROW_IFFALSE(ReadInInventoryStats(gArmsDealerAdditional[1],fileName),ADITIONALDEALER_2_INVENTORYFILENAME); - - strcpy(fileName, directoryName); - strcat(fileName, ADITIONALDEALER_3_INVENTORYFILENAME); - SGP_THROW_IFFALSE(ReadInInventoryStats(gArmsDealerAdditional[2],fileName),ADITIONALDEALER_3_INVENTORYFILENAME); - - strcpy(fileName, directoryName); - strcat(fileName, ADITIONALDEALER_4_INVENTORYFILENAME); - SGP_THROW_IFFALSE(ReadInInventoryStats(gArmsDealerAdditional[3],fileName),ADITIONALDEALER_4_INVENTORYFILENAME); - - strcpy(fileName, directoryName); - strcat(fileName, ADITIONALDEALER_5_INVENTORYFILENAME); - SGP_THROW_IFFALSE(ReadInInventoryStats(gArmsDealerAdditional[4],fileName),ADITIONALDEALER_5_INVENTORYFILENAME); - - strcpy(fileName, directoryName); - strcat(fileName, ADITIONALDEALER_6_INVENTORYFILENAME); - SGP_THROW_IFFALSE(ReadInInventoryStats(gArmsDealerAdditional[5],fileName),ADITIONALDEALER_6_INVENTORYFILENAME); - - strcpy(fileName, directoryName); - strcat(fileName, ADITIONALDEALER_7_INVENTORYFILENAME); - SGP_THROW_IFFALSE(ReadInInventoryStats(gArmsDealerAdditional[6],fileName),ADITIONALDEALER_7_INVENTORYFILENAME); - - strcpy(fileName, directoryName); - strcat(fileName, ADITIONALDEALER_8_INVENTORYFILENAME); - SGP_THROW_IFFALSE(ReadInInventoryStats(gArmsDealerAdditional[7],fileName),ADITIONALDEALER_8_INVENTORYFILENAME); - - strcpy(fileName, directoryName); - strcat(fileName, ADITIONALDEALER_9_INVENTORYFILENAME); - SGP_THROW_IFFALSE(ReadInInventoryStats(gArmsDealerAdditional[8],fileName),ADITIONALDEALER_9_INVENTORYFILENAME); - - strcpy(fileName, directoryName); - strcat(fileName, ADITIONALDEALER_10_INVENTORYFILENAME); - SGP_THROW_IFFALSE(ReadInInventoryStats(gArmsDealerAdditional[9],fileName),ADITIONALDEALER_10_INVENTORYFILENAME); - - strcpy(fileName, directoryName); - strcat(fileName, ADITIONALDEALER_11_INVENTORYFILENAME); - SGP_THROW_IFFALSE(ReadInInventoryStats(gArmsDealerAdditional[10],fileName),ADITIONALDEALER_11_INVENTORYFILENAME); - - strcpy(fileName, directoryName); - strcat(fileName, ADITIONALDEALER_12_INVENTORYFILENAME); - SGP_THROW_IFFALSE(ReadInInventoryStats(gArmsDealerAdditional[11],fileName),ADITIONALDEALER_12_INVENTORYFILENAME); - strcpy(fileName, directoryName); - strcat(fileName, ADITIONALDEALER_13_INVENTORYFILENAME); - SGP_THROW_IFFALSE(ReadInInventoryStats(gArmsDealerAdditional[12],fileName),ADITIONALDEALER_13_INVENTORYFILENAME); - - strcpy(fileName, directoryName); - strcat(fileName, ADITIONALDEALER_14_INVENTORYFILENAME); - SGP_THROW_IFFALSE(ReadInInventoryStats(gArmsDealerAdditional[13],fileName),ADITIONALDEALER_14_INVENTORYFILENAME); - - strcpy(fileName, directoryName); - strcat(fileName, ADITIONALDEALER_15_INVENTORYFILENAME); - SGP_THROW_IFFALSE(ReadInInventoryStats(gArmsDealerAdditional[14],fileName),ADITIONALDEALER_15_INVENTORYFILENAME); - - strcpy(fileName, directoryName); - strcat(fileName, ADITIONALDEALER_16_INVENTORYFILENAME); - SGP_THROW_IFFALSE(ReadInInventoryStats(gArmsDealerAdditional[15],fileName),ADITIONALDEALER_16_INVENTORYFILENAME); - - strcpy(fileName, directoryName); - strcat(fileName, ADITIONALDEALER_17_INVENTORYFILENAME); - SGP_THROW_IFFALSE(ReadInInventoryStats(gArmsDealerAdditional[16],fileName),ADITIONALDEALER_17_INVENTORYFILENAME); - - strcpy(fileName, directoryName); - strcat(fileName, ADITIONALDEALER_18_INVENTORYFILENAME); - SGP_THROW_IFFALSE(ReadInInventoryStats(gArmsDealerAdditional[17],fileName),ADITIONALDEALER_18_INVENTORYFILENAME); - - strcpy(fileName, directoryName); - strcat(fileName, ADITIONALDEALER_19_INVENTORYFILENAME); - SGP_THROW_IFFALSE(ReadInInventoryStats(gArmsDealerAdditional[18],fileName),ADITIONALDEALER_19_INVENTORYFILENAME); - - strcpy(fileName, directoryName); - strcat(fileName, ADITIONALDEALER_20_INVENTORYFILENAME); - SGP_THROW_IFFALSE(ReadInInventoryStats(gArmsDealerAdditional[19],fileName),ADITIONALDEALER_20_INVENTORYFILENAME); + for ( int i = 0; i < ADDITIONAL_ARMS_DEALERS; ++i ) + { + sprintf_s( fileName, 260, "%s%s%d%s", directoryName, ADITIONALDEALER_INVENTORYFILENAME_A, i + 1, ADITIONALDEALER_INVENTORYFILENAME_B ); + ReadInInventoryStats( gArmsDealerAdditional[i], fileName ); + } strcpy(fileName, directoryName); strcat(fileName, CITYTABLEFILENAME); diff --git a/SaveLoadGame.cpp b/SaveLoadGame.cpp index ba85a136..7f8c552b 100644 --- a/SaveLoadGame.cpp +++ b/SaveLoadGame.cpp @@ -747,11 +747,23 @@ BOOLEAN LoadArmsDealerInventoryFromSavedGameFile( HWFILE hFile ) { return( FALSE ); } + gArmsDealersInventory.resize(dealers); - if (!FileRead( hFile, gArmsDealerStatus, sizeof( gArmsDealerStatus ), &uiNumBytesRead )) + if ( guiCurrentSaveGameVersion >= NONPROFILE_MERCHANTS ) { - return( FALSE ); + if ( !FileRead( hFile, gArmsDealerStatus, sizeof(gArmsDealerStatus), &uiNumBytesRead ) ) + { + return(FALSE); + } + } + else + { + // Flugente: as we increased the number of dealers, we can only read part of the array + if ( !FileRead( hFile, gArmsDealerStatus, sizeof(ARMS_DEALER_STATUS) * 40, &uiNumBytesRead ) ) + { + return(FALSE); + } } //loop through all the dealers inventories @@ -2179,7 +2191,7 @@ BOOLEAN SOLDIERTYPE::Load(HWFILE hFile) } numBytesRead = ReadFieldByField(hFile, &this->bScopeMode, sizeof(bScopeMode), sizeof(INT8), numBytesRead); numBytesRead = ReadFieldByField(hFile, &this->ubMilitiaAssists, sizeof(ubMilitiaAssists), sizeof(UINT8), numBytesRead ); - numBytesRead = ReadFieldByField(hFile, &this->bUnusedINT8_2, sizeof(bUnusedINT8_2), sizeof(INT8), numBytesRead ); + numBytesRead = ReadFieldByField(hFile, &this->sNonNPCTraderID, sizeof(sNonNPCTraderID), sizeof(INT8), numBytesRead ); numBytesRead = ReadFieldByField(hFile, &this->bUnusedINT8_3, sizeof(bUnusedINT8_3), sizeof(INT8), numBytesRead ); numBytesRead = ReadFieldByField(hFile, &this->bUnusedINT16_4, sizeof(bUnusedINT16_4), sizeof(INT16), numBytesRead ); numBytesRead = ReadFieldByField(hFile, &this->bUnusedINT16_5, sizeof(bUnusedINT16_5), sizeof(INT16), numBytesRead ); diff --git a/Strategic/LuaInitNPCs.cpp b/Strategic/LuaInitNPCs.cpp index 2ace73aa..27ff0eec 100644 --- a/Strategic/LuaInitNPCs.cpp +++ b/Strategic/LuaInitNPCs.cpp @@ -13080,23 +13080,24 @@ static int l_CreateArmedCivilain( lua_State *L ) static int l_CreateCivilian( lua_State *L ) { - if ( lua_gettop( L ) >= 11 ) + if ( lua_gettop( L ) >= 12 ) { INT32 sGridNo = lua_tointeger( L, 1 ); UINT8 usCivilianGroup = lua_tointeger( L, 2 ); - INT8 sBodyType = lua_tointeger( L, 3 ); - INT8 aVest = lua_tointeger( L, 4 ); - INT8 aPants = lua_tointeger( L, 5 ); - INT8 aHair = lua_tointeger( L, 6 ); - INT8 aSkin = lua_tointeger( L, 7 ); - INT16 item1 = lua_tointeger( L, 8 ); - INT16 item2 = lua_tointeger( L, 9 ); - INT16 item3 = lua_tointeger( L, 10 ); - INT16 item4 = lua_tointeger( L, 11 ); + INT8 usTraderID = lua_tointeger( L, 3 ); + INT8 sBodyType = lua_tointeger( L, 4 ); + INT8 aVest = lua_tointeger( L, 5 ); + INT8 aPants = lua_tointeger( L, 6 ); + INT8 aHair = lua_tointeger( L, 7 ); + INT8 aSkin = lua_tointeger( L, 8 ); + INT16 item1 = lua_tointeger( L, 9 ); + INT16 item2 = lua_tointeger( L, 10 ); + INT16 item3 = lua_tointeger( L, 11 ); + INT16 item4 = lua_tointeger( L, 12 ); if ( gGameExternalOptions.bExtraCivilians ) { - SOLDIERTYPE* pSoldier = TacticalCreateCivilian( sGridNo, usCivilianGroup, sBodyType, aVest, aPants, aHair, aSkin, item1, item2, item3, item4 ); + SOLDIERTYPE* pSoldier = TacticalCreateCivilian( sGridNo, usCivilianGroup, usTraderID, sBodyType, aVest, aPants, aHair, aSkin, item1, item2, item3, item4 ); if ( pSoldier ) { diff --git a/Tactical/Arms Dealer Init.cpp b/Tactical/Arms Dealer Init.cpp index 8c3a91aa..01fed092 100644 --- a/Tactical/Arms Dealer Init.cpp +++ b/Tactical/Arms Dealer Init.cpp @@ -17,6 +17,7 @@ #include "Quests.h" #include "Scheduling.h" #include "GameSettings.h" + #include "Overhead.h" // added by Flugente for MercPtrs[] #endif #ifdef JA2UB @@ -1161,14 +1162,10 @@ BOOLEAN IsMercADealer( UINT8 ubMercID ) return( FALSE ); } - - INT8 GetArmsDealerIDFromMercID( UINT8 ubMercID ) { - INT8 cnt; - //loop through the list of arms dealers - for( cnt=0; cntsNonNPCTraderID > 0 ) + bArmsDealer = pSoldier->sNonNPCTraderID; + else + bArmsDealer = -1; + } + + if( bArmsDealer <= -1 ) { // not a dealer, that's ok, we get called for every dude that croaks. return( FALSE ); } - - SOLDIERTYPE *pSoldier; - + // mark the dealer as being out of business! gArmsDealerStatus[ bArmsDealer ].fOutOfBusiness = TRUE; - - //Get a pointer to the dealer - pSoldier = FindSoldierByProfileID( ubMercID, FALSE ); if( pSoldier == NULL ) { // This should never happen, a dealer getting knocked off without the sector being loaded, should it? @@ -1815,8 +1816,7 @@ BOOLEAN AddDeadArmsDealerItemsToWorld( UINT8 ubMercID ) } gArmsDealersInventory[bArmsDealer].clear(); - - + //if the dealer has money if( gArmsDealerStatus[ bArmsDealer ].uiArmsDealersCash > 0 ) { @@ -1831,6 +1831,7 @@ BOOLEAN AddDeadArmsDealerItemsToWorld( UINT8 ubMercID ) gArmsDealerStatus[ bArmsDealer ].uiArmsDealersCash = 0; } + return( TRUE ); } diff --git a/Tactical/Arms Dealer Init.h b/Tactical/Arms Dealer Init.h index 5293e4c9..194bfc04 100644 --- a/Tactical/Arms Dealer Init.h +++ b/Tactical/Arms Dealer Init.h @@ -3,7 +3,7 @@ #include "Store Inventory.h" -#define ADDITIONAL_ARMS_DEALERS 20 // Flugente: this does NOT include Tina, this is for the as-of-yet undetermined slots +#define ADDITIONAL_ARMS_DEALERS 60 // Flugente: this does NOT include Tina, this is for the as-of-yet undetermined slots //enums for the various arms dealers enum @@ -184,6 +184,7 @@ typedef struct UINT32 daysDelayMax; // 1-10 bool useBRSetting; bool allInventoryAlwaysAvailable; + UINT8 nonprofile_loyaltyrequired; // Flugente: if dealer is a non-profile NPC in a city, this is the loyalty required for us to deal with him } ARMS_DEALER_INFO; @@ -372,7 +373,7 @@ UINT32 GetTimeToFixItemBeingRepaired( UINT8 ubArmsDealer, UINT16 usItemIndex, UI BOOLEAN CanDealerTransactItem( UINT8 ubArmsDealer, UINT16 usItemIndex, BOOLEAN fPurchaseFromPlayer ); BOOLEAN CanDealerRepairItem( UINT8 ubArmsDealer, UINT16 usItemIndex ); -BOOLEAN AddDeadArmsDealerItemsToWorld( UINT8 ubMercID ); +BOOLEAN AddDeadArmsDealerItemsToWorld( UINT8 usProfileID, UINT8 aMercID ); void MakeObjectOutOfDealerItems( DEALER_SPECIAL_ITEM *pSpclItemInfo, OBJECTTYPE *pObject ); void CreateObjectForDealer( int usItem, int status, int numObjects, OBJECTTYPE *pObject ); diff --git a/Tactical/Overhead.cpp b/Tactical/Overhead.cpp index 806f12a2..224efcb7 100644 --- a/Tactical/Overhead.cpp +++ b/Tactical/Overhead.cpp @@ -4030,7 +4030,7 @@ void HandleNPCTeamMemberDeath( SOLDIERTYPE *pSoldierOld ) } //if the NPC is a dealer, add the dealers items to the ground - AddDeadArmsDealerItemsToWorld( pSoldierOld->ubProfile ); + AddDeadArmsDealerItemsToWorld( pSoldierOld->ubProfile, pSoldierOld->ubID ); #ifdef JA2UB HandleDeathInPowerGenSector( pSoldierOld ); //ja25 ub diff --git a/Tactical/ShopKeeper Interface.cpp b/Tactical/ShopKeeper Interface.cpp index ea5e1903..0024d822 100644 --- a/Tactical/ShopKeeper Interface.cpp +++ b/Tactical/ShopKeeper Interface.cpp @@ -332,6 +332,9 @@ INT32 giSKIMessageBox=-1; INT8 gbSelectedArmsDealerID = -1; //Contains the enum value for the currently selected arms dealer +// Flugente: while we're trading, store the ID of non-NPC dealers +UINT8 gusIDOfCivTrader = NOBODY; + //the quote that is in progress, in certain circumstances, we don't want queuing of related but different quotes INT32 giShopKeepDialogueEventinProgress = - 1; @@ -609,7 +612,7 @@ 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 ); +BOOLEAN InitShopKeepersFace( UINT8 usProfileID ); void DisplayTalkingArmsDealer(); BOOLEAN StartShopKeeperTalking( UINT16 usQuoteNum ); void HandleShopKeeperDialog( UINT8 ubInit ); @@ -792,8 +795,7 @@ UINT32 ShopKeeperScreenHandle() RestoreBackgroundRects(); GetShopKeeperInterfaceUserInput(); - - + // Check for any newly added items... if ( gpSMCurrentMerc->flags.fCheckForNewlyAddedItems ) { @@ -802,10 +804,8 @@ UINT32 ShopKeeperScreenHandle() gpSMCurrentMerc->flags.fCheckForNewlyAddedItems = FALSE; } - HandleShopKeeperInterface(); - - + if( gubSkiDirtyLevel == SKI_DIRTY_LEVEL2 ) { RenderShopKeeperInterface(); @@ -860,13 +860,10 @@ UINT32 ShopKeeperScreenHandle() gfDisplayNoRoomMsg = FALSE; } - - + return( SHOPKEEPER_SCREEN ); } - - UINT32 ShopKeeperScreenShutdown() { ShutDownArmsDealers(); @@ -874,18 +871,10 @@ UINT32 ShopKeeperScreenShutdown() return( TRUE ); } - - - - // // // - - - - BOOLEAN EnterShopKeeperInterface() { VOBJECT_DESC VObjectDesc; @@ -893,12 +882,17 @@ BOOLEAN EnterShopKeeperInterface() 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 ); + SOLDIERTYPE* pShopkeeper = NULL; + + if ( gusIDOfCivTrader != NOBODY ) + pShopkeeper = MercPtrs[gusIDOfCivTrader]; + else + pShopkeeper = FindSoldierByProfileID( armsDealerInfo[gbSelectedArmsDealerID].ubShopKeeperID, FALSE ); + if ( GetRangeFromGridNoDiff( pSoldier->sGridNo, pShopkeeper->sGridNo ) > NPC_TALK_RADIUS ) { //so now we know we are too far away to trade, so instead of just quitting, @@ -950,7 +944,6 @@ BOOLEAN EnterShopKeeperInterface() } } - // 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"); @@ -1010,7 +1003,6 @@ BOOLEAN EnterShopKeeperInterface() return( FALSE ); } - // load the Main trade screen background image VObjectDesc.fCreateFlags=VOBJECT_CREATE_FROMFILE; FilenameForBPP("InterFace\\itemcrossout.sti", VObjectDesc.ImageFile); @@ -1022,8 +1014,6 @@ BOOLEAN EnterShopKeeperInterface() return( FALSE ); } - - /* ATM: // load the Main trade screen backgroiund image @@ -1047,16 +1037,15 @@ ATM: //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 ); - } + + 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; @@ -1089,9 +1078,7 @@ ATM: 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 ); @@ -1142,10 +1129,7 @@ ATM: 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 @@ -1156,9 +1140,7 @@ ATM: //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 @@ -1166,14 +1148,12 @@ ATM: //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(); @@ -1189,13 +1169,9 @@ ATM: 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; @@ -1260,7 +1236,6 @@ ATM: // Dirty the bottom panel fInterfacePanelDirty = DIRTYLEVEL2; - gfDealerHasSaidTheEvaluateQuoteOnceThisSession = FALSE; guiRandomQuoteDelayTime = SKI_DEALERS_RANDOM_QUOTE_DELAY; @@ -1290,32 +1265,31 @@ ATM: } -BOOLEAN InitShopKeepersFace( UINT8 ubMercID ) +BOOLEAN InitShopKeepersFace( UINT8 usProfileID ) { - SOLDIERTYPE *pSoldier = FindSoldierByProfileID( armsDealerInfo[ gbSelectedArmsDealerID ].ubShopKeeperID, FALSE ); + SOLDIERTYPE *pSoldier = NULL; + + // if this is a non-NPC, don't set this up + if ( gusIDOfCivTrader == NOBODY ) + pSoldier = FindSoldierByProfileID( armsDealerInfo[gbSelectedArmsDealerID].ubShopKeeperID, FALSE ); if( pSoldier == NULL ) { - #ifdef JA2TESTVERSION - //Create the facial index - giShopKeeperFaceIndex = InitFace( ubMercID, NOBODY, FACE_BIGFACE ); - #else - return( FALSE ); - #endif + //Create the facial index + giShopKeeperFaceIndex = InitFace( usProfileID, NOBODY, FACE_BIGFACE ); } else { //Create the facial index - giShopKeeperFaceIndex = InitFace( ubMercID, pSoldier->ubID, FACE_BIGFACE ); + giShopKeeperFaceIndex = InitFace( usProfileID, pSoldier->ubID, FACE_BIGFACE ); } - SetAutoFaceActive( FRAME_BUFFER, FACE_AUTO_RESTORE_BUFFER, giShopKeeperFaceIndex, SKI_FACE_X, SKI_FACE_Y ); //Set it so the face cannot be set InActive - gFacesData[ giShopKeeperFaceIndex ].uiFlags |= FACE_INACTIVE_HANDLED_ELSEWHERE; - - + if ( giShopKeeperFaceIndex > -1 ) + gFacesData[ giShopKeeperFaceIndex ].uiFlags |= FACE_INACTIVE_HANDLED_ELSEWHERE; + RenderAutoFace( giShopKeeperFaceIndex ); return(TRUE); @@ -1443,7 +1417,6 @@ void HandleShopKeeperInterface() INT32 iCounter = 0; - //if we are in the item desc panel, disable the buttons if( InItemDescriptionBox( ) && pShopKeeperItemDescObject != NULL ) { @@ -1467,8 +1440,7 @@ void HandleShopKeeperInterface() return; } - - + if( gubSkiDirtyLevel == SKI_DIRTY_LEVEL2 ) { fInterfacePanelDirty = DIRTYLEVEL2; @@ -1505,7 +1477,6 @@ void HandleShopKeeperInterface() DisplayTalkingArmsDealer(); - DisplayArmsDealerCurrentInventoryPage( ); DisplayArmsDealerOfferArea(); @@ -1583,14 +1554,12 @@ BOOLEAN RenderShopKeeperInterface() CHAR16 zMoney[128]; HVSURFACE hDestVSurface, hSrcVSurface; SGPRect SrcRect; - - + if( InItemDescriptionBox( ) && pShopKeeperItemDescObject != NULL ) { return( TRUE ); } - - + // RenderSMPanel( &fDirty ); // RenderTacticalInterface( ); @@ -1618,8 +1587,7 @@ BOOLEAN RenderShopKeeperInterface() //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 ); @@ -2569,19 +2537,37 @@ void SelectSkiInventoryMovementAreaRegionCallBack(MOUSE_REGION * pRegion, INT32 } } - - - void EnterShopKeeperInterfaceScreen( UINT8 ubArmsDealer ) { + gusIDOfCivTrader = NOBODY; + //Get Dealer ID from from merc Id gbSelectedArmsDealerID = GetArmsDealerIDFromMercID( ubArmsDealer ); - if( gbSelectedArmsDealerID == -1 ) + if( gbSelectedArmsDealerID <= -1 ) { - #ifdef JA2BETAVERSION +#ifdef JA2BETAVERSION ScreenMsg( FONT_MCOLOR_WHITE, MSG_BETAVERSION, L"Failed to find Arms Dealer ID From Merc ID #%d", ubArmsDealer ); - #endif +#endif + gfSKIScreenExit = TRUE; + } + + LeaveTacticalScreen( SHOPKEEPER_SCREEN ); +} + +// Flugente: set up shopkeeper with a non-NPC +void EnterShopKeeperInterfaceScreen_NonNPC( INT8 ubArmsDealerID, UINT8 aMercID ) +{ + gusIDOfCivTrader = aMercID; + + //Get Dealer ID from from merc Id + gbSelectedArmsDealerID = ubArmsDealerID; + + if ( gbSelectedArmsDealerID <= -1 ) + { +#ifdef JA2BETAVERSION + ScreenMsg( FONT_MCOLOR_WHITE, MSG_BETAVERSION, L"Failed to find Arms Dealer ID From Merc ID #%d", ubArmsDealerID ); +#endif gfSKIScreenExit = TRUE; } @@ -4015,13 +4001,11 @@ void PerformTransaction( UINT32 uiMoneyFromPlayersAccount ) 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 ) { @@ -4934,7 +4918,8 @@ void DisplayTalkingArmsDealer() //Make sure the Dealers doesn't get disabled - gFacesData[ giShopKeeperFaceIndex ].fDisabled = FALSE; + if ( giShopKeeperFaceIndex > -1 ) + gFacesData[ giShopKeeperFaceIndex ].fDisabled = FALSE; HandleDialogue(); @@ -4944,8 +4929,9 @@ void DisplayTalkingArmsDealer() HandleTalkingAutoFaces( ); HandleShopKeeperDialog( 0 ); - - gfIsTheShopKeeperTalking = gFacesData[ giShopKeeperFaceIndex ].fTalking; + gfIsTheShopKeeperTalking = FALSE; + if ( giShopKeeperFaceIndex > -1 ) + gfIsTheShopKeeperTalking = gFacesData[ giShopKeeperFaceIndex ].fTalking; //if the merc just started talking // if( gfIsTheShopKeeperTalking && !fWasTheMercTalking ) @@ -7070,16 +7056,13 @@ void StartSKIDescriptionBox( void ) void DeleteShopKeeperItemDescBox() { - INT32 iCnt; - - pShopKeeperItemDescObject = NULL; gubSkiDirtyLevel = SKI_DIRTY_LEVEL2; //Redraw the face - gFacesData[ giShopKeeperFaceIndex ].uiFlags |= FACE_REDRAW_WHOLE_FACE_NEXT_FRAME; - - + if ( giShopKeeperFaceIndex > -1 ) + gFacesData[ giShopKeeperFaceIndex ].uiFlags |= FACE_REDRAW_WHOLE_FACE_NEXT_FRAME; + // enable almost everything! CheckForDisabledForGiveItem( ); @@ -7104,7 +7087,7 @@ void DeleteShopKeeperItemDescBox() EnableAllDealersInventorySlots(); EnableAllDealersOfferSlots(); - for (iCnt = 0; iCnt < SKI_NUM_TRADING_INV_SLOTS; iCnt++) + for ( INT32 iCnt = 0; iCnt < SKI_NUM_TRADING_INV_SLOTS; ++iCnt ) { MSYS_EnableRegion( &gPlayersOfferSlotsMouseRegions[ iCnt ] ); MSYS_EnableRegion( &gPlayersOfferSlotsSmallFaceMouseRegions[ iCnt ] ); @@ -7119,11 +7102,9 @@ void DeleteShopKeeperItemDescBox() // if( giItemDescAmmoButton >= 0 && ButtonList[ giItemDescAmmoButton ]. // EnableButton( giItemDescAmmoButton ); - } - BOOLEAN OfferObjectToDealer( OBJECTTYPE *pComplexObject, UINT8 ubOwnerProfileId, INT16 bOwnerSlotId ) { UINT8 ubRepairableSubObjects; @@ -7136,7 +7117,6 @@ BOOLEAN OfferObjectToDealer( OBJECTTYPE *pComplexObject, UINT8 ubOwnerProfileId, 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 @@ -7367,8 +7347,7 @@ BOOLEAN ShopkeeperAutoPlaceObject( SOLDIERTYPE * pSoldier, OBJECTTYPE * pObject, // 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 @@ -7389,7 +7368,6 @@ BOOLEAN ShopkeeperAutoPlaceObject( SOLDIERTYPE * pSoldier, OBJECTTYPE * pObject, } - // 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 ) @@ -7408,8 +7386,6 @@ void ShopkeeperAddItemToPool( INT32 sGridNo, OBJECTTYPE *pObject, INT8 bVisible, } } - - void IfMercOwnedCopyItemToMercInv( INVENTORY_IN_SLOT *pInv ) { INT16 sSoldierID; @@ -7494,7 +7470,6 @@ BOOLEAN SKITryToReturnInvToOwnerOrCurrentMerc( INVENTORY_IN_SLOT *pInv ) // 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 ) { @@ -7514,8 +7489,7 @@ BOOLEAN SKITryToAddInvToMercsInventory( INVENTORY_IN_SLOT *pInv, SOLDIERTYPE *pS { INT8 bMoneyInvPos; BOOLEAN fNewItem = FALSE; - - + //if the item is money if( Item[ pInv->sItemIndex ].usItemClass == IC_MONEY ) { @@ -7538,7 +7512,6 @@ BOOLEAN SKITryToAddInvToMercsInventory( INVENTORY_IN_SLOT *pInv, SOLDIERTYPE *pS } } - // 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 ) ); @@ -7552,20 +7525,21 @@ BOOLEAN SKITryToAddInvToMercsInventory( INVENTORY_IN_SLOT *pInv, SOLDIERTYPE *pS 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 ); + if ( gusIDOfCivTrader != NOBODY ) + pShopkeeper = MercPtrs[gusIDOfCivTrader]; + else + pShopkeeper = FindSoldierByProfileID( armsDealerInfo[gbSelectedArmsDealerID].ubShopKeeperID, FALSE ); + Assert( pShopkeeper != NULL ); Assert( pShopkeeper->bActive ); Assert( pShopkeeper->bInSector ); @@ -7597,8 +7571,6 @@ BOOLEAN CanMercInteractWithSelectedShopkeeper( SOLDIERTYPE *pSoldier ) return( FALSE ); } - - #ifdef JA2TESTVERSION void AddShopkeeperToGridNo( UINT8 ubProfile, INT32 sGridNo ) @@ -7632,8 +7604,7 @@ void AddShopkeeperToGridNo( UINT8 ubProfile, INT32 sGridNo ) void ExitSKIRequested() { BOOLEAN fPlayerOwnedStuffOnTable = FALSE; - - + ShutUpShopKeeper(); if( !gfRemindedPlayerToPickUpHisStuff ) @@ -7669,8 +7640,7 @@ void ExitSKIRequested() } } } - - + // if the player hasn't already requested to leave if( !gfUserHasRequestedToLeave ) { @@ -7688,8 +7658,6 @@ void ExitSKIRequested() } } - - void ResetAllQuoteSaidFlags() { UINT8 uiCnt; @@ -7703,14 +7671,11 @@ void ResetAllQuoteSaidFlags() 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. @@ -7755,8 +7720,6 @@ void DealWithItemsStillOnTheTable() } } - - void ReturnItemToPlayerSomehow( INVENTORY_IN_SLOT *pInvSlot, SOLDIERTYPE *pDropSoldier ) { //if the item doesn't have a duplicate copy in its owner merc's inventory slot @@ -7770,6 +7733,7 @@ void ReturnItemToPlayerSomehow( INVENTORY_IN_SLOT *pInvSlot, SOLDIERTYPE *pDropS 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) @@ -7779,8 +7743,6 @@ void ReturnItemToPlayerSomehow( INVENTORY_IN_SLOT *pInvSlot, SOLDIERTYPE *pDropS } } - - void GivePlayerSomeChange( UINT32 uiAmount ) { INVENTORY_IN_SLOT MoneyInvSlot; @@ -7805,8 +7767,6 @@ void GivePlayerSomeChange( UINT32 uiAmount ) gArmsDealerStatus[ gbSelectedArmsDealerID ].uiArmsDealersCash -= uiAmount; } - - void DealerGetsBribed( UINT8 ubProfileId, UINT32 uiMoneyAmount ) { BOOLEAN fBribable = FALSE; @@ -7836,13 +7796,10 @@ void DealerGetsBribed( UINT8 ubProfileId, UINT32 uiMoneyAmount ) } } - - void HandlePossibleRepairDelays() { UINT32 uiHoursSinceAnyItemsShouldHaveBeenRepaired = 0; - - + // assume there won't be a delay gfStartWithRepairsDelayedQuote = FALSE; @@ -7884,8 +7841,6 @@ void HandlePossibleRepairDelays() } } - - BOOLEAN RepairmanFixingAnyItemsThatShouldBeDoneNow( UINT32 *puiHoursSinceOldestItemRepaired ) { //if the dealer is not a repair dealer, return @@ -7929,12 +7884,10 @@ BOOLEAN RepairmanFixingAnyItemsThatShouldBeDoneNow( UINT32 *puiHoursSinceOldestI } } - // 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; @@ -7959,8 +7912,6 @@ void DelayRepairsInProgressBy( UINT32 uiMinutesDelayed ) } } - - //Mouse Call back for the Arms delaers face void SelectArmsDealersDropItemToGroundRegionCallBack(MOUSE_REGION * pRegion, INT32 iReason ) { diff --git a/Tactical/ShopKeeper Interface.h b/Tactical/ShopKeeper Interface.h index c869709b..fccffaf6 100644 --- a/Tactical/ShopKeeper Interface.h +++ b/Tactical/ShopKeeper Interface.h @@ -116,9 +116,12 @@ UINT32 ShopKeeperScreenShutdown( void ); bool ArmsDealerItemQsortCompare(INVENTORY_IN_SLOT& pInvSlot1, INVENTORY_IN_SLOT& pInvSlot2); bool RepairmanItemQsortCompare(INVENTORY_IN_SLOT& pInvSlot1, INVENTORY_IN_SLOT& pInvSlot2); - void EnterShopKeeperInterfaceScreen( UINT8 ubArmsDealer ); -void EnableDisableShopkeeperButtons(UINT32 uiCurrentItemDescriptionScreen, int bpAction); + +// Flugente: set up shopkeeper with a non-NPC +void EnterShopKeeperInterfaceScreen_NonNPC( INT8 ubArmsDealerID, UINT8 aMercID ); + +void EnableDisableShopkeeperButtons(UINT32 uiCurrentItemDescriptionScreen, int bpAction); void DrawHatchOnInventory( UINT32 uiSurface, UINT16 usPosX, UINT16 usPosY, UINT16 usWidth, UINT16 usHeight, UINT16 usColor = 0 ); void DrawHatchOnInventory_MilitiaAccess( UINT32 uiSurface, UINT16 usPosX, UINT16 usPosY, UINT16 usWidth, UINT16 usHeight, UINT16 usColor = 0 ); diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index cb762898..de389e3f 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -99,6 +99,7 @@ #include "Facilities.h" // added by Flugente #include "Cheats.h" // added by Flugente #include "MilitiaIndividual.h" // added by Flugente +#include "Arms Dealer Init.h" // added by Flugente for armsDealerInfo[] #endif #include "ub_config.h" @@ -20512,6 +20513,40 @@ BOOLEAN SOLDIERTYPE::PlayerSoldierStartTalking( UINT8 ubTargetID, BOOLEAN fValid HandleSurrenderOffer( pTSoldier ); return(FALSE); } + else if ( pTSoldier->sNonNPCTraderID > 0 ) + { + UINT8 ubTownID = StrategicMap[CALCULATE_STRATEGIC_INDEX( pTSoldier->sSectorX, pTSoldier->sSectorY )].bNameId; + + // not possible if this guy is hostile towards us + // however, if we are covert as a soldier, this check does not apply - merchants know better than do defy the army + if ( !pTSoldier->aiData.bNeutral && !(this->usSoldierFlagMask & SOLDIER_COVERT_SOLDIER) ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, szNonProfileMerchantText[0] ); + } + // not possible if this guy is incapitated + else if ( pTSoldier->bCollapsed || pTSoldier->bBreathCollapsed || pTSoldier->stats.bLife < OKLIFE ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, szNonProfileMerchantText[1] ); + } + // not possible in combat + else if ( gTacticalStatus.uiFlags & INCOMBAT ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, szNonProfileMerchantText[2] ); + } + // not possible of loyalty too low + // however, if we are covert as a soldier, this check does not apply - merchants know better than do defy the army + else if ( ubTownID != BLANK_SECTOR && gTownLoyalty[ubTownID].ubRating < armsDealerInfo[pTSoldier->sNonNPCTraderID].nonprofile_loyaltyrequired && !(this->usSoldierFlagMask & SOLDIER_COVERT_SOLDIER) ) + { + ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, szNonProfileMerchantText[3] ); + } + // if we passed the above check, we can trade + else + { + EnterShopKeeperInterfaceScreen_NonNPC( pTSoldier->sNonNPCTraderID, pTSoldier->ubID ); + } + + return FALSE; + } else { // Flugente: if this guy is a potential volunteer, we might be able to sway him diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index d35d9a7b..576c79fd 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -1480,7 +1480,7 @@ public: /////////////////////////////////////////////////////// // Flugente: this was the location of required variables required for the now removed poison feature. They can be used again UINT8 ubMilitiaAssists; // Flugente: stores militia assists - INT8 bUnusedINT8_2; + INT8 sNonNPCTraderID; // Flugente: we can set up non-NPC soldiers to be merchants, we store their dealer id here (value > 0 means arms dealer entry x) INT8 bUnusedINT8_3; INT16 bUnusedINT16_4; diff --git a/Tactical/Soldier Create.cpp b/Tactical/Soldier Create.cpp index 14d740e7..37934bb4 100644 --- a/Tactical/Soldier Create.cpp +++ b/Tactical/Soldier Create.cpp @@ -3397,7 +3397,9 @@ SOLDIERTYPE* TacticalCreateArmedCivilian( UINT8 usSoldierClass ) return(pSoldier); } -SOLDIERTYPE* TacticalCreateCivilian( INT32 sGridNo, UINT8 usCivilianGroup, INT8 sBodyType, INT8 aVest, INT8 aPants, INT8 aHair, INT8 aSkin, INT16 sItem1, INT16 sItem2, INT16 sItem3, INT16 sItem4 ) +SOLDIERTYPE* TacticalCreateCivilian( INT32 sGridNo, UINT8 usCivilianGroup, INT8 usTraderID, + INT8 sBodyType, INT8 aVest, INT8 aPants, INT8 aHair, INT8 aSkin, + INT16 sItem1, INT16 sItem2, INT16 sItem3, INT16 sItem4 ) { // not in autoresolve! if ( guiCurrentScreen == AUTORESOLVE_SCREEN ) @@ -3416,7 +3418,7 @@ SOLDIERTYPE* TacticalCreateCivilian( INT32 sGridNo, UINT8 usCivilianGroup, INT8 MercCreateStruct.sInsertionGridNo = sGridNo; MercCreateStruct.ubDirection = Random( NUM_WORLD_DIRECTIONS ); - while ( sBodyType < 0 || sBodyType >= CRIPPLECIV || (sBodyType >= ADULTFEMALEMONSTER && sBodyType <= QUEENMONSTER) ) + while ( sBodyType < 0 || sBodyType > CRIPPLECIV || (sBodyType >= ADULTFEMALEMONSTER && sBodyType <= QUEENMONSTER) ) { sBodyType = Random( CRIPPLECIV + 1 ); } @@ -3458,6 +3460,17 @@ SOLDIERTYPE* TacticalCreateCivilian( INT32 sGridNo, UINT8 usCivilianGroup, INT8 // set correct civ group pSoldier->ubCivilianGroup = usCivilianGroup; + pSoldier->sNonNPCTraderID = -1; + + if ( gGameExternalOptions.bExtraMerchants ) + { + pSoldier->sNonNPCTraderID = usTraderID; + + // if we're a dealer, don't walk around that much + if ( pSoldier->sNonNPCTraderID > 0 ) + pSoldier->aiData.bOrders = ONGUARD; + } + // make him wear administrator uniform UINT16 usPaletteAnimSurface = LoadSoldierAnimationSurface( pSoldier, pSoldier->usAnimState ); diff --git a/Tactical/Soldier Create.h b/Tactical/Soldier Create.h index ebf3c8d8..d92bbcf1 100644 --- a/Tactical/Soldier Create.h +++ b/Tactical/Soldier Create.h @@ -475,7 +475,9 @@ SOLDIERTYPE* TacticalCreateEnemyJeep( ); // Flugente: create an armed civilian SOLDIERTYPE* TacticalCreateArmedCivilian( UINT8 usSoldierClass = SOLDIER_CLASS_ARMY ); -SOLDIERTYPE* TacticalCreateCivilian( INT32 sGridNo, UINT8 usCivilianGroup, INT8 sBodyType, INT8 aVest, INT8 aPants, INT8 aHair, INT8 aSkin, INT16 sItem1, INT16 sItem2, INT16 sItem3, INT16 sItem4 ); +SOLDIERTYPE* TacticalCreateCivilian( INT32 sGridNo, UINT8 usCivilianGroup, INT8 usTraderID, + INT8 sBodyType, INT8 aVest, INT8 aPants, INT8 aHair, INT8 aSkin, + INT16 sItem1, INT16 sItem2, INT16 sItem3, INT16 sItem4 ); // Flugente: assassins are elite soldiers of the civ team that go hostile on a certain event, otherwise they just blend in SOLDIERTYPE* TacticalCreateEnemyAssassin(UINT8 disguisetype); diff --git a/Tactical/XML.h b/Tactical/XML.h index ff7b209f..75eb87bc 100644 --- a/Tactical/XML.h +++ b/Tactical/XML.h @@ -136,26 +136,10 @@ typedef PARSE_STAGE; #define PERKOINVENTORYFILENAME "NPCInventory\\PerkoInventory.xml" #define FREDOINVENTORYFILENAME "NPCInventory\\FredoInventory.xml" #define TINAINVENTORYFILENAME "NPCInventory\\TinaInventory.xml" -#define ADITIONALDEALER_1_INVENTORYFILENAME "NPCInventory\\AdditionalDealer_1_Inventory.xml" -#define ADITIONALDEALER_2_INVENTORYFILENAME "NPCInventory\\AdditionalDealer_2_Inventory.xml" -#define ADITIONALDEALER_3_INVENTORYFILENAME "NPCInventory\\AdditionalDealer_3_Inventory.xml" -#define ADITIONALDEALER_4_INVENTORYFILENAME "NPCInventory\\AdditionalDealer_4_Inventory.xml" -#define ADITIONALDEALER_5_INVENTORYFILENAME "NPCInventory\\AdditionalDealer_5_Inventory.xml" -#define ADITIONALDEALER_6_INVENTORYFILENAME "NPCInventory\\AdditionalDealer_6_Inventory.xml" -#define ADITIONALDEALER_7_INVENTORYFILENAME "NPCInventory\\AdditionalDealer_7_Inventory.xml" -#define ADITIONALDEALER_8_INVENTORYFILENAME "NPCInventory\\AdditionalDealer_8_Inventory.xml" -#define ADITIONALDEALER_9_INVENTORYFILENAME "NPCInventory\\AdditionalDealer_9_Inventory.xml" -#define ADITIONALDEALER_10_INVENTORYFILENAME "NPCInventory\\AdditionalDealer_10_Inventory.xml" -#define ADITIONALDEALER_11_INVENTORYFILENAME "NPCInventory\\AdditionalDealer_11_Inventory.xml" -#define ADITIONALDEALER_12_INVENTORYFILENAME "NPCInventory\\AdditionalDealer_12_Inventory.xml" -#define ADITIONALDEALER_13_INVENTORYFILENAME "NPCInventory\\AdditionalDealer_13_Inventory.xml" -#define ADITIONALDEALER_14_INVENTORYFILENAME "NPCInventory\\AdditionalDealer_14_Inventory.xml" -#define ADITIONALDEALER_15_INVENTORYFILENAME "NPCInventory\\AdditionalDealer_15_Inventory.xml" -#define ADITIONALDEALER_16_INVENTORYFILENAME "NPCInventory\\AdditionalDealer_16_Inventory.xml" -#define ADITIONALDEALER_17_INVENTORYFILENAME "NPCInventory\\AdditionalDealer_17_Inventory.xml" -#define ADITIONALDEALER_18_INVENTORYFILENAME "NPCInventory\\AdditionalDealer_18_Inventory.xml" -#define ADITIONALDEALER_19_INVENTORYFILENAME "NPCInventory\\AdditionalDealer_19_Inventory.xml" -#define ADITIONALDEALER_20_INVENTORYFILENAME "NPCInventory\\AdditionalDealer_20_Inventory.xml" + +// Flugente: create additional filenames by putting them together +#define ADITIONALDEALER_INVENTORYFILENAME_A "NPCInventory\\AdditionalDealer_" +#define ADITIONALDEALER_INVENTORYFILENAME_B "_Inventory.xml" #ifdef JA2UB #define BETTYINVENTORYFILENAME "NPCInventory\\BettyInventory.xml" diff --git a/Tactical/XML_Merchants.cpp b/Tactical/XML_Merchants.cpp index fac98724..7eda77fd 100644 --- a/Tactical/XML_Merchants.cpp +++ b/Tactical/XML_Merchants.cpp @@ -65,7 +65,8 @@ merchantStartElementHandle(void *userData, const XML_Char *name, const XML_Char strcmp(name, "daysDelayMin") == 0 || strcmp(name, "daysDelayMax") == 0 || strcmp(name, "useBRSetting") == 0 || - strcmp(name, "allInventoryAlwaysAvailable") == 0 )) + strcmp(name, "allInventoryAlwaysAvailable" ) == 0 || + strcmp(name, "nonprofile_loyaltyrequired") == 0 )) { pData->curElement = ELEMENT_PROPERTY; @@ -223,6 +224,11 @@ merchantEndElementHandle(void *userData, const XML_Char *name) pData->curElement = ELEMENT; pData->curMerchant.allInventoryAlwaysAvailable = (BOOLEAN) atol(pData->szCharData); } + else if ( strcmp( name, "nonprofile_loyaltyrequired" ) == 0 ) + { + pData->curElement = ELEMENT; + pData->curMerchant.nonprofile_loyaltyrequired = (UINT8)strtoul( pData->szCharData, NULL, 0 ); + } pData->maxReadDepth--; } @@ -333,6 +339,7 @@ BOOLEAN WriteMerchantStats() FilePrintf(hFile,"\t\t%d\r\n", armsDealerInfo[cnt].daysDelayMax ); FilePrintf(hFile,"\t\t%d\r\n", armsDealerInfo[cnt].useBRSetting ); FilePrintf(hFile,"\t\t%d\r\n", armsDealerInfo[cnt].allInventoryAlwaysAvailable ); + FilePrintf(hFile,"\t\t%d\r\n", armsDealerInfo[cnt].nonprofile_loyaltyrequired ); FilePrintf(hFile,"\t\r\n"); } diff --git a/Utils/Text.h b/Utils/Text.h index aff8691a..b6de6a93 100644 --- a/Utils/Text.h +++ b/Utils/Text.h @@ -2959,6 +2959,9 @@ extern STR16 szIdividualMilitiaWebsiteFilterText_Rank[] ; extern STR16 szIdividualMilitiaWebsiteFilterText_Origin[]; extern STR16 szIdividualMilitiaWebsiteFilterText_Sector[]; +// Flugente: non-profile merchants +extern STR16 szNonProfileMerchantText[]; + #define TACTICAL_INVENTORY_DIALOG_NUM 16 #define TACTICAL_COVER_DIALOG_NUM 16 diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index 636e52ec..6013cdef 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -11102,6 +11102,13 @@ STR16 szIdividualMilitiaWebsiteFilterText_Sector[] = L"All sectors", }; +STR16 szNonProfileMerchantText[] = +{ + L"Merchant is hostile and does not want to trade.", + L"Merchant is in no state to do business.", + L"Merchant won't trade during combat.", + L"Merchant refuses to interact with you.", +}; // WANNE: Some Chinese specific strings that needs to be in unicode! STR16 ChineseSpecString1 = L"%%"; //defined in _ChineseText.cpp as this file is already unicode diff --git a/Utils/_DutchText.cpp b/Utils/_DutchText.cpp index 9300f696..0212cfb3 100644 --- a/Utils/_DutchText.cpp +++ b/Utils/_DutchText.cpp @@ -11119,5 +11119,12 @@ STR16 szIdividualMilitiaWebsiteFilterText_Sector[] = L"All sectors", }; +STR16 szNonProfileMerchantText[] = +{ + L"Merchant is hostile and does not want to trade.", + L"Merchant is in no state to do business.", + L"Merchant won't trade during combat.", + L"Merchant refuses to interact with you.", +}; #endif //DUTCH diff --git a/Utils/_EnglishText.cpp b/Utils/_EnglishText.cpp index 2a50d285..d01321a1 100644 --- a/Utils/_EnglishText.cpp +++ b/Utils/_EnglishText.cpp @@ -11102,4 +11102,12 @@ STR16 szIdividualMilitiaWebsiteFilterText_Sector[] = L"All sectors", }; +STR16 szNonProfileMerchantText[] = +{ + L"Merchant is hostile and does not want to trade.", + L"Merchant is in no state to do business.", + L"Merchant won't trade during combat.", + L"Merchant refuses to interact with you.", +}; + #endif //ENGLISH diff --git a/Utils/_FrenchText.cpp b/Utils/_FrenchText.cpp index 9d85e4be..5f2ee8ed 100644 --- a/Utils/_FrenchText.cpp +++ b/Utils/_FrenchText.cpp @@ -11101,5 +11101,12 @@ STR16 szIdividualMilitiaWebsiteFilterText_Sector[] = L"All sectors", }; +STR16 szNonProfileMerchantText[] = +{ + L"Merchant is hostile and does not want to trade.", + L"Merchant is in no state to do business.", + L"Merchant won't trade during combat.", + L"Merchant refuses to interact with you.", +}; #endif //FRENCH diff --git a/Utils/_GermanText.cpp b/Utils/_GermanText.cpp index 10829b23..a753360a 100644 --- a/Utils/_GermanText.cpp +++ b/Utils/_GermanText.cpp @@ -10931,5 +10931,12 @@ STR16 szIdividualMilitiaWebsiteFilterText_Sector[] = L"All sectors", }; +STR16 szNonProfileMerchantText[] = +{ + L"Merchant is hostile and does not want to trade.", + L"Merchant is in no state to do business.", + L"Merchant won't trade during combat.", + L"Merchant refuses to interact with you.", +}; #endif //GERMAN diff --git a/Utils/_ItalianText.cpp b/Utils/_ItalianText.cpp index e8bc700b..5abced89 100644 --- a/Utils/_ItalianText.cpp +++ b/Utils/_ItalianText.cpp @@ -11110,5 +11110,12 @@ STR16 szIdividualMilitiaWebsiteFilterText_Sector[] = L"All sectors", }; +STR16 szNonProfileMerchantText[] = +{ + L"Merchant is hostile and does not want to trade.", + L"Merchant is in no state to do business.", + L"Merchant won't trade during combat.", + L"Merchant refuses to interact with you.", +}; #endif //ITALIAN diff --git a/Utils/_PolishText.cpp b/Utils/_PolishText.cpp index e4a9f69b..9f99dd11 100644 --- a/Utils/_PolishText.cpp +++ b/Utils/_PolishText.cpp @@ -11123,5 +11123,12 @@ STR16 szIdividualMilitiaWebsiteFilterText_Sector[] = L"All sectors", }; +STR16 szNonProfileMerchantText[] = +{ + L"Merchant is hostile and does not want to trade.", + L"Merchant is in no state to do business.", + L"Merchant won't trade during combat.", + L"Merchant refuses to interact with you.", +}; #endif //POLISH diff --git a/Utils/_RussianText.cpp b/Utils/_RussianText.cpp index b65938f7..a5239815 100644 --- a/Utils/_RussianText.cpp +++ b/Utils/_RussianText.cpp @@ -11102,5 +11102,12 @@ STR16 szIdividualMilitiaWebsiteFilterText_Sector[] = L"All sectors", }; +STR16 szNonProfileMerchantText[] = +{ + L"Merchant is hostile and does not want to trade.", + L"Merchant is in no state to do business.", + L"Merchant won't trade during combat.", + L"Merchant refuses to interact with you.", +}; #endif //RUSSIAN