/** * @file * @author Flugente (bears-pit.com) * @brief This file contains definitions of classes and functions used for dynamic dialogue */ #include "DynamicDialogue.h" #include "DynamicDialogueWidget.h" #include "SaveLoadGame.h" #include "GameVersion.h" #include "Text.h" #include "MercTextBox.h" #include "Meanwhile.h" #include "soldier profile type.h" #include "Soldier Profile.h" #include "Interface.h" #include "CampaignStats.h" #include "Drugs And Alcohol.h" #include "Overhead.h" #include "Food.h" #include "finances.h" #include "message.h" #include "Soldier macros.h" #include "Dialogue Control.h" #include "Soldier Add.h" #include "random.h" #include "strategicmap.h" #include "connect.h" #include "Campaign.h" #include "Points.h" // event id counter // has to be saved/loaded/reset UINT32 gDynamicDialogueEventCounter = 0; // stores data of the currently used event and is used to identify wether dialogue is added to the current one or is from a new event // has to be saved/loaded/reset DynamicOpinionSpeechEvent_EventData gCurrentDynamicDialogueEventData; // when we add an event, we add it to this vector. // we create speech from this and add it to gDynamicOpinionSpeechEventVector // has to be saved/loaded/reset std::vector gDynamicOpinionSpeechEventArchiveVector; // this vector contains speech events that are a result of dynamic opinions and the 'dialogue' between mercs that results from this // the vector is filled when we add an opinion event and then create spech out of it // it is emptied when 'sending' the events to the dialogue system // this vector is not saved and should be cleaned every time we reload std::vector gDynamicOpinionSpeechEventVector; // collects all opinion events that happen as a RESULT of the ongoing dialogue. The event that starts the dialogue itself is not used here! // This vector is not saved. It's contents will be added and cleared once a dialogue chain finishes. std::vector gOpionionEventsDuringDialogue; // stores the events used in the current dialogue. Used to determine coordinates and dailogue reactions // This vector is not saved. It's contents will be added and cleared once a dialogue chain finishes. std::vector gDynamicOpinionSpeechInCurrentDialogue; // Flugente: calculate A's opinion of B INT8 SoldierRelation( SOLDIERTYPE* pSoldierA, SOLDIERTYPE* pSoldierB ) { INT8 bOpinion = 0; if ( !OKToCheckOpinion( pSoldierA->ubProfile ) || !OKToCheckOpinion( pSoldierB->ubProfile ) ) return 0; MERCPROFILESTRUCT* pProfile = &(gMercProfiles[pSoldierA->ubProfile]); bOpinion = pProfile->bMercOpinion[pSoldierB->ubProfile]; // Flugente: evaluate appearance/refinement/hated nationalities // some people loathe ugly people and like beautiful people. It's a mean world. // we also handle sexism here switch ( gMercProfiles[pSoldierB->ubProfile].bAppearance ) { case APPEARANCE_UGLY: { if ( pProfile->bAppearanceCareLevel == CARELEVEL_SOME ) bOpinion -= gGameExternalOptions.sMoraleModAppearance * 2; else if ( pProfile->bAppearanceCareLevel == CARELEVEL_EXTREME ) bOpinion -= gGameExternalOptions.sMoraleModAppearance * 4; // some people are sexists if ( pProfile->bSexist && gMercProfiles[pSoldierB->ubProfile].bSex != pProfile->bSex ) { if ( pProfile->bSexist == SOMEWHAT_SEXIST ) bOpinion -= gGameExternalOptions.sMoraleModSexism * 2; else if ( pProfile->bSexist == VERY_SEXIST ) bOpinion -= gGameExternalOptions.sMoraleModSexism * 4; } } break; case APPEARANCE_HOMELY: { if ( pProfile->bAppearanceCareLevel == CARELEVEL_SOME ) bOpinion -= gGameExternalOptions.sMoraleModAppearance; else if ( pProfile->bAppearanceCareLevel == CARELEVEL_EXTREME ) bOpinion -= gGameExternalOptions.sMoraleModAppearance * 2; // some people are sexists if ( pProfile->bSexist && gMercProfiles[pSoldierB->ubProfile].bSex != pProfile->bSex ) { if ( pProfile->bSexist == SOMEWHAT_SEXIST ) bOpinion -= gGameExternalOptions.sMoraleModSexism; else if ( pProfile->bSexist == VERY_SEXIST ) bOpinion -= gGameExternalOptions.sMoraleModSexism * 2; } } break; case APPEARANCE_ATTRACTIVE: { if ( pProfile->bAppearanceCareLevel == CARELEVEL_SOME ) bOpinion += gGameExternalOptions.sMoraleModAppearance; else if ( pProfile->bAppearanceCareLevel == CARELEVEL_EXTREME ) bOpinion += gGameExternalOptions.sMoraleModAppearance * 2; // some people are sexists if ( pProfile->bSexist && gMercProfiles[pSoldierB->ubProfile].bSex != pProfile->bSex ) { if ( pProfile->bSexist == SOMEWHAT_SEXIST ) bOpinion += gGameExternalOptions.sMoraleModSexism; else if ( pProfile->bSexist == VERY_SEXIST ) bOpinion += gGameExternalOptions.sMoraleModSexism * 2; } } break; case APPEARANCE_BABE: { if ( pProfile->bAppearanceCareLevel == CARELEVEL_SOME ) bOpinion += gGameExternalOptions.sMoraleModAppearance * 2; else if ( pProfile->bAppearanceCareLevel == CARELEVEL_EXTREME ) bOpinion += gGameExternalOptions.sMoraleModAppearance * 4; // some people are sexists if ( pProfile->bSexist && gMercProfiles[pSoldierB->ubProfile].bSex != pProfile->bSex ) { if ( pProfile->bSexist == SOMEWHAT_SEXIST ) bOpinion += gGameExternalOptions.sMoraleModSexism * 2; else if ( pProfile->bSexist == VERY_SEXIST ) bOpinion += gGameExternalOptions.sMoraleModSexism * 4; } } break; } // some people care about how distuingished other people are. Malus if on different ends of the spectrum, a small bonus if on the same and its really important to the person // also give a malus if the other person is a slob or snob and we are average but care extremely (we don't like people who behave differently) // if we don't care, doesn't matter if ( pProfile->bRefinementCareLevel == CARELEVEL_NONE ) { // nothing to do... } // if we care somewhat, malus on slob/snob else if ( pProfile->bRefinementCareLevel == CARELEVEL_SOME ) { if ( pProfile->bRefinement * gMercProfiles[pSoldierB->ubProfile].bRefinement == 2 ) bOpinion -= gGameExternalOptions.sMoraleModRefinement; } // if we care extremely, reward for similarity, malus otherwise else //if( pProfile->bRefinementCareLevel == CARELEVEL_EXTREME ) { if ( pProfile->bRefinement * gMercProfiles[pSoldierB->ubProfile].bRefinement == 2 ) bOpinion -= 2 * gGameExternalOptions.sMoraleModRefinement; else if ( (pProfile->bRefinement * gMercProfiles[pSoldierB->ubProfile].bRefinement == 0) && (pProfile->bRefinement != gMercProfiles[pSoldierB->ubProfile].bRefinement) ) bOpinion -= gGameExternalOptions.sMoraleModRefinement; else bOpinion += gGameExternalOptions.sMoraleModRefinement; } // some people hate other nationalities (do not mix up with racism, which uses bRace) if ( pProfile->bHatedNationality > -1 && gMercProfiles[pSoldierB->ubProfile].bNationality == pProfile->bHatedNationality ) { if ( pProfile->bHatedNationalityCareLevel == CARELEVEL_SOME ) bOpinion -= gGameExternalOptions.sMoraleModHatedNationality; else if ( pProfile->bHatedNationalityCareLevel == CARELEVEL_EXTREME ) bOpinion -= gGameExternalOptions.sMoraleModHatedNationality * 2; } // some people are racists if ( pProfile->bRacist && gMercProfiles[pSoldierB->ubProfile].bRace != pProfile->bRace ) { if ( pProfile->bRacist == RACIST_SOME ) bOpinion -= gGameExternalOptions.sMoraleModRacism; else if ( pProfile->bRacist == RACIST_VERY ) bOpinion -= gGameExternalOptions.sMoraleModRacism * 2; } // Flugente: backgrounds if ( pSoldierA->GetBackgroundValue( BG_DISLIKEBG ) && pSoldierA->GetBackgroundValue( BG_DISLIKEBG ) == -pSoldierB->GetBackgroundValue( BG_DISLIKEBG ) ) { bOpinion -= 2; } // smoker INT16 smokerA = pSoldierA->GetBackgroundValue( BG_SMOKERTYPE ); INT16 smokerB = pSoldierB->GetBackgroundValue( BG_SMOKERTYPE ); if ( smokerA && smokerB ) { if ( smokerA != smokerB ) bOpinion -= 2; else bOpinion += 1; } if ( pSoldierA->HasBackgroundFlag( BACKGROUND_XENOPHOBIC ) && pSoldierB->ubProfile != NO_PROFILE && gMercProfiles[pSoldierA->ubProfile].usBackground != gMercProfiles[pSoldierB->ubProfile].usBackground ) bOpinion -= gGameExternalOptions.sMoraleModXenophobicBackGround; // Flugente: dynamic opinions if ( gGameExternalOptions.fDynamicOpinions ) { for ( UINT8 opinionevent = OPINIONEVENT_FRIENDLYFIRE; opinionevent < OPINIONEVENT_MAX; ++opinionevent ) { bOpinion += GetDynamicOpinion( pSoldierA->ubProfile, pSoldierB->ubProfile, opinionevent ); } } // long-term memory bOpinion += gMercProfiles[pSoldierA->ubProfile].sDynamicOpinionLongTerm[pSoldierB->ubProfile]; // reasonable values bOpinion = min( BUDDY_OPINION, bOpinion ); bOpinion = max( HATED_OPINION, bOpinion ); return bOpinion; } INT8 SoldierRelation( UINT8 usProfileA, UINT8 usProfileB ) { INT16 idA = GetSoldierIDFromMercID( usProfileA ); INT16 idB = GetSoldierIDFromMercID( usProfileB ); if ( idA < 0 || idB < 0 ) return 0; SOLDIERTYPE* pSoldierA = MercPtrs[idA]; SOLDIERTYPE* pSoldierB = MercPtrs[idB]; if ( !pSoldierA || !pSoldierB ) return 0; return SoldierRelation( pSoldierA, pSoldierB ); } // it could happen that we load events incorrectly. To detect that early, we write an extra eyecacher befoer and after the list of events. Should we fail to read it correctly, we will knowm where to look. UINT32 gDynDiaSaveCheck = 0xF123456F; BOOLEAN SaveDynamicDialogue( HWFILE hwFile ) { UINT32 uiNumBytesWritten = 0; if ( !FileWrite( hwFile, &gCurrentDynamicDialogueEventData, sizeof(DynamicOpinionSpeechEvent_EventData), &uiNumBytesWritten ) ) return(FALSE); if ( !FileWrite( hwFile, &gDynamicDialogueEventCounter, sizeof(UINT32), &uiNumBytesWritten ) ) return(FALSE); // in order to save a vector, we first save its size and then its content UINT32 size = gDynamicOpinionSpeechEventArchiveVector.size( ); if ( !FileWrite( hwFile, &size, sizeof(UINT32), &uiNumBytesWritten ) ) return(FALSE); if ( !FileWrite( hwFile, &gDynDiaSaveCheck, sizeof(UINT32), &uiNumBytesWritten ) ) return(FALSE); for ( UINT32 i = 0; i < size; ++i ) { if ( !FileWrite( hwFile, &gDynamicOpinionSpeechEventArchiveVector[i], sizeof(DynamicOpinionSpeechEvent), &uiNumBytesWritten ) ) return(FALSE); } if ( !FileWrite( hwFile, &gDynDiaSaveCheck, sizeof(UINT32), &uiNumBytesWritten ) ) return(FALSE); return(TRUE); } BOOLEAN LoadDynamicDialogue( HWFILE hwFile ) { // clear any artefacts prior to loading DestroyAllDynamicDialogueBoxes( ); gDynamicDialogueEventCounter = 0; gCurrentDynamicDialogueEventData.usId = 0; gCurrentDynamicDialogueEventData.usPriority = 0; gDynamicOpinionSpeechEventVector.clear( ); gDynamicOpinionSpeechEventArchiveVector.clear( ); gOpionionEventsDuringDialogue.clear( ); gDynamicOpinionSpeechInCurrentDialogue.clear( ); SetfDynamicOpinionSpeechInProgress( FALSE ); if ( guiCurrentSaveGameVersion >= DYNAMIC_DIALOGUE ) { UINT32 numBytesRead = 0; numBytesRead = ReadFieldByField( hwFile, &gCurrentDynamicDialogueEventData, sizeof(gCurrentDynamicDialogueEventData), sizeof(DynamicOpinionSpeechEvent_EventData), numBytesRead ); numBytesRead = ReadFieldByField( hwFile, &gDynamicDialogueEventCounter, sizeof(gDynamicDialogueEventCounter), sizeof(UINT32), numBytesRead ); UINT32 size = 0; numBytesRead = ReadFieldByField( hwFile, &size, sizeof(size), sizeof(UINT32), numBytesRead ); UINT32 safetycheck1 = 0; numBytesRead = ReadFieldByField( hwFile, &safetycheck1, sizeof(safetycheck1), sizeof(UINT32), numBytesRead ); for ( UINT32 i = 0; i < size; ++i ) { DynamicOpinionSpeechEvent event; numBytesRead = ReadFieldByField( hwFile, &event, sizeof(event), sizeof(DynamicOpinionSpeechEvent), 0 ); gDynamicOpinionSpeechEventArchiveVector.push_back( event ); } UINT32 safetycheck2 = 0; numBytesRead = ReadFieldByField( hwFile, &safetycheck2, sizeof(safetycheck2), sizeof(UINT32), numBytesRead ); if ( safetycheck1 != gDynDiaSaveCheck || safetycheck2 != gDynDiaSaveCheck ) { ScreenMsg( FONT_MCOLOR_LTGREEN, MSG_INTERFACE, L"Eyecatcher corruption detected while loading dynamic dialogue events!" ); return FALSE; } } return TRUE; } void CleanDynamicOpinionSpeechEvents( ) { gDynamicOpinionSpeechEventVector.clear( ); } void AddDynamicOpinionEvent_New( OpinionEvent aEvent ) { DynamicOpinionSpeechEvent_EventData data; data.event = aEvent; data.ubProfileINTERJECTOR = NO_PROFILE; data.usId = 0; // has to be 0 here, do not use a different value! data.usPriority = DOST_PRIORITY_LOW; memset( data.ubProfileSideCause, NO_PROFILE, DOST_INTERJECTORS_MAX * sizeof(UINT8) ); memset( data.ubProfileSideComplainant, NO_PROFILE, DOST_INTERJECTORS_MAX * sizeof(UINT8) ); // get the profile of a merc that can interjet. Pick an IMP if possible. UINT8 impprofile = GetFittingInterjectorProfile( aEvent.ubEventId, aEvent.ubProfileComplainant, aEvent.ubProfileCause ); if ( impprofile != NO_PROFILE ) data.ubProfileINTERJECTOR = impprofile; DynamicOpinionSpeechEvent event; event.data = data; event.ubEventType = DOST_VICTIM_INITIATE; event.usQueueNumber = 0; event.usSpeaker = event.data.event.ubProfileComplainant; event.usSide = DOST_POSITION_LEFT; event.usNumonside = 0; event.usStarttime = 0; AddDynamicOpinionEvent_Continue( event ); } void AddDynamicOpinionEvent_Continue( DynamicOpinionSpeechEvent aEvent ) { // If this event already has an Id that is already used in our list of current events, then this must be a reaction from one of our mercs in an ongoing dialogue. // In this case, don't add it to the archive, but continue the dialogue if ( aEvent.data.usId > 0 && (aEvent.data.event.ubEventId == gCurrentDynamicDialogueEventData.event.ubEventId) && (aEvent.data.usId == gCurrentDynamicDialogueEventData.usId) ) { CreateSpeechEventsFromDynamicOpinionEvent( aEvent ); } else { ++gDynamicDialogueEventCounter; // aEvent gets a new Id. This it the only place where this is supposed to happen! aEvent.data.usId = gDynamicDialogueEventCounter; aEvent.data.usPriority = DOST_PRIORITY_LOW; gDynamicOpinionSpeechEventArchiveVector.push_back( aEvent ); } } void CreateSpeechEventsFromDynamicOpinionEvent( DynamicOpinionSpeechEvent aEvent ) { // This is the point where we could determine the 'dialogue' between mercs // we determine dialogue an resulting dialogue for each type of event an determine dialogue for it // that way, if several opinon events regarding the same thing are set, we will have dialogue over all of that UINT8 cnt = 0; aEvent.usStarttime = GetJA2Clock( ) + cnt * gGameExternalOptions.sDynamicDialogueTimeOffset; UINT8 queuenumber = aEvent.usQueueNumber + 1; // interjector responeses are handled later in the if-else statement if ( aEvent.ubEventType < DOST_INTERJECTOR_TO_VICTIM_DENY ) { gDynamicOpinionSpeechEventVector.push_back( aEvent ); gDynamicOpinionSpeechInCurrentDialogue.push_back( aEvent ); ++cnt; } // if appropriate, new opinion events will be created here and added to gOpionionEventsDuringDialogue if ( aEvent.ubEventType == DOST_VICTIM_INITIATE ) { // cause either denies or agrees with complainant UINT32 chancetoagree = 0; // the more we dislike the other guy, the higher the chance we disagree // personality also influences this INT8 agreemodifier_personal = SoldierRelation( aEvent.data.event.ubProfileCause, aEvent.data.event.ubProfileComplainant ); switch ( gMercProfiles[aEvent.data.event.ubProfileCause].bCharacterTrait ) { case(CHAR_TRAIT_NORMAL) : agreemodifier_personal += 0; break; case(CHAR_TRAIT_SOCIABLE) : agreemodifier_personal += 10; break; case(CHAR_TRAIT_LONER) : agreemodifier_personal += -3; break; case(CHAR_TRAIT_OPTIMIST) : agreemodifier_personal += 5; break; case(CHAR_TRAIT_ASSERTIVE) : agreemodifier_personal += -5; break; case(CHAR_TRAIT_INTELLECTUAL) : agreemodifier_personal += 2; break; case(CHAR_TRAIT_PRIMITIVE) : agreemodifier_personal += -4; break; case(CHAR_TRAIT_AGGRESSIVE) : agreemodifier_personal += -10; break; case(CHAR_TRAIT_PHLEGMATIC) : agreemodifier_personal += -20; break; case(CHAR_TRAIT_DAUNTLESS) : agreemodifier_personal += -10; break; case(CHAR_TRAIT_PACIFIST) : agreemodifier_personal += 20; break; case(CHAR_TRAIT_MALICIOUS) : agreemodifier_personal += -8; break; case(CHAR_TRAIT_SHOWOFF) : agreemodifier_personal += -3; break; case CHAR_TRAIT_COWARD: agreemodifier_personal += 5; break; } // if the event is positive, then we are much more likely to agree if ( gDynamicOpinionEvent[aEvent.data.event.ubEventId].sOpinionModifier > 0 ) agreemodifier_personal += 50; chancetoagree = max( 0, 25 + agreemodifier_personal ); // if he agrees, then this dialogue will soon be over - interjector won't have to play his part if ( Chance( chancetoagree ) ) { DynamicOpinionSpeechEvent response; response.data = aEvent.data; response.ubEventType = DOST_CAUSE_TO_VICTIM_AGREE; response.usSpeaker = aEvent.data.event.ubProfileCause; response.usQueueNumber = queuenumber++; response.usSide = DOST_POSITION_LEFT; response.usNumonside = (UINT8)GetSidePosition( response.usSide ); response.usStarttime = GetJA2Clock( ) + cnt * gGameExternalOptions.sDynamicDialogueTimeOffset; gDynamicOpinionSpeechEventVector.push_back( response ); gDynamicOpinionSpeechInCurrentDialogue.push_back( response ); ++cnt; } else { DynamicOpinionSpeechEvent response; response.data = aEvent.data; response.ubEventType = DOST_CAUSE_TO_VICTIM_DENY; response.usSpeaker = aEvent.data.event.ubProfileCause; response.usQueueNumber = queuenumber++; response.usSide = DOST_POSITION_RIGHT; response.usNumonside = (UINT8)GetSidePosition( response.usSide ); response.usStarttime = GetJA2Clock( ) + cnt * gGameExternalOptions.sDynamicDialogueTimeOffset; gDynamicOpinionSpeechEventVector.push_back( response ); gDynamicOpinionSpeechInCurrentDialogue.push_back( response ); ++cnt; // chance that an INTERJECTOR tries to interfere if ( aEvent.data.ubProfileINTERJECTOR != NO_PROFILE ) { DynamicOpinionSpeechEvent impaction; impaction.data = aEvent.data; impaction.usSpeaker = aEvent.data.ubProfileINTERJECTOR; impaction.usSide = DOST_POSITION_MIDDLE; BOOLEAN fIsImp = FALSE; INT16 idInterjector = GetSoldierIDFromMercID( aEvent.data.ubProfileINTERJECTOR ); if ( idInterjector != NOBODY && idInterjector >= 0 ) { if ( MercPtrs[idInterjector]->ubWhatKindOfMercAmI == MERC_TYPE__PLAYER_CHARACTER ) fIsImp = TRUE; // if it's an IMP, we have a choice, otherwise pick one answer randomly if ( fIsImp ) { impaction.ubEventType = DOST_INTERJECTOR_DIALOGUESELECTION; } else { // determine interjectors reaction // chance to say nothing UINT8 chance_nothing = 10; if ( gMercProfiles[aEvent.data.ubProfileINTERJECTOR].bCharacterTrait == CHAR_TRAIT_LONER ) chance_nothing += 20; else if ( gMercProfiles[aEvent.data.ubProfileINTERJECTOR].bCharacterTrait == CHAR_TRAIT_LONER ) chance_nothing += 20; if ( Chance( chance_nothing ) ) { // say nothing... impaction.ubEventType = DOST_INTERJECTOR_NOTHING; } else { // chance to try to stop the argument (either with reason or authority) UINT8 chance_to_reason = 30; UINT8 chance_to_agression = 30; switch ( gMercProfiles[aEvent.data.ubProfileINTERJECTOR].bCharacterTrait ) { case(CHAR_TRAIT_NORMAL) : chance_to_reason += 0; chance_to_agression += 0; break; case(CHAR_TRAIT_SOCIABLE) : chance_to_reason += 40; chance_to_agression += -15; break; case(CHAR_TRAIT_LONER) : chance_to_reason += 0; chance_to_agression += 0; break; case(CHAR_TRAIT_OPTIMIST) : chance_to_reason += 5; chance_to_agression += -5; break; case(CHAR_TRAIT_ASSERTIVE) : chance_to_reason += 5; chance_to_agression += 10; break; case(CHAR_TRAIT_INTELLECTUAL) : chance_to_reason += 10; chance_to_agression += -3; break; case(CHAR_TRAIT_PRIMITIVE) : chance_to_reason += 0; chance_to_agression += 6; break; case(CHAR_TRAIT_AGGRESSIVE) : chance_to_reason += -15; chance_to_agression += 30; break; case(CHAR_TRAIT_PHLEGMATIC) : chance_to_reason += 0; chance_to_agression += 0; break; case(CHAR_TRAIT_DAUNTLESS) : chance_to_reason += 0; chance_to_agression += 10; break; case(CHAR_TRAIT_PACIFIST) : chance_to_reason += 50; chance_to_agression += -20; break; case(CHAR_TRAIT_MALICIOUS) : chance_to_reason += -20; chance_to_agression += 40; break; case(CHAR_TRAIT_SHOWOFF) : chance_to_reason += -2; chance_to_agression += 2; break; case CHAR_TRAIT_COWARD: chance_to_reason += 20; chance_to_agression += -20; break; } if ( Chance( chance_to_reason ) ) { impaction.ubEventType = DOST_INTERJECTOR_SOLVE_REASON; } else if ( Chance( chance_to_agression ) ) { impaction.ubEventType = DOST_INTERJECTOR_SOLVE_AGGRESSIVE; } else { // we side with one of the argument's sides INT8 agreewithcausechance = 50 + SoldierRelation( aEvent.data.ubProfileINTERJECTOR, aEvent.data.event.ubProfileCause ) - SoldierRelation( aEvent.data.ubProfileINTERJECTOR, aEvent.data.event.ubProfileComplainant ); if ( Chance( agreewithcausechance ) ) { if ( Random( 2 ) ) impaction.ubEventType = DOST_INTERJECTOR_TO_CAUSE_AGREE; else impaction.ubEventType = DOST_INTERJECTOR_TO_VICTIM_DENY; impaction.usSide = DOST_POSITION_RIGHT; } else { if ( Random( 2 ) ) impaction.ubEventType = DOST_INTERJECTOR_TO_VICTIM_AGREE; else impaction.ubEventType = DOST_INTERJECTOR_TO_CAUSE_DENY; impaction.usSide = DOST_POSITION_LEFT; } } } } if ( impaction.ubEventType != DOST_INTERJECTOR_NOTHING ) { impaction.usQueueNumber = queuenumber++; impaction.usNumonside = (UINT8)GetSidePosition( impaction.usSide ); impaction.usStarttime = GetJA2Clock( ) + cnt * gGameExternalOptions.sDynamicDialogueTimeOffset; gDynamicOpinionSpeechEventVector.push_back( impaction ); gDynamicOpinionSpeechInCurrentDialogue.push_back( impaction ); ++cnt; } } } } // if other people are involved too, they can speak too for ( UINT8 i = 0; i < DOST_INTERJECTORS_MAX; ++i ) { if ( aEvent.data.ubProfileSideComplainant[i] != NO_PROFILE ) { DynamicOpinionSpeechEvent response; response.data = aEvent.data; response.ubEventType = DOST_SIDEWITH_VICTIM; response.usSpeaker = aEvent.data.ubProfileSideComplainant[i]; response.usQueueNumber = queuenumber++; response.usSide = DOST_POSITION_LEFT; response.usNumonside = (UINT8)GetSidePosition( response.usSide ); response.usStarttime = GetJA2Clock( ) + cnt * gGameExternalOptions.sDynamicDialogueTimeOffset; gDynamicOpinionSpeechEventVector.push_back( response ); gDynamicOpinionSpeechInCurrentDialogue.push_back( response ); ++cnt; } if ( aEvent.data.ubProfileSideCause[i] != NO_PROFILE ) { DynamicOpinionSpeechEvent response; response.data = aEvent.data; response.ubEventType = DOST_SIDEWITH_CAUSE; response.usSpeaker = aEvent.data.ubProfileSideCause[i]; response.usQueueNumber = queuenumber++; response.usSide = DOST_POSITION_RIGHT; response.usNumonside = (UINT8)GetSidePosition( response.usSide ); response.usStarttime = GetJA2Clock( ) + cnt * gGameExternalOptions.sDynamicDialogueTimeOffset; gDynamicOpinionSpeechEventVector.push_back( response ); gDynamicOpinionSpeechInCurrentDialogue.push_back( response ); ++cnt; } } } else if ( aEvent.ubEventType >= DOST_INTERJECTOR_TO_VICTIM_DENY && aEvent.ubEventType <= DOST_INTERJECTOR_SOLVE_AGGRESSIVE ) { DynamicOpinionSpeechEvent impinterference; impinterference.data = aEvent.data; impinterference.ubEventType = aEvent.ubEventType; impinterference.usSpeaker = aEvent.data.ubProfileINTERJECTOR; impinterference.usQueueNumber = queuenumber++; switch ( aEvent.ubEventType ) { case DOST_INTERJECTOR_TO_VICTIM_DENY: case DOST_INTERJECTOR_TO_CAUSE_AGREE: impinterference.usSide = DOST_POSITION_RIGHT; break; case DOST_INTERJECTOR_TO_VICTIM_AGREE: case DOST_INTERJECTOR_TO_CAUSE_DENY: impinterference.usSide = DOST_POSITION_LEFT; break; case DOST_INTERJECTOR_SOLVE_REASON: case DOST_INTERJECTOR_SOLVE_AGGRESSIVE: impinterference.usSide = DOST_POSITION_MIDDLE; break; } impinterference.usNumonside = (UINT8)GetSidePosition( impinterference.usSide ); impinterference.usStarttime = GetJA2Clock( ) + cnt * gGameExternalOptions.sDynamicDialogueTimeOffset; gDynamicOpinionSpeechEventVector.push_back( impinterference ); gDynamicOpinionSpeechInCurrentDialogue.push_back( impinterference ); ++cnt; // our response can have consequences on the opinions of others if ( Chance( 70 ) ) { OpinionEvent opinionevent; opinionevent.ubProfileCause = aEvent.usSpeaker; switch ( aEvent.ubEventType ) { case DOST_INTERJECTOR_TO_VICTIM_DENY: { opinionevent.ubEventId = OPINIONEVENT_AGAINST_US; opinionevent.ubProfileComplainant = aEvent.data.event.ubProfileComplainant; } break; case DOST_INTERJECTOR_TO_CAUSE_AGREE: { opinionevent.ubEventId = OPINIONEVENT_FOR_US; opinionevent.ubProfileComplainant = aEvent.data.event.ubProfileCause; } break; case DOST_INTERJECTOR_TO_VICTIM_AGREE: { opinionevent.ubEventId = OPINIONEVENT_FOR_US; opinionevent.ubProfileComplainant = aEvent.data.event.ubProfileComplainant; } break; case DOST_INTERJECTOR_TO_CAUSE_DENY: { opinionevent.ubEventId = OPINIONEVENT_AGAINST_US; opinionevent.ubProfileComplainant = aEvent.data.event.ubProfileCause; } break; case DOST_INTERJECTOR_SOLVE_REASON: { opinionevent.ubEventId = OPINIONEVENT_SOLVECONFLICT_REASON_GOOD; opinionevent.ubProfileComplainant = aEvent.data.event.ubProfileComplainant; } break; case DOST_INTERJECTOR_SOLVE_AGGRESSIVE: { opinionevent.ubEventId = OPINIONEVENT_SOLVECONFLICT_AGGRESSIVE_BAD; opinionevent.ubProfileComplainant = aEvent.data.event.ubProfileComplainant; } break; } gOpionionEventsDuringDialogue.push_back( opinionevent ); } // we directly inject new dialogue without waiting. The normal way of starting requires the old even to finish, but we want to be a part of it StartDynamicOpinionDialogue( ); } } extern BOOLEAN DynamicDialogueBoxesActive( ); void HandleDynamicOpinionSpeechEvents( ) { // if dialogue is in progress, check wether it is finished if ( DynamicOpinionSpeechInProgress( ) ) { if ( !DynamicDialogueBoxesActive( ) ) { // dialogue is finished, delete handled event from list of events std::vector::iterator itend = gDynamicOpinionSpeechEventArchiveVector.end( ); for ( std::vector::iterator it = gDynamicOpinionSpeechEventArchiveVector.begin( ); it != itend; ++it ) { DynamicOpinionSpeechEvent event = (*it); // if this is the current event, delete it if ( (event.data.event.ubEventId == gCurrentDynamicDialogueEventData.event.ubEventId) && (event.data.usId == gCurrentDynamicDialogueEventData.usId) ) { gDynamicOpinionSpeechEventArchiveVector.erase( it ); gCurrentDynamicDialogueEventData.usId = 0; gCurrentDynamicDialogueEventData.usPriority = 0; break; } } // award any opinion events that have happened... // these better not start dialogues themselves, or we might cause a loop here for ( size_t i = 0, opinioneentscollectedsize = gOpionionEventsDuringDialogue.size(); i < opinioneentscollectedsize; ++i ) { OpinionEvent event = gOpionionEventsDuringDialogue[i]; AddOpinionEvent( event.ubProfileComplainant, event.ubProfileCause, event.ubEventId, FALSE ); } gOpionionEventsDuringDialogue.clear( ); gDynamicOpinionSpeechInCurrentDialogue.clear( ); SetfDynamicOpinionSpeechInProgress( FALSE ); } } // if no dialogue is in progress, we can start something new else { // we now select the next event to be turned into dialogue // This isn't necessarily the first one, different events can have different priorities, making later events more urgent if ( !gDynamicOpinionSpeechEventArchiveVector.empty( ) ) { // this is the place were we can also merge events together... PerformPossibleDynamicOpinionEventMerges( ); DynamicOpinionSpeechEvent bestevent = (*gDynamicOpinionSpeechEventArchiveVector.begin( )); std::vector::iterator itend = gDynamicOpinionSpeechEventArchiveVector.end( ); for ( std::vector::iterator it = gDynamicOpinionSpeechEventArchiveVector.begin( ); it != itend; ++it ) { DynamicOpinionSpeechEvent event = (*it); if ( event.data.usPriority > bestevent.data.usPriority ) bestevent = event; } // important: we do NOT delete the event now. During the dialogue, different events can happen depedning on the player's actions // When reloading the game, we cannot start in a dialogue section, as it would be very confusing. We thus have to restart the entire dialogue. // We thus only delete an event once it's dialogue chain of events is over, and only award opinion changes at that point gCurrentDynamicDialogueEventData = bestevent.data; SetfDynamicOpinionSpeechInProgress( TRUE ); CreateSpeechEventsFromDynamicOpinionEvent( bestevent ); } // no pending events... else { // ... and no pending dialogue either. Time for some clean up. if ( !DynamicDialogueBoxesActive( ) ) { gDynamicDialogueEventCounter = 0; gCurrentDynamicDialogueEventData.usId = 0; gCurrentDynamicDialogueEventData.usPriority = 0; } } } // if we have any pending event dialogue bits, play them now StartDynamicOpinionDialogue( ); } void StartDynamicOpinionDialogue( ) { std::vector::iterator itend = gDynamicOpinionSpeechEventVector.end( ); for ( std::vector::iterator it = gDynamicOpinionSpeechEventVector.begin( ); it != itend; ++it ) { DynamicOpinionSpeechEvent event = (*it); // say this info DynamicOpinionTacticalCharacterDialogue( event ); } CleanDynamicOpinionSpeechEvents( ); } // if possible, merge events if possible, thereby creating fewer but deeper dialogues void PerformPossibleDynamicOpinionEventMerges( ) { for ( std::vector::iterator it = gDynamicOpinionSpeechEventArchiveVector.begin( ); it != gDynamicOpinionSpeechEventArchiveVector.end( ); ) // not having ++it here is intentional! { // if two events have the same ubEventId, then they are about the same kind of event. // Depending on the kin of people involved, it might be reasonable to merge them. // On a merge, the mercs involed in the second and following events simply shortly agree with cause or complainant DynamicOpinionSpeechEvent& event1 = (*it); // loop over all following events and compare for ( std::vector::iterator it2 = it + 1; it2 != gDynamicOpinionSpeechEventArchiveVector.end( ); ) // not having ++it here is intentional! { DynamicOpinionSpeechEvent event2 = (*it2); BOOLEAN fErased = FALSE; if ( event1.data.event.ubEventId == event2.data.event.ubEventId ) { // if cause are the same if ( event1.data.event.ubProfileCause == event2.data.event.ubProfileCause ) { // if there is still room, add to the list of complainants and delete this event for ( UINT8 i = 0; i < DOST_INTERJECTORS_MAX; ++i ) { if ( event1.data.ubProfileSideComplainant[i] == NO_PROFILE ) { event1.data.ubProfileSideComplainant[i] = event2.data.event.ubProfileComplainant; it2 = gDynamicOpinionSpeechEventArchiveVector.erase( it2 ); fErased = TRUE; break; } } } if ( !fErased && event1.data.event.ubProfileComplainant == event2.data.event.ubProfileComplainant ) { // if there is still room, add to the lsit of complainants and delete this event for ( UINT8 i = 0; i < DOST_INTERJECTORS_MAX; ++i ) { if ( event1.data.ubProfileSideCause[i] == NO_PROFILE ) { event1.data.ubProfileSideCause[i] = event2.data.event.ubProfileCause; it2 = gDynamicOpinionSpeechEventArchiveVector.erase( it2 ); fErased = TRUE; break; } } } } if ( !fErased ) { ++it2; } } ++it; } } CHAR16 stupidarray[DOST_CHOICE_MAX][100]; BOOLEAN DynamicOpinionTacticalCharacterDialogue( DynamicOpinionSpeechEvent& aEvent ) { // Haydent if ( is_client ) return(FALSE); //somewhere amongst all this it causes a paase of merc movement while making the quote which throws out the movement sync between clients... : hayden. SOLDIERTYPE* pSoldier = FindSoldierByProfileID( aEvent.usSpeaker, TRUE ); if ( !pSoldier || !pSoldier->bActive ) return FALSE; if ( pSoldier->ubProfile == NO_PROFILE ) return(FALSE); #if (defined JA2UB) //Ja25 no meanwhiles #else if ( AreInMeanwhile( ) ) { return(FALSE); } #endif // let's see what happens... if ( pSoldier->stats.bLife < OKLIFE ) return(FALSE); if ( pSoldier->flags.uiStatusFlags & SOLDIER_GASSED ) return(FALSE); if ( AM_A_ROBOT( pSoldier ) ) return(FALSE); if ( pSoldier->bAssignment == ASSIGNMENT_POW ) return(FALSE); if( pSoldier->bAssignment == ASSIGNMENT_MINIEVENT ) return( FALSE ); if( pSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND) return( FALSE ); CHAR16 gzQuoteStr[500]; // remove old box, in case that still exists RemoveDDBox( aEvent.usSpeaker ); DDBox *pDDBox = GetDDBox( aEvent.usSpeaker ); if ( !pDDBox ) { return FALSE; } UINT16 sX = 10; if ( aEvent.usSide == DOST_POSITION_RIGHT ) sX = SCREEN_WIDTH - (MYBOX_FACE_OFFSET + MYBOX_TEXT_MAXWIDTH); else if ( aEvent.usSide == DOST_POSITION_MIDDLE ) sX = (SCREEN_WIDTH - (MYBOX_FACE_OFFSET + MYBOX_TEXT_MAXWIDTH) - 120) / 2; UINT16 sY = aEvent.usNumonside * 50; switch ( aEvent.ubEventType ) { // victim denies what INTERJECTOR said case DOST_VICTIM_TO_INTERJECTOR_DENY: // cause denies what imp said case DOST_CAUSE_TO_INTERJECTOR_DENY: // yep, same dialogue wcscpy( gzQuoteStr, szDynamicDialogueText_DOST_VICTIM_TO_INTERJECTOR_DENY[Random( 28 )] ); break; // victim agrees what imp said case DOST_VICTIM_TO_INTERJECTOR_AGREE: // cause agrees what imp said case DOST_CAUSE_TO_INTERJECTOR_AGREE: // yep, same dialogue wcscpy( gzQuoteStr, szDynamicDialogueText_DOST_VICTIM_TO_INTERJECTOR_AGREE[Random( 17 )] ); break; // interjector decides to say nothing case DOST_INTERJECTOR_NOTHING: wcscpy( gzQuoteStr, L"I don't want to say anything, but a bug forces me to! Help! Inform Flugente!" ); break; // another person sides with victim case DOST_SIDEWITH_VICTIM: wcscpy( gzQuoteStr, szDynamicDialogueText_DOST_SIDEWITH_VICTIM[Random( 19 )] ); break; // another person sides with cause case DOST_SIDEWITH_CAUSE: wcscpy( gzQuoteStr, szDynamicDialogueText_DOST_SIDEWITH_CAUSE[Random( 18 )] ); break; default: wcscpy( gzQuoteStr, szDynamicDialogueText[aEvent.data.event.ubEventId][aEvent.ubEventType] ); break; } // insert character nicknames ReplaceTextWithOtherText( gzQuoteStr, L"$VICTIM$", gMercProfiles[aEvent.data.event.ubProfileComplainant].zNickname ); ReplaceTextWithOtherText( gzQuoteStr, L"$CAUSE$", gMercProfiles[aEvent.data.event.ubProfileCause].zNickname ); ReplaceTextWithOtherText( gzQuoteStr, L"$INTERJECTOR$", gMercProfiles[aEvent.data.ubProfileINTERJECTOR].zNickname ); ReplaceTextWithOtherText( gzQuoteStr, L"$SPEAKER$", gMercProfiles[aEvent.usSpeaker].zNickname ); // insert 'he'/'she', 'him'/'her' ReplaceTextWithOtherText( gzQuoteStr, L"$VICTIM_GENDER$", (gMercProfiles[aEvent.data.event.ubProfileComplainant].bSex == MALE) ? szDynamicDialogueText_GenderText[0] : szDynamicDialogueText_GenderText[1] ); ReplaceTextWithOtherText( gzQuoteStr, L"$VICTIM_PRONOUN$", (gMercProfiles[aEvent.data.event.ubProfileComplainant].bSex == MALE) ? szDynamicDialogueText_GenderText[2] : szDynamicDialogueText_GenderText[3] ); ReplaceTextWithOtherText( gzQuoteStr, L"$CAUSE_GENDER$", (gMercProfiles[aEvent.data.event.ubProfileCause].bSex == MALE) ? szDynamicDialogueText_GenderText[0] : szDynamicDialogueText_GenderText[1] ); ReplaceTextWithOtherText( gzQuoteStr, L"$CAUSE_PRONOUN$", (gMercProfiles[aEvent.data.event.ubProfileCause].bSex == MALE) ? szDynamicDialogueText_GenderText[2] : szDynamicDialogueText_GenderText[3] ); ReplaceTextWithOtherText( gzQuoteStr, L"$INTERJECTOR_GENDER$", (gMercProfiles[aEvent.data.ubProfileINTERJECTOR].bSex == MALE) ? szDynamicDialogueText_GenderText[0] : szDynamicDialogueText_GenderText[1] ); ReplaceTextWithOtherText( gzQuoteStr, L"$INTERJECTOR_PRONOUN$", (gMercProfiles[aEvent.data.ubProfileINTERJECTOR].bSex == MALE) ? szDynamicDialogueText_GenderText[2] : szDynamicDialogueText_GenderText[3] ); pDDBox->SetText( gzQuoteStr ); pDDBox->SetEvent( aEvent ); pDDBox->Create( sX, sY ); pDDBox->SetStartTime( aEvent.usStarttime ); // print out the dialogue to the log, too ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s: %s", pSoldier->GetName(), gzQuoteStr ); // delay destruction of all current boxes - as long as new dialogue comes in, they will be kept DelayBoxDestruction( aEvent.usStarttime + gGameExternalOptions.sDynamicDialogueTimeOffset * 3.5f ); // on certain dialogues, we can choose an answer if ( aEvent.ubEventType == DOST_INTERJECTOR_DIALOGUESELECTION ) { std::vector > entryvec; for ( UINT8 i = 0; i < DOST_CHOICE_MAX; ++i ) { wcscpy( stupidarray[i], szDynamicDialogueText_DOST_INTERJECTOR_DIALOGUESELECTION_SHORTTEXT[i] ); // insert character nicknames ReplaceTextWithOtherText( stupidarray[i], L"$VICTIM$", gMercProfiles[aEvent.data.event.ubProfileComplainant].zNickname ); ReplaceTextWithOtherText( stupidarray[i], L"$CAUSE$", gMercProfiles[aEvent.data.event.ubProfileCause].zNickname ); ReplaceTextWithOtherText( stupidarray[i], L"$INTERJECTOR$", gMercProfiles[aEvent.data.ubProfileINTERJECTOR].zNickname ); ReplaceTextWithOtherText( stupidarray[i], L"SPEAKER", gMercProfiles[aEvent.usSpeaker].zNickname ); // insert 'he'/'she', 'him'/'her' ReplaceTextWithOtherText( stupidarray[i], L"$VICTIM_GENDER$", (gMercProfiles[aEvent.data.event.ubProfileComplainant].bSex == MALE) ? szDynamicDialogueText_GenderText[0] : szDynamicDialogueText_GenderText[1] ); ReplaceTextWithOtherText( stupidarray[i], L"$VICTIM_PRONOUN$", (gMercProfiles[aEvent.data.event.ubProfileComplainant].bSex == MALE) ? szDynamicDialogueText_GenderText[2] : szDynamicDialogueText_GenderText[3] ); ReplaceTextWithOtherText( stupidarray[i], L"$CAUSE_GENDER$", (gMercProfiles[aEvent.data.event.ubProfileCause].bSex == MALE) ? szDynamicDialogueText_GenderText[0] : szDynamicDialogueText_GenderText[1] ); ReplaceTextWithOtherText( stupidarray[i], L"$CAUSE_PRONOUN$", (gMercProfiles[aEvent.data.event.ubProfileCause].bSex == MALE) ? szDynamicDialogueText_GenderText[2] : szDynamicDialogueText_GenderText[3] ); entryvec.push_back( std::make_pair( i, stupidarray[i] ) ); } IMPDialogueChooseBox_Static::getInstance( ).SetEvent( aEvent ); IMPDialogueChooseBox_Static::getInstance( ).SetEntries( entryvec ); IMPDialogueChooseBox_Static::getInstance( ).SetHelpText( szDynamicDialogueText[aEvent.data.event.ubEventId][DOST_INTERJECTOR_DIALOGUESELECTION] ); IMPDialogueChooseBox_Static::getInstance( ).Create( sX, sY + 60 ); IMPDialogueChooseBox_Static::getInstance( ).SetStartTime( aEvent.usStarttime ); IMPDialogueChooseBox_Static::getInstance( ).SetEndTime( aEvent.usStarttime + 2 * gGameExternalOptions.sDynamicDialogueTimeOffset ); } return TRUE; } INT8 GetSidePosition( UINT8 aDostPosition ) { INT8 bestpos = -1; for ( size_t i = 0, size = gDynamicOpinionSpeechInCurrentDialogue.size(); i < size; ++i ) { if ( gDynamicOpinionSpeechInCurrentDialogue[i].usSide == aDostPosition ) { bestpos = max( bestpos, gDynamicOpinionSpeechInCurrentDialogue[i].usNumonside ); } } return bestpos + 1; } BOOLEAN fDynamicOpinionSpeechInProgress = FALSE; BOOLEAN DynamicOpinionSpeechInProgress( ) { return fDynamicOpinionSpeechInProgress; } void SetfDynamicOpinionSpeechInProgress( BOOLEAN aVal ) { fDynamicOpinionSpeechInProgress = aVal; } void AddOpinionEvent( UINT8 usProfileA, UINT8 usProfileB, UINT8 usEvent, BOOLEAN fStartDialogue ) { if ( usProfileA == NO_PROFILE || usProfileB == NO_PROFILE ) return; if ( usProfileA == usProfileB ) return; if ( usEvent >= OPINIONEVENT_MAX ) return; // don't use event if opinion modifier is off (thereby providing an easy way to turn off parts of this feature) if ( !gDynamicOpinionEvent[usEvent].sOpinionModifier ) return; // we don't want any events for people not on our team or vehicles SOLDIERTYPE* pSoldier = FindSoldierByProfileID( usProfileA, TRUE ); if ( !pSoldier || pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE || AM_A_ROBOT(pSoldier)) return; pSoldier = FindSoldierByProfileID( usProfileB, TRUE ); if ( !pSoldier || pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) return; UINT32 ustmpFlagmask[OPINION_FLAGMASKS_NUMBER]; for ( UINT8 i = 0; i < OPINION_FLAGMASKS_NUMBER; ++i ) { ustmpFlagmask[i] = gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][i]; } // we add a flag signifiying that an event happened today switch ( usEvent ) { case OPINIONEVENT_FRIENDLYFIRE: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] |= OPINIONFLAG_STAGE1_FRIENDLYFIRE; break; case OPINIONEVENT_SNITCHSOLDMEOUT: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] |= OPINIONFLAG_STAGE1_SNITCHSOLDMEOUT; break; case OPINIONEVENT_SNITCHINTERFERENCE: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] |= OPINIONFLAG_STAGE1_INTERFERENCE; break; case OPINIONEVENT_FRIENDSWITHHATED: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] |= OPINIONFLAG_STAGE1_FRIENDSWITHHATED; break; case OPINIONEVENT_CONTRACTEXTENSION: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] |= OPINIONFLAG_STAGE1_CONTRACTEXTENSION; break; case OPINIONEVENT_ORDEREDRETREAT: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] |= OPINIONFLAG_STAGE1_ORDEREDRETREAT; break; case OPINIONEVENT_CIVKILLER: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] |= OPINIONFLAG_STAGE1_CIVKILLER; break; case OPINIONEVENT_SLOWSUSDOWN: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] |= OPINIONFLAG_STAGE1_SLOWSUSDOWN; break; case OPINIONEVENT_NOSHARINGFOOD: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] |= OPINIONFLAG_STAGE1_NOSHARINGFOOD; break; case OPINIONEVENT_ANNOYINGDISABILITY: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] |= OPINIONFLAG_STAGE1_ANNOYINGDISABILITY; break; case OPINIONEVENT_ADDICT: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] |= OPINIONFLAG_STAGE1_ADDICT; break; case OPINIONEVENT_THIEF: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] |= OPINIONFLAG_STAGE1_THIEF; break; case OPINIONEVENT_WORSTCOMMANDEREVER: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] |= OPINIONFLAG_STAGE1_WORSTCOMMANDEREVER; break; case OPINIONEVENT_RICHGUY: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] |= OPINIONFLAG_STAGE1_RICHGUY; break; case OPINIONEVENT_BETTERGEAR: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] |= OPINIONFLAG_STAGE1_BETTERGEAR; break; case OPINIONEVENT_YOUMOUNTEDAGUNONMYBREASTS: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] |= OPINIONFLAG_STAGE1_YOUMOUNTEDAGUNONMYBREASTS; break; case OPINIONEVENT_BANDAGED: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] |= OPINIONFLAG_STAGE1_BANDAGED; break; // drinking flags are a bit different - drinking can apply the GOOD or BAD flag, and they cancel each other out. // GOOD on GOOD causes SUPER, which cannot be removed (and stops this for today) // BAD on BAD causes WORSE, which cannot be removed (and stops this for today) case OPINIONEVENT_DRINKBUDDIES_GOOD: // if we do not already have the SUPER flag... if ( !(gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & (OPINIONFLAG_STAGE1_DRINKBUDDIES_SUPER | OPINIONFLAG_STAGE1_DRINKBUDDIES_WORSE)) ) { // if we already have GOOD, get SUPER if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE1_DRINKBUDDIES_GOOD ) { gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] |= OPINIONFLAG_STAGE1_DRINKBUDDIES_SUPER; gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] &= ~OPINIONFLAG_STAGE1_DRINKBUDDIES_GOOD; } // if we have BAD, cancel it out else if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE1_DRINKBUDDIES_BAD ) gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] &= ~OPINIONFLAG_STAGE1_DRINKBUDDIES_BAD; // if we have nothing, get GOOD else gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] |= OPINIONFLAG_STAGE1_DRINKBUDDIES_GOOD; } break; case OPINIONEVENT_DRINKBUDDIES_BAD: // if we do not already have the SUPER flag... if ( !(gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & (OPINIONFLAG_STAGE1_DRINKBUDDIES_SUPER | OPINIONFLAG_STAGE1_DRINKBUDDIES_WORSE)) ) { // if we already have BAD, get WORSE if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE1_DRINKBUDDIES_BAD ) { gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] |= OPINIONFLAG_STAGE1_DRINKBUDDIES_WORSE; gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] &= ~OPINIONFLAG_STAGE1_DRINKBUDDIES_BAD; } // if we have GOOD, cancel it out else if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE1_DRINKBUDDIES_GOOD ) gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] &= ~OPINIONFLAG_STAGE1_DRINKBUDDIES_GOOD; // if we have nothing, get BAD else gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] |= OPINIONFLAG_STAGE1_DRINKBUDDIES_BAD; } break; case OPINIONEVENT_AGAINST_US: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] |= OPINIONFLAG_STAGE1_AGAINST_US; break; case OPINIONEVENT_FOR_US: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] |= OPINIONFLAG_STAGE1_FOR_US; break; case OPINIONEVENT_AGAINST_ENEMY: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] |= OPINIONFLAG_STAGE1_AGAINST_ENEMY; break; case OPINIONEVENT_FOR_ENEMY: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] |= OPINIONFLAG_STAGE1_FOR_ENEMY; break; case OPINIONEVENT_SOLVECONFLICT_REASON_GOOD: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] |= OPINIONFLAG_STAGE1_SOLVECONFLICT_REASON_GOOD; break; case OPINIONEVENT_SOLVECONFLICT_REASON_BAD: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] |= OPINIONFLAG_STAGE1_SOLVECONFLICT_REASON_BAD; break; case OPINIONEVENT_SOLVECONFLICT_AGGRESSIVE_GOOD:gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] |= OPINIONFLAG_STAGE1_SOLVECONFLICT_AGGRESSIVE_GOOD; break; case OPINIONEVENT_SOLVECONFLICT_AGGRESSIVE_BAD: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] |= OPINIONFLAG_STAGE1_SOLVECONFLICT_AGGRESSIVE_BAD; break; case OPINIONEVENT_DISEASE_DISGUSTING: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] |= OPINIONFLAG_STAGE1_DISEASE_DISGUSTING; break; case OPINIONEVENT_DISEASE_TREATMENT: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] |= OPINIONFLAG_STAGE1_DISEASE_TREATMENT; break; case OPINIONEVENT_BRUTAL_GOOD: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] |= OPINIONFLAG_STAGE1_BRUTAL_GOOD; break; case OPINIONEVENT_BRUTAL_BAD: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] |= OPINIONFLAG_STAGE1_BRUTAL_BAD; break; case OPINIONEVENT_TEACHER: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] |= OPINIONFLAG_STAGE1_TEACHER; break; case OPINIONEVENT_BESTCOMMANDEREVER: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] |= OPINIONFLAG_STAGE1_BESTCOMMANDEREVER; break; case OPINIONEVENT_BATTLE_SAVIOUR: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] |= OPINIONFLAG_STAGE1_BATTLE_SAVIOUR; break; case OPINIONEVENT_FRAGTHIEF: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] |= OPINIONFLAG_STAGE1_FRAGTHIEF; break; case OPINIONEVENT_BATTLE_ASSIST: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] |= OPINIONFLAG_STAGE1_BATTLE_ASSIST; break; case OPINIONEVENT_BATTLE_TOOK_PRISONER: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] |= OPINIONFLAG_STAGE1_BATTLE_TOOK_PRISONER; break; case OPINIONEVENT_CIV_ATTACKER: gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] |= OPINIONFLAG_STAGE1_CIV_ATTACKER; break; default: break; } // if we set a flag, then this was the first time an event happened today - we can start a dialogue BOOLEAN fSomethingChanged = FALSE; for ( UINT8 i = 0; i < OPINION_FLAGMASKS_NUMBER; ++i ) { if ( ustmpFlagmask[i] != gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][i] ) { fSomethingChanged = TRUE; break; } } if ( fSomethingChanged ) { // if this option is turned on, a small message will show us how opinions have changed if ( gGameExternalOptions.fDynamicOpinionsShowChange ) { ScreenMsg( FONT_MCOLOR_LTGREEN, MSG_INTERFACE, gDynamicOpinionEvent[usEvent].sOpinionModifier >= 0 ? L"%s: %s +%d" : L"%s: %s %d", gMercProfiles[usProfileA].zNickname, gMercProfiles[usProfileB].zNickname, gDynamicOpinionEvent[usEvent].sOpinionModifier ); } // if dialogue is allowed, set up dialogue if ( fStartDialogue && gGameExternalOptions.fDynamicDialogue ) { // we can set up dialogues now OpinionEvent opinionevent; opinionevent.ubEventId = usEvent; // the dynamic opinion event this is all about opinionevent.ubProfileComplainant = usProfileA; // the merc who started this dialogue by complaining opinionevent.ubProfileCause = usProfileB; // the merc who caused the event INT16 idA = GetSoldierIDFromMercID( usProfileA ); if ( idA >= 0 && MercPtrs[idA] ) { opinionevent.usSector = SECTOR( MercPtrs[idA]->sSectorX, MercPtrs[idA]->sSectorY ); AddDynamicOpinionEvent_New( opinionevent ); } } } } // get usProfileA's opinion of usProfileB concerning usEvent on one of the last 4 days INT8 GetDynamicOpinionDay( UINT8 usProfileA, UINT8 usProfileB, UINT8 usDay ) { INT32 opinion = 0; if ( usProfileA == NO_PROFILE || usProfileB == NO_PROFILE ) return opinion; // only for today (0) or one of the last 3 days (1-3) if ( usDay > 3 ) return opinion; for ( UINT8 event = 0; event < OPINIONEVENT_MAX; ++event ) { // every 8 events, we use another mask (one mask = 32 bits, one event use 4 bits) UINT8 mask = event / 8; UINT32 flag = 0x00000001 << (4 * (event % 8)); if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][mask] & flag << usDay ) opinion += gDynamicOpinionEvent[event].sOpinionModifier; } // cut it down to INT8 return (INT8)opinion; } // get usProfileA's opinion of usProfileB concerning usEvent INT8 GetDynamicOpinion( UINT8 usProfileA, UINT8 usProfileB, UINT8 usEvent ) { INT32 opinion = 0; if ( usProfileA == NO_PROFILE || usProfileB == NO_PROFILE ) return opinion; // machines aren't people if ( gMercProfiles[usProfileA].ubBodyType == ROBOTNOWEAPON || gMercProfiles[usProfileB].ubBodyType == ROBOTNOWEAPON ) return opinion; if ( usEvent >= OPINIONEVENT_MAX ) return opinion; // count how many relevant flags are set UINT8 numflags = 0; switch ( usEvent ) { case OPINIONEVENT_FRIENDLYFIRE: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE1_FRIENDLYFIRE ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE2_FRIENDLYFIRE ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE3_FRIENDLYFIRE ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE4_FRIENDLYFIRE ) ++numflags; break; case OPINIONEVENT_SNITCHSOLDMEOUT: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE1_SNITCHSOLDMEOUT ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE2_SNITCHSOLDMEOUT ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE3_SNITCHSOLDMEOUT ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE4_SNITCHSOLDMEOUT ) ++numflags; break; case OPINIONEVENT_SNITCHINTERFERENCE: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE1_INTERFERENCE ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE2_INTERFERENCE ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE3_INTERFERENCE ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE4_INTERFERENCE ) ++numflags; break; case OPINIONEVENT_FRIENDSWITHHATED: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE1_FRIENDSWITHHATED ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE2_FRIENDSWITHHATED ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE3_FRIENDSWITHHATED ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE4_FRIENDSWITHHATED ) ++numflags; break; case OPINIONEVENT_CONTRACTEXTENSION: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE1_CONTRACTEXTENSION ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE2_CONTRACTEXTENSION ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE3_CONTRACTEXTENSION ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE4_CONTRACTEXTENSION ) ++numflags; break; case OPINIONEVENT_ORDEREDRETREAT: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE1_ORDEREDRETREAT ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE2_ORDEREDRETREAT ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE3_ORDEREDRETREAT ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE4_ORDEREDRETREAT ) ++numflags; break; case OPINIONEVENT_CIVKILLER: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE1_CIVKILLER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE2_CIVKILLER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE3_CIVKILLER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE4_CIVKILLER ) ++numflags; break; case OPINIONEVENT_SLOWSUSDOWN: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE1_SLOWSUSDOWN ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE2_SLOWSUSDOWN ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE3_SLOWSUSDOWN ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE4_SLOWSUSDOWN ) ++numflags; break; case OPINIONEVENT_NOSHARINGFOOD: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE1_NOSHARINGFOOD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE2_NOSHARINGFOOD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE3_NOSHARINGFOOD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE4_NOSHARINGFOOD ) ++numflags; break; case OPINIONEVENT_ANNOYINGDISABILITY: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE1_ANNOYINGDISABILITY ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE2_ANNOYINGDISABILITY ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE3_ANNOYINGDISABILITY ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE4_ANNOYINGDISABILITY ) ++numflags; break; case OPINIONEVENT_ADDICT: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE1_ADDICT ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE2_ADDICT ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE3_ADDICT ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE4_ADDICT ) ++numflags; break; case OPINIONEVENT_THIEF: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE1_THIEF ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE2_THIEF ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE3_THIEF ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE4_THIEF ) ++numflags; break; case OPINIONEVENT_WORSTCOMMANDEREVER: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE1_WORSTCOMMANDEREVER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE2_WORSTCOMMANDEREVER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE3_WORSTCOMMANDEREVER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE4_WORSTCOMMANDEREVER ) ++numflags; break; case OPINIONEVENT_RICHGUY: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE1_RICHGUY ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE2_RICHGUY ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE3_RICHGUY ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE4_RICHGUY ) ++numflags; break; case OPINIONEVENT_BETTERGEAR: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE1_BETTERGEAR ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE2_BETTERGEAR ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE3_BETTERGEAR ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE4_BETTERGEAR ) ++numflags; break; case OPINIONEVENT_YOUMOUNTEDAGUNONMYBREASTS: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE1_YOUMOUNTEDAGUNONMYBREASTS ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE2_YOUMOUNTEDAGUNONMYBREASTS ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE3_YOUMOUNTEDAGUNONMYBREASTS ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE4_YOUMOUNTEDAGUNONMYBREASTS ) ++numflags; break; case OPINIONEVENT_BANDAGED: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE1_BANDAGED ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE2_BANDAGED ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE3_BANDAGED ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE4_BANDAGED ) ++numflags; break; case OPINIONEVENT_DRINKBUDDIES_GOOD: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE1_DRINKBUDDIES_GOOD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE2_DRINKBUDDIES_GOOD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE3_DRINKBUDDIES_GOOD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE4_DRINKBUDDIES_GOOD ) ++numflags; break; case OPINIONEVENT_DRINKBUDDIES_SUPER: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE1_DRINKBUDDIES_SUPER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE2_DRINKBUDDIES_SUPER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE3_DRINKBUDDIES_SUPER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE4_DRINKBUDDIES_SUPER ) ++numflags; break; case OPINIONEVENT_DRINKBUDDIES_BAD: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE1_DRINKBUDDIES_BAD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE2_DRINKBUDDIES_BAD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE3_DRINKBUDDIES_BAD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE4_DRINKBUDDIES_BAD ) ++numflags; break; case OPINIONEVENT_DRINKBUDDIES_WORSE: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE1_DRINKBUDDIES_WORSE ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE2_DRINKBUDDIES_WORSE ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE3_DRINKBUDDIES_WORSE ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE4_DRINKBUDDIES_WORSE ) ++numflags; break; case OPINIONEVENT_AGAINST_US: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE1_AGAINST_US ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE2_AGAINST_US ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE3_AGAINST_US ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE4_AGAINST_US ) ++numflags; break; case OPINIONEVENT_FOR_US: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE1_FOR_US ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE2_FOR_US ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE3_FOR_US ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE4_FOR_US ) ++numflags; break; case OPINIONEVENT_AGAINST_ENEMY: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE1_AGAINST_ENEMY ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE2_AGAINST_ENEMY ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE3_AGAINST_ENEMY ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE4_AGAINST_ENEMY ) ++numflags; break; case OPINIONEVENT_FOR_ENEMY: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE1_FOR_ENEMY ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE2_FOR_ENEMY ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE3_FOR_ENEMY ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE4_FOR_ENEMY ) ++numflags; break; case OPINIONEVENT_SOLVECONFLICT_REASON_GOOD: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE1_SOLVECONFLICT_REASON_GOOD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE2_SOLVECONFLICT_REASON_GOOD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE3_SOLVECONFLICT_REASON_GOOD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE4_SOLVECONFLICT_REASON_GOOD ) ++numflags; break; case OPINIONEVENT_SOLVECONFLICT_REASON_BAD: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE1_SOLVECONFLICT_REASON_BAD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE2_SOLVECONFLICT_REASON_BAD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE3_SOLVECONFLICT_REASON_BAD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE4_SOLVECONFLICT_REASON_BAD ) ++numflags; break; case OPINIONEVENT_SOLVECONFLICT_AGGRESSIVE_GOOD: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE1_SOLVECONFLICT_AGGRESSIVE_GOOD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE2_SOLVECONFLICT_AGGRESSIVE_GOOD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE3_SOLVECONFLICT_AGGRESSIVE_GOOD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE4_SOLVECONFLICT_AGGRESSIVE_GOOD ) ++numflags; break; case OPINIONEVENT_SOLVECONFLICT_AGGRESSIVE_BAD: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE1_SOLVECONFLICT_AGGRESSIVE_BAD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE2_SOLVECONFLICT_AGGRESSIVE_BAD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE3_SOLVECONFLICT_AGGRESSIVE_BAD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE4_SOLVECONFLICT_AGGRESSIVE_BAD ) ++numflags; break; case OPINIONEVENT_DISEASE_DISGUSTING: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE1_DISEASE_DISGUSTING ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE2_DISEASE_DISGUSTING ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE3_DISEASE_DISGUSTING ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE4_DISEASE_DISGUSTING ) ++numflags; break; case OPINIONEVENT_DISEASE_TREATMENT: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE1_DISEASE_TREATMENT ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE2_DISEASE_TREATMENT ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE3_DISEASE_TREATMENT ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE4_DISEASE_TREATMENT ) ++numflags; break; case OPINIONEVENT_BRUTAL_GOOD: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE1_BRUTAL_GOOD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE2_BRUTAL_GOOD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE3_BRUTAL_GOOD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE4_BRUTAL_GOOD ) ++numflags; break; case OPINIONEVENT_BRUTAL_BAD: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE1_BRUTAL_BAD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE2_BRUTAL_BAD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE3_BRUTAL_BAD ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE4_BRUTAL_BAD ) ++numflags; break; case OPINIONEVENT_TEACHER: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE1_TEACHER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE2_TEACHER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE3_TEACHER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE4_TEACHER ) ++numflags; break; case OPINIONEVENT_BESTCOMMANDEREVER: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE1_BESTCOMMANDEREVER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE2_BESTCOMMANDEREVER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE3_BESTCOMMANDEREVER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE4_BESTCOMMANDEREVER ) ++numflags; break; case OPINIONEVENT_BATTLE_SAVIOUR: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE1_BATTLE_SAVIOUR ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE2_BATTLE_SAVIOUR ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE3_BATTLE_SAVIOUR ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE4_BATTLE_SAVIOUR ) ++numflags; break; case OPINIONEVENT_FRAGTHIEF: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE1_FRAGTHIEF ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE2_FRAGTHIEF ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE3_FRAGTHIEF ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE4_FRAGTHIEF ) ++numflags; break; case OPINIONEVENT_BATTLE_ASSIST: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE1_BATTLE_ASSIST ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE2_BATTLE_ASSIST ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE3_BATTLE_ASSIST ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE4_BATTLE_ASSIST ) ++numflags; break; case OPINIONEVENT_BATTLE_TOOK_PRISONER: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE1_BATTLE_TOOK_PRISONER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE2_BATTLE_TOOK_PRISONER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE3_BATTLE_TOOK_PRISONER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE4_BATTLE_TOOK_PRISONER ) ++numflags; break; case OPINIONEVENT_CIV_ATTACKER: if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE1_CIV_ATTACKER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE2_CIV_ATTACKER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE3_CIV_ATTACKER ) ++numflags; if ( gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][4] & OPINIONFLAG_STAGE4_CIV_ATTACKER ) ++numflags; break; default: break; } // event opinion is number of times this happened times opinion modifer opinion = numflags * gDynamicOpinionEvent[usEvent].sOpinionModifier; // cut it down to INT8 return (INT8)opinion; } // daily refresh of opinions void HandleDynamicOpinionsDailyRefresh( ) { // a day has passed. We move all opinions one day over, but first, we remember the events of day 3 to 'long-term memory' for ( UINT8 usProfileA = 0; usProfileA < NUM_PROFILES; ++usProfileA ) { for ( UINT8 usProfileB = 0; usProfileB < NUM_PROFILES; ++usProfileB ) { // only do this if there are any flags to begin with if ( (gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][0] & OPINIONFLAG_STAGE4_ALL) || (gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][1] & OPINIONFLAG_STAGE4_ALL) || (gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][2] & OPINIONFLAG_STAGE4_ALL) || (gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][3] & OPINIONFLAG_STAGE4_ALL) ) { INT8 oldday3opinion = GetDynamicOpinionDay( usProfileA, usProfileB, 3 ); // we as time passes, past events aren't that important to us anymore - remember only a fraction of it oldday3opinion = oldday3opinion / 5; // add this to our long-term memory gMercProfiles[usProfileA].sDynamicOpinionLongTerm[usProfileB] += oldday3opinion; } } // each profile has its opinions on everyone else renewed RolloverDynamicOpinions( usProfileA ); } SOLDIERTYPE* pSoldier = NULL; UINT16 bMercID = gTacticalStatus.Team[gbPlayerNum].bFirstID; UINT16 bLastTeamID = gTacticalStatus.Team[gbPlayerNum].bLastID; for ( pSoldier = MercPtrs[bMercID]; bMercID <= bLastTeamID; ++bMercID, pSoldier++ ) { if ( pSoldier->bActive && pSoldier->ubProfile != NO_PROFILE && !(pSoldier->bAssignment == IN_TRANSIT || AM_A_ROBOT(pSoldier) || pSoldier->bAssignment == ASSIGNMENT_DEAD) ) { // or each profile, check wether everyone else is a friend of someone else we hate CheckForFriendsofHated( pSoldier ); // get annoyed on mercs that receive a lot more money than we do HandleDynamicOpinionChange( pSoldier, OPINIONEVENT_RICHGUY, FALSE, FALSE ); // get annoyed if other people have better gear HandleDynamicOpinionChange( pSoldier, OPINIONEVENT_BETTERGEAR, FALSE, FALSE ); } } } // a day has passed, 'age' opinions void RolloverDynamicOpinions( UINT8 usProfileA ) { for ( UINT8 usProfileB = 0; usProfileB < NUM_PROFILES; ++usProfileB ) { for ( UINT8 i = 0; i < OPINION_FLAGMASKS_NUMBER; ++i ) { // events that are at stage 4 are forgotten gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][i] &= ~OPINIONFLAG_STAGE4_ALL; // all other events move up one stage gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][i] = gMercProfiles[usProfileA].usDynamicOpinionFlagmask[usProfileB][i] << 1; } } } // check wether other people are friends with someone else we hate. All persons must be in Arulco void CheckForFriendsofHated( SOLDIERTYPE* pSoldier ) { INT8 bMercID, bOtherID, bThirdID; INT8 bOpinion = -1; INT8 bSecondOpinion = -1; INT8 bLastTeamID; SOLDIERTYPE* pOtherSoldier; SOLDIERTYPE* pThirdSoldier; // make sure we ourselves aren't in transit if ( !pSoldier->bActive || pSoldier->ubProfile == NO_PROFILE || AM_A_ROBOT(pSoldier) || pSoldier->bAssignment == IN_TRANSIT || pSoldier->bAssignment == ASSIGNMENT_DEAD ) return; bMercID = pSoldier->ubID; bLastTeamID = gTacticalStatus.Team[gbPlayerNum].bLastID; // loop through all other mercs bOtherID = gTacticalStatus.Team[gbPlayerNum].bFirstID; for ( pOtherSoldier = MercPtrs[bOtherID]; bOtherID <= bLastTeamID; ++bOtherID, pOtherSoldier++ ) { // skip past ourselves and all inactive mercs if ( bOtherID != bMercID && pOtherSoldier->bActive && pOtherSoldier->ubProfile != NO_PROFILE && !(pOtherSoldier->bAssignment == IN_TRANSIT || pOtherSoldier->bAssignment == ASSIGNMENT_DEAD) ) { bOpinion = SoldierRelation( pSoldier, pOtherSoldier ); // we cannot simply check for HATED_OPINION here - this very feature makes opinions not so easy anymore. Simply check for a range if ( bOpinion < -20 ) { // there is someone in our team that we hate. We dislike his friends somewhat, purely because they like our foe // loop through all other mercs bThirdID = gTacticalStatus.Team[gbPlayerNum].bFirstID; for ( pThirdSoldier = MercPtrs[bThirdID]; bThirdID <= bLastTeamID; ++bThirdID, pThirdSoldier++ ) { // skip past ourselves and all inactive mercs if ( bThirdID != bMercID && bThirdID != bOtherID && pThirdSoldier->bActive && pThirdSoldier->ubProfile != NO_PROFILE && !(pThirdSoldier->bAssignment == IN_TRANSIT || pThirdSoldier->bAssignment == ASSIGNMENT_DEAD) ) { bSecondOpinion = SoldierRelation( pThirdSoldier, pOtherSoldier ); if ( bSecondOpinion > 20 ) { // this guy is friends with someone we hate! We dislike him a bit for that AddOpinionEvent( pSoldier->ubProfile, pThirdSoldier->ubProfile, OPINIONEVENT_FRIENDSWITHHATED ); } } } } } } } void HandleDynamicOpinionOnContractExtension( UINT8 ubCode, UINT8 usProfile ) { if ( usProfile == NO_PROFILE ) return; if ( ubCode == EXTENDED_CONTRACT_BY_1_DAY || ubCode == EXTENDED_CONTRACT_BY_1_WEEK || ubCode == EXTENDED_CONTRACT_BY_2_WEEKS ) { INT16 id = GetSoldierIDFromMercID( usProfile ); if ( id > -1 ) { SOLDIERTYPE* pSoldierWhoGotPaid = MercPtrs[id]; // only for AIM mercs if ( pSoldierWhoGotPaid->ubWhatKindOfMercAmI != MERC_TYPE__AIM_MERC ) return; // determine the remaining length of his contract BEFORE it got renewed INT32 oldcontract = pSoldierWhoGotPaid->iEndofContractTime; if ( ubCode == EXTENDED_CONTRACT_BY_1_DAY ) oldcontract -= 1440; else if ( ubCode == EXTENDED_CONTRACT_BY_1_WEEK ) oldcontract -= 7 * 1440; else if ( ubCode == EXTENDED_CONTRACT_BY_2_WEEKS ) oldcontract -= 14 * 1440; // someones contract got extended. Other mercs who have less time on their contract will be annoyed by this, as they feel they should have been paid first SOLDIERTYPE* pSoldier = NULL; UINT16 bMercID = gTacticalStatus.Team[gbPlayerNum].bFirstID; UINT16 bLastTeamID = gTacticalStatus.Team[gbPlayerNum].bLastID; for ( pSoldier = MercPtrs[bMercID]; bMercID <= bLastTeamID; ++bMercID, pSoldier++ ) { if ( pSoldier->bActive && pSoldier->ubProfile != NO_PROFILE && pSoldier->ubProfile != usProfile && !(pSoldier->bAssignment == IN_TRANSIT || pSoldier->bAssignment == ASSIGNMENT_DEAD) ) { // only for AIM mercs if ( pSoldier->ubProfile == NO_PROFILE || pSoldier->ubWhatKindOfMercAmI != MERC_TYPE__AIM_MERC ) continue; if ( pSoldier->iEndofContractTime < oldcontract ) { // this guy got paid at a point where we had less time than he did! Favouritism! AddOpinionEvent( pSoldier->ubProfile, usProfile, OPINIONEVENT_CONTRACTEXTENSION ); } } } } } } // depending on what happened in the battle, our mercs might complain or celebrate void HandleDynamicOpinionBattleFinished( BOOLEAN fBattleWon ) { UINT32 ourlosses = 0; UINT32 enemylosses = 0; UINT32 oursidesize = 0; UINT32 enemysidesize = 0; // pick the 'leader' who gets all the praise (or blame, depending on how this went) UINT16 leaderid = GetBestMercLeaderInSector( SECTORX( gCurrentIncident.usSector ), SECTORY( gCurrentIncident.usSector ), (INT8)gCurrentIncident.usLevel ); if ( leaderid != NOBODY ) { // while it would be logical to also take civilian losses into account, this would lead to mercs always disapproving engaging hostile civilain factions like Kingpin and the Hicks for ( UINT16 i = 0; i < CAMPAIGNHISTORY_SD_CIV; ++i ) { ourlosses += 10 * gCurrentIncident.usKills[i]; ourlosses += gCurrentIncident.usWounds[i]; ourlosses += 8 * gCurrentIncident.usPrisoners[i]; // having our side being captured is not quite as bad as having them killed oursidesize += gCurrentIncident.usParticipants[i]; } for ( UINT16 i = CAMPAIGNHISTORY_SD_ENEMY_ADMIN; i < CAMPAIGNHISTORY_SD_MAX; ++i ) { enemylosses += 10 * gCurrentIncident.usKills[i]; enemylosses += 11 * gCurrentIncident.usPrisoners[i]; // capturing the enemy is even better than killing them enemysidesize += gCurrentIncident.usParticipants[i]; } // if we've taken more losses than the enemy, our mercs will complain - even if we won. Our mercs don't seem to like phyrric victories... if ( ourlosses > enemylosses && ourlosses > 75 ) { HandleDynamicOpinionChange( MercPtrs[leaderid], OPINIONEVENT_WORSTCOMMANDEREVER, TRUE, TRUE ); } else { // our mercs praise their leader if // they won the battle // the enemy outnumbered them at least 2:1 // the enemy had at least 4 times their losses // the enemy patrol was of significant size (this is supposed to be given for huge battles only) if ( fBattleWon && 2 * oursidesize < enemysidesize && 4 * ourlosses < enemylosses && enemylosses > 10 * gGameExternalOptions.iMaxEnemyGroupSize ) { HandleDynamicOpinionChange( MercPtrs[leaderid], OPINIONEVENT_BESTCOMMANDEREVER, TRUE, TRUE ); } } } } void HandleDynamicOpinionRetreat( ) { // This was a disaster (Ignoring of how high the enemies losses were to create drama :-) )! Let's blame the player -> blame an IMP! std::vector aTaboo; UINT16 impid = GetBestMercLeaderInSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ); // we've found someone competent. Let's all blame him for this disaster! if ( impid != NOBODY ) { HandleDynamicOpinionChange( MercPtrs[impid], OPINIONEVENT_ORDEREDRETREAT, TRUE, TRUE ); } } void HandleDynamicOpinionTeamDrinking( SOLDIERTYPE* pSoldier ) { // need to be drunk for this if ( !pSoldier || pSoldier->ubProfile == NO_PROFILE || AM_A_ROBOT(pSoldier) || pSoldier->newdrugs.drinkstaken <= 0.0 ) return; SOLDIERTYPE* pTeamSoldier = NULL; UINT16 bMercID = gTacticalStatus.Team[gbPlayerNum].bFirstID; UINT16 bLastTeamID = gTacticalStatus.Team[gbPlayerNum].bLastID; for ( pTeamSoldier = MercPtrs[bMercID]; bMercID <= bLastTeamID; ++bMercID, pTeamSoldier++ ) { // everybody other merc in the same sector can get updated if they are drugged too if ( pTeamSoldier->bActive && pTeamSoldier->ubProfile != NO_PROFILE && pTeamSoldier->ubProfile != pSoldier->ubProfile && pTeamSoldier->sSectorX == pSoldier->sSectorX && pTeamSoldier->sSectorY == pSoldier->sSectorY && pTeamSoldier->bSectorZ == pSoldier->bSectorZ && pTeamSoldier->newdrugs.drinkstaken > 0.0 && !(pTeamSoldier->bAssignment == IN_TRANSIT || pTeamSoldier->bAssignment == ASSIGNMENT_DEAD) ) { // both mercs drink together, they opinion either improve or worsen // added a few chance calls, because this can lead to an absurd amount of events if ( Chance( 50 ) ) { if ( Chance( 67 ) ) AddOpinionEvent( pTeamSoldier->ubProfile, pSoldier->ubProfile, OPINIONEVENT_DRINKBUDDIES_GOOD, Chance( 25 ) ); else AddOpinionEvent( pTeamSoldier->ubProfile, pSoldier->ubProfile, OPINIONEVENT_DRINKBUDDIES_BAD, Chance( 25 ) ); } if ( Chance( 50 ) ) { if ( Chance( 67 ) ) AddOpinionEvent( pSoldier->ubProfile, pTeamSoldier->ubProfile, OPINIONEVENT_DRINKBUDDIES_GOOD, Chance( 25 ) ); else AddOpinionEvent( pSoldier->ubProfile, pTeamSoldier->ubProfile, OPINIONEVENT_DRINKBUDDIES_BAD, Chance( 25 ) ); } } } } void HandleDynamicOpinionTeaching( SOLDIERTYPE* pSoldier, UINT8 ubStat ) { if ( !pSoldier || pSoldier->ubProfile == NO_PROFILE || AM_A_ROBOT(pSoldier)) return; // because this code is as annoying as it is, ubStat has a different numbering than bTrainStat. The only reasonable explanation for that is that the designers decided to be jerks on purpose // we have to get the correct bTrainStat INT8 trainstat = 0; switch ( ubStat ) { case STRAMT: trainstat = STRENGTH; break; case DEXTAMT: trainstat = DEXTERITY; break; case AGILAMT: trainstat = AGILITY; break; case HEALTHAMT: trainstat = HEALTH; break; case LDRAMT: trainstat = LEADERSHIP; break; case MARKAMT: trainstat = MARKSMANSHIP; break; case EXPLODEAMT: trainstat = EXPLOSIVE_ASSIGN; break; case MEDICALAMT: trainstat = MEDICAL; break; case MECHANAMT: trainstat = MECHANICAL; break; default: // other stats cannot be trained (including wisdom), so get out of here return; break; } SOLDIERTYPE* pTeamSoldier = NULL; UINT16 bMercID = gTacticalStatus.Team[gbPlayerNum].bFirstID; UINT16 bLastTeamID = gTacticalStatus.Team[gbPlayerNum].bLastID; for ( pTeamSoldier = MercPtrs[bMercID]; bMercID <= bLastTeamID; ++bMercID, pTeamSoldier++ ) { // award event for every trainer in this sector if ( pTeamSoldier->bActive && pTeamSoldier->ubProfile != NO_PROFILE && pTeamSoldier->ubProfile != pSoldier->ubProfile && pTeamSoldier->sSectorX == pSoldier->sSectorX && pTeamSoldier->sSectorY == pSoldier->sSectorY && pTeamSoldier->bSectorZ == pSoldier->bSectorZ && !(pTeamSoldier->bAssignment == IN_TRANSIT || pTeamSoldier->bAssignment == ASSIGNMENT_DEAD) ) { // if he's training teammates in this stat if ( (pTeamSoldier->bAssignment == TRAIN_TEAMMATE) && (pTeamSoldier->bTrainStat == trainstat) && (EnoughTimeOnAssignment( pTeamSoldier )) && !pTeamSoldier->flags.fMercAsleep ) { AddOpinionEvent( pSoldier->ubProfile, pTeamSoldier->ubProfile, OPINIONEVENT_TEACHER ); } } } } // some events require a 'leader' - merc the team will regard as the one being in charge, and subsequently being praised or damned for the way things develop UINT32 GetSoldierLeaderRating( SOLDIERTYPE* pSoldier ) { if ( !pSoldier ) return 0; // rating is based on leadership, experience and the squad leader trait. IMPs get a higher rating, as they represent the player closest UINT32 rating = 0; rating += pSoldier->stats.bLeadership; rating += 10 * pSoldier->stats.bExpLevel; rating += 30 * NUM_SKILL_TRAITS( pSoldier, SQUADLEADER_NT ); rating += 50 * (pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__PLAYER_CHARACTER); return rating; } UINT16 GetBestMercLeaderInSector( INT16 sX, INT16 sY, INT8 sZ ) { UINT32 highestrating = 0; UINT16 bestid = NOBODY; SOLDIERTYPE* pSoldier = NULL; UINT16 bMercID = gTacticalStatus.Team[gbPlayerNum].bFirstID; UINT16 bLastTeamID = gTacticalStatus.Team[gbPlayerNum].bLastID; for ( pSoldier = MercPtrs[bMercID]; bMercID <= bLastTeamID; ++bMercID, ++pSoldier ) { // everybody other merc in the same sector gets annoyed if ( pSoldier->bActive && pSoldier->ubProfile != NO_PROFILE && pSoldier->sSectorX == sX && pSoldier->sSectorY == sY && pSoldier->bSectorZ == sZ && !(pSoldier->bAssignment == IN_TRANSIT || pSoldier->bAssignment == ASSIGNMENT_DEAD) ) { UINT32 rating = GetSoldierLeaderRating( pSoldier ); if ( rating > highestrating ) { highestrating = rating; bestid = bMercID; } } } return bestid; } UINT8 GetRandomMercInSectorNotInList( INT16 sX, INT16 sY, INT8 sZ, std::vector aTaboo, BOOLEAN fImpOnly ) { std::vector resultvector; SOLDIERTYPE* pTeamSoldier = NULL; UINT16 bMercID = gTacticalStatus.Team[gbPlayerNum].bFirstID; UINT16 bLastTeamID = gTacticalStatus.Team[gbPlayerNum].bLastID; for ( pTeamSoldier = MercPtrs[bMercID]; bMercID <= bLastTeamID; ++bMercID, ++pTeamSoldier ) { // everybody other merc in the same sector gets annoyed if ( pTeamSoldier->bActive && pTeamSoldier->ubProfile != NO_PROFILE && pTeamSoldier->sSectorX == sX && pTeamSoldier->sSectorY == sY && pTeamSoldier->bSectorZ == sZ && (!fImpOnly || pTeamSoldier->ubWhatKindOfMercAmI == MERC_TYPE__PLAYER_CHARACTER) && !(pTeamSoldier->bAssignment == IN_TRANSIT || pTeamSoldier->bAssignment == ASSIGNMENT_DEAD) ) { // only add if not already in list if ( std::find( aTaboo.begin( ), aTaboo.end( ), pTeamSoldier->ubProfile ) == aTaboo.end( ) ) resultvector.push_back( bMercID ); } } if ( !resultvector.empty( ) ) { return resultvector[Random( resultvector.size( ) )]; } return NOBODY; } UINT8 GetFittingInterjectorProfile( UINT8 usEvent, UINT8 usProfileVictim, UINT8 usProfileCause ) { if ( usEvent >= OPINIONEVENT_MAX ) return NO_PROFILE; INT16 idVictim = GetSoldierIDFromMercID( usProfileVictim ); INT16 idCause = GetSoldierIDFromMercID( usProfileCause ); if ( idVictim < 0 || idCause < 0 ) return NO_PROFILE; SOLDIERTYPE* pSoldierVictim = MercPtrs[idVictim]; SOLDIERTYPE* pSoldierCause = MercPtrs[idCause]; if ( !pSoldierVictim || !pSoldierCause ) return NO_PROFILE; std::vector impprofilevector; std::vector profilevector; SOLDIERTYPE* pTeamSoldier = NULL; UINT16 bMercID = gTacticalStatus.Team[pSoldierVictim->bTeam].bFirstID; UINT16 bLastTeamID = gTacticalStatus.Team[pSoldierVictim->bTeam].bLastID; for ( pTeamSoldier = MercPtrs[bMercID]; bMercID <= bLastTeamID; ++bMercID, pTeamSoldier++ ) { // only people that are here if ( !pTeamSoldier->bActive || pTeamSoldier->bAssignment == IN_TRANSIT || pTeamSoldier->bAssignment == ASSIGNMENT_DEAD || pTeamSoldier->bAssignment == ASSIGNMENT_POW || pTeamSoldier->bAssignment == ASSIGNMENT_MINIEVENT || pTeamSoldier->bAssignment == ASSIGNMENT_REBELCOMMAND ) continue; // if fSameSector is TRUE then the teammate must be in the same sector if ( gDynamicOpinionEvent[usEvent].fSameSector && (pTeamSoldier->sSectorX != pSoldierVictim->sSectorX || pTeamSoldier->sSectorY != pSoldierVictim->sSectorY || pTeamSoldier->bSectorZ != pSoldierVictim->bSectorZ) ) continue; // if fSameAssignment is TRUE then both mercs must be on the same assignment if ( gDynamicOpinionEvent[usEvent].fSameAssignment && pTeamSoldier->bAssignment != pSoldierVictim->bAssignment ) continue; // if fAwake is TRUE then only awake mercs count if ( gDynamicOpinionEvent[usEvent].fAwake && pTeamSoldier->flags.fMercAsleep ) continue; // if fSquadsOnly is TRUE then only mercs in combat squads count if ( gDynamicOpinionEvent[usEvent].fSquadsOnly && pTeamSoldier->bAssignment >= ON_DUTY ) continue; // exclude victim and cause if ( pTeamSoldier->ubProfile == usProfileVictim || pTeamSoldier->ubProfile == usProfileCause ) continue; profilevector.push_back( pTeamSoldier->ubProfile ); if ( pTeamSoldier->ubWhatKindOfMercAmI == MERC_TYPE__PLAYER_CHARACTER ) impprofilevector.push_back( pTeamSoldier->ubProfile ); } // return a random IMP, if there is one, otherwise just pick a random team member if ( !impprofilevector.empty() ) return impprofilevector[Random( impprofilevector.size( ) )]; if ( !profilevector.empty( ) ) return profilevector[Random( profilevector.size( ) )]; return NO_PROFILE; } UINT8 HighestInventoryCoolness( SOLDIERTYPE* pSoldier ) { UINT8 coolness = 0; if ( !pSoldier ) return coolness; // search for food in our 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 Item exists if ( pSoldier->inv[bLoop].exists( ) ) { coolness = max( coolness, Item[pSoldier->inv[bLoop].usItem].ubCoolness ); } } return coolness; } void HandleDynamicOpinionChange( SOLDIERTYPE* pSoldier, UINT8 usEvent, BOOLEAN fOffender, BOOLEAN fStartDialogue ) { if ( !pSoldier || pSoldier->ubProfile == NO_PROFILE || AM_A_ROBOT(pSoldier)) return; // we might have to compare the soldier to other teammates, determine relevant values INT32 iPercentEncumbranceOffended = 0; BOOLEAN fCheckFood = FALSE; BOOLEAN fCheckDrink = FALSE; UINT8 highestcoolness = 0; UINT32 meanwage = 0; UINT8 explevel = pSoldier->stats.bExpLevel; switch ( usEvent ) { case OPINIONEVENT_CONTRACTEXTENSION: case OPINIONEVENT_ORDEREDRETREAT: case OPINIONEVENT_CIVKILLER: case OPINIONEVENT_ANNOYINGDISABILITY: case OPINIONEVENT_ADDICT: case OPINIONEVENT_THIEF: case OPINIONEVENT_WORSTCOMMANDEREVER: case OPINIONEVENT_DISEASE_DISGUSTING: case OPINIONEVENT_DISEASE_TREATMENT: case OPINIONEVENT_BRUTAL_GOOD: case OPINIONEVENT_BRUTAL_BAD: case OPINIONEVENT_TEACHER: case OPINIONEVENT_BESTCOMMANDEREVER: case OPINIONEVENT_BATTLE_SAVIOUR: case OPINIONEVENT_BATTLE_TOOK_PRISONER: case OPINIONEVENT_FRAGTHIEF: case OPINIONEVENT_BATTLE_ASSIST: case OPINIONEVENT_CIV_ATTACKER: break; case OPINIONEVENT_SLOWSUSDOWN: iPercentEncumbranceOffended = CalculateCarriedWeight( pSoldier ); break; case OPINIONEVENT_NOSHARINGFOOD: fCheckFood = (pSoldier->bFoodLevel < FoodMoraleMods[FOOD_MERC_START_SHOW_HUNGER_SYMBOL].bThreshold); fCheckDrink = (pSoldier->bDrinkLevel < FoodMoraleMods[FOOD_MERC_START_SHOW_HUNGER_SYMBOL].bThreshold); // no hunger - no problem if ( !fCheckFood && !fCheckDrink ) return; break; case OPINIONEVENT_RICHGUY: if ( !pSoldier->iTotalContractLength || (pSoldier->ubWhatKindOfMercAmI != MERC_TYPE__AIM_MERC && pSoldier->ubWhatKindOfMercAmI != MERC_TYPE__MERC && pSoldier->ubWhatKindOfMercAmI != MERC_TYPE__NPC_WITH_UNEXTENDABLE_CONTRACT) ) return; // determine our mean daily wage meanwage = gMercProfiles[pSoldier->ubProfile].uiTotalCostToDate / pSoldier->iTotalContractLength; // hu? if ( !explevel ) return; break; case OPINIONEVENT_BETTERGEAR: highestcoolness = HighestInventoryCoolness( pSoldier ); break; default: // either unknown event, or event is handled elsewhere - exit return; break; } UINT8 usEventUsed; // it is possible that the individual event is switched SOLDIERTYPE* pTeamSoldier = NULL; UINT16 bMercID = gTacticalStatus.Team[gbPlayerNum].bFirstID; UINT16 bLastTeamID = gTacticalStatus.Team[gbPlayerNum].bLastID; for ( pTeamSoldier = MercPtrs[bMercID]; bMercID <= bLastTeamID; ++bMercID, pTeamSoldier++ ) { // we test several conditions before we allow adding an opinion // other merc must be active, have a profile, be someone else and not be in transit or dead if ( pTeamSoldier->bActive && pTeamSoldier->ubProfile != NO_PROFILE && pTeamSoldier->ubProfile != pSoldier->ubProfile && !(pTeamSoldier->bAssignment == IN_TRANSIT || pTeamSoldier->bAssignment == ASSIGNMENT_DEAD) ) { // if fSameSector is TRUE then the teammate must be in the same sector if ( gDynamicOpinionEvent[usEvent].fSameSector && (pTeamSoldier->sSectorX != pSoldier->sSectorX || pTeamSoldier->sSectorY != pSoldier->sSectorY || pTeamSoldier->bSectorZ != pSoldier->bSectorZ) ) continue; // if fSameAssignment is TRUE then both mercs must be on the same assignment if ( gDynamicOpinionEvent[usEvent].fSameAssignment && pTeamSoldier->bAssignment != pSoldier->bAssignment ) continue; // if fAwake is TRUE then only awake mercs count if ( gDynamicOpinionEvent[usEvent].fAwake && pTeamSoldier->flags.fMercAsleep ) continue; // if fSquadsOnly is TRUE then only mercs in combat squads count if ( gDynamicOpinionEvent[usEvent].fSquadsOnly && pTeamSoldier->bAssignment >= ON_DUTY ) continue; usEventUsed = usEvent; // event-specific checks switch ( usEventUsed ) { case OPINIONEVENT_CONTRACTEXTENSION: break; case OPINIONEVENT_ORDEREDRETREAT: // cowards obviously won't complain about a retreat, pacifists are also okay with it if ( DoesMercHavePersonality( pTeamSoldier, CHAR_TRAIT_COWARD ) || DoesMercHavePersonality( pTeamSoldier, CHAR_TRAIT_PACIFIST ) ) continue; break; case OPINIONEVENT_CIVKILLER: case OPINIONEVENT_CIV_ATTACKER: // only continue if this is a good guy, not malicious and not a psacho if ( !(gMercProfiles[pTeamSoldier->ubProfile].ubMiscFlags3 & PROFILE_MISC_FLAG3_GOODGUY) || DoesMercHavePersonality( pTeamSoldier, CHAR_TRAIT_MALICIOUS ) || DoesMercHaveDisability( pTeamSoldier, PSYCHO ) ) continue; break; case OPINIONEVENT_SLOWSUSDOWN: { INT32 iPercentEncumbrance = CalculateCarriedWeight( pTeamSoldier ); if ( iPercentEncumbrance > 100 || iPercentEncumbrance > 0.7 * iPercentEncumbranceOffended ) continue; } break; case OPINIONEVENT_NOSHARINGFOOD: // do not add if we don't have food if ( !HasFoodInInventory( pTeamSoldier, fCheckFood, fCheckDrink ) ) continue; break; case OPINIONEVENT_ANNOYINGDISABILITY: // do not add if we have the same disability if ( gMercProfiles[pTeamSoldier->ubProfile].bDisability == gMercProfiles[pSoldier->ubProfile].bDisability ) continue; break; case OPINIONEVENT_ADDICT: // do not add if we are an addict too if ( pTeamSoldier->HasBackgroundFlag( BACKGROUND_DRUGUSE ) ) continue; break; case OPINIONEVENT_THIEF: // do not add if we are a thief too if ( pTeamSoldier->HasBackgroundFlag( BACKGROUND_SCROUNGING ) ) continue; break; case OPINIONEVENT_WORSTCOMMANDEREVER: break; case OPINIONEVENT_RICHGUY: { if ( !pTeamSoldier->iTotalContractLength || (pTeamSoldier->ubWhatKindOfMercAmI != MERC_TYPE__AIM_MERC && pTeamSoldier->ubWhatKindOfMercAmI != MERC_TYPE__MERC && pTeamSoldier->ubWhatKindOfMercAmI != MERC_TYPE__NPC_WITH_UNEXTENDABLE_CONTRACT) ) continue; // A gets offended of B if // mean wage(B) >= ( WAGE_ACCEPTANCE_FACTOR * level(B) / level(A) ) * mean wage(A) // which equals // mean wage(B) / level(B) >= WAGE_ACCEPTANCE_FACTOR * mean wage(A) / level(A) // their wage UINT32 theirmeanwage = gMercProfiles[pTeamSoldier->ubProfile].uiTotalCostToDate / pTeamSoldier->iTotalContractLength; // adjust this for experience levels FLOAT explevelfactor = gGameExternalOptions.fDynamicWageFactor * pTeamSoldier->stats.bExpLevel / explevel; // abort if their mean wage isn't that high if ( theirmeanwage < explevelfactor * meanwage ) continue; } break; case OPINIONEVENT_BETTERGEAR: // do not add if they don't have cooler stuff if ( HighestInventoryCoolness( pTeamSoldier ) < highestcoolness + 2 ) continue; break; case OPINIONEVENT_DRINKBUDDIES_GOOD: break; case OPINIONEVENT_DRINKBUDDIES_BAD: break; case OPINIONEVENT_DISEASE_DISGUSTING: // not if the other guy is a doctor, is currently used as a doctor, or has disease themself if ( IS_DOCTOR(pTeamSoldier->bAssignment) || pTeamSoldier->bAssignment == DISEASE_DOCTOR_SECTOR || HAS_SKILL_TRAIT( pTeamSoldier, DOCTOR_NT ) || pTeamSoldier->HasDisease(TRUE, FALSE, FALSE) ) continue; break; case OPINIONEVENT_DISEASE_TREATMENT: break; case OPINIONEVENT_BRUTAL_GOOD: case OPINIONEVENT_BRUTAL_BAD: // if we are a good guy or a pacifist, then we dislike excessive violence if ( gMercProfiles[pTeamSoldier->ubProfile].ubMiscFlags3 & PROFILE_MISC_FLAG3_GOODGUY || DoesMercHavePersonality( pTeamSoldier, CHAR_TRAIT_PACIFIST ) ) usEventUsed = OPINIONEVENT_BRUTAL_BAD; // if we are malicious, agressive or a psycho, then we like it else if ( DoesMercHaveDisability( pTeamSoldier, PSYCHO ) || DoesMercHavePersonality( pTeamSoldier, CHAR_TRAIT_AGGRESSIVE ) || DoesMercHavePersonality( pTeamSoldier, CHAR_TRAIT_MALICIOUS ) ) usEventUsed = OPINIONEVENT_BRUTAL_GOOD; else continue; break; case OPINIONEVENT_TEACHER: case OPINIONEVENT_BESTCOMMANDEREVER: break; case OPINIONEVENT_FRAGTHIEF: // we care if we were going for the same target at the same location if ( pTeamSoldier->ubTargetID == pSoldier->ubTargetID && pTeamSoldier->sTargetGridNo == pSoldier->sTargetGridNo ) { // are we ambitious enough to care about kill counts? if ( DoesMercHavePersonality( pTeamSoldier, CHAR_TRAIT_ASSERTIVE ) || DoesMercHavePersonality( pTeamSoldier, CHAR_TRAIT_SHOWOFF ) ) { // did we have enough AP to claim we were about to finish them? if ( !EnoughPoints( pTeamSoldier, 60, 0, FALSE ) ) continue; } else { // we are grateful for the assistance instead usEventUsed = OPINIONEVENT_BATTLE_ASSIST; } } else continue; break; case OPINIONEVENT_BATTLE_TOOK_PRISONER: // we only care if we prefer the enemy to be taken alive if ( !(gMercProfiles[pTeamSoldier->ubProfile].ubMiscFlags3 & PROFILE_MISC_FLAG3_GOODGUY || DoesMercHavePersonality( pTeamSoldier, CHAR_TRAIT_INTELLECTUAL ) || DoesMercHavePersonality( pTeamSoldier, CHAR_TRAIT_PACIFIST ) ) ) continue; break; default: // either unknown event, or event is handled elsewhere - exit return; break; } if ( fOffender ) AddOpinionEvent( pTeamSoldier->ubProfile, pSoldier->ubProfile, usEventUsed, fStartDialogue ); else AddOpinionEvent( pSoldier->ubProfile, pTeamSoldier->ubProfile, usEventUsed, fStartDialogue ); } } }