diff --git a/Strategic/Campaign Types.h b/Strategic/Campaign Types.h index af25aa9e..37be5a19 100644 --- a/Strategic/Campaign Types.h +++ b/Strategic/Campaign Types.h @@ -3,6 +3,7 @@ #include "types.h" #include "DEBUG.H" +#include const int MAXIMUM_VALID_X_COORDINATE = 16; const int MINIMUM_VALID_X_COORDINATE = 1; @@ -223,6 +224,11 @@ enum NUM_RISKS, }; +enum class FacilityRiskVectorTypes +{ + RISK_DRUG_ITEMS, +}; + typedef struct FACILITYRISKTYPE { // The risks involved with perfoming an assignment at a specific facility. @@ -231,6 +237,7 @@ typedef struct FACILITYRISKTYPE INT8 bBaseEffect; // Base result. If negative, result will always be negative. If positive, result will always be positive. // If 0, result can be either negative or positive. UINT8 ubRange; // Range of deviation for the base effect. + std::map> valueVectors; // Optional additional data } FACILITYRISKTYPE; @@ -333,7 +340,6 @@ typedef struct FACILITYTYPE std::vector ProductionData; } FACILITYTYPE; -#define FACILITYTYPE_SIZEOF_POD offsetof(FACILITYTYPE, ProductionData) // HEADROCK HAM 3.5: Maximum number of different facility types #define MAX_NUM_FACILITY_TYPES 255 diff --git a/Strategic/Facilities.cpp b/Strategic/Facilities.cpp index cf039a44..52975d1a 100644 --- a/Strategic/Facilities.cpp +++ b/Strategic/Facilities.cpp @@ -34,6 +34,7 @@ #include "Isometric Utils.h" #include "MilitiaSquads.h" #include "Tactical Save.h" +#include INT16 gsSkyriderCostModifier; // HEADROCK HAM 3.6: Strategic info variable, total of income/costs accumulated for the use of facilities today. @@ -1808,9 +1809,25 @@ void HandleRisksForSoldierFacilityAssignment( SOLDIERTYPE *pSoldier, UINT8 ubFac } // Add effects - CreateItem( ALCOHOL, Item[ALCOHOL].usPortionSize, &gTempObject ); - - ApplyConsumable( pSoldier, &gTempObject, TRUE, FALSE ); + { + std::vector& riskDrugItems = + gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].Risk[iCounter].valueVectors[FacilityRiskVectorTypes::RISK_DRUG_ITEMS]; + + if (riskDrugItems.empty()) + { + CreateItem(ALCOHOL, Item[ALCOHOL].usPortionSize, &gTempObject); + + ApplyConsumable(pSoldier, &gTempObject, TRUE, FALSE); + } + else + { + INT16 sItemId = riskDrugItems[std::uniform_int_distribution<>(0, riskDrugItems.size() - 1)(std::mt19937{ std::random_device{}() })]; + + CreateItem(sItemId, Item[sItemId].usPortionSize, &gTempObject); + + ApplyConsumable(pSoldier, &gTempObject, TRUE, FALSE); + } + } //pSoldier->AddDrugValues( DRUG_TYPE_ALCOHOL, Drug[DRUG_TYPE_ALCOHOL].ubDrugEffect, Drug[DRUG_TYPE_ALCOHOL].ubDrugTravelRate, Drug[DRUG_TYPE_ALCOHOL].ubDrugSideEffect ); diff --git a/Strategic/Hourly Update.cpp b/Strategic/Hourly Update.cpp index d9e0ddfa..4cd628c2 100644 --- a/Strategic/Hourly Update.cpp +++ b/Strategic/Hourly Update.cpp @@ -39,8 +39,8 @@ // anv: for hourly heli repair #include "Vehicles.h" #include "Map Screen Helicopter.h" -// anv: transition to save screen in extreme iron man mode -#include "gameloop.h" +#include "gameloop.h" // anv: transition to save screen in extreme iron man mode +#include // anv: shuffle drug items in hourly update void HourlyQuestUpdate(); void HourlyLarryUpdate(); @@ -342,6 +342,7 @@ void HourlyQuestUpdate() } #define BAR_TEMPTATION 4 +#define INVENTORY_DRUG_TEMPTATION 5 // Flugente: abandoned the LarryItems for the new drug system /*#define NUM_LARRY_ITEMS 6 @@ -377,40 +378,105 @@ void HourlyLarryUpdate() { fTookDrugs = FALSE; - if ( pSoldier->bAssignment < ON_DUTY && !pSoldier->flags.fBetweenSectors && pSoldier->bInSector && !( gTacticalStatus.fEnemyInSector || guiCurrentScreen == GAME_SCREEN ) ) + const std::vector drugItems = pSoldier->GetBackgroundValueVector(BackgroundVectorTypes::BG_DRUGUSE_ITEMS); + const std::vector drugTypes = pSoldier->GetBackgroundValueVector(BackgroundVectorTypes::BG_DRUGUSE_TYPES); + + if ( pSoldier->bAssignment < ON_DUTY && !pSoldier->flags.fBetweenSectors && !( gTacticalStatus.fEnemyInSector || guiCurrentScreen == GAME_SCREEN ) ) { // Flugente: reworked this for the new drug system. We now loop over our entire inventory INT8 invsize = (INT8)pSoldier->inv.size(); // remember inventorysize, so we don't call size() repeatedly for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop) // ... for all items in our inventory ... { - if ( pSoldier->inv[bLoop].exists() && Item[ pSoldier->inv[bLoop].usItem ].drugtype > 0 ) + if (pSoldier->inv[bLoop].exists()) { - pObj = &(pSoldier->inv[bLoop]); - - usTemptation = 5; - - // any drug will do... I'm not going to create a new tag for sth minor like this - break; + INT16 sCurrentItemId = pSoldier->inv[bLoop].usItem; + INT16 sCurrentDrugType = Item[sCurrentItemId].drugtype; + if (Item[sCurrentItemId].drugtype > 0 && Item[sCurrentItemId].usItemClass & (IC_KIT | IC_MISC)) + { + // anv: if drug user has no items or types specified, assume any is valid, otherwise check drug item id or type + if ((drugItems.empty() && drugTypes.empty()) || + (!drugItems.empty() && std::find(drugItems.begin(), drugItems.end(), sCurrentItemId) != drugItems.end()) || + (!drugTypes.empty() && std::find(drugTypes.begin(), drugTypes.end(), sCurrentDrugType) != drugTypes.end())) + { + pObj = &(pSoldier->inv[bLoop]); + usTemptation = INVENTORY_DRUG_TEMPTATION; + break; + } + } } } // check to see if we're in a bar sector, if we are, we have access to alcohol // which may be better than anything we've got... - if ( usTemptation < BAR_TEMPTATION && GetCurrentBalance() >= Item[ ALCOHOL ].usPrice ) + INT16 sBarDrugItemId = 0; + if ( usTemptation < BAR_TEMPTATION ) { - if ( pSoldier->bSectorZ == 0 && - ( ( pSoldier->sSectorX == 13 && pSoldier->sSectorY == MAP_ROW_D) || - ( pSoldier->sSectorX == 13 && pSoldier->sSectorY == MAP_ROW_C) || - ( pSoldier->sSectorX == 5 && pSoldier->sSectorY == MAP_ROW_C) || - ( pSoldier->sSectorX == 6 && pSoldier->sSectorY == MAP_ROW_C) || - ( pSoldier->sSectorX == 5 && pSoldier->sSectorY == MAP_ROW_D) || - ( pSoldier->sSectorX == 2 && pSoldier->sSectorY == MAP_ROW_H) - ) - ) + // sevenfm: check facility + if (pSoldier->bSectorZ == 0) { - // in a bar! - fBar = TRUE; - usTemptation = BAR_TEMPTATION; + for (UINT16 usFacilityType = 0; usFacilityType < NUM_FACILITY_TYPES; usFacilityType++) + { + // Is this facility here? + if (gFacilityLocations[SECTOR(pSoldier->sSectorX, pSoldier->sSectorY)][usFacilityType].fFacilityHere) + { + for (UINT16 usFacilityAssignment = 0; usFacilityAssignment < NUM_FACILITY_ASSIGNMENTS; usFacilityAssignment++) + { + // is it a place with risk of getting drunk? + if (gFacilityTypes[usFacilityType].AssignmentData[usFacilityAssignment].Risk[RISK_DRUNK].usChance > 0) + { + // anv: check all items available for this risk + const std::vector& riskDrugItems = + gFacilityTypes[usFacilityType].AssignmentData[usFacilityAssignment].Risk[RISK_DRUNK].valueVectors[FacilityRiskVectorTypes::RISK_DRUG_ITEMS]; + + if (riskDrugItems.empty()) + { + if (GetCurrentBalance() >= Item[ALCOHOL].usPrice) + { + if ((drugItems.empty() && drugTypes.empty()) || + (!drugItems.empty() && std::find(drugItems.begin(), drugItems.end(), sBarDrugItemId) != drugItems.end()) || + (!drugTypes.empty() && std::find(drugTypes.begin(), drugTypes.end(), Item[ALCOHOL].drugtype) != drugTypes.end())) + { + sBarDrugItemId = ALCOHOL; + + // Cool. + fBar = TRUE; + } + } + } + else + { + std::vector shuffledRiskDrugItems = riskDrugItems; + std::shuffle(shuffledRiskDrugItems.begin(), shuffledRiskDrugItems.end(), std::mt19937{ std::random_device{}() }); + for (INT16 sItemId : shuffledRiskDrugItems) + { + if (sItemId < MAXITEMS && Item[sItemId].usItemClass & (IC_KIT | IC_MISC) && GetCurrentBalance() >= Item[sItemId].usPrice) + { + if ((drugItems.empty() && drugTypes.empty()) || + (!drugItems.empty() && std::find(drugItems.begin(), drugItems.end(), sItemId) != drugItems.end()) || + (!drugTypes.empty() && std::find(drugTypes.begin(), drugTypes.end(), Item[sItemId].drugtype) != drugTypes.end())) + { + sBarDrugItemId = sItemId; + + // Cool. + fBar = TRUE; + + // sevenfm: stop searching + break; + } + } + } + } + } + + + if (fBar) + { + usTemptation = BAR_TEMPTATION; + break; + } + } + } + } } } @@ -473,10 +539,10 @@ void HourlyLarryUpdate() bBoozeSlot = FindEmptySlotWithin( pSoldier, HANDPOS, NUM_INV_SLOTS ); if ( bBoozeSlot != NO_SLOT ) { + INVTYPE drugItem = Item[sBarDrugItemId]; // take $ from player's account // silversurfer: changed the price to reflect the changed amount of 25% below - //usCashAmount = Item[ ALCOHOL ].usPrice; - usCashAmount = (UINT16)( Item[ALCOHOL].usPrice / 4.0f ); + usCashAmount = (UINT16)(drugItem.usPrice / 4.0f); AddTransactionToPlayersBook ( TRANSFER_FUNDS_TO_MERC, pSoldier->ubProfile, GetWorldTotalMin(), -( usCashAmount ) ); // give Larry booze here @@ -484,10 +550,10 @@ void HourlyLarryUpdate() // Now the bottle will be fully consumed below and vanishes from inventory before the player even gets to see it. This simulates going to a bar to have a drink there. UINT8 portionsize = 25; - if ( Item[ALCOHOL].usPortionSize > 0 && Item[ALCOHOL].usPortionSize < 25 ) - portionsize = Item[ALCOHOL].usPortionSize; + if (drugItem.usPortionSize > 0 && drugItem.usPortionSize < 25) + portionsize = drugItem.usPortionSize; - CreateItem( ALCOHOL, portionsize, &( pSoldier->inv[bBoozeSlot] ) ); + CreateItem( sBarDrugItemId, portionsize, &( pSoldier->inv[bBoozeSlot] ) ); ApplyConsumable( pSoldier, &( pSoldier->inv[bBoozeSlot] ), TRUE, FALSE ); } diff --git a/Strategic/XML_FacilityTypes.cpp b/Strategic/XML_FacilityTypes.cpp index 33468d38..598cf88c 100644 --- a/Strategic/XML_FacilityTypes.cpp +++ b/Strategic/XML_FacilityTypes.cpp @@ -41,6 +41,7 @@ typedef enum typedef struct { FACILITYTYPE_PARSE_STAGE curElement; + PARSE_STAGE curGenericElement; CHAR8 szCharData[MAX_CHAR_DATA_LENGTH+1]; INT16 curAssignmentType; INT16 curRisk; @@ -168,7 +169,7 @@ facilitytypeStartElementHandle(void *userData, const XML_Char *name, const XML_C pData->curElement = FACILITYTYPE_TYPE; // Set all values to default before applying XML data - memset(&pData->curFacilityTypeData, 0, FACILITYTYPE_SIZEOF_POD); + pData->curFacilityTypeData = FACILITYTYPE(); InitFacilityTypeEntry( pData ); //DebugMsg(TOPIC_JA2, DBG_LEVEL_3,"MergeStartElementHandle: setting memory for curMerge"); @@ -405,6 +406,23 @@ facilitytypeStartElementHandle(void *userData, const XML_Char *name, const XML_C pData->maxReadDepth++; } + else if (pData->curElement == FACILITYTYPE_RISK && + pData->curRisk == RISK_DRUNK && + strcmp(name, "drugitems") == 0) + { + pData->curGenericElement = ELEMENT_VECTOR_OF_NUMBERS; + pData->curElement = FACILITYTYPE_RISK_ELEMENT; + pData->curAssignmentData.Risk[pData->curRisk].valueVectors[FacilityRiskVectorTypes::RISK_DRUG_ITEMS].clear(); + + pData->maxReadDepth++; + } + else if (pData->curGenericElement == ELEMENT_VECTOR_OF_NUMBERS && + strcmp(name, "drugitem") == 0) + { + pData->curGenericElement = ELEMENT_VECTOR_OF_NUMBERS_NUMBER; + pData->maxReadDepth++; + } + else if ( pData->curElement == FACILITYTYPE_PRODUCTION && ( strcmp( name, "szProductionName" ) == 0 || strcmp( name, "szAdditionalRequirementTips" ) == 0 || @@ -530,6 +548,7 @@ facilitytypeEndElementHandle(void *userData, const XML_Char *name) gFacilityTypes[pData->curIndex].AssignmentData[cnt].Risk[cntB].usChance = pData->curFacilityTypeData.AssignmentData[cnt].Risk[cntB].usChance; gFacilityTypes[pData->curIndex].AssignmentData[cnt].Risk[cntB].bBaseEffect = pData->curFacilityTypeData.AssignmentData[cnt].Risk[cntB].bBaseEffect; gFacilityTypes[pData->curIndex].AssignmentData[cnt].Risk[cntB].ubRange = pData->curFacilityTypeData.AssignmentData[cnt].Risk[cntB].ubRange; + gFacilityTypes[pData->curIndex].AssignmentData[cnt].Risk[cntB].valueVectors = pData->curFacilityTypeData.AssignmentData[cnt].Risk[cntB].valueVectors; } } @@ -673,6 +692,7 @@ facilitytypeEndElementHandle(void *userData, const XML_Char *name) pData->curFacilityTypeData.AssignmentData[pData->curAssignmentType].Risk[cnt].usChance = pData->curAssignmentData.Risk[cnt].usChance; pData->curFacilityTypeData.AssignmentData[pData->curAssignmentType].Risk[cnt].bBaseEffect = pData->curAssignmentData.Risk[cnt].bBaseEffect; pData->curFacilityTypeData.AssignmentData[pData->curAssignmentType].Risk[cnt].ubRange = pData->curAssignmentData.Risk[cnt].ubRange; + pData->curFacilityTypeData.AssignmentData[pData->curAssignmentType].Risk[cnt].valueVectors = pData->curAssignmentData.Risk[cnt].valueVectors; } } else @@ -1252,6 +1272,14 @@ facilitytypeEndElementHandle(void *userData, const XML_Char *name) pData->curProductionData.usOptional_PreProducts.push_back( data ); } } + else if (pData->curGenericElement == ELEMENT_VECTOR_OF_NUMBERS_NUMBER && strcmp(name, "drugitem") == 0) + { + pData->curGenericElement = ELEMENT_VECTOR_OF_NUMBERS; + pData->curElement = FACILITYTYPE_RISK; + + INT16 drugValue = (INT16)atol(pData->szCharData); + pData->curAssignmentData.Risk[pData->curRisk].valueVectors[FacilityRiskVectorTypes::RISK_DRUG_ITEMS].push_back(drugValue); + } pData->maxReadDepth--; } @@ -1299,7 +1327,7 @@ BOOLEAN ReadInFacilityTypes(STR fileName, BOOLEAN localizedVersion) XML_SetCharacterDataHandler(parser, facilitytypeCharacterDataHandle); - memset(&pData, 0, FACILITYTYPEPARSEDATA_SIZE_OF_POD); + pData = facilitytypeParseData(); pData.maxArraySize = MAXITEMS; pData.curIndex = 0; diff --git a/Tactical/Interface.h b/Tactical/Interface.h index ff767245..0df8a4c6 100644 --- a/Tactical/Interface.h +++ b/Tactical/Interface.h @@ -211,6 +211,11 @@ enum { BG_MAX, }; +enum class BackgroundVectorTypes { + BG_DRUGUSE_TYPES, + BG_DRUGUSE_ITEMS, +}; + typedef struct { UINT16 uiIndex; @@ -220,6 +225,7 @@ typedef struct UINT64 uiFlags; // this flagmask defines what special properties this background has (on/off behaviour) INT16 value[BG_MAX]; // property values + std::map> valueVectors; // optional additional data } BACKGROUND_VALUES; #define NUM_BACKGROUND 500 diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 271368d0..103ceb66 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -17605,6 +17605,24 @@ INT16 SOLDIERTYPE::GetBackgroundValue( UINT16 aNr ) return 0; } +const std::vector& SOLDIERTYPE::GetBackgroundValueVector(BackgroundVectorTypes backgroundVectorType) const +{ + static const std::vector emptyVector; + + if (UsingBackGroundSystem() && this->ubProfile != NO_PROFILE) + { + const BACKGROUND_VALUES& background = zBackground[gMercProfiles[this->ubProfile].usBackground]; + auto iterator = background.valueVectors.find(backgroundVectorType); + + if (iterator != background.valueVectors.end()) + { + return iterator->second; + } + } + + return emptyVector; +} + INT8 SOLDIERTYPE::GetSuppressionResistanceBonus( ) { INT8 bonus = 0; diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index 69745618..5873618d 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -1070,6 +1070,8 @@ public: INT8 bPathStored; // good for AI to reduct redundancy }; +enum class BackgroundVectorTypes; + class SOLDIERTYPE//last edited at version 102 { public: @@ -1954,6 +1956,8 @@ public: BOOLEAN HasBackgroundFlag( UINT64 aFlag ); INT16 GetBackgroundValue( UINT16 aNr ); + const std::vector& SOLDIERTYPE::GetBackgroundValueVector(BackgroundVectorTypes backgroundVectorType) const; + INT8 GetSuppressionResistanceBonus(); // bonus to resistance against suppression INT16 GetMeleeDamageBonus(); INT16 GetAPBonus(); diff --git a/Tactical/XML.h b/Tactical/XML.h index 03a95811..efa28c25 100644 --- a/Tactical/XML.h +++ b/Tactical/XML.h @@ -44,7 +44,10 @@ enum ELEMENT_DISABILITY_EFFECT, ELEMENT_DISABILITY_EFFECT_PROPERTY, ELEMENT_PERSONALITY_EFFECT, - ELEMENT_PERSONALITY_EFFECT_PROPERTY + ELEMENT_PERSONALITY_EFFECT_PROPERTY, + + ELEMENT_VECTOR_OF_NUMBERS, + ELEMENT_VECTOR_OF_NUMBERS_NUMBER, } typedef PARSE_STAGE; diff --git a/Tactical/XML_Background.cpp b/Tactical/XML_Background.cpp index 347f1442..5caac1cd 100644 --- a/Tactical/XML_Background.cpp +++ b/Tactical/XML_Background.cpp @@ -41,8 +41,13 @@ backgroundStartElementHandle(void *userData, const XML_Char *name, const XML_Cha { pData->curElement = ELEMENT_LIST; - if ( !localizedTextOnly_BG ) - memset(pData->curArray,0,sizeof(BACKGROUND_VALUES)*pData->maxArraySize); + if (!localizedTextOnly_BG) + { + for (UINT32 i = 0; i < pData->maxArraySize; i++) + { + pData->curArray[i] = BACKGROUND_VALUES(); + } + } pData->maxReadDepth++; //we are not skipping this element } @@ -50,8 +55,8 @@ backgroundStartElementHandle(void *userData, const XML_Char *name, const XML_Cha { pData->curElement = ELEMENT; - if ( !localizedTextOnly_BG ) - memset(&pData->curBackground,0,sizeof(BACKGROUND_VALUES)); + if (!localizedTextOnly_BG) + pData->curBackground = BACKGROUND_VALUES(); pData->maxReadDepth++; //we are not skipping this element } @@ -153,6 +158,27 @@ backgroundStartElementHandle(void *userData, const XML_Char *name, const XML_Cha pData->maxReadDepth++; //we are not skipping this element } + else if (strcmp(name, "drugtypes") == 0 && pData->curElement == ELEMENT) + { + pData->curElement = ELEMENT_VECTOR_OF_NUMBERS; + pData->curBackground.valueVectors[BackgroundVectorTypes::BG_DRUGUSE_TYPES].clear(); + + pData->maxReadDepth++; //we are not skipping this element + } + else if (strcmp(name, "drugitems") == 0 && pData->curElement == ELEMENT) + { + pData->curElement = ELEMENT_VECTOR_OF_NUMBERS; + pData->curBackground.valueVectors[BackgroundVectorTypes::BG_DRUGUSE_TYPES].clear(); + + pData->maxReadDepth++; //we are not skipping this element + } + else if (pData->curElement == ELEMENT_VECTOR_OF_NUMBERS && + (strcmp(name, "drugtype") == 0 || + strcmp(name, "drugitem") == 0)) + { + pData->curElement = ELEMENT_VECTOR_OF_NUMBERS_NUMBER; + pData->maxReadDepth++; //we are not skipping this element + } pData->szCharData[0] = '\0'; } @@ -667,7 +693,19 @@ backgroundEndElementHandle(void *userData, const XML_Char *name) pData->curElement = ELEMENT; pData->curBackground.uiFlags |= (UINT16)atol(pData->szCharData) ? BACKGROUND_CIVGROUPLOYAL : 0; } - + else if (strcmp(name, "drugtype") == 0) + { + pData->curElement = ELEMENT_VECTOR_OF_NUMBERS; + INT16 drugValue = (INT16)atol(pData->szCharData); + pData->curBackground.valueVectors[BackgroundVectorTypes::BG_DRUGUSE_TYPES].push_back(drugValue); + } + else if (strcmp(name, "drugitem") == 0) + { + pData->curElement = ELEMENT_VECTOR_OF_NUMBERS; + INT16 drugValue = (INT16)atol(pData->szCharData); + pData->curBackground.valueVectors[BackgroundVectorTypes::BG_DRUGUSE_ITEMS].push_back(drugValue); + } + pData->maxReadDepth--; } pData->currentDepth--; @@ -711,7 +749,7 @@ BOOLEAN ReadInBackgrounds(STR fileName, BOOLEAN localizedVersion) XML_SetCharacterDataHandler(parser, backgroundCharacterDataHandle); - memset(&pData,0,sizeof(pData)); + pData = enemyRankParseData(); pData.curArray = zBackground; pData.maxArraySize = NUM_BACKGROUND;