////////////////////////////////////////////////////////////////// // HEADROCK HAM 3.5: Facilities // // This file contains functions that test facility data (read // from XML) whenever necessary. See XML_Facilities.cpp and // XML_FacilityTypes.cpp for the reading XML functions. ////////////////////////////////////////////////////////////////// #include "builddefines.h" #include "Campaign Types.h" #include "Facilities.h" #include "Soldier Control.h" #include "Map Screen Interface.h" #include "Assignments.h" #include "GameSettings.h" #include "LaptopSave.h" #include "finances.h" #include "Text.h" #include "Strategic Town Loyalty.h" #include "Strategic Mines.h" #include "SkillCheck.h" #include "random.h" #include "strategicmap.h" #include "Game Clock.h" #include "Campaign.h" #include "Drugs And Alcohol.h" #include "Interface.h" #include "message.h" #include "Morale.h" #include "Isometric Utils.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. // Account settle and reset at midnight, unless player can't pay (in which case, carries over to the next day). // This variable is SAVED and LOADED. INT32 giTotalEarnedForFacilityOperationsToday = 0; INT32 giTotalOwedForFacilityOperationsToday = 0; BOOLEAN gfOutstandingFacilityDebt = TRUE; // This function returns a specific modifier out of the FacilityTypes array. Modifiers exist inside an assignment // sub-array, so the final result depends on the assignment the soldier is performing. The difference between // this and simply reading data from the facility array is that in this case, "AMBIENT" effects are also included, // which take effect regardless of the assignment. INT16 GetFacilityModifier( UINT8 ubModifierType, UINT8 ubFacilityType, UINT8 ubAssignmentType ) { INT16 sAssignmentModifier; INT16 sAmbientModifier; // Are we checking an actual assignment at a specific facility? if (ubAssignmentType != FAC_AMBIENT && ubFacilityType > 0 ) { switch (ubModifierType) { case FACILITY_PERFORMANCE_MOD: sAssignmentModifier = gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].usPerformance; sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].usPerformance; sAssignmentModifier = 100 + ((sAssignmentModifier-100) + (sAmbientModifier-100)); return (sAssignmentModifier); case FACILITY_FATIGUE_MOD: sAssignmentModifier = gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].usFatigue; sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].usFatigue; sAssignmentModifier = 100 + ((sAssignmentModifier-100) + (sAmbientModifier-100)); return (sAssignmentModifier); case FACILITY_SLEEP_MOD: sAssignmentModifier = gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].usSleep; sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].usSleep; sAssignmentModifier = 100 + ((sAssignmentModifier-100) + (sAmbientModifier-100)); return (sAssignmentModifier); case FACILITY_KIT_DEGRADE_MOD: sAssignmentModifier = gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].usKitDegrade; sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].usKitDegrade; sAssignmentModifier = 100 + ((sAssignmentModifier-100) + (sAmbientModifier-100)); return (sAssignmentModifier); case FACILITY_MAX_MORALE: sAssignmentModifier = gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].ubMaximumMorale; sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].ubMaximumMorale; sAssignmentModifier = __min(sAssignmentModifier, sAmbientModifier); return (sAssignmentModifier); case FACILITY_MAX_BREATH: sAssignmentModifier = gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].ubMaximumBreath; sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].ubMaximumBreath; sAssignmentModifier = __min(sAssignmentModifier, sAmbientModifier); return (sAssignmentModifier); case FACILITY_DETECT_IMMEDIATE: sAssignmentModifier = gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].ubDetectEnemiesImmediate; sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].ubDetectEnemiesImmediate; sAssignmentModifier = __max(sAssignmentModifier, sAmbientModifier); return (sAssignmentModifier); case FACILITY_DETECT_DYNAMIC: sAssignmentModifier = gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].fDetectEnemiesDynamic; sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].fDetectEnemiesDynamic; sAssignmentModifier = __max(sAssignmentModifier, sAmbientModifier); return (sAssignmentModifier); case FACILITY_DETECT_LONGRANGE: sAssignmentModifier = gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].fDetectEnemiesLongrange; sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].fDetectEnemiesLongrange; sAssignmentModifier = __max(sAssignmentModifier, sAmbientModifier); return (sAssignmentModifier); case FACILITY_DETECT_ANYWHERE: sAssignmentModifier = gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].fDetectEnemiesAnywhere; sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].fDetectEnemiesAnywhere; sAssignmentModifier = __max(sAssignmentModifier, sAmbientModifier); return (sAssignmentModifier); case FACILITY_COUNT_INWILD: sAssignmentModifier = gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].fCountEnemiesInWild; sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].fCountEnemiesInWild; sAssignmentModifier = __max(sAssignmentModifier, sAmbientModifier); return (sAssignmentModifier); case FACILITY_COUNT_INCITIES: sAssignmentModifier = gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].fCountEnemiesInCities; sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].fCountEnemiesInCities; sAssignmentModifier = __max(sAssignmentModifier, sAmbientModifier); return (sAssignmentModifier); case FACILITY_SKYRIDER_COST_MOD: sAssignmentModifier = gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].sSkyriderCostModifier; sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].sSkyriderCostModifier; sAssignmentModifier = sAssignmentModifier + sAmbientModifier; return (sAssignmentModifier); case FACILITY_MINE_INCOME_MOD: sAssignmentModifier = gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].usMineIncomeModifier; sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].usMineIncomeModifier; sAssignmentModifier = 100 + ((sAssignmentModifier-100) + (sAmbientModifier-100)); return (sAssignmentModifier); // Flugente: not needed anywhere atm case FACILITY_CANTINA_MOD: sAssignmentModifier = gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].sCantinaFoodModifier; sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].sCantinaFoodModifier; sAssignmentModifier = 100 + ((sAssignmentModifier-100) + (sAmbientModifier-100)); return (sAssignmentModifier); case FACILITY_PRISON_MOD: sAssignmentModifier = gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].usPrisonBaseLimit; sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].usPrisonBaseLimit; sAssignmentModifier = 100 + ((sAssignmentModifier-100) + (sAmbientModifier-100)); return (sAssignmentModifier); default: return (0); } } // Only ambient effect please else { switch (ubModifierType) { case FACILITY_PERFORMANCE_MOD: sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].usPerformance; return (sAmbientModifier); case FACILITY_FATIGUE_MOD: sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].usFatigue; return (sAmbientModifier); case FACILITY_SLEEP_MOD: sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].usSleep; return (sAmbientModifier); case FACILITY_KIT_DEGRADE_MOD: sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].usKitDegrade; return (sAmbientModifier); case FACILITY_MAX_MORALE: sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].ubMaximumMorale; return (sAmbientModifier); case FACILITY_MAX_BREATH: sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].ubMaximumBreath; return (sAmbientModifier); case FACILITY_DETECT_IMMEDIATE: sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].ubDetectEnemiesImmediate; return (sAmbientModifier); case FACILITY_DETECT_DYNAMIC: sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].fDetectEnemiesDynamic; return (sAmbientModifier); case FACILITY_DETECT_LONGRANGE: sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].fDetectEnemiesLongrange; return (sAmbientModifier); case FACILITY_DETECT_ANYWHERE: sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].fDetectEnemiesAnywhere; return (sAmbientModifier); case FACILITY_COUNT_INWILD: sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].fCountEnemiesInWild; return (sAmbientModifier); case FACILITY_COUNT_INCITIES: sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].fCountEnemiesInCities; return (sAmbientModifier); case FACILITY_SKYRIDER_COST_MOD: sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].sSkyriderCostModifier; return (sAmbientModifier); case FACILITY_MINE_INCOME_MOD: sAmbientModifier = gFacilityTypes[ubFacilityType].AssignmentData[0].usMineIncomeModifier; return (sAmbientModifier); default: return (0); } } } // This is a handler function that runs through every facility in the soldier's sector, retrieving one type of // modifier from each facility. INT16 GetSectorModifier( SOLDIERTYPE *pSoldier, UINT8 ubModifierType ) { INT16 sCurrent; INT16 Result; UINT8 ubSectorID = SECTOR(pSoldier->sSectorX, pSoldier->sSectorY); INT8 bAssignmentType = GetSoldierFacilityAssignmentIndex(pSoldier); // Set "default" value. switch (ubModifierType) { case FACILITY_PERFORMANCE_MOD: case FACILITY_FATIGUE_MOD: case FACILITY_SLEEP_MOD: case FACILITY_KIT_DEGRADE_MOD: case FACILITY_MINE_INCOME_MOD: Result = 100; break; case FACILITY_MAX_MORALE: case FACILITY_MAX_BREATH: Result = 100; break; case FACILITY_DETECT_IMMEDIATE: case FACILITY_DETECT_DYNAMIC: case FACILITY_DETECT_LONGRANGE: case FACILITY_DETECT_ANYWHERE: case FACILITY_COUNT_INWILD: case FACILITY_COUNT_INCITIES: Result = 0; break; case FACILITY_SKYRIDER_COST_MOD: Result = 0; break; } for (UINT16 cnt = 0; cnt < NUM_FACILITY_TYPES; cnt++) { if (gFacilityLocations[ubSectorID][cnt].fFacilityHere) { if ( bAssignmentType != -1) { // Soldier has a facility-based assignment. Is he working this type of facility? if ((UINT8)pSoldier->sFacilityTypeOperated == (UINT8)cnt) { sCurrent = GetFacilityModifier( ubModifierType, (UINT8)cnt, (UINT8)bAssignmentType ); } else // He's operating another type of facility. Get only the ambient effect. { sCurrent = GetFacilityModifier( ubModifierType, (UINT8)cnt, FAC_AMBIENT ); } } else { // Soldier is not doing facility-specific work. Get only the ambient effect. sCurrent = GetFacilityModifier( ubModifierType, (UINT8)cnt, FAC_AMBIENT ); } // Calculate overall result so far. Calculation method is based on the modifier we're looking for. switch (ubModifierType) { case FACILITY_PERFORMANCE_MOD: case FACILITY_FATIGUE_MOD: case FACILITY_SLEEP_MOD: case FACILITY_KIT_DEGRADE_MOD: case FACILITY_MINE_INCOME_MOD: Result = 100 + ((Result-100) + (sCurrent-100)); break; case FACILITY_MAX_MORALE: case FACILITY_MAX_BREATH: Result = __min(Result, sCurrent); break; case FACILITY_DETECT_IMMEDIATE: case FACILITY_DETECT_DYNAMIC: case FACILITY_DETECT_LONGRANGE: case FACILITY_DETECT_ANYWHERE: case FACILITY_COUNT_INWILD: case FACILITY_COUNT_INCITIES: Result = __max(Result, sCurrent); break; case FACILITY_SKYRIDER_COST_MOD: Result += sCurrent; break; } } } return (Result); } ////////////////////////////////////////////////// // This function reads the status of intelligence gathering currently being done by your mercs. They work at special // facilities that can enable enemy detection in certain sectors. // // There are several detection methods, and they allow/disallow detection in different groups of sectors. // DETECT_ENEMIES_IMMEDIATE: Detection in sectors near to where the merc is working. // DETECT_ENEMIES_DYNAMIC: Detection in sectors where Mobile Militia are allowed to roam. // DETECT_ENEMIES_LONGRANGE: Detection in all explored sectors. // DETECT_ENEMIES_ANYWHERE: Detect enemy units all over the map. // Also there are two Counting methods, which allow reading the exact number of troops in specific sectors. // COUNT_ENEMIES_IN_WILD: Can count enemy numbers in non-city sectors only. // COUNT_ENEMIES_IN_CITIES: Can counter enemy numbers in city sectors only. // // This function runs through each working merc, and flags all the appropriate sectors. Later on, the program reads // these flags when it wants to find out whether an enemy unit is visible on the map, and whether the exact number of // enemies in that unit is also shown. void UpdateStrategicDetectionLevel( ) { SOLDIERTYPE * pSoldier; UINT8 ubCounter = 0; // Reset the detection flag in SectorInfo for all sectors. for (UINT16 X = 0; X < 256; X++) { // Reset sector detection flags. SectorInfo[X].ubDetectionLevel = 0; } // Flag to hold the current detection level before calculating its effects on the detection mask. UINT8 ubStrategicDetectionLevel = 0; // Run through list of player-characters while(gCharactersList[ubCounter].fValid) { pSoldier = gCharactersList[ ubCounter ].usSolID; // Is character truly valid? if( !( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) && pSoldier->bSectorZ == 0 && pSoldier->stats.bLife >= OKLIFE && !(pSoldier->flags.fMercAsleep) ) { UINT8 ubSector = SECTOR(pSoldier->sSectorX, pSoldier->sSectorY); UINT8 ubFacilityType = (UINT8)pSoldier->sFacilityTypeOperated; if (GetSoldierFacilityAssignmentIndex( pSoldier ) == -1) { // Skip this soldier, he is not performing a facility assignment. ubCounter++; continue; } UINT8 ubAssignmentType = (UINT8)GetSoldierFacilityAssignmentIndex( pSoldier ); // Test to see whether the character is properly set up at this facility. if ( CanCharacterFacility( pSoldier, ubFacilityType, ubAssignmentType ) && GetWorldTotalMin() - pSoldier->uiLastAssignmentChangeMin >= (UINT32)gGameExternalOptions.ubMinutesForAssignmentToCount ) { // Make sure facility type is valid. Assert(ubFacilityType < MAX_NUM_FACILITY_TYPES); if (!(gFacilityLocations[ubSector][ubFacilityType].fFacilityHere)) { // Somehow, the character is working in a nonexistant facility. Odd. Skip him. ubCounter++; continue; } ///////////////////////////////////////////////////////// // Begin testing for facility detection bonuses. // Does facility allow Long-Range Detection? if (GetFacilityModifier(FACILITY_DETECT_LONGRANGE, ubFacilityType, ubAssignmentType)) { ubStrategicDetectionLevel |= (1 << DETECT_ENEMIES_LONGRANGE); } // Does facility allow Omniscient Detection? if (GetFacilityModifier(FACILITY_DETECT_ANYWHERE, ubFacilityType, ubAssignmentType)) { ubStrategicDetectionLevel |= (1 << DETECT_ENEMIES_ANYWHERE); } // Does facility allow Counting Enemies in the Wilderness? if (GetFacilityModifier(FACILITY_COUNT_INWILD, ubFacilityType, ubAssignmentType)) { ubStrategicDetectionLevel |= (1 << COUNT_ENEMIES_IN_WILD); } // Does facility allow Counting Enemies in the Wilderness? if (GetFacilityModifier(FACILITY_COUNT_INCITIES, ubFacilityType, ubAssignmentType)) { ubStrategicDetectionLevel |= (1 << COUNT_ENEMIES_IN_CITIES); } // "Immediate" detection allows spotting enemies within a certain radius of the facility. UINT16 usImmediateDetectionRange = (UINT16)GetFacilityModifier(FACILITY_DETECT_IMMEDIATE, ubFacilityType, ubAssignmentType); if (usImmediateDetectionRange) { // Loop through all nearby sectors. UINT8 sMapX; UINT8 sMapY; for (sMapX = pSoldier->sSectorX - usImmediateDetectionRange; sMapX <= pSoldier->sSectorX + usImmediateDetectionRange; sMapX++) { for (sMapY = pSoldier->sSectorY - usImmediateDetectionRange; sMapY <= pSoldier->sSectorY + usImmediateDetectionRange; sMapY++) { if (sMapX <= 0 || sMapX >= MAP_WORLD_X - 1 || sMapY <= 0 || sMapY >= MAP_WORLD_Y - 1 ) { // Sector out of bounds. continue; } if (abs(pSoldier->sSectorX - sMapX) <= usImmediateDetectionRange && abs(pSoldier->sSectorY - sMapY) <= usImmediateDetectionRange) { // Sector within range. Update its detection flag. SectorInfo[SECTOR(sMapX, sMapY)].ubDetectionLevel |= 1; } } } } } } ++ubCounter; } // Some flags have been set. Run through all sectors and apply this. for (UINT16 X = 0; X < 256; ++X) { // Reset detection level first // sevenfm: this will disable enemy detection using facility, and detection levels are already zeroed in this function //SectorInfo[X].ubDetectionLevel = 0; if (ubStrategicDetectionLevel & (1<flags.uiStatusFlags & SOLDIER_VEHICLE ) && pSoldier->bSectorZ == 0 && pSoldier->stats.bLife >= OKLIFE && !(pSoldier->flags.fMercAsleep) ) { UINT8 ubSector = SECTOR(pSoldier->sSectorX, pSoldier->sSectorY); UINT8 ubFacilityType = (UINT8)pSoldier->sFacilityTypeOperated; if (GetSoldierFacilityAssignmentIndex( pSoldier ) == -1) { // Skip this soldier, he is not performing a facility assignment. ubCounter++; continue; } UINT8 ubAssignmentType = (UINT8)GetSoldierFacilityAssignmentIndex( pSoldier ); // Is character using a facility? if ( CanCharacterFacility( pSoldier, ubFacilityType, ubAssignmentType ) && GetWorldTotalMin() - pSoldier->uiLastAssignmentChangeMin >= (UINT32)gGameExternalOptions.ubMinutesForAssignmentToCount ) { // Make sure facility type is valid. Assert(ubFacilityType < MAX_NUM_FACILITY_TYPES); if (!(gFacilityLocations[ubSector][ubFacilityType].fFacilityHere)) { // Somehow, the character is working in a nonexistant facility. Odd. Skip him. ubCounter++; continue; } // Does facility change Skyrider Costs at all? gsSkyriderCostModifier += GetFacilityModifier( FACILITY_SKYRIDER_COST_MOD, ubFacilityType, ubAssignmentType ); } } ubCounter++; } } // HEADROCK HAM 3.6: Goes through all characters "STAFFING" a facility, and checks to see whether they alter any // strategic variables. void UpdateGlobalVariablesFromFacilities( void ) { UpdateStrategicDetectionLevel( ); UpdateSkyriderCostModifier( ); UpdateFacilityUsageCosts( ); } // This function runs hourly. It calculates the total cost of all facilities being operated currently, // and adds them to a global "debt" variable that tracks expenditure. At the end of the day, the debt is deducted from // the player's account. void UpdateFacilityUsageCosts( ) { SOLDIERTYPE * pSoldier; UINT8 ubCounter = 0; INT16 sCost = 0; while(gCharactersList[ubCounter].fValid) { pSoldier = gCharactersList[ ubCounter ].usSolID; // Is character truly valid? if( !( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) && pSoldier->bSectorZ == 0 && pSoldier->stats.bLife >= OKLIFE && !(pSoldier->flags.fMercAsleep) ) { UINT8 ubSector = SECTOR(pSoldier->sSectorX, pSoldier->sSectorY); UINT8 ubFacilityType = (UINT8)pSoldier->sFacilityTypeOperated; if (GetSoldierFacilityAssignmentIndex( pSoldier ) == -1) { // Skip this soldier, he is not performing a facility assignment. ubCounter++; continue; } UINT8 ubAssignmentType = (UINT8)GetSoldierFacilityAssignmentIndex( pSoldier ); // Is character using a facility? if ( CanCharacterFacility( pSoldier, ubFacilityType, ubAssignmentType ) && GetWorldTotalMin() - pSoldier->uiLastAssignmentChangeMin >= (UINT32)gGameExternalOptions.ubMinutesForAssignmentToCount ) { // Make sure facility type is valid. Assert(ubFacilityType < MAX_NUM_FACILITY_TYPES); if (!(gFacilityLocations[ubSector][ubFacilityType].fFacilityHere)) { // Somehow, the character is working in a nonexistant facility. Odd. Skip him. ubCounter++; continue; } sCost = gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].sCostPerHour; if (sCost > 0) { ///////////////////////////////////////////////////////// // Increase debt for operating this facility giTotalOwedForFacilityOperationsToday += gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].sCostPerHour; } else if (sCost < 0) { ///////////////////////////////////////////////////////// // Increase income for operating this facility giTotalEarnedForFacilityOperationsToday += (-1 * gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].sCostPerHour); } } } ubCounter++; } } void HandleDailyPaymentFacilityIncome( void ) { // Pay total income. AddTransactionToPlayersBook( FACILITY_OPERATIONS, 0, GetWorldTotalMin(), giTotalEarnedForFacilityOperationsToday ); giTotalEarnedForFacilityOperationsToday = 0; } // HEADROCK HAM 3.6: This function runs once at the end of each day. // It handles debt accrued by operation facilities. If there is enough money to pay the debt off, this // is done automatically. If only part of the money is available, or none of it, your account is emptied and all // "expensive" facility work ends immediately. You also suffer a loyalty hit across Arulco based on how much money // you owe. Facility work cannot continue before the debt is paid off! void HandleDailyPaymentFacilityDebt( void ) { CHAR16 sString[256]; INT32 iToPay = 0; if (LaptopSaveInfo.iCurrentBalance >= giTotalOwedForFacilityOperationsToday) { // Pay total debt. iToPay = giTotalOwedForFacilityOperationsToday; gfOutstandingFacilityDebt = FALSE; } else if (LaptopSaveInfo.iCurrentBalance < giTotalOwedForFacilityOperationsToday) { if (LaptopSaveInfo.iCurrentBalance > 0) { // Still have some money in the bank. Pay it. iToPay = LaptopSaveInfo.iCurrentBalance; swprintf( sString, New113HAMMessage[14], iToPay, giTotalOwedForFacilityOperationsToday - iToPay ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); HandleFacilityDebtLoyaltyHit(); ResetAllExpensiveFacilityAssignments(); gfOutstandingFacilityDebt = TRUE; } else if (LaptopSaveInfo.iCurrentBalance <= 0) { // No money to pay! iToPay = 0; swprintf( sString, New113HAMMessage[15], giTotalOwedForFacilityOperationsToday); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); HandleFacilityDebtLoyaltyHit(); ResetAllExpensiveFacilityAssignments(); gfOutstandingFacilityDebt = TRUE; } } if (iToPay > 0) { AddTransactionToPlayersBook( FACILITY_OPERATIONS, 0, GetWorldTotalMin(), -( iToPay ) ); giTotalOwedForFacilityOperationsToday -= iToPay; } } // HEADROCK HAM 3.6: This functionruns a player-requested payoff of facility debt. It is only possible if the player // has enough money in his account to pay off the ENTIRE debt. The function is called when a player tries to assign // a merc to facility duty while an outstanding debt exists. Debts are only outstanding if they failed to be paid off // during the previous midnight. void HandleManualPaymentFacilityDebt( void ) { INT32 iToPay = 0; // Is there any debt at all? if (giTotalOwedForFacilityOperationsToday > 0) { // This function should ONLY be called if there's enough money in the bank to pay off the entire debt. Assert( LaptopSaveInfo.iCurrentBalance >= giTotalOwedForFacilityOperationsToday ); // Pay total debt. iToPay = giTotalOwedForFacilityOperationsToday; AddTransactionToPlayersBook( FACILITY_OPERATIONS, 0, GetWorldTotalMin(), -( iToPay ) ); giTotalOwedForFacilityOperationsToday = 0; gfOutstandingFacilityDebt = FALSE; } } // HEADROCK HAM 3.6: Apply income bonuses from Facility Work to a specific mine. INT32 MineIncomeModifierFromFacility( UINT8 ubMine ) { Assert (ubMine >= 0 && ubMine < MAX_NUMBER_OF_MINES); SOLDIERTYPE *pSoldier; INT32 iModifier = 0; UINT8 ubCounter = 0; while (gCharactersList[ubCounter].fValid) { pSoldier = gCharactersList[ ubCounter ].usSolID; // Is character truly valid? if( !( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) && pSoldier->bSectorZ == 0 && pSoldier->stats.bLife >= OKLIFE && !(pSoldier->flags.fMercAsleep) ) { UINT8 ubSector = SECTOR(pSoldier->sSectorX, pSoldier->sSectorY); UINT8 ubFacilityType = (UINT8)pSoldier->sFacilityTypeOperated; if (GetSoldierFacilityAssignmentIndex( pSoldier ) == -1) { // Skip this soldier, he is not performing a facility assignment. ubCounter++; continue; } UINT8 ubAssignmentType = (UINT8)GetSoldierFacilityAssignmentIndex( pSoldier ); // Is character using a facility? if ( CanCharacterFacility( pSoldier, ubFacilityType, ubAssignmentType ) && GetWorldTotalMin() - pSoldier->uiLastAssignmentChangeMin >= (UINT32)gGameExternalOptions.ubMinutesForAssignmentToCount ) { if (GetFacilityModifier( FACILITY_MINE_INCOME_MOD, ubFacilityType, ubAssignmentType ) && // Facility adjusts mine income (gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].fOnlyLocalMineAffected == 0 || // All mines affected GetMineIndexForSector( pSoldier->sSectorX, pSoldier->sSectorY ) == ubMine ) ) // This mine is here { iModifier += (gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].usMineIncomeModifier - 100); } } } ubCounter++; } return iModifier; } // This function converts a soldier's current assignment into a single Facility Assignment Type Index. // The Assignment Type Index is used for referencing various data and effects associated with performing a // SPECIFIC assignment with the help of a SPECIFIC facility. It's basically a reference number for the gFacilityTypes // array. INT8 GetSoldierFacilityAssignmentIndex( SOLDIERTYPE *pSoldier ) { // Read soldier's assignment INT8 bAssignment = pSoldier->bAssignment; INT8 bAssignmentIndex = -1; // Return variable. -1 = Non-facility assignment. if (pSoldier->sFacilityTypeOperated == -1) { // Soldier is not set to work at a facility... return (-1); } // switch of ALL possible facility assignments switch (bAssignment) { case FACILITY_DOCTOR: bAssignmentIndex = FAC_DOCTOR; break; case FACILITY_PATIENT: bAssignmentIndex = FAC_PATIENT; break; case FACILITY_REPAIR: // Determine which kind of repair is he performing if ( pSoldier->bVehicleUnderRepairID != -1 ) { bAssignmentIndex = FAC_REPAIR_VEHICLE; } else if ( pSoldier->flags.fFixingRobot ) { bAssignmentIndex = FAC_REPAIR_ROBOT; } else { bAssignmentIndex = FAC_REPAIR_ITEMS; } break; case TRAIN_SELF: switch (pSoldier->bTrainStat) { case STRENGTH: bAssignmentIndex = FAC_PRACTICE_STRENGTH; break; case DEXTERITY: bAssignmentIndex = FAC_PRACTICE_DEXTERITY; break; case AGILITY: bAssignmentIndex = FAC_PRACTICE_AGILITY; break; case HEALTH: bAssignmentIndex = FAC_PRACTICE_HEALTH; break; case MARKSMANSHIP: bAssignmentIndex = FAC_PRACTICE_MARKSMANSHIP; break; case MEDICAL: bAssignmentIndex = FAC_PRACTICE_MEDICAL; break; case MECHANICAL: bAssignmentIndex = FAC_PRACTICE_MECHANICAL; break; case LEADERSHIP: bAssignmentIndex = FAC_PRACTICE_LEADERSHIP; break; case EXPLOSIVE_ASSIGN: bAssignmentIndex = FAC_PRACTICE_EXPLOSIVES; break; default: bAssignmentIndex = -1; break; } break; case TRAIN_TEAMMATE: switch (pSoldier->bTrainStat) { case STRENGTH: bAssignmentIndex = FAC_TRAINER_STRENGTH; break; case DEXTERITY: bAssignmentIndex = FAC_TRAINER_DEXTERITY; break; case AGILITY: bAssignmentIndex = FAC_TRAINER_AGILITY; break; case HEALTH: bAssignmentIndex = FAC_TRAINER_HEALTH; break; case MARKSMANSHIP: bAssignmentIndex = FAC_TRAINER_MARKSMANSHIP; break; case MEDICAL: bAssignmentIndex = FAC_TRAINER_MEDICAL; break; case MECHANICAL: bAssignmentIndex = FAC_TRAINER_MECHANICAL; break; case LEADERSHIP: bAssignmentIndex = FAC_TRAINER_LEADERSHIP; break; case EXPLOSIVE_ASSIGN: bAssignmentIndex = FAC_TRAINER_EXPLOSIVES; break; default: bAssignmentIndex = -1; break; } break; case TRAIN_BY_OTHER: switch (pSoldier->bTrainStat) { case STRENGTH: bAssignmentIndex = FAC_STUDENT_STRENGTH; break; case DEXTERITY: bAssignmentIndex = FAC_STUDENT_DEXTERITY; break; case AGILITY: bAssignmentIndex = FAC_STUDENT_AGILITY; break; case HEALTH: bAssignmentIndex = FAC_STUDENT_HEALTH; break; case MARKSMANSHIP: bAssignmentIndex = FAC_STUDENT_MARKSMANSHIP; break; case MEDICAL: bAssignmentIndex = FAC_STUDENT_MEDICAL; break; case MECHANICAL: bAssignmentIndex = FAC_STUDENT_MECHANICAL; break; case LEADERSHIP: bAssignmentIndex = FAC_STUDENT_LEADERSHIP; break; case EXPLOSIVE_ASSIGN: bAssignmentIndex = FAC_STUDENT_EXPLOSIVES; break; default: bAssignmentIndex = -1; break; } break; case FACILITY_STAFF: bAssignmentIndex = FAC_STAFF; break; case FACILITY_EAT: bAssignmentIndex = FAC_FOOD; break; case FACILITY_REST: bAssignmentIndex = FAC_REST; break; case FACILITY_INTERROGATE_PRISONERS: bAssignmentIndex = FAC_INTERROGATE_PRISONERS; break; case FACILITY_PRISON_SNITCH: bAssignmentIndex = FAC_PRISON_SNITCH; break; case FACILITY_SPREAD_PROPAGANDA: bAssignmentIndex = FAC_SPREAD_PROPAGANDA; break; case FACILITY_SPREAD_PROPAGANDA_GLOBAL: bAssignmentIndex = FAC_SPREAD_PROPAGANDA_GLOBAL; break; case FACILITY_GATHER_RUMOURS: bAssignmentIndex = FAC_GATHER_RUMOURS; break; case FACILITY_STRATEGIC_MILITIA_MOVEMENT: bAssignmentIndex = FAC_STRATEGIC_MILITIA_MOVEMENT; break; default: // Soldier is performing a distinctly NON-FACILITY assignment. bAssignmentIndex = -1; break; } return (bAssignmentIndex); } // This function runs a specific kind of RISK associated with a facility. The character's stats may affect the result. INT16 FacilityRiskResult( SOLDIERTYPE *pSoldier, UINT8 ubRiskType, UINT8 ubFacilityType, UINT8 ubAssignmentType ) { INT16 Result = 0; INT32 iChance; INT16 bBaseEffect; UINT16 ubRange; // Read risk data directly from facility array. iChance = gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].Risk[ubRiskType].usChance; bBaseEffect = gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].Risk[ubRiskType].bBaseEffect; ubRange = gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].Risk[ubRiskType].ubRange; // For now, only these variables can effect risk outcome. In the future there may be more effects, like traits // and personality, or even sex. INT16 ubStrength = EffectiveStrength( pSoldier, FALSE ); INT16 ubAgility = EffectiveAgility( pSoldier, FALSE ); INT16 ubDexterity = EffectiveDexterity( pSoldier, FALSE ); UINT8 ubHealth = __min(pSoldier->stats.bLife, pSoldier->stats.bLifeMax); INT16 ubWisdom = EffectiveWisdom( pSoldier ); UINT8 ubLeadership = EffectiveLeadership( pSoldier ); UINT8 ubExplosives = EffectiveExplosive( pSoldier ); UINT8 ubExpLevel = EffectiveExpLevel( pSoldier, FALSE); UINT8 ubLocalLoyalty = 0; if (GetTownIdForSector( pSoldier->sSectorX, pSoldier->sSectorY ) != BLANK_SECTOR ) { ubLocalLoyalty = gTownLoyalty[ GetTownIdForSector( pSoldier->sSectorX, pSoldier->sSectorY ) ].ubRating; } else { ubLocalLoyalty = 0; } /////////////// INT16 sAbsoluteMaxResult; INT16 sAbsoluteMinResult; // Calculate absolute maximum variance. The result can never be outside these. if (bBaseEffect < 0) { sAbsoluteMaxResult = __min((bBaseEffect + ubRange), -1); sAbsoluteMinResult = bBaseEffect - ubRange; } else if (bBaseEffect > 0) { sAbsoluteMaxResult = bBaseEffect + ubRange; sAbsoluteMinResult = __max(1, (bBaseEffect - ubRange)); } else { sAbsoluteMaxResult = bBaseEffect + ubRange; sAbsoluteMinResult = bBaseEffect - ubRange; } INT16 bCombinedStats; // Begin calculating the effect of skills on increasing/reducing final result. Use a mix of stats to reach a range // of 0 to 100. switch (ubRiskType) { case RISK_STRENGTH: bCombinedStats = (INT16)__min(100, (ubWisdom * 0.2) + (ubAgility * 0.3) + (ubExpLevel * 5)); break; case RISK_DEXTERITY: bCombinedStats = (INT16)__min(100, (ubWisdom * 0.4) + (ubExpLevel * 6)); break; case RISK_AGILITY: bCombinedStats = (INT16)__min(100, (ubWisdom * 0.2) + (ubExpLevel * 8)); break; case RISK_HEALTH: bCombinedStats = (INT16)__min(100, (ubWisdom * 0.5) + (ubExpLevel * 5)); break; case RISK_WISDOM: bCombinedStats = (INT16)__min(100, (ubWisdom * 0.6) + (ubExpLevel * 4)); break; case RISK_MARKSMANSHIP: bCombinedStats = (INT16)__min(100, (ubWisdom * 0.2) + (ubAgility * 0.4) + (ubExpLevel * 4)); break; case RISK_MEDICAL: bCombinedStats = (INT16)__min(100, (ubWisdom * 0.3) + (ubDexterity * 0.4) + (ubExpLevel * 3)); break; case RISK_MECHANICAL: bCombinedStats = (INT16)__min(100, (ubWisdom * 0.3) + (ubDexterity * 0.5) + (ubExpLevel * 2)); break; case RISK_LEADERSHIP: bCombinedStats = (INT16)__min(100, (ubWisdom * 0.4) + (ubLeadership * 0.3) + (ubExpLevel * 3)); break; case RISK_EXPLOSIVES: bCombinedStats = (INT16)__min(100, (ubWisdom * 0.2) + (ubAgility * 0.3) + (ubDexterity * 0.3) + (ubExpLevel * 2)); break; case RISK_INJURY: bCombinedStats = (INT16)__min(100, (ubWisdom * 0.2) + (ubDexterity * 0.2) + (ubAgility * 0.4) + (ubExpLevel * 2)); break; case RISK_MORALE: bCombinedStats = (INT16)__min(100, (ubWisdom * 0.2) + (ubLeadership * 0.4) + (ubExpLevel * 4)); break; case RISK_FATIGUE: bCombinedStats = (INT16)__min(100, (ubAgility * 0.1) + (ubStrength * 0.3) + (ubHealth * 0.2) + (ubExpLevel * 4)); break; case RISK_DRUNK: bCombinedStats = (INT16)__min(100, (ubWisdom * 0.5) + (ubHealth * 0.3) + (ubExpLevel * 2)); break; case RISK_LOYALTY_LOCAL: bCombinedStats = (INT16)__min(100, (ubWisdom * 0.2) + (ubLeadership * 0.3) + (ubStrength * 0.1) + (ubExpLevel * 2) + (ubLocalLoyalty * 0.2) ); break; case RISK_LOYALTY_GLOBAL: bCombinedStats = (INT16)__min(100, (ubWisdom * 0.3) + (ubLeadership * 0.3) + (ubExpLevel * 2) + (ubLocalLoyalty * 0.2)); break; } if (bCombinedStats >= 100) { // Character is as skilled as possible. Automatic "best" result. return (sAbsoluteMaxResult); } if (bCombinedStats <= 0) { // Character is completely inept? Strange. Automatic "worst" result. return (sAbsoluteMinResult); } // we now have a range of 0 to 100. Let's apply an externalized modifier of 0 to -100. bCombinedStats -= gGameExternalOptions.ubFacilityDangerRate; // The chance of triggering an event is also based on stats. if (bBaseEffect > 0 ) { // Chance increases with good stats, decreased with bad stats. iChance = iChance + ((bCombinedStats * iChance) / 100 ); } else if (bBaseEffect < 0 ) { // Chance decreases with good stats, increases with bad stats. iChance = iChance - ((bCombinedStats * iChance) / 100 ); } else { // Chance is unaffected } // Limit iChance into a 0 to 50000 range. iChance = __max(0, __min(iChance, 50000)); // Alright, let's see if the event is triggered. if (PreRandom(gGameExternalOptions.usFacilityEventRarity)>(UINT32)iChance) { // Chance failed to trigger. return (0); } else { // Let's find out how bad/good the result is. // By now, the Combined Stats variable is anywhere between -100 and +100. // We use this to move the Base Effect point from its original location, effectively giving us a better // or worse result based on our stats. bBaseEffect += (bCombinedStats * (ubRange+1)) / 100; bBaseEffect = __min(bBaseEffect, sAbsoluteMaxResult); bBaseEffect = __max(bBaseEffect, sAbsoluteMinResult); // The deviation is also affected by our stats. ubRange -= abs((bCombinedStats * (ubRange+1)) / 100); // If the Base Effect has moved any, then the range shrinks accordingly. // Effectively what happens is that skilled characters will get better results more consistently, and avoid // bad results consistently as well. // The opposite is also true - characters with insufficient skills tend to get worse results consistently, // and rarely (if ever) get the best results possible. //The final result is: Result = bBaseEffect + ((PreRandom((ubRange*2)+1)) - ubRange); // Naturally, the character can't ever breach the range of possible results as defined by the facility data. Result = __min(Result, sAbsoluteMaxResult); Result = __max(Result, sAbsoluteMinResult); return (Result); } } void HandleHourlyRisks() { INT32 iCounter = 0; SOLDIERTYPE *pSoldier; while (gCharactersList[iCounter].fValid) { pSoldier = gCharactersList[ iCounter ].usSolID; // Is character truly valid? if( !( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) && pSoldier->bSectorZ == 0 && pSoldier->stats.bLife >= OKLIFE && !(pSoldier->flags.fMercAsleep) ) { // Run this character's risks. HandleRisksForSoldier( pSoldier ); } iCounter++; } } void HandleRisksForSoldier( SOLDIERTYPE *pSoldier ) { INT32 iCounter = 0; INT32 iCounterB = 0; UINT8 ubFacilityType = 0; UINT8 ubAssignmentType = 0; if (pSoldier->sFacilityTypeOperated != -1 && GetSoldierFacilityAssignmentIndex( pSoldier ) != -1) { // Soldier is working at a specific facility. Risks associated with this specific facility and assignment // have priority. ubAssignmentType = (UINT8)GetSoldierFacilityAssignmentIndex( pSoldier ); ubFacilityType = (UINT8)pSoldier->sFacilityTypeOperated; HandleRisksForSoldierFacilityAssignment( pSoldier, ubFacilityType, ubAssignmentType ); } // Test for triggers related to any AMBIENT facility in the same sector. for (iCounterB; iCounterB < NUM_FACILITY_TYPES; iCounterB++) { if (gFacilityLocations[SECTOR(pSoldier->sSectorX, pSoldier->sSectorY)][iCounterB].fFacilityHere) { // Test this facility for ambient effects. HandleRisksForSoldierFacilityAssignment( pSoldier, iCounterB, FAC_AMBIENT ); } } } void HandleRisksForSoldierFacilityAssignment( SOLDIERTYPE *pSoldier, UINT8 ubFacilityType, UINT8 ubAssignmentType ) { INT32 iCounter = 0; INT32 Result = 0; INT32 iNewValue = 0; CHAR16 sString[256]; CHAR16 szSectorGrid[10]; SGPRect pCenteringRect= {0, 0, 640, INV_INTERFACE_START_Y }; UINT8 ubTownID = 0; BOOLEAN fOperatingFacility = (GetSoldierFacilityAssignmentIndex( pSoldier ) == -1) ? FALSE : TRUE; BOOLEAN fBadResult = FALSE; UINT16 usColor = FONT_MCOLOR_LTRED; for (iCounter; iCounter < NUM_RISKS; iCounter++) { fBadResult = FALSE; if (gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].Risk[iCounter].usChance > 0) { if (iCounter == RISK_LOYALTY_LOCAL) { ubTownID = GetTownIdForSector( pSoldier->sSectorX, pSoldier->sSectorY ); if (ubTownID == BLANK_SECTOR) { // No town here, so this risk doesn't have any effect anyway... continue; } } Result = FacilityRiskResult( pSoldier, iCounter, ubFacilityType, ubAssignmentType); if (Result != 0) { switch (iCounter) { case RISK_STRENGTH: if (Result > 0) { // Soldier doesn't gain stat points directly, instead he gets a number of chances // equal to Result*SubPointsPerLevel. Optimally this leads to gaining a number of // levels equal to result, but this isn't always the case. Result *= gGameExternalOptions.usStrengthSubpointsToImprove; StatChange( pSoldier, STRAMT, Result, TRUE ); } else { // Soldier is suffering direct stat damage. /////////////////////////////////////////////////////////////////////////////////////////// // SANDRO - if our stat is damaged through facility event, make it healable if ( (-1*Result) > (pSoldier->stats.bStrength - 1)) Result = -1*(pSoldier->stats.bStrength - 1); pSoldier->stats.bStrength += Result; if ( gGameOptions.fNewTraitSystem && (Result <= 0) ) { pSoldier->ubCriticalStatDamage[ DAMAGED_STAT_STRENGTH ] -= Result; } else { // make this stat RED for a while... pSoldier->timeChanges.uiChangeStrengthTime = GetJA2Clock(); pSoldier->usValueGoneUp &= ~( STRENGTH_INCREASE ); } // Update Profile gMercProfiles[ pSoldier->ubProfile ].bStrength = pSoldier->stats.bStrength; // merc records - stat damaged if( Result < 0 ) gMercProfiles[ pSoldier->ubProfile ].records.usTimesStatDamaged++; gMercProfiles[ pSoldier->ubProfile ].records.usFacilityAccidents++; /////////////////////////////////////////////////////////////////////////////////////////// fBadResult = TRUE; // Log message BuildStatChangeString( sString, pSoldier->GetName(), FALSE, Result, STRAMT ); ScreenMsg( usColor, MSG_INTERFACE, sString ); // Do Screen Message and stop time. GetShortSectorString( pSoldier->sSectorX, pSoldier->sSectorY, szSectorGrid ); if (!fOperatingFacility) swprintf( sString, gzFacilityErrorMessage[17], pSoldier->GetName(), gzFacilityRiskResultStrings[0], szSectorGrid ); else swprintf( sString, gzFacilityErrorMessage[18], pSoldier->GetName(), gzFacilityRiskResultStrings[0], gFacilityTypes[ubFacilityType].szFacilityName, szSectorGrid ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); StopTimeCompression(); } break; case RISK_AGILITY: if (Result > 0) { // Soldier doesn't gain stat points directly, instead he gets a number of chances // equal to Result*SubPointsPerLevel. Optimally this leads to gaining a number of // levels equal to result, but this isn't always the case. Result *= gGameExternalOptions.usAgilitySubpointsToImprove; StatChange( pSoldier, AGILAMT, Result, TRUE ); } else { // Soldier is suffering direct stat damage. /////////////////////////////////////////////////////////////////////////////////////////// // SANDRO - if our stat is damaged through facility event, make it healable if ( (-1*Result) > (pSoldier->stats.bAgility - 1)) Result = -1*(pSoldier->stats.bAgility - 1); pSoldier->stats.bAgility += Result; if ( gGameOptions.fNewTraitSystem && (Result <= 0) ) { pSoldier->ubCriticalStatDamage[ DAMAGED_STAT_AGILITY ] -= Result; } else { // make this stat RED for a while... pSoldier->timeChanges.uiChangeAgilityTime = GetJA2Clock(); pSoldier->usValueGoneUp &= ~( AGIL_INCREASE ); } // Update Profile gMercProfiles[ pSoldier->ubProfile ].bAgility = pSoldier->stats.bAgility; // merc records - stat damaged if( Result < 0 ) gMercProfiles[ pSoldier->ubProfile ].records.usTimesStatDamaged++; gMercProfiles[ pSoldier->ubProfile ].records.usFacilityAccidents++; /////////////////////////////////////////////////////////////////////////////////////////// fBadResult = TRUE; // Log message BuildStatChangeString( sString, pSoldier->GetName(), FALSE, Result, AGILAMT ); ScreenMsg( usColor, MSG_INTERFACE, sString ); // Do Screen Message and stop time. GetShortSectorString( pSoldier->sSectorX, pSoldier->sSectorY, szSectorGrid ); if (!fOperatingFacility) swprintf( sString, gzFacilityErrorMessage[17], pSoldier->GetName(), gzFacilityRiskResultStrings[1], szSectorGrid ); else swprintf( sString, gzFacilityErrorMessage[18], pSoldier->GetName(), gzFacilityRiskResultStrings[1], gFacilityTypes[ubFacilityType].szFacilityName, szSectorGrid ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); StopTimeCompression(); } break; case RISK_DEXTERITY: if (Result > 0) { // Soldier doesn't gain stat points directly, instead he gets a number of chances // equal to Result*SubPointsPerLevel. Optimally this leads to gaining a number of // levels equal to result, but this isn't always the case. Result *= gGameExternalOptions.usDexteritySubpointsToImprove; StatChange( pSoldier, DEXTAMT, Result, TRUE ); } else { // Soldier is suffering direct stat damage. /////////////////////////////////////////////////////////////////////////////////////////// // SANDRO - if our stat is damaged through facility event, make it healable if ( (-1*Result) > (pSoldier->stats.bDexterity - 1)) Result = -1*(pSoldier->stats.bDexterity - 1); pSoldier->stats.bDexterity += Result; if ( gGameOptions.fNewTraitSystem && (Result <= 0) ) { pSoldier->ubCriticalStatDamage[ DAMAGED_STAT_DEXTERITY ] -= Result; } else { // make this stat RED for a while... pSoldier->timeChanges.uiChangeDexterityTime = GetJA2Clock(); pSoldier->usValueGoneUp &= ~( DEX_INCREASE ); } // Update Profile gMercProfiles[ pSoldier->ubProfile ].bDexterity = pSoldier->stats.bDexterity; // merc records - stat damaged if( Result < 0 ) gMercProfiles[ pSoldier->ubProfile ].records.usTimesStatDamaged++; gMercProfiles[ pSoldier->ubProfile ].records.usFacilityAccidents++; /////////////////////////////////////////////////////////////////////////////////////////// fBadResult = TRUE; // Log message BuildStatChangeString( sString, pSoldier->name, FALSE, Result, DEXTAMT ); ScreenMsg( usColor, MSG_INTERFACE, sString ); // Do Screen Message and stop time. GetShortSectorString( pSoldier->sSectorX, pSoldier->sSectorY, szSectorGrid ); if (!fOperatingFacility) swprintf( sString, gzFacilityErrorMessage[17], pSoldier->GetName(), gzFacilityRiskResultStrings[2], szSectorGrid ); else swprintf( sString, gzFacilityErrorMessage[18], pSoldier->GetName(), gzFacilityRiskResultStrings[2], gFacilityTypes[ubFacilityType].szFacilityName, szSectorGrid ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); StopTimeCompression(); } break; case RISK_WISDOM: if (Result > 0) { // Soldier doesn't gain stat points directly, instead he gets a number of chances // equal to Result*SubPointsPerLevel. Optimally this leads to gaining a number of // levels equal to result, but this isn't always the case. Result *= gGameExternalOptions.usWisdomSubpointsToImprove; StatChange( pSoldier, WISDOMAMT, Result, TRUE ); } else { // Soldier is suffering direct stat damage. /////////////////////////////////////////////////////////////////////////////////////////// // SANDRO - if our stat is damaged through facility event, make it healable if ( (-1*Result) > (pSoldier->stats.bWisdom - 1)) Result = -1*(pSoldier->stats.bWisdom - 1); pSoldier->stats.bWisdom += Result; if ( gGameOptions.fNewTraitSystem && (Result <= 0) ) { pSoldier->ubCriticalStatDamage[ DAMAGED_STAT_WISDOM ] -= Result; } else { // make this stat RED for a while... pSoldier->timeChanges.uiChangeWisdomTime = GetJA2Clock(); pSoldier->usValueGoneUp &= ~( WIS_INCREASE ); } // Update Profile gMercProfiles[ pSoldier->ubProfile ].bWisdom = pSoldier->stats.bWisdom; // merc records - stat damaged if( Result < 0 ) gMercProfiles[ pSoldier->ubProfile ].records.usTimesStatDamaged++; gMercProfiles[ pSoldier->ubProfile ].records.usFacilityAccidents++; /////////////////////////////////////////////////////////////////////////////////////////// fBadResult = TRUE; // Log message BuildStatChangeString( sString, pSoldier->GetName(), FALSE, Result, WISDOMAMT ); ScreenMsg( usColor, MSG_INTERFACE, sString ); // Do Screen Message and stop time. GetShortSectorString( pSoldier->sSectorX, pSoldier->sSectorY, szSectorGrid ); if (!fOperatingFacility) swprintf( sString, gzFacilityErrorMessage[17], pSoldier->GetName(), gzFacilityRiskResultStrings[3], szSectorGrid ); else swprintf( sString, gzFacilityErrorMessage[18], pSoldier->GetName(), gzFacilityRiskResultStrings[3], gFacilityTypes[ubFacilityType].szFacilityName, szSectorGrid ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); StopTimeCompression(); } break; case RISK_HEALTH: if (Result > 0) { // Soldier doesn't gain stat points directly, instead he gets a number of chances // equal to Result*SubPointsPerLevel. Optimally this leads to gaining a number of // levels equal to result, but this isn't always the case. Result *= gGameExternalOptions.usHealthSubpointsToImprove; StatChange( pSoldier, HEALTHAMT, Result, TRUE ); } else { // Soldier is suffering direct stat damage. /////////////////////////////////////////////////////////////////////////////////////////// // SANDRO - if our stat is damaged through facility event, make it healable if ( (-1*Result) > (pSoldier->stats.bLifeMax - 1)) Result = -1*(pSoldier->stats.bLifeMax - 1); pSoldier->stats.bLifeMax += Result; pSoldier->stats.bLife += Result; if ( gGameOptions.fNewTraitSystem && (Result <= 0) ) { pSoldier->ubCriticalStatDamage[ DAMAGED_STAT_HEALTH ] -= Result; } else { // make this stat RED for a while... pSoldier->timeChanges.uiChangeHealthTime = GetJA2Clock(); pSoldier->usValueGoneUp &= ~( HEALTH_INCREASE ); } // Update Profile gMercProfiles[ pSoldier->ubProfile ].bLifeMax = pSoldier->stats.bLifeMax; // merc records - stat damaged if( Result < 0 ) gMercProfiles[ pSoldier->ubProfile ].records.usTimesStatDamaged++; gMercProfiles[ pSoldier->ubProfile ].records.usFacilityAccidents++; /////////////////////////////////////////////////////////////////////////////////////////// if (pSoldier->stats.bLife < OKLIFE) { HandleTakeDamageDeath( pSoldier, 0, TAKE_DAMAGE_BLOODLOSS ); } fBadResult = TRUE; // Log message BuildStatChangeString( sString, pSoldier->GetName(), FALSE, Result, HEALTHAMT ); ScreenMsg( usColor, MSG_INTERFACE, sString ); if (pSoldier->stats.bLife >= OKLIFE) { // Do Screen Message and stop time. GetShortSectorString( pSoldier->sSectorX, pSoldier->sSectorY, szSectorGrid ); if (!fOperatingFacility) swprintf( sString, gzFacilityErrorMessage[17], pSoldier->GetName(), gzFacilityRiskResultStrings[4], szSectorGrid ); else swprintf( sString, gzFacilityErrorMessage[18], pSoldier->GetName(), gzFacilityRiskResultStrings[4], gFacilityTypes[ubFacilityType].szFacilityName, szSectorGrid ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); StopTimeCompression(); } else if(pSoldier->stats.bLife < OKLIFE && pSoldier->stats.bLife > 0) { // Soldier's health is driven very low. Automatically removed from duty. // Do Screen Message and stop time. GetShortSectorString( pSoldier->sSectorX, pSoldier->sSectorY, szSectorGrid ); if (!fOperatingFacility) swprintf( sString, gzFacilityErrorMessage[28], pSoldier->GetName(), szSectorGrid ); else swprintf( sString, gzFacilityErrorMessage[29], pSoldier->GetName(), gFacilityTypes[ubFacilityType].szFacilityName, szSectorGrid ); AddCharacterToAnySquad( pSoldier ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); StopTimeCompression(); } } break; case RISK_MARKSMANSHIP: if (Result > 0) { // Soldier doesn't gain stat points directly, instead he gets a number of chances // equal to Result*SubPointsPerLevel. Optimally this leads to gaining a number of // levels equal to result, but this isn't always the case. Result *= gGameExternalOptions.usMarksmanshipSubpointsToImprove; StatChange( pSoldier, MARKAMT, Result, TRUE ); } else { // Soldier is suffering direct stat damage. /////////////////////////////////////////////////////////////////////////////////////////// // SANDRO - if our stat is damaged through facility event, make it healable if ( (-1*Result) > (pSoldier->stats.bMarksmanship - 1)) Result = -1*(pSoldier->stats.bMarksmanship - 1); pSoldier->stats.bMarksmanship += Result; if ( gGameOptions.fNewTraitSystem && (Result <= 0) ) { pSoldier->ubCriticalStatDamage[ DAMAGED_STAT_MARKSMANSHIP ] -= Result; } else { // make this stat RED for a while... pSoldier->timeChanges.uiChangeMarksmanshipTime = GetJA2Clock(); pSoldier->usValueGoneUp &= ~( MRK_INCREASE ); } // Update Profile gMercProfiles[ pSoldier->ubProfile ].bMarksmanship = pSoldier->stats.bMarksmanship; // merc records - stat damaged if( Result < 0 ) gMercProfiles[ pSoldier->ubProfile ].records.usTimesStatDamaged++; gMercProfiles[ pSoldier->ubProfile ].records.usFacilityAccidents++; /////////////////////////////////////////////////////////////////////////////////////////// fBadResult = TRUE; // Log message BuildStatChangeString( sString, pSoldier->GetName(), FALSE, Result, MARKAMT ); ScreenMsg( usColor, MSG_INTERFACE, sString ); // Do Screen Message and stop time. GetShortSectorString( pSoldier->sSectorX, pSoldier->sSectorY, szSectorGrid ); if (!fOperatingFacility) swprintf( sString, gzFacilityErrorMessage[17], pSoldier->GetName(), gzFacilityRiskResultStrings[5], szSectorGrid ); else swprintf( sString, gzFacilityErrorMessage[18], pSoldier->GetName(), gzFacilityRiskResultStrings[5], gFacilityTypes[ubFacilityType].szFacilityName, szSectorGrid ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); StopTimeCompression(); } break; case RISK_LEADERSHIP: if (Result > 0) { // Soldier doesn't gain stat points directly, instead he gets a number of chances // equal to Result*SubPointsPerLevel. Optimally this leads to gaining a number of // levels equal to result, but this isn't always the case. Result *= gGameExternalOptions.usLeadershipSubpointsToImprove; StatChange( pSoldier, LDRAMT, Result, TRUE ); } else { // Soldier is suffering direct stat damage. /////////////////////////////////////////////////////////////////////////////////////////// // SANDRO - if our stat is damaged through facility event, make it healable if ( (-1*Result) > (pSoldier->stats.bLeadership - 1)) Result = -1*(pSoldier->stats.bLeadership - 1); pSoldier->stats.bLeadership += Result; if ( gGameOptions.fNewTraitSystem && (Result <= 0) ) { pSoldier->ubCriticalStatDamage[ DAMAGED_STAT_LEADERSHIP ] -= Result; } else { // make this stat RED for a while... pSoldier->timeChanges.uiChangeLeadershipTime = GetJA2Clock(); pSoldier->usValueGoneUp &= ~( LDR_INCREASE ); } // Update Profile gMercProfiles[ pSoldier->ubProfile ].bLeadership = pSoldier->stats.bLeadership; // merc records - stat damaged if( Result < 0 ) gMercProfiles[ pSoldier->ubProfile ].records.usTimesStatDamaged++; gMercProfiles[ pSoldier->ubProfile ].records.usFacilityAccidents++; /////////////////////////////////////////////////////////////////////////////////////////// fBadResult = TRUE; // Log message BuildStatChangeString( sString, pSoldier->GetName(), FALSE, Result, LDRAMT ); ScreenMsg( usColor, MSG_INTERFACE, sString ); // Do Screen Message and stop time. GetShortSectorString( pSoldier->sSectorX, pSoldier->sSectorY, szSectorGrid ); if (!fOperatingFacility) swprintf( sString, gzFacilityErrorMessage[17], pSoldier->GetName(), gzFacilityRiskResultStrings[6], szSectorGrid ); else swprintf( sString, gzFacilityErrorMessage[18], pSoldier->GetName(), gzFacilityRiskResultStrings[6], gFacilityTypes[ubFacilityType].szFacilityName, szSectorGrid ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); StopTimeCompression(); } break; case RISK_MECHANICAL: if (Result > 0) { // Soldier doesn't gain stat points directly, instead he gets a number of chances // equal to Result*SubPointsPerLevel. Optimally this leads to gaining a number of // levels equal to result, but this isn't always the case. Result *= gGameExternalOptions.usMechanicalSubpointsToImprove; StatChange( pSoldier, MECHANAMT, Result, TRUE ); } else { // Soldier is suffering direct stat damage. /////////////////////////////////////////////////////////////////////////////////////////// // SANDRO - if our stat is damaged through facility event, make it healable if ( (-1*Result) > (pSoldier->stats.bMechanical - 1)) Result = -1*(pSoldier->stats.bMechanical - 1); pSoldier->stats.bMechanical += Result; if ( gGameOptions.fNewTraitSystem && (Result <= 0) ) { pSoldier->ubCriticalStatDamage[ DAMAGED_STAT_MECHANICAL ] -= Result; } else { // make this stat RED for a while... pSoldier->timeChanges.uiChangeMechanicalTime = GetJA2Clock(); pSoldier->usValueGoneUp &= ~( MECH_INCREASE ); } // Update Profile gMercProfiles[ pSoldier->ubProfile ].bMechanical = pSoldier->stats.bMechanical; // merc records - stat damaged if( Result < 0 ) gMercProfiles[ pSoldier->ubProfile ].records.usTimesStatDamaged++; gMercProfiles[ pSoldier->ubProfile ].records.usFacilityAccidents++; /////////////////////////////////////////////////////////////////////////////////////////// fBadResult = TRUE; // Log message BuildStatChangeString( sString, pSoldier->GetName(), FALSE, Result, MECHANAMT ); ScreenMsg( usColor, MSG_INTERFACE, sString ); // Do Screen Message and stop time. GetShortSectorString( pSoldier->sSectorX, pSoldier->sSectorY, szSectorGrid ); if (!fOperatingFacility) swprintf( sString, gzFacilityErrorMessage[17], pSoldier->GetName(), gzFacilityRiskResultStrings[7], szSectorGrid ); else swprintf( sString, gzFacilityErrorMessage[18], pSoldier->GetName(), gzFacilityRiskResultStrings[7], gFacilityTypes[ubFacilityType].szFacilityName, szSectorGrid ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); StopTimeCompression(); } break; case RISK_MEDICAL: if (Result > 0) { // Soldier doesn't gain stat points directly, instead he gets a number of chances // equal to Result*SubPointsPerLevel. Optimally this leads to gaining a number of // levels equal to result, but this isn't always the case. Result *= gGameExternalOptions.usMedicalSubpointsToImprove; StatChange( pSoldier, MEDICALAMT, Result, TRUE ); } else { // Soldier is suffering direct stat damage. /////////////////////////////////////////////////////////////////////////////////////////// // SANDRO - if our stat is damaged through facility event, make it healable if ( (-1*Result) > (pSoldier->stats.bMedical - 1)) Result = -1*(pSoldier->stats.bMedical - 1); pSoldier->stats.bMedical += Result; if ( gGameOptions.fNewTraitSystem && (Result <= 0) ) { pSoldier->ubCriticalStatDamage[ DAMAGED_STAT_MEDICAL ] -= Result; } else { // make this stat RED for a while... pSoldier->timeChanges.uiChangeMedicalTime = GetJA2Clock(); pSoldier->usValueGoneUp &= ~( MED_INCREASE ); } // Update Profile gMercProfiles[ pSoldier->ubProfile ].bMedical = pSoldier->stats.bMedical; // merc records - stat damaged if( Result < 0 ) gMercProfiles[ pSoldier->ubProfile ].records.usTimesStatDamaged++; gMercProfiles[ pSoldier->ubProfile ].records.usFacilityAccidents++; /////////////////////////////////////////////////////////////////////////////////////////// fBadResult = TRUE; // Log message BuildStatChangeString( sString, pSoldier->GetName(), FALSE, Result, MEDICALAMT ); ScreenMsg( usColor, MSG_INTERFACE, sString ); // Do Screen Message and stop time. GetShortSectorString( pSoldier->sSectorX, pSoldier->sSectorY, szSectorGrid ); if (!fOperatingFacility) swprintf( sString, gzFacilityErrorMessage[17], pSoldier->GetName(), gzFacilityRiskResultStrings[8], szSectorGrid ); else swprintf( sString, gzFacilityErrorMessage[18], pSoldier->GetName(), gzFacilityRiskResultStrings[8], gFacilityTypes[ubFacilityType].szFacilityName, szSectorGrid ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); StopTimeCompression(); } break; case RISK_EXPLOSIVES: if (Result > 0) { // Soldier doesn't gain stat points directly, instead he gets a number of chances // equal to Result*SubPointsPerLevel. Optimally this leads to gaining a number of // levels equal to result, but this isn't always the case. Result *= gGameExternalOptions.usExplosivesSubpointsToImprove; StatChange( pSoldier, EXPLODEAMT, Result, TRUE ); } else { // Soldier is suffering direct stat damage. /////////////////////////////////////////////////////////////////////////////////////////// // SANDRO - if our stat is damaged through facility event, make it healable if ( (-1*Result) > (pSoldier->stats.bExplosive - 1)) Result = -1*(pSoldier->stats.bExplosive - 1); pSoldier->stats.bExplosive += Result; if ( gGameOptions.fNewTraitSystem && (Result <= 0) ) { pSoldier->ubCriticalStatDamage[ DAMAGED_STAT_EXPLOSIVES ] -= Result; } else { // make this stat RED for a while... pSoldier->timeChanges.uiChangeExplosivesTime = GetJA2Clock(); pSoldier->usValueGoneUp &= ~( EXP_INCREASE ); } // Update Profile gMercProfiles[ pSoldier->ubProfile ].bExplosive = pSoldier->stats.bExplosive; // merc records - stat damaged if( Result < 0 ) gMercProfiles[ pSoldier->ubProfile ].records.usTimesStatDamaged++; gMercProfiles[ pSoldier->ubProfile ].records.usFacilityAccidents++; /////////////////////////////////////////////////////////////////////////////////////////// fBadResult = TRUE; // Log message BuildStatChangeString( sString, pSoldier->GetName(), FALSE, Result, EXPLODEAMT ); ScreenMsg( usColor, MSG_INTERFACE, sString ); // Do Screen Message and stop time. GetShortSectorString( pSoldier->sSectorX, pSoldier->sSectorY, szSectorGrid ); if (!fOperatingFacility) swprintf( sString, gzFacilityErrorMessage[17], pSoldier->GetName(), gzFacilityRiskResultStrings[9], szSectorGrid ); else swprintf( sString, gzFacilityErrorMessage[18], pSoldier->GetName(), gzFacilityRiskResultStrings[9], gFacilityTypes[ubFacilityType].szFacilityName, szSectorGrid ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); StopTimeCompression(); } break; case RISK_INJURY: // This can only be a negative effect... for now. if (Result < 0) { // Soldier is suffering direct unbandaged injury. pSoldier->SoldierTakeDamage( 0, abs(Result), abs(Result), TAKE_DAMAGE_BLOODLOSS, NOBODY, NOWHERE, 0, FALSE ); fBadResult = TRUE; // SANDRO - add to merc records - facility accidents counter gMercProfiles[ pSoldier->ubProfile ].records.usFacilityAccidents++; if (pSoldier->stats.bLife > 0) { if (pSoldier->bBleeding > MIN_BLEEDING_THRESHOLD) { // Log message GetShortSectorString( pSoldier->sSectorX, pSoldier->sSectorY, szSectorGrid ); swprintf( sString, gzFacilityErrorMessage[31], pSoldier->GetName(), szSectorGrid ); ScreenMsg( usColor, MSG_INTERFACE, sString ); // Do Screen Message, stop time, and take character off duty immediately. GetShortSectorString( pSoldier->sSectorX, pSoldier->sSectorY, szSectorGrid ); if (!fOperatingFacility) swprintf( sString, gzFacilityErrorMessage[19], pSoldier->GetName(), szSectorGrid ); else swprintf( sString, gzFacilityErrorMessage[20], pSoldier->GetName(), gFacilityTypes[ubFacilityType].szFacilityName, szSectorGrid ); AddCharacterToAnySquad( pSoldier ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); StopTimeCompression(); } else { // Log message GetShortSectorString( pSoldier->sSectorX, pSoldier->sSectorY, szSectorGrid ); swprintf( sString, gzFacilityErrorMessage[30], pSoldier->GetName(), szSectorGrid ); ScreenMsg( usColor, MSG_INTERFACE, sString ); // Soldier isn't bleeding too bad. Let the player know, but don't take any action. // Do Screen Message and stop time. GetShortSectorString( pSoldier->sSectorX, pSoldier->sSectorY, szSectorGrid ); if (!fOperatingFacility) swprintf( sString, gzFacilityErrorMessage[21], pSoldier->GetName(), szSectorGrid ); else swprintf( sString, gzFacilityErrorMessage[22], pSoldier->GetName(), gFacilityTypes[ubFacilityType].szFacilityName, szSectorGrid ); DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL ); StopTimeCompression(); } } } break; case RISK_MORALE: pSoldier->aiData.bMorale = __min(100,__max(0, pSoldier->aiData.bMorale + Result)); pSoldier->aiData.bStrategicMoraleMod = __min(50,__max(-50, pSoldier->aiData.bStrategicMoraleMod + Result)); RefreshSoldierMorale( pSoldier ); // SANDRO - add to merc records - facility accidents counter if ( Result < 0 ) gMercProfiles[ pSoldier->ubProfile ].records.usFacilityAccidents++; break; case RISK_FATIGUE: pSoldier->bBreathMax = __min(100,__max(0, pSoldier->bBreathMax + Result)); pSoldier->bBreath = pSoldier->bBreathMax; // SANDRO - add to merc records - facility accidents counter if ( Result < 0 ) gMercProfiles[ pSoldier->ubProfile ].records.usFacilityAccidents++; break; case RISK_DRUNK: // Has soldier just drunk enough to make him drunk off his head? if (GetDrunkLevel(pSoldier) == BORDERLINE) { // Log message swprintf( sString, gzFacilityErrorMessage[27], pSoldier->GetName() ); ScreenMsg( usColor, MSG_INTERFACE, sString ); } // Add effects { 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 ); /*// ATE: Make guy collapse from heart attack if too much stuff taken.... if ( pSoldier->drugs.bDrugSideEffectRate[ DRUG_TYPE_ALCOHOL ] > ( Drug[DRUG_TYPE_ALCOHOL].ubDrugSideEffect * 3 ) ) { if ( pSoldier->ubProfile == LARRY_NORMAL ) { pSoldier = SwapLarrysProfiles( pSoldier ); } else if ( pSoldier->ubProfile == LARRY_DRUNK ) { gMercProfiles[ LARRY_DRUNK ].bNPCData = 0; } // Keel over... DeductPoints( pSoldier, 0, 10000 ); /////////////////////////////////////////////////////////////////////////////////////////// // SANDRO - if our stat is damaged through facility event, make it healable // Permanently lower certain stats... if ( gGameOptions.fNewTraitSystem ) { // WISDOM decrease if ( pSoldier->stats.bWisdom > 5 ) { pSoldier->ubCriticalStatDamage[ DAMAGED_STAT_WISDOM ] += 5; pSoldier->stats.bWisdom -= 5; } else { pSoldier->ubCriticalStatDamage[ DAMAGED_STAT_WISDOM ] += (pSoldier->stats.bWisdom - 1); pSoldier->stats.bWisdom = 1; } // DEXTERITY decrease if ( pSoldier->stats.bDexterity > 5 ) { pSoldier->ubCriticalStatDamage[ DAMAGED_STAT_DEXTERITY ] += 5; pSoldier->stats.bDexterity -= 5; } else { pSoldier->ubCriticalStatDamage[ DAMAGED_STAT_DEXTERITY ] += (pSoldier->stats.bDexterity - 1); pSoldier->stats.bDexterity = 1; } // STRENGTH decrease if ( pSoldier->stats.bStrength > 5 ) { pSoldier->ubCriticalStatDamage[ DAMAGED_STAT_STRENGTH ] += 5; pSoldier->stats.bStrength -= 5; } else { pSoldier->ubCriticalStatDamage[ DAMAGED_STAT_STRENGTH ] += (pSoldier->stats.bStrength - 1); pSoldier->stats.bStrength = 1; } // AGILITY decrease if ( pSoldier->stats.bAgility > 5 ) { pSoldier->ubCriticalStatDamage[ DAMAGED_STAT_AGILITY ] += 5; pSoldier->stats.bAgility -= 5; } else { pSoldier->ubCriticalStatDamage[ DAMAGED_STAT_AGILITY ] += (pSoldier->stats.bAgility - 1); pSoldier->stats.bAgility = 1; } } else // old system { pSoldier->stats.bAgility = __max(1, pSoldier->stats.bWisdom-5); pSoldier->stats.bDexterity = __max(1, pSoldier->stats.bDexterity-5); pSoldier->stats.bStrength = __max(1, pSoldier->stats.bStrength-5); pSoldier->stats.bAgility = __max(1, pSoldier->stats.bAgility-5); // make those stats RED for a while... // SANDRO - we don't need to do this with new system, as we simply show all damaged stats in red until healed pSoldier->timeChanges.uiChangeWisdomTime = GetJA2Clock(); pSoldier->usValueGoneUp &= ~( WIS_INCREASE ); pSoldier->timeChanges.uiChangeDexterityTime = GetJA2Clock(); pSoldier->usValueGoneUp &= ~( DEX_INCREASE ); pSoldier->timeChanges.uiChangeStrengthTime = GetJA2Clock(); pSoldier->usValueGoneUp &= ~( STRENGTH_INCREASE ); pSoldier->timeChanges.uiChangeAgilityTime = GetJA2Clock(); pSoldier->usValueGoneUp &= ~( AGIL_INCREASE ); } // export stat changes to profile gMercProfiles[ pSoldier->ubProfile ].bWisdom = pSoldier->stats.bWisdom; gMercProfiles[ pSoldier->ubProfile ].bDexterity = pSoldier->stats.bDexterity; gMercProfiles[ pSoldier->ubProfile ].bStrength = pSoldier->stats.bStrength; gMercProfiles[ pSoldier->ubProfile ].bAgility = pSoldier->stats.bAgility; fBadResult = TRUE; // stop the time, call a doctor, we had a heart attack! // merc records - stat damaged gMercProfiles[ pSoldier->ubProfile ].records.usTimesStatDamaged++; gMercProfiles[ pSoldier->ubProfile ].records.usFacilityAccidents++; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// }*/ break; case RISK_LOYALTY_LOCAL: Result *= GAIN_PTS_PER_LOYALTY_PT; // Transform points into actual loyalty. if (Result > 0) { IncrementTownLoyalty( ubTownID, Result ); } else { DecrementTownLoyalty( ubTownID, abs(Result) ); // Log message if (!fOperatingFacility) swprintf( sString, gzFacilityErrorMessage[23], pTownNames[ubTownID], pSoldier->GetName() ); else swprintf( sString, gzFacilityErrorMessage[24], pTownNames[ubTownID], pSoldier->GetName(), gFacilityTypes[ubFacilityType].szFacilityName ); ScreenMsg( usColor, MSG_INTERFACE, sString ); // SANDRO - add to merc records - facility accidents counter if ( Result < 0 ) gMercProfiles[ pSoldier->ubProfile ].records.usFacilityAccidents++; } break; case RISK_LOYALTY_GLOBAL: Result *= GAIN_PTS_PER_LOYALTY_PT; // Transform points into actual loyalty. if (Result > 0) { IncrementTownLoyaltyEverywhere( Result ); } else { DecrementTownLoyaltyEverywhere( abs(Result) ); // Log message GetShortSectorString( pSoldier->sSectorX, pSoldier->sSectorY, szSectorGrid ); if (!fOperatingFacility) swprintf( sString, gzFacilityErrorMessage[25], pSoldier->GetName(), szSectorGrid ); else swprintf( sString, gzFacilityErrorMessage[26], pSoldier->GetName(), szSectorGrid, gFacilityTypes[ubFacilityType].szFacilityName ); ScreenMsg( usColor, MSG_INTERFACE, sString ); // SANDRO - add to merc records - facility accidents counter if ( Result < 0 ) gMercProfiles[ pSoldier->ubProfile ].records.usFacilityAccidents++; } break; default: break; } if (fBadResult) { StopTimeCompression(); } } } } } INT32 GetTotalFacilityHourlyCosts( BOOLEAN fPositive ) { SOLDIERTYPE *pSoldier; INT32 iTotal = 0; UINT8 ubCounter = 0; // Run through list of player-characters while(gCharactersList[ubCounter].fValid) { pSoldier = gCharactersList[ ubCounter ].usSolID; // Is character truly valid? if( pSoldier != NULL && !( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) && pSoldier->bSectorZ == 0 && pSoldier->stats.bLife >= OKLIFE && !(pSoldier->flags.fMercAsleep) ) { INT8 ubAssignmentType = GetSoldierFacilityAssignmentIndex( pSoldier ); if (ubAssignmentType == -1) { // Skip this character. ubCounter++; continue; } //UINT8 ubSector = SECTOR(pSoldier->sSectorX, pSoldier->sSectorY); INT16 ubFacilityType = pSoldier->sFacilityTypeOperated; if (!fPositive && ubFacilityType != -1 && // We want facilities that cost money to operate gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].sCostPerHour > 0) // This facility costs money { iTotal += gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].sCostPerHour; } else if (fPositive && ubFacilityType != -1 && // We want facilities that GENERATE money gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].sCostPerHour < 0) { // Flip the value to positive iTotal += (-1 * gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].sCostPerHour); } } ubCounter++; } return (iTotal); } void InitFacilities() { // Initialize facility accounts. giTotalEarnedForFacilityOperationsToday = 0; giTotalOwedForFacilityOperationsToday = 0; gfOutstandingFacilityDebt = FALSE; }