#include "builddefines.h" #include #include "types.h" #include "Civ Quotes.h" #include "mousesystem.h" #include "strategicmap.h" #include "WCheck.h" #include "FileMan.h" #include "Encrypted File.h" #include "MessageBoxScreen.h" #include "Queen Command.h" #include "Overhead.h" #include "Render Dirty.h" #include "MercTextBox.h" #include "ai.h" #include "Text.h" #include "screenids.h" #include "Animation Data.h" #include "video.h" #include "Font Control.h" #include "message.h" #include "local.h" #include "renderworld.h" #include "Interface.h" #include "Cursors.h" #include "Dialogue Control.h" #include "Quests.h" #include "Strategic Town Loyalty.h" #include "NPC.h" #include "Strategic Mines.h" #include "random.h" #include "connect.h" // for enemy taunts #include "Soldier Profile.h" #include "Campaign.h" #include "opplist.h" #include "Sound Control.h" // sevenfm: voice taunts #define DIALOGUE_DEFAULT_WIDTH 200 #define EXTREAMLY_LOW_TOWN_LOYALTY 20 #define HIGH_TOWN_LOYALTY 80 #define CIV_QUOTE_HINT 99 #define MAX_APPLICABLE_TAUNTS 512 extern void CaptureTimerCallback( void ); BOOLEAN gfSurrendered = FALSE; //-------------------------------------------------------------- //Not used typedef struct { UINT16 ubNumEntries; UINT16 ubUnusedCurrentEntry; //Unused } CIV_QUOTE; CIV_QUOTE gCivQuotes[ NUM_CIV_QUOTES]; //Not used UINT16 gubNumEntries[ NUM_CIV_QUOTES ] = // Not used { 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 5, 5, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 15, 15, 10, 10, 5, 3, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }; //-------------------------------------------------------------- typedef struct { BOOLEAN bActive; MOUSE_REGION MouseRegion; INT32 iVideoOverlay; INT32 iDialogueBox; UINT32 uiTimeOfCreation; UINT32 uiDelayTime; SOLDIERTYPE * pCiv; } QUOTE_SYSTEM_STRUCT; QUOTE_SYSTEM_STRUCT gCivQuoteData; CHAR16 gzCivQuote[ 320 ]; UINT16 gusCivQuoteBoxWidth; UINT16 gusCivQuoteBoxHeight; // anv: store times, when enemy taunt will be finished (so they won't taunt 50 times / second) UINT32 uiTauntFinishTimes[ TOTAL_SOLDIERS ]; TAUNT_VALUES zApplicableTaunts[NUM_TAUNT]; //-------------------------------------------------------------- void CopyNumEntriesIntoQuoteStruct( ) // Not used { INT32 cnt; for ( cnt = 0; cnt < NUM_CIV_QUOTES; ++cnt ) { if (cnt <= 50) gCivQuotes[ cnt ].ubNumEntries = gubNumEntries[ cnt ]; else gCivQuotes[ cnt ].ubNumEntries = 15; } } //-------------------------------------------------------------- BOOLEAN GetCivQuoteText(UINT16 ubCivQuoteID, UINT16 ubEntryID, STR16 zQuote ) { CHAR8 zFileName[164]; // Build filename.... if ( ubCivQuoteID == CIV_QUOTE_HINT ) { if ( gbWorldSectorZ > 0 ) { //sprintf( zFileName, "NPCData\\miners.edt" ); sprintf( zFileName,"NPCDATA\\CIV%02d.edt", CIV_QUOTE_MINERS_NOT_FOR_PLAYER ); } else { sprintf( zFileName, "NPCData\\%c%d.edt", 'A' + (gWorldSectorY - 1) , gWorldSectorX ); } } else { if (ubCivQuoteID <= 9) sprintf( zFileName,"NPCDATA\\CIV%02d.edt",ubCivQuoteID ); else sprintf( zFileName,"NPCDATA\\CIV%d.edt",ubCivQuoteID ); } CHECKF( FileExists( zFileName ) ); // Get data... LoadEncryptedDataFromFile( zFileName, zQuote, ubEntryID * 320, 320 ); if( zQuote[0] == 0 ) { return( FALSE ); } return( TRUE ); } void SurrenderMessageBoxCallBack( UINT8 ubExitValue ) { if ( ubExitValue == MSG_BOX_RETURN_YES ) { AttemptToCapturePlayerSoldiers(); } gTacticalStatus.fEnemyFlags |= ENEMY_OFFERED_SURRENDER; ActionDone( gCivQuoteData.pCiv ); } void ShutDownQuoteBox( BOOLEAN fForce ) { if ( !gCivQuoteData.bActive ) { return; } // Check for min time.... if ( ( GetJA2Clock( ) - gCivQuoteData.uiTimeOfCreation ) > 300 || fForce ) { RemoveVideoOverlay( gCivQuoteData.iVideoOverlay ); // Remove mouse region... MSYS_RemoveRegion( &(gCivQuoteData.MouseRegion) ); RemoveMercPopupBoxFromIndex( gCivQuoteData.iDialogueBox ); gCivQuoteData.iDialogueBox = -1; gCivQuoteData.bActive = FALSE; #ifdef JA2UB // no UB #else // do we need to do anything at the end of the civ quote? if ( gCivQuoteData.pCiv && gCivQuoteData.pCiv->aiData.bAction == AI_ACTION_OFFER_SURRENDER ) { // Haydent if(!is_networked) { DoMessageBox( MSG_BOX_BASIC_STYLE, Message[ STR_SURRENDER ], GAME_SCREEN, ( UINT8 )MSG_BOX_FLAG_YESNO, SurrenderMessageBoxCallBack, NULL ); } else { ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[39] ); ActionDone( gCivQuoteData.pCiv ); } } #endif } } BOOLEAN ShutDownQuoteBoxIfActive( ) { if ( gCivQuoteData.bActive ) { ShutDownQuoteBox( TRUE ); return( TRUE ); } return( FALSE ); } INT8 GetCivType( SOLDIERTYPE *pCiv ) { if ( pCiv->ubProfile != NO_PROFILE ) { return( CIV_TYPE_NA ); } // ATE: Check if this person is married..... // 1 ) check sector.... if ( gWorldSectorX == 10 && gWorldSectorY == 6 && gbWorldSectorZ == 0 ) { // 2 ) the only female.... if ( pCiv->ubCivilianGroup == 0 && pCiv->bTeam != gbPlayerNum && pCiv->ubBodyType == REGFEMALE ) { // She's a ho! return( CIV_TYPE_MARRIED_PC ); } } // OK, look for enemy type - MUST be on enemy team, merc bodytype if ( pCiv->bTeam == ENEMY_TEAM && IS_MERC_BODY_TYPE( pCiv ) ) { return( CIV_TYPE_ENEMY ); } if ( pCiv->bTeam == CREATURE_TEAM && pCiv->ubSoldierClass == SOLDIER_CLASS_BANDIT ) { return( CIV_TYPE_ENEMY ); } if ( pCiv->bTeam != CIV_TEAM && pCiv->bTeam != MILITIA_TEAM ) { return( CIV_TYPE_NA ); } switch( pCiv->ubBodyType ) { case REGMALE: case BIGMALE: case STOCKYMALE: case REGFEMALE: case FATCIV: case MANCIV: case MINICIV: case DRESSCIV: case CRIPPLECIV: return( CIV_TYPE_ADULT ); break; case ADULTFEMALEMONSTER: case AM_MONSTER: case YAF_MONSTER: case YAM_MONSTER: case LARVAE_MONSTER: case INFANT_MONSTER: case QUEENMONSTER: return( CIV_TYPE_NA ); case HATKIDCIV: case KIDCIV: return( CIV_TYPE_KID ); default: return( CIV_TYPE_NA ); } //return( CIV_TYPE_NA ); // not needed when there is a default! (jonathanl) } void RenderCivQuoteBoxOverlay( VIDEO_OVERLAY *pBlitter ) { if ( gCivQuoteData.iVideoOverlay != -1 ) { RenderMercPopUpBoxFromIndex( gCivQuoteData.iDialogueBox, pBlitter->sX, pBlitter->sY, pBlitter->uiDestBuff ); InvalidateRegion( pBlitter->sX, pBlitter->sY, pBlitter->sX + gusCivQuoteBoxWidth, pBlitter->sY + gusCivQuoteBoxHeight ); } } void QuoteOverlayClickCallback( MOUSE_REGION * pRegion, INT32 iReason ) { static BOOLEAN fLButtonDown = FALSE; if (iReason & MSYS_CALLBACK_REASON_LBUTTON_DWN ) { fLButtonDown = TRUE; } if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP && fLButtonDown ) { // Shutdown quote box.... ShutDownQuoteBox( FALSE ); } else if (iReason & MSYS_CALLBACK_REASON_LOST_MOUSE ) { fLButtonDown = FALSE; } } void BeginCivQuote( SOLDIERTYPE *pCiv, UINT16 ubCivQuoteID, UINT16 ubEntryID, INT16 sX, INT16 sY ) { VIDEO_OVERLAY_DESC VideoOverlayDesc; CHAR16 zQuote[ 320 ]; // OK, do we have another on? if ( gCivQuoteData.bActive ) { // Delete? ShutDownQuoteBox( TRUE ); } // get text if ( !GetCivQuoteText( ubCivQuoteID, ubEntryID, zQuote ) ) { return; } swprintf( gzCivQuote, L"\"%s\"", zQuote ); if ( ubCivQuoteID == CIV_QUOTE_HINT ) { MapScreenMessage( FONT_MCOLOR_WHITE, MSG_DIALOG, L"%s", gzCivQuote ); } // Create video oeverlay.... memset( &VideoOverlayDesc, 0, sizeof( VIDEO_OVERLAY_DESC ) ); //never use it anymore //SET_USE_WINFONTS( TRUE ); //SET_WINFONT( giSubTitleWinFont ); // Prepare text box gCivQuoteData.iDialogueBox = PrepareMercPopupBox( gCivQuoteData.iDialogueBox , BASIC_MERC_POPUP_BACKGROUND, BASIC_MERC_POPUP_BORDER, gzCivQuote, DIALOGUE_DEFAULT_WIDTH, 0, 0, 0, &gusCivQuoteBoxWidth, &gusCivQuoteBoxHeight ); //SET_USE_WINFONTS( FALSE ); // OK, find center for box...... sX = sX - ( gusCivQuoteBoxWidth / 2 ); sY = sY - ( gusCivQuoteBoxHeight / 2 ); // OK, limit to screen...... { if ( sX < 0 ) { sX = 0; } // CHECK FOR LEFT/RIGHT if ( ( sX + gusCivQuoteBoxWidth ) > SCREEN_WIDTH ) { sX = SCREEN_WIDTH - gusCivQuoteBoxWidth; } // Now check for top if ( sY < gsVIEWPORT_WINDOW_START_Y ) { sY = gsVIEWPORT_WINDOW_START_Y; } // Check for bottom if ( ( sY + gusCivQuoteBoxHeight ) > (SCREEN_HEIGHT - INV_INTERFACE_HEIGHT)) { sY = (SCREEN_HEIGHT - INV_INTERFACE_HEIGHT) - gusCivQuoteBoxHeight; } } VideoOverlayDesc.sLeft = sX; VideoOverlayDesc.sTop = sY; VideoOverlayDesc.sRight = VideoOverlayDesc.sLeft + gusCivQuoteBoxWidth; VideoOverlayDesc.sBottom = VideoOverlayDesc.sTop + gusCivQuoteBoxHeight; VideoOverlayDesc.sX = VideoOverlayDesc.sLeft; VideoOverlayDesc.sY = VideoOverlayDesc.sTop; VideoOverlayDesc.BltCallback = RenderCivQuoteBoxOverlay; gCivQuoteData.iVideoOverlay = RegisterVideoOverlay( 0, &VideoOverlayDesc ); //Define main region MSYS_DefineRegion( &(gCivQuoteData.MouseRegion), VideoOverlayDesc.sLeft, VideoOverlayDesc.sTop, VideoOverlayDesc.sRight, VideoOverlayDesc.sBottom, MSYS_PRIORITY_HIGHEST, CURSOR_NORMAL, MSYS_NO_CALLBACK, QuoteOverlayClickCallback ); // Add region MSYS_AddRegion( &(gCivQuoteData.MouseRegion) ); gCivQuoteData.bActive = TRUE; gCivQuoteData.uiTimeOfCreation = GetJA2Clock( ); gCivQuoteData.uiDelayTime = FindDelayForString( gzCivQuote ) + 500; gCivQuoteData.pCiv = pCiv; } void BeginChatQuote( SOLDIERTYPE *pCiv, INT16 sX, INT16 sY ) { VIDEO_OVERLAY_DESC VideoOverlayDesc; // OK, do we have another on? if ( gCivQuoteData.bActive ) { // Delete? ShutDownQuoteBox( TRUE ); } if ( pCiv->bTeam == gbPlayerNum ) swprintf( gzCivQuote, L"\"%s\"", szChatTextSpy[Random( 24 )] ); else swprintf( gzCivQuote, L"\"%s\"", szChatTextEnemy[Random( 24 )] ); // Create video oeverlay.... memset( &VideoOverlayDesc, 0, sizeof( VIDEO_OVERLAY_DESC ) ); // Prepare text box gCivQuoteData.iDialogueBox = PrepareMercPopupBox( gCivQuoteData.iDialogueBox, BASIC_MERC_POPUP_BACKGROUND, BASIC_MERC_POPUP_BORDER, gzCivQuote, DIALOGUE_DEFAULT_WIDTH, 0, 0, 0, &gusCivQuoteBoxWidth, &gusCivQuoteBoxHeight ); // Flugente: have the box appear a bit above the soldier. Otherwise it will obstruct us from aiming at him, which is annoying if it happens very often sY -= 30; // OK, find center for box...... sX = sX - ( gusCivQuoteBoxWidth / 2 ); sY = sY - ( gusCivQuoteBoxHeight / 2 ); // OK, limit to screen...... { if ( sX < 0 ) { sX = 0; } // CHECK FOR LEFT/RIGHT if ( ( sX + gusCivQuoteBoxWidth ) > SCREEN_WIDTH ) { sX = SCREEN_WIDTH - gusCivQuoteBoxWidth; } // Now check for top if ( sY < gsVIEWPORT_WINDOW_START_Y ) { sY = gsVIEWPORT_WINDOW_START_Y; } // Check for bottom if ( ( sY + gusCivQuoteBoxHeight ) > ( SCREEN_HEIGHT - INV_INTERFACE_HEIGHT ) ) { sY = ( SCREEN_HEIGHT - INV_INTERFACE_HEIGHT ) - gusCivQuoteBoxHeight; } } VideoOverlayDesc.sLeft = sX; VideoOverlayDesc.sTop = sY; VideoOverlayDesc.sRight = VideoOverlayDesc.sLeft + gusCivQuoteBoxWidth; VideoOverlayDesc.sBottom = VideoOverlayDesc.sTop + gusCivQuoteBoxHeight; VideoOverlayDesc.sX = VideoOverlayDesc.sLeft; VideoOverlayDesc.sY = VideoOverlayDesc.sTop; VideoOverlayDesc.BltCallback = RenderCivQuoteBoxOverlay; gCivQuoteData.iVideoOverlay = RegisterVideoOverlay( 0, &VideoOverlayDesc ); //Define main region MSYS_DefineRegion( &( gCivQuoteData.MouseRegion ), VideoOverlayDesc.sLeft, VideoOverlayDesc.sTop, VideoOverlayDesc.sRight, VideoOverlayDesc.sBottom, MSYS_PRIORITY_HIGHEST, CURSOR_NORMAL, MSYS_NO_CALLBACK, QuoteOverlayClickCallback ); // Add region MSYS_AddRegion( &( gCivQuoteData.MouseRegion ) ); gCivQuoteData.bActive = TRUE; gCivQuoteData.uiTimeOfCreation = GetJA2Clock(); gCivQuoteData.uiDelayTime = FindDelayForString( gzCivQuote ) + 500; gCivQuoteData.pCiv = pCiv; } UINT16 DetermineCivQuoteEntry( SOLDIERTYPE *pCiv, UINT16 *pubCivHintToUse, BOOLEAN fCanUseHints ) { UINT8 ubCivType; #ifdef JA2UB #else INT8 bTownId; INT8 bCivHint; INT8 bMineId; #endif BOOLEAN bCivLowLoyalty = FALSE; BOOLEAN bCivHighLoyalty = FALSE; BOOLEAN bMiners = FALSE; UINT16 FileEDTQUoteID; (*pubCivHintToUse) = 0; ubCivType = GetCivType( pCiv ); if ( pCiv->ubCivilianGroup < NUM_CIV_GROUPS ) { #ifdef JA2UB if ( pCiv->ubCivilianGroup > UNNAMED_CIV_GROUP_19 ) #else if ( pCiv->ubCivilianGroup > QUEENS_CIV_GROUP ) #endif { if ( pCiv->aiData.bNeutral ) { return(FileEDTQUoteID = pCiv->ubCivilianGroup * 2 + 10); } else { return(FileEDTQUoteID = pCiv->ubCivilianGroup * 2 + 11); } } } #ifdef JA2UB if( ubCivType != CIV_TYPE_ENEMY ) { //if the civ is not an enemy if ( pCiv->aiData.bNeutral ) { return( CIV_QUOTE__CIV_NOT_ENEMY ); //43 } else { // //the civ is an enemy // //if the civ can fight if( pCiv->ubBodyType == REGMALE || pCiv->ubBodyType == REGFEMALE || pCiv->ubBodyType == BIGMALE ) { return( CIV_QUOTE__CIV_ENEMY_CAN_FIGHT); //40 } else if( pCiv->stats.bLife < pCiv->stats.bLifeMax ) { return( CIV_QUOTE__CIV_HURT ); //42 } else { return( CIV_QUOTE__CIV_ENEMY_GENERIC ); //41 } } } if( ubCivType == CIV_TYPE_ENEMY ) { // Determine what type of quote to say... // Are are we going to attack? if ( pCiv->aiData.bAction == AI_ACTION_TOSS_PROJECTILE || pCiv->aiData.bAction == AI_ACTION_FIRE_GUN || pCiv->aiData.bAction == AI_ACTION_FIRE_GUN || pCiv->aiData.bAction == AI_ACTION_KNIFE_MOVE ) { return( CIV_QUOTE_ENEMY_THREAT ); } // Hurt? else if ( pCiv->stats.bLife < 30 ) { return( CIV_QUOTE_ENEMY_HURT ); } // elite? else if ( pCiv->ubSoldierClass == SOLDIER_CLASS_ELITE ) { return( CIV_QUOTE_ENEMY_ELITE ); } else { return( CIV_QUOTE_ENEMY_ADMIN ); } } return( 255 ); #else if ( ubCivType == CIV_TYPE_ENEMY ) { // Determine what type of quote to say... // Are are we going to attack? if ( pCiv->aiData.bAction == AI_ACTION_TOSS_PROJECTILE || pCiv->aiData.bAction == AI_ACTION_FIRE_GUN || pCiv->aiData.bAction == AI_ACTION_THROW_KNIFE || pCiv->aiData.bAction == AI_ACTION_KNIFE_MOVE ) { return( CIV_QUOTE_ENEMY_THREAT ); } else if ( pCiv->aiData.bAction == AI_ACTION_OFFER_SURRENDER ) { return( CIV_QUOTE_ENEMY_OFFER_SURRENDER ); } // Hurt? else if ( pCiv->stats.bLife < 30 ) { return( CIV_QUOTE_ENEMY_HURT ); } // elite? else if ( pCiv->ubSoldierClass == SOLDIER_CLASS_ELITE ) { return( CIV_QUOTE_ENEMY_ELITE ); } else { return( CIV_QUOTE_ENEMY_ADMIN ); } } // Are we in a town sector? // get town id bTownId = GetTownIdForSector( gWorldSectorX, gWorldSectorY ); // If a married PC... if ( ubCivType == CIV_TYPE_MARRIED_PC ) { return( CIV_QUOTE_PC_MARRIED ); } // CIV GROUPS FIRST! // Hicks..... if ( pCiv->ubCivilianGroup == HICKS_CIV_GROUP ) { // Are they friendly? //if ( gTacticalStatus.fCivGroupHostile[ HICKS_CIV_GROUP ] < CIV_GROUP_WILL_BECOME_HOSTILE ) if ( pCiv->aiData.bNeutral ) { return( CIV_QUOTE_HICKS_FRIENDLY ); } else { return( CIV_QUOTE_HICKS_ENEMIES ); } } // Goons..... if ( pCiv->ubCivilianGroup == KINGPIN_CIV_GROUP ) { // Are they friendly? //if ( gTacticalStatus.fCivGroupHostile[ KINGPIN_CIV_GROUP ] < CIV_GROUP_WILL_BECOME_HOSTILE ) if ( pCiv->aiData.bNeutral ) { return( CIV_QUOTE_GOONS_FRIENDLY ); } else { return( CIV_QUOTE_GOONS_ENEMIES ); } } // ATE: Cowering people take precedence.... if ( ( pCiv->flags.uiStatusFlags & SOLDIER_COWERING ) || ( pCiv->bTeam == CIV_TEAM && ( gTacticalStatus.uiFlags & INCOMBAT ) ) ) { if ( ubCivType == CIV_TYPE_ADULT ) { return( CIV_QUOTE_ADULTS_COWER ); } else { return( CIV_QUOTE_KIDS_COWER ); } } // Kid slaves... if ( pCiv->ubCivilianGroup == FACTORY_KIDS_GROUP ) { // Check fact..... if ( CheckFact( FACT_DOREEN_HAD_CHANGE_OF_HEART, 0 ) || !CheckFact( FACT_DOREEN_ALIVE, 0 ) ) { return( CIV_QUOTE_KID_SLAVES_FREE ); } else { return( CIV_QUOTE_KID_SLAVES ); } } // BEGGERS if ( pCiv->ubCivilianGroup == BEGGARS_CIV_GROUP ) { // Check if we are in a town... if( bTownId != BLANK_SECTOR && gbWorldSectorZ == 0 ) { if ( bTownId == SAN_MONA && ubCivType == CIV_TYPE_ADULT ) { return( CIV_QUOTE_SAN_MONA_BEGGERS ); } } // DO normal beggers... if ( ubCivType == CIV_TYPE_ADULT ) { return( CIV_QUOTE_ADULTS_BEGGING ); } else { return( CIV_QUOTE_KIDS_BEGGING ); } } // REBELS if ( pCiv->ubCivilianGroup == REBEL_CIV_GROUP ) { // DO normal beggers... if ( ubCivType == CIV_TYPE_ADULT ) { return( CIV_QUOTE_ADULTS_REBELS ); } else { return( CIV_QUOTE_KIDS_REBELS ); } } // Do miltitia... if ( pCiv->bTeam == MILITIA_TEAM ) { // Different types.... if ( pCiv->ubSoldierClass == SOLDIER_CLASS_GREEN_MILITIA ) { return( CIV_QUOTE_GREEN_MILITIA ); } if ( pCiv->ubSoldierClass == SOLDIER_CLASS_REG_MILITIA ) { return( CIV_QUOTE_MEDIUM_MILITIA ); } if ( pCiv->ubSoldierClass == SOLDIER_CLASS_ELITE_MILITIA ) { return( CIV_QUOTE_ELITE_MILITIA ); } } // If we are in medunna, and queen is dead, use these... if ( bTownId == MEDUNA && CheckFact( FACT_QUEEN_DEAD, 0 ) ) { return( CIV_QUOTE_DEIDRANNA_DEAD ); } // if in a town if( ( bTownId != BLANK_SECTOR ) && ( gbWorldSectorZ == 0 ) && gfTownUsesLoyalty[ bTownId ] ) { // Check loyalty special quotes..... // EXTREMELY LOW TOWN LOYALTY... if ( gTownLoyalty[ bTownId ].ubRating < EXTREAMLY_LOW_TOWN_LOYALTY ) { bCivLowLoyalty = TRUE; } // HIGH TOWN LOYALTY... if ( gTownLoyalty[ bTownId ].ubRating >= HIGH_TOWN_LOYALTY ) { bCivHighLoyalty = TRUE; } } // ATE: OK, check if we should look for a civ hint.... if ( fCanUseHints ) { bCivHint = ConsiderCivilianQuotes( gWorldSectorX, gWorldSectorY, gbWorldSectorZ, FALSE ); } else { bCivHint = -1; } // ATE: check miners...... if ( pCiv->ubSoldierClass == SOLDIER_CLASS_MINER ) { bMiners = TRUE; // If not a civ hint available... if ( bCivHint == -1 ) { // Check if they are under our control... // Should I go talk to miner? // Not done yet. // Are they working for us? bMineId = GetIdOfMineForSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ); if ( PlayerControlsMine( bMineId ) ) { return( CIV_QUOTE_MINERS_FOR_PLAYER ); } else { return( CIV_QUOTE_MINERS_NOT_FOR_PLAYER ); } } } // Is one availible? // If we are to say low loyalty, do chance if ( bCivHint != -1 && bCivLowLoyalty && !bMiners ) { if ( Random( 100 ) < 25 ) { // Get rid of hint... bCivHint = -1; } } // Say hint if availible... if ( bCivHint != -1 ) { if ( ubCivType == CIV_TYPE_ADULT ) { (*pubCivHintToUse) = bCivHint; // Set quote as used... ConsiderCivilianQuotes( gWorldSectorX, gWorldSectorY, gbWorldSectorZ, TRUE ); // retrun value.... return( CIV_QUOTE_HINT ); } } if ( bCivLowLoyalty ) { if ( ubCivType == CIV_TYPE_ADULT ) { return( CIV_QUOTE_ADULTS_EXTREMLY_LOW_LOYALTY ); } else { return( CIV_QUOTE_KIDS_EXTREMLY_LOW_LOYALTY ); } } if ( bCivHighLoyalty ) { if ( ubCivType == CIV_TYPE_ADULT ) { return( CIV_QUOTE_ADULTS_HIGH_LOYALTY ); } else { return( CIV_QUOTE_KIDS_HIGH_LOYALTY ); } } // All purpose quote here.... if ( ubCivType == CIV_TYPE_ADULT ) { return( CIV_QUOTE_ADULTS_ALL_PURPOSE ); } else { return( CIV_QUOTE_KIDS_ALL_PURPOSE ); } #endif } void HandleCivQuote( ) { if ( gCivQuoteData.bActive ) { // Check for min time.... if ( ( GetJA2Clock( ) - gCivQuoteData.uiTimeOfCreation ) > gCivQuoteData.uiDelayTime ) { // Stop! ShutDownQuoteBox( TRUE ); } } } void StartCivQuote( SOLDIERTYPE *pCiv ) { UINT16 ubCivQuoteID; UINT16 ubEntryID = 0; INT16 sScreenX, sScreenY; UINT16 ubCivHintToUse; UINT16 CivQuoteDelta = 0; UINT16 ubCivQuoteID2; UINT16 RandomVal; // ATE: Check for old quote..... // This could have been stored on last attempt... if ( pCiv->bCurrentCivQuote == CIV_QUOTE_HINT ) { // Determine which quote to say..... // CAN'T USE HINTS, since we just did one... pCiv->bCurrentCivQuote = -1; pCiv->bCurrentCivQuoteDelta = 0; ubCivQuoteID = DetermineCivQuoteEntry( pCiv, &ubCivHintToUse, FALSE ); } else { // Determine which quote to say..... ubCivQuoteID = DetermineCivQuoteEntry( pCiv, &ubCivHintToUse, TRUE ); } if (ubCivQuoteID == CIV_QUOTE_ADULTS_REBELS || ubCivQuoteID == CIV_QUOTE_KIDS_REBELS || ubCivQuoteID == CIV_QUOTE_ENEMY_OFFER_SURRENDER ) { RandomVal = 5; } else if (ubCivQuoteID == CIV_QUOTE_PC_MARRIED) { RandomVal = 2; } else if (ubCivQuoteID == CIV_QUOTE_HICKS_SEE_US_AT_NIGHT) { RandomVal = 3; } else RandomVal = 15; #ifdef JA2UB if( ubCivQuoteID == 255 ) { return; } #endif // Determine entry id // ATE: Try and get entry from soldier pointer.... if ( ubCivQuoteID != CIV_QUOTE_HINT ) { if ( pCiv->bCurrentCivQuote == -1 ) { // Pick random one //pCiv->bCurrentCivQuote = (INT8)Random( gCivQuotes[ ubCivQuoteID ].ubNumEntries - 2 ); ubCivQuoteID2 = Random(RandomVal-2); pCiv->bCurrentCivQuoteDelta = 0; } else { ubCivQuoteID2 = pCiv->bCurrentCivQuote; } //ubEntryID = pCiv->bCurrentCivQuote + pCiv->bCurrentCivQuoteDelta; ubEntryID = ubCivQuoteID2 + pCiv->bCurrentCivQuoteDelta; } else { ubEntryID =ubCivHintToUse; // ATE: set value for quote ID..... //pCiv->bCurrentCivQuote = ubCivQuoteID; ubCivQuoteID2 = ubCivQuoteID; CivQuoteDelta = ubEntryID; //pCiv->bCurrentCivQuoteDelta = ubEntryID; } // Flugente: if we are an assassin, we speak like the militia we emulate if ( pCiv->usSoldierFlagMask & SOLDIER_ASSASSIN ) { switch ( pCiv->GetUniformType() ) { case UNIFORM_MILITIA_REGULAR: ubCivQuoteID = CIV_QUOTE_MEDIUM_MILITIA; break; case UNIFORM_MILITIA_ELITE: ubCivQuoteID = CIV_QUOTE_ELITE_MILITIA; break; default: ubCivQuoteID = CIV_QUOTE_GREEN_MILITIA; break; } } // Determine location... // Get location of civ on screen..... GetSoldierScreenPos( pCiv, &sScreenX, &sScreenY ); // begin quote BeginCivQuote( pCiv, ubCivQuoteID, ubEntryID, sScreenX, sScreenY ); // Increment use if ( ubCivQuoteID != CIV_QUOTE_HINT ) { //pCiv->bCurrentCivQuoteDelta++; ++CivQuoteDelta; /* if ( pCiv->bCurrentCivQuoteDelta == 2 ) { pCiv->bCurrentCivQuoteDelta = 0; } */ if ( CivQuoteDelta == 2 ) { CivQuoteDelta = 0; } } } void InitCivQuoteSystem( ) { memset( &gCivQuotes, 0, sizeof( gCivQuotes ) ); //Not used memset( &gCivQuoteData, 0, sizeof( gCivQuoteData ) ); gCivQuoteData.bActive = FALSE; gCivQuoteData.iVideoOverlay = -1; gCivQuoteData.iDialogueBox = -1; } //-------------------------------------------------------------- //is allowed remove. Not used and remove from SaveLoadGame.cpp. BOOLEAN SaveCivQuotesToSaveGameFile( HWFILE hFile ) { UINT32 uiNumBytesWritten; FileWrite( hFile, &gCivQuotes, sizeof( gCivQuotes), &uiNumBytesWritten ); if( uiNumBytesWritten != sizeof( gCivQuotes ) ) { return( FALSE ); } return( TRUE ); } // anv: used now //is allowed remove. Not used and remove from SaveLoadGame.cpp. BOOLEAN LoadCivQuotesFromLoadGameFile( HWFILE hFile ) { UINT32 uiNumBytesRead; // anv: reset uiTauntFinishTimes after game is loaded (so enemies can taunt after guiBaseJA2Clock is decreased) memset( &uiTauntFinishTimes, 0, sizeof( uiTauntFinishTimes ) ); FileRead( hFile, &gCivQuotes, sizeof( gCivQuotes ), &uiNumBytesRead ); if( uiNumBytesRead != sizeof( gCivQuotes ) ) { return( FALSE ); } CopyNumEntriesIntoQuoteStruct( ); //Not used return( TRUE ); } //-------------------------------------------------------------- // anv: start enemy taunt with probabilty depending on taunt settings void PossiblyStartEnemyTaunt( SOLDIERTYPE *pCiv, TAUNTTYPE iTauntType, SoldierID uiTargetID ) { SOLDIERTYPE *pTarget = NULL; if( uiTargetID != NOBODY ) { pTarget = MercPtrs[uiTargetID]; } if (is_networked) // No taunts in multiplayer return; // taunts disabled? if( gGameSettings.fOptions[TOPTION_ALLOW_TAUNTS] == FALSE ) { return; } // uh, just in case if( pCiv == NULL ) { return; } // is enemy blocked from taunting at the moment? if( uiTauntFinishTimes[pCiv->ubID] > GetJA2Clock() ) { return; } // check if generated person if ( !(IS_MERC_BODY_TYPE( pCiv )) || !(pCiv->ubProfile == NO_PROFILE) ) { return; } // only enemies and militia taunt // sevenfm: allow voice taunts for hostile civilians with group if (pCiv->bTeam != ENEMY_TEAM && pCiv->bTeam != MILITIA_TEAM && !(pCiv->bTeam == CIV_TEAM && pCiv->ubCivilianGroup != NON_CIV_GROUP && !pCiv->aiData.bNeutral && gTauntsSettings.fTauntVoice)) { return; } /*if ( pCiv->bTeam != ENEMY_TEAM && pCiv->bTeam != MILITIA_TEAM ) { return; }*/ // only visible enemies taunt (unless set otherwise) if ( ( pCiv->bVisible == -1 ) && ( gTauntsSettings.fTauntOnlyVisibleEnemies == TRUE ) ) { return; } // only enemies that are able to speak at the moment can taunt if ( pCiv->stats.bLife < OKLIFE || pCiv->bCollapsed || pCiv->bBreathCollapsed ) { return; } // check probability switch(iTauntType) { case TAUNT_FIRE_GUN: if( Random(100)+1 > gTauntsSettings.ubTauntFireGunChance ) return; break; case TAUNT_FIRE_LAUNCHER: if( Random(100)+1 > gTauntsSettings.ubTauntFireLauncherChance ) return; break; case TAUNT_ATTACK_BLADE: if( Random(100)+1 > gTauntsSettings.ubTauntAttackBladeChance) return; break; case TAUNT_ATTACK_HTH: if( Random(100)+1 > gTauntsSettings.ubTauntAttackHTHChance ) return; break; case TAUNT_THROW_KNIFE: if( Random(100)+1 > gTauntsSettings.ubTauntThrowKnifeChance ) return; break; case TAUNT_THROW_GRENADE: if( Random(100)+1 > gTauntsSettings.ubTauntThrowGrenadeChance ) return; break; case TAUNT_CHARGE_BLADE: if( Random(100)+1 > gTauntsSettings.ubTauntChargeKnifeChance ) return; break; case TAUNT_CHARGE_HTH: if( Random(100)+1 > gTauntsSettings.ubTauntChargeFistsChance ) return; break; case TAUNT_STEAL: if( Random(100)+1 > gTauntsSettings.ubTauntStealChance ) return; break; case TAUNT_RUN_AWAY: if( Random(100)+1 > gTauntsSettings.ubTauntRunAwayChance) return; break; case TAUNT_SEEK_NOISE: if( Random(100)+1 > gTauntsSettings.ubTauntSeekNoiseChance ) return; break; case TAUNT_ALERT: if( Random(100)+1 > gTauntsSettings.ubTauntAlertChance ) return; break; case TAUNT_SUSPICIOUS: if( Random(100)+1 > gTauntsSettings.ubTauntSuspiciousChance) return; break; case TAUNT_GOT_HIT: case TAUNT_GOT_HIT_BLOODLOSS: case TAUNT_GOT_HIT_EXPLOSION: case TAUNT_GOT_HIT_FALLROOF: case TAUNT_GOT_HIT_GAS: case TAUNT_GOT_HIT_GUNFIRE: case TAUNT_GOT_HIT_HTH: case TAUNT_GOT_HIT_BLADE: case TAUNT_GOT_HIT_OBJECT: case TAUNT_GOT_HIT_STRUCTURE_EXPLOSION: case TAUNT_GOT_HIT_TENTACLES: case TAUNT_GOT_HIT_THROWING_KNIFE: if( Random(100)+1 > gTauntsSettings.ubTauntGotHitChance ) return; break; case TAUNT_GOT_BLINDED: case TAUNT_GOT_DEAFENED: if( Random(100)+1 > gTauntsSettings.ubTauntGotDeafenedBlindedChance) return; break; case TAUNT_GOT_ROBBED: if( Random(100)+1 > gTauntsSettings.ubTauntGotRobbedChance) return; break; case TAUNT_GOT_MISSED: case TAUNT_GOT_MISSED_GUNFIRE: case TAUNT_GOT_MISSED_BLADE: case TAUNT_GOT_MISSED_HTH: case TAUNT_GOT_MISSED_THROWING_KNIFE: if( Random(100)+1 > gTauntsSettings.ubTauntGotMissedChance ) return; break; case TAUNT_HIT: case TAUNT_HIT_GUNFIRE: case TAUNT_HIT_BLADE: case TAUNT_HIT_HTH: case TAUNT_HIT_THROWING_KNIFE: if( Random(100)+1 > gTauntsSettings.ubTauntHitChance ) return; break; case TAUNT_KILL: case TAUNT_KILL_GUNFIRE: case TAUNT_KILL_BLADE: case TAUNT_KILL_HTH: case TAUNT_KILL_THROWING_KNIFE: if( Random(100)+1 > gTauntsSettings.ubTauntKillChance ) return; break; case TAUNT_HEAD_POP: if( Random(100)+1 > gTauntsSettings.ubTauntHeadPopChance ) return; break; case TAUNT_MISS: case TAUNT_MISS_GUNFIRE: case TAUNT_MISS_BLADE: case TAUNT_MISS_HTH: case TAUNT_MISS_THROWING_KNIFE: if( Random(100)+1 > gTauntsSettings.ubTauntMissChance ) return; break; case TAUNT_OUT_OF_AMMO: if( Random(100)+1 > gTauntsSettings.ubTauntOutOfAmmoChance ) return; break; case TAUNT_RELOAD: if( Random(100)+1 > gTauntsSettings.ubTauntReloadChance ) return; break; case TAUNT_NOTICED_UNSEEN: if( Random(100)+1 > gTauntsSettings.ubTauntNoticedUnseenChance ) return; break; case TAUNT_SAY_HI: if( Random(100)+1 > gTauntsSettings.ubTauntSayHiChance ) return; break; case TAUNT_INFORM_ABOUT: if( Random(100)+1 > gTauntsSettings.ubTauntInformAboutChance ) return; break; case TAUNT_RIPOSTE: if( Random(100)+1 > gTauntsSettings.ubRiposteChance) return; break; default: break; } StartEnemyTaunt( pCiv, iTauntType, pTarget ); } // SANDRO - soldier taunts void StartEnemyTaunt( SOLDIERTYPE *pCiv, TAUNTTYPE iTauntType, SOLDIERTYPE *pTarget ) { CHAR16 sTauntText[ 320 ]; CHAR16 gzTauntQuote[ 320 ]; UINT16 iApplicableTaunts = 0; // Flugente: zombies don't talk if ( pCiv->IsZombie() ) return; // sevenfm: play audio taunt if possible if (gTauntsSettings.fTauntVoice) { // try to play voice taunt, use noise if gTauntsSettings.fTauntMakeNoise is TRUE PlayVoiceTaunt(pCiv, iTauntType, pTarget); // block this enemy from taunting for a time being uiTauntFinishTimes[pCiv->ubID] = GetJA2Clock() + min(gTauntsSettings.sMaxDelay, max(gTauntsSettings.sMinDelay, FindDelayForString(L"You're the disease and I'm the cure!") + gTauntsSettings.sModDelay)); return; } // anv: check all taunts, and remember those applicable for(UINT16 i=0; iaiData.bAttitude ) { case CUNNINGAID: if( !(zTaunt[ i ].uiFlags & TAUNT_A_CUNNING_AID) ) continue; break; case CUNNINGSOLO: if( !(zTaunt[ i ].uiFlags & TAUNT_A_CUNNING_SOLO) ) continue; break; case BRAVEAID: if( !(zTaunt[ i ].uiFlags & TAUNT_A_BRAVE_AID) ) continue; case BRAVESOLO: if( !(zTaunt[ i ].uiFlags & TAUNT_A_BRAVE_SOLO) ) continue; break; case DEFENSIVE: if( !(zTaunt[ i ].uiFlags & TAUNT_A_DEFENSIVE) ) continue; break; case AGGRESSIVE: if( !(zTaunt[ i ].uiFlags & TAUNT_A_AGGRESSIVE) ) continue; break; default: break; } // check if situation is ok switch(iTauntType) { // actions case TAUNT_FIRE_GUN: if( !(zTaunt[ i ].uiFlags & TAUNT_S_FIRE_GUN) ) continue; break; case TAUNT_FIRE_LAUNCHER: if( !(zTaunt[ i ].uiFlags & TAUNT_S_FIRE_LAUNCHER) ) continue; break; case TAUNT_ATTACK_BLADE: if( !(zTaunt[ i ].uiFlags & TAUNT_S_ATTACK_BLADE) ) continue; break; case TAUNT_ATTACK_HTH: if( !(zTaunt[ i ].uiFlags & TAUNT_S_ATTACK_HTH) ) continue; break; case TAUNT_THROW_KNIFE: if( !(zTaunt[ i ].uiFlags & TAUNT_S_THROW_KNIFE) ) continue; break; case TAUNT_THROW_GRENADE: if( !(zTaunt[ i ].uiFlags & TAUNT_S_THROW_GRENADE) ) continue; break; case TAUNT_OUT_OF_AMMO: if( !(zTaunt[ i ].uiFlags & TAUNT_S_OUT_OF_AMMO) ) continue; break; case TAUNT_RELOAD: if( !(zTaunt[ i ].uiFlags & TAUNT_S_RELOAD) ) continue; break; // AI routines case TAUNT_CHARGE_BLADE: if( !(zTaunt[ i ].uiFlags & TAUNT_S_CHARGE_BLADE) ) continue; break; case TAUNT_CHARGE_HTH: if( !(zTaunt[ i ].uiFlags & TAUNT_S_CHARGE_HTH) ) continue; break; case TAUNT_STEAL: if( !(zTaunt[ i ].uiFlags & TAUNT_S_STEAL) ) continue; break; case TAUNT_RUN_AWAY: if( !(zTaunt[ i ].uiFlags & TAUNT_S_RUN_AWAY) ) continue; break; case TAUNT_SEEK_NOISE: if( !(zTaunt[ i ].uiFlags & TAUNT_S_SEEK_NOISE) ) continue; break; case TAUNT_ALERT: if( !(zTaunt[ i ].uiFlags & TAUNT_S_ALERT) ) continue; break; case TAUNT_SUSPICIOUS: if( !(zTaunt[ i ].uiFlags & TAUNT_S_SUSPICIOUS) ) continue; break; case TAUNT_NOTICED_UNSEEN: if( !(zTaunt[ i ].uiFlags & TAUNT_S_NOTICED_UNSEEN) ) continue; break; case TAUNT_SAY_HI: if( !(zTaunt[ i ].uiFlags & TAUNT_S_SAY_HI) ) continue; break; case TAUNT_INFORM_ABOUT: if( !(zTaunt[ i ].uiFlags & TAUNT_S_INFORM_ABOUT) ) continue; break; // got_hit_xxx case TAUNT_GOT_HIT: if( !(zTaunt[ i ].uiFlags & TAUNT_S_GOT_HIT) ) continue; break; case TAUNT_GOT_HIT_GUNFIRE: if( !(zTaunt[ i ].uiFlags & TAUNT_S_GOT_HIT_GUNFIRE) ) continue; break; case TAUNT_GOT_HIT_BLADE: if( !(zTaunt[ i ].uiFlags & TAUNT_S_GOT_HIT_BLADE) ) continue; break; case TAUNT_GOT_HIT_HTH: if( !(zTaunt[ i ].uiFlags & TAUNT_S_GOT_HIT_HTH) ) continue; break; case TAUNT_GOT_HIT_FALLROOF: if( !(zTaunt[ i ].uiFlags & TAUNT_S_GOT_HIT_FALLROOF) ) continue; break; case TAUNT_GOT_HIT_BLOODLOSS: if( !(zTaunt[ i ].uiFlags & TAUNT_S_GOT_HIT_BLOODLOSS) ) continue; break; case TAUNT_GOT_HIT_EXPLOSION: if( !(zTaunt[ i ].uiFlags & TAUNT_S_GOT_HIT_EXPLOSION) ) continue; break; case TAUNT_GOT_HIT_GAS: if( !(zTaunt[ i ].uiFlags & TAUNT_S_GOT_HIT_GAS) ) continue; break; case TAUNT_GOT_HIT_TENTACLES: if( !(zTaunt[ i ].uiFlags & TAUNT_S_GOT_HIT_TENTACLES) ) continue; break; case TAUNT_GOT_HIT_STRUCTURE_EXPLOSION: if( !(zTaunt[ i ].uiFlags & TAUNT_S_GOT_HIT_STRUCTURE_EXPLOSION) ) continue; break; case TAUNT_GOT_HIT_OBJECT: if( !(zTaunt[ i ].uiFlags & TAUNT_S_GOT_HIT_OBJECT) ) continue; break; case TAUNT_GOT_HIT_THROWING_KNIFE: if( !(zTaunt[ i ].uiFlags & TAUNT_S_GOT_HIT) ) continue; break; case TAUNT_GOT_DEAFENED: if( !(zTaunt[ i ].uiFlags & TAUNT_S_GOT_DEAFENED) ) continue; break; case TAUNT_GOT_BLINDED: if( !(zTaunt[ i ].uiFlags & TAUNT_S_GOT_BLINDED) ) continue; break; // got_missed_xxx case TAUNT_GOT_MISSED: if( !(zTaunt[ i ].uiFlags & TAUNT_S_GOT_MISSED) ) continue; break; case TAUNT_GOT_MISSED_GUNFIRE: if( !(zTaunt[ i ].uiFlags & TAUNT_S_GOT_MISSED_GUNFIRE) ) continue; break; case TAUNT_GOT_MISSED_BLADE: if( !(zTaunt[ i ].uiFlags & TAUNT_S_GOT_MISSED_BLADE) ) continue; break; case TAUNT_GOT_MISSED_HTH: if( !(zTaunt[ i ].uiFlags & TAUNT_S_GOT_MISSED_HTH) ) continue; break; case TAUNT_GOT_MISSED_THROWING_KNIFE: if( !(zTaunt[ i ].uiFlags & TAUNT_S_GOT_MISSED_THROWING_KNIFE) ) continue; break; // hit_xxx case TAUNT_HIT: if( !(zTaunt[ i ].uiFlags & TAUNT_S_HIT) ) continue; break; case TAUNT_HIT_GUNFIRE: if( !(zTaunt[ i ].uiFlags & TAUNT_S_HIT_GUNFIRE) ) continue; break; case TAUNT_HIT_BLADE: if( !(zTaunt[ i ].uiFlags & TAUNT_S_HIT_BLADE) ) continue; break; case TAUNT_HIT_HTH: if( !(zTaunt[ i ].uiFlags & TAUNT_S_HIT_HTH) ) continue; break; case TAUNT_HIT_THROWING_KNIFE: if( !(zTaunt[ i ].uiFlags & TAUNT_S_HIT_THROWING_KNIFE) ) continue; break; // kill_xxx case TAUNT_KILL: if( !(zTaunt[ i ].uiFlags & TAUNT_S_KILL) ) continue; break; case TAUNT_KILL_GUNFIRE: if( !(zTaunt[ i ].uiFlags & TAUNT_S_KILL_GUNFIRE) ) continue; break; case TAUNT_KILL_BLADE: if( !(zTaunt[ i ].uiFlags & TAUNT_S_KILL_BLADE) ) continue; break; case TAUNT_KILL_HTH: if( !(zTaunt[ i ].uiFlags & TAUNT_S_KILL_HTH) ) continue; break; case TAUNT_KILL_THROWING_KNIFE: if( !(zTaunt[ i ].uiFlags & TAUNT_S_KILL_THROWING_KNIFE) ) continue; break; case TAUNT_HEAD_POP: if( !(zTaunt[ i ].uiFlags & TAUNT_S_HEAD_POP) ) continue; break; // miss_xxx case TAUNT_MISS: if( !(zTaunt[ i ].uiFlags & TAUNT_S_MISS) ) continue; break; case TAUNT_MISS_GUNFIRE: if( !(zTaunt[ i ].uiFlags & TAUNT_S_MISS_GUNFIRE) ) continue; break; case TAUNT_MISS_BLADE: if( !(zTaunt[ i ].uiFlags & TAUNT_S_MISS_BLADE) ) continue; break; case TAUNT_MISS_HTH: if( !(zTaunt[ i ].uiFlags & TAUNT_S_MISS_HTH) ) continue; break; case TAUNT_MISS_THROWING_KNIFE: if( !(zTaunt[ i ].uiFlags & TAUNT_S_MISS_THROWING_KNIFE) ) continue; break; // ripostes to merc quotes case TAUNT_RIPOSTE: if( !(gTacticalStatus.ubLastQuoteSaid == zTaunt[ i ].value[TAUNT_RIPOSTE_QUOTE]) ) continue; break; default: continue; break; } // class and predefined profiles switch( pCiv->ubSoldierClass ) { case SOLDIER_CLASS_ADMINISTRATOR: case SOLDIER_CLASS_BANDIT: if( !(zTaunt[ i ].uiFlags2 & TAUNT_C_ADMIN) ) continue; if( (zTaunt[ i ].value[TAUNT_PROFILE_ADMIN] != -1 ) && !(zTaunt[ i ].value[TAUNT_PROFILE_ADMIN] == pCiv->usSoldierProfile ) ) continue; break; case SOLDIER_CLASS_ARMY: if( !(zTaunt[ i ].uiFlags2 & TAUNT_C_ARMY) ) continue; if( (zTaunt[ i ].value[TAUNT_PROFILE_ARMY] != -1 ) && !(zTaunt[ i ].value[TAUNT_PROFILE_ARMY] == pCiv->usSoldierProfile ) ) continue; break; case SOLDIER_CLASS_ELITE: if( !(zTaunt[ i ].uiFlags2 & TAUNT_C_ELITE) ) continue; if( (zTaunt[ i ].value[TAUNT_PROFILE_ELITE] != -1 ) && !(zTaunt[ i ].value[TAUNT_PROFILE_ELITE] == pCiv->usSoldierProfile ) ) continue; break; case SOLDIER_CLASS_GREEN_MILITIA: if( !(zTaunt[ i ].uiFlags2 & TAUNT_C_GREEN) ) continue; if( (zTaunt[ i ].value[TAUNT_PROFILE_GREEN] != -1 ) && !( zTaunt[ i ].value[TAUNT_PROFILE_GREEN] == pCiv->usSoldierProfile ) ) continue; break; case SOLDIER_CLASS_REG_MILITIA: if( !(zTaunt[ i ].uiFlags2 & TAUNT_C_REGULAR) ) continue; if( (zTaunt[ i ].value[TAUNT_PROFILE_ARMY] != -1 ) && !(zTaunt[ i ].value[TAUNT_PROFILE_ARMY] == pCiv->usSoldierProfile ) ) continue; break; case SOLDIER_CLASS_ELITE_MILITIA: if( !(zTaunt[ i ].uiFlags2 & TAUNT_C_VETERAN) ) continue; if( (zTaunt[ i ].value[TAUNT_PROFILE_ARMY] != -1 ) && !(zTaunt[ i ].value[TAUNT_PROFILE_ARMY] == pCiv->usSoldierProfile ) ) continue; break; default: return; break; } // gender switch( pCiv->ubBodyType ) { case REGMALE: case BIGMALE: case STOCKYMALE: if( !(zTaunt[ i ].uiFlags2 & TAUNT_G_MALE) ) continue; break; case REGFEMALE: if( !(zTaunt[ i ].uiFlags2 & TAUNT_G_FEMALE) ) continue; break; default: return; break; } // energy, health if( zTaunt[ i ].value[TAUNT_ENERGY_GT] != -1 ) { if( pCiv->bBreath <= zTaunt[ i ].value[TAUNT_ENERGY_GT] ) continue; } if( zTaunt[ i ].value[TAUNT_ENERGY_LT] != -1 ) { if( pCiv->bBreath >= zTaunt[ i ].value[TAUNT_ENERGY_LT] ) continue; } if( zTaunt[ i ].value[TAUNT_ENERGY_MAX_GT] != -1 ) { if( pCiv->bBreathMax <= zTaunt[ i ].value[TAUNT_ENERGY_MAX_GT] ) continue; } if( zTaunt[ i ].value[TAUNT_ENERGY_MAX_LT] != -1 ) { if( pCiv->bBreathMax >= zTaunt[ i ].value[TAUNT_ENERGY_MAX_LT] ) continue; } if( zTaunt[ i ].value[TAUNT_HEALTH_GT] != -1 ) { if( pCiv->stats.bLife <= zTaunt[ i ].value[TAUNT_HEALTH_GT] ) continue; } if( zTaunt[ i ].value[TAUNT_HEALTH_GT] != -1 ) { if( pCiv->stats.bLife <= zTaunt[ i ].value[TAUNT_TARGET_HEALTH_GT] ) continue; } if( zTaunt[ i ].value[TAUNT_HEALTH_LT] != -1 ) { if( pCiv->stats.bLife >= zTaunt[ i ].value[TAUNT_HEALTH_LT] ) continue; } if( zTaunt[ i ].value[TAUNT_HEALTH_MAX_GT] != -1 ) { if( pCiv->stats.bLifeMax <= zTaunt[ i ].value[TAUNT_HEALTH_MAX_GT] ) continue; } if( zTaunt[ i ].value[TAUNT_HEALTH_MAX_LT] != -1 ) { if( pCiv->stats.bLifeMax >= zTaunt[ i ].value[TAUNT_HEALTH_MAX_LT] ) continue; } // morale if( zTaunt[ i ].value[TAUNT_MORALE_GT] != -1 ) { if( pCiv->aiData.bMorale <= zTaunt[ i ].value[TAUNT_MORALE_GT] ) continue; } if( zTaunt[ i ].value[TAUNT_MORALE_LT] != -1 ) { if( pCiv->aiData.bMorale >= zTaunt[ i ].value[TAUNT_MORALE_LT] ) continue; } // experience if( zTaunt[ i ].value[TAUNT_EXP_LEVEL_GT] != -1 ) { if( pCiv->stats.bExpLevel <= zTaunt[ i ].value[TAUNT_EXP_LEVEL_GT] ) continue; } if( zTaunt[ i ].value[TAUNT_EXP_LEVEL_LT] != -1 ) { if( pCiv->stats.bExpLevel >= zTaunt[ i ].value[TAUNT_EXP_LEVEL_LT] ) continue; } // game progress if( zTaunt[ i ].value[TAUNT_PROGRESS_GT] != -1 ) { if( zTaunt[ i ].value[TAUNT_PROGRESS_GT] >= CurrentPlayerProgressPercentage() ) continue; } if( zTaunt[ i ].value[TAUNT_PROGRESS_LT] != -1 ) { if( zTaunt[ i ].value[TAUNT_PROGRESS_GT] <= CurrentPlayerProgressPercentage() ) continue; } // facts if( zTaunt[ i ].value[TAUNT_FACT_TRUE] != -1 ) { if( gubFact[ zTaunt[ i ].value[TAUNT_FACT_TRUE] ] != TRUE ) continue; } if( zTaunt[ i ].value[TAUNT_FACT_FALSE] != -1 ) { if( gubFact[ zTaunt[ i ].value[TAUNT_FACT_FALSE] ] != FALSE ) continue; } // target limitations if( pTarget != NULL ) { // target should be zombie if( zTaunt[ i ].uiFlags2 & TAUNT_T_ZOMBIE ) { // anv: moved ifdef - if zombies are off, we want to skip any taunts with TAUNT_T_ZOMBIE flag if( !pTarget->IsZombie() ) continue; } // target merc profile if( zTaunt[ i ].value[TAUNT_TARGET_MERC_PROFILE] != -1 ) { if( pTarget->ubProfile != zTaunt[ i ].value[TAUNT_TARGET_MERC_PROFILE] ) continue; } // target type if( zTaunt[ i ].value[TAUNT_TARGET_TYPE] != -1 ) { if( pTarget->ubProfile != zTaunt[ i ].value[TAUNT_TARGET_TYPE] ) continue; } // target gender switch( pTarget->ubBodyType ) { case REGMALE: case BIGMALE: case STOCKYMALE: if( !(zTaunt[ i ].uiFlags2 & TAUNT_T_MALE) ) continue; break; case REGFEMALE: if( !(zTaunt[ i ].uiFlags2 & TAUNT_T_FEMALE) ) continue; break; default: return; break; } // target appearance if( zTaunt[ i ].value[TAUNT_TARGET_APPEARANCE] != -1 ) { // check if pTarget has his own predefined profile (ubProfile = 200 for generated characters) if( pTarget->ubProfile != NO_PROFILE ) { if( gMercProfiles[pTarget->ubProfile].bAppearance != zTaunt[ i ].value[TAUNT_TARGET_APPEARANCE] ) continue; } else continue; } // target type switch( zTaunt[ i ].value[TAUNT_TARGET_TYPE] ) { case 0: if( pTarget->ubWhatKindOfMercAmI != MERC_TYPE__PLAYER_CHARACTER ) continue; break; case 1: if( pTarget->ubWhatKindOfMercAmI != MERC_TYPE__AIM_MERC ) continue; break; case 2: if( pTarget->ubWhatKindOfMercAmI != MERC_TYPE__MERC ) continue; break; case 3: if( pTarget->ubWhatKindOfMercAmI != MERC_TYPE__NPC ) continue; break; case 4: if( pTarget->ubWhatKindOfMercAmI != MERC_TYPE__EPC ) continue; break; case 5: if( pTarget->ubWhatKindOfMercAmI != MERC_TYPE__NPC_WITH_UNEXTENDABLE_CONTRACT ) continue; break; case 6: if( pTarget->ubWhatKindOfMercAmI != MERC_TYPE__VEHICLE ) continue; break; default: break; } // target energy, health if( zTaunt[ i ].value[TAUNT_TARGET_ENERGY_GT] != -1 ) { if( pTarget->bBreath <= zTaunt[ i ].value[TAUNT_TARGET_ENERGY_GT] ) continue; } if( zTaunt[ i ].value[TAUNT_TARGET_ENERGY_LT] != -1 ) { if( pTarget->bBreath >= zTaunt[ i ].value[TAUNT_TARGET_ENERGY_LT] ) continue; } if( zTaunt[ i ].value[TAUNT_TARGET_ENERGY_MAX_GT] != -1 ) { if( pTarget->bBreathMax <= zTaunt[ i ].value[TAUNT_TARGET_ENERGY_MAX_GT] ) continue; } if( zTaunt[ i ].value[TAUNT_TARGET_ENERGY_MAX_LT] != -1 ) { if( pTarget->bBreathMax >= zTaunt[ i ].value[TAUNT_TARGET_ENERGY_MAX_LT] ) continue; } if( zTaunt[ i ].value[TAUNT_TARGET_HEALTH_GT] != -1 ) { if( pTarget->stats.bLife <= zTaunt[ i ].value[TAUNT_TARGET_HEALTH_GT] ) continue; } if( zTaunt[ i ].value[TAUNT_TARGET_HEALTH_GT] != -1 ) { if( pTarget->stats.bLife <= zTaunt[ i ].value[TAUNT_TARGET_HEALTH_GT] ) continue; } if( zTaunt[ i ].value[TAUNT_TARGET_HEALTH_LT] != -1 ) { if( pTarget->stats.bLife >= zTaunt[ i ].value[TAUNT_TARGET_HEALTH_LT] ) continue; } if( zTaunt[ i ].value[TAUNT_TARGET_HEALTH_MAX_GT] != -1 ) { if( pTarget->stats.bLifeMax <= zTaunt[ i ].value[TAUNT_TARGET_HEALTH_MAX_GT] ) continue; } if( zTaunt[ i ].value[TAUNT_TARGET_HEALTH_MAX_LT] != -1 ) { if( pTarget->stats.bLifeMax >= zTaunt[ i ].value[TAUNT_TARGET_HEALTH_MAX_LT] ) continue; } // morale if( zTaunt[ i ].value[TAUNT_TARGET_MORALE_GT] != -1 ) { if( pTarget->aiData.bMorale <= zTaunt[ i ].value[TAUNT_TARGET_MORALE_GT] ) continue; } if( zTaunt[ i ].value[TAUNT_TARGET_MORALE_LT] != -1 ) { if( pTarget->aiData.bMorale >= zTaunt[ i ].value[TAUNT_TARGET_MORALE_LT] ) continue; } // experience if( zTaunt[ i ].value[TAUNT_TARGET_EXP_LEVEL_GT] != -1 ) { if( pTarget->stats.bExpLevel <= zTaunt[ i ].value[TAUNT_TARGET_EXP_LEVEL_GT] ) continue; } if( zTaunt[ i ].value[TAUNT_TARGET_EXP_LEVEL_LT] != -1 ) { if( pTarget->stats.bExpLevel >= zTaunt[ i ].value[TAUNT_TARGET_EXP_LEVEL_LT] ) continue; } } else // pTarget==NULL { if( ( zTaunt[ i ].value[TAUNT_TARGET_MERC_PROFILE] != -1 ) || ( zTaunt[ i ].value[TAUNT_TARGET_APPEARANCE] != -1 ) || ( zTaunt[ i ].value[TAUNT_TARGET_ENERGY_GT] != -1 ) || ( zTaunt[ i ].value[TAUNT_TARGET_ENERGY_LT] != -1 ) || ( zTaunt[ i ].value[TAUNT_TARGET_ENERGY_MAX_GT] != -1 ) || ( zTaunt[ i ].value[TAUNT_TARGET_ENERGY_MAX_LT] != -1 ) || ( zTaunt[ i ].value[TAUNT_TARGET_HEALTH_GT] != -1 ) || ( zTaunt[ i ].value[TAUNT_TARGET_HEALTH_LT] != -1 ) || ( zTaunt[ i ].value[TAUNT_TARGET_HEALTH_MAX_GT] != -1 ) || ( zTaunt[ i ].value[TAUNT_TARGET_HEALTH_MAX_LT] != -1 ) || ( zTaunt[ i ].value[TAUNT_TARGET_EXP_LEVEL_GT] != -1 ) || ( zTaunt[ i ].value[TAUNT_TARGET_EXP_LEVEL_LT] != -1 ) || ( zTaunt[ i ].value[TAUNT_TARGET_MORALE_GT] != -1 ) || ( zTaunt[ i ].value[TAUNT_TARGET_MORALE_LT] != -1 ) || ( zTaunt[ i ].value[TAUNT_TARGET_TYPE] != -1 ) || ( zTaunt[ i ].uiFlags2 & TAUNT_T_ZOMBIE ) ) continue; } // everything ok, current taunt is applicable, remember it zApplicableTaunts[iApplicableTaunts] = zTaunt[i]; iApplicableTaunts++; if(iApplicableTaunts >= MAX_APPLICABLE_TAUNTS) continue; } // are there any applicable taunts? if( iApplicableTaunts > 0 ) { // use random one // use censored version if setting is set UINT16 iChosenTaunt = Random(iApplicableTaunts); if( gTauntsSettings.fTauntCensoredMode == TRUE && zApplicableTaunts[ iChosenTaunt ].szCensoredText[0] != 0 ) { swprintf( sTauntText, zApplicableTaunts[ iChosenTaunt ].szCensoredText ); } else { swprintf( sTauntText, zApplicableTaunts[ iChosenTaunt ].szText ); } swprintf( gzTauntQuote, L"\"%s\"", sTauntText ); // block this enemy from taunting for a time being uiTauntFinishTimes[pCiv->ubID] = GetJA2Clock() + min( gTauntsSettings.sMaxDelay , max( gTauntsSettings.sMinDelay, FindDelayForString( gzTauntQuote ) + gTauntsSettings.sModDelay ) ); if( gTauntsSettings.fTauntMakeNoise == TRUE ) MakeNoise( pCiv->ubID, pCiv->sGridNo, pCiv->pathing.bLevel, pCiv->bOverTerrainType, (UINT8)gTauntsSettings.sVolume, NOISE_VOICE, gzTauntQuote ); else { if(gTauntsSettings.fTauntShowPopupBox == TRUE) { if( gbPublicOpplist[gbPlayerNum][pCiv->ubID] == SEEN_CURRENTLY || gTauntsSettings.fTauntAlwaysShowPopupBox == TRUE ) { ShowTauntPopupBox( pCiv, gzTauntQuote ); } } if(gTauntsSettings.fTauntShowInLog == TRUE) { if( gbPublicOpplist[gbPlayerNum][pCiv->ubID] == SEEN_CURRENTLY || gTauntsSettings.fTauntAlwaysShowInLog == TRUE ) { ScreenMsg( FONT_GRAY2, MSG_INTERFACE, L"%s: %s", pCiv->GetName(), gzTauntQuote ); } } } } else { return; } } void ShowTauntPopupBox( SOLDIERTYPE *pCiv, STR16 gzTauntQuote ) { INT16 sX, sY; INT16 sScreenX, sScreenY; VIDEO_OVERLAY_DESC VideoOverlayDesc; // stop if other civ quote is already being shown if( gCivQuoteData.bActive == TRUE ) { return; } // Determine location... // Get location of civ on screen..... GetSoldierScreenPos( pCiv, &sScreenX, &sScreenY ); sX = sScreenX; // Flugente: have the box appear a bit above the soldier. Otherwise it will obstruct us from aiming at him, which is annoying if it happens very often sY = sScreenY - 40; // Create video oeverlay.... memset( &VideoOverlayDesc, 0, sizeof( VIDEO_OVERLAY_DESC ) ); // Prepare text box gCivQuoteData.iDialogueBox = PrepareMercPopupBox( gCivQuoteData.iDialogueBox , BASIC_MERC_POPUP_BACKGROUND, BASIC_MERC_POPUP_BORDER, gzTauntQuote, DIALOGUE_DEFAULT_WIDTH, 0, 0, 0, &gusCivQuoteBoxWidth, &gusCivQuoteBoxHeight ); // OK, find center for box...... sX = sX - ( gusCivQuoteBoxWidth / 2 ); sY = sY - ( gusCivQuoteBoxHeight / 2 ); // OK, limit to screen...... { if ( sX < 0 ) { sX = 0; } // CHECK FOR LEFT/RIGHT if ( ( sX + gusCivQuoteBoxWidth ) > SCREEN_WIDTH ) { sX = SCREEN_WIDTH - gusCivQuoteBoxWidth; } // Now check for top if ( sY < gsVIEWPORT_WINDOW_START_Y ) { sY = gsVIEWPORT_WINDOW_START_Y; } // Check for bottom if ( ( sY + gusCivQuoteBoxHeight ) > (SCREEN_HEIGHT - INV_INTERFACE_HEIGHT)) { sY = (SCREEN_HEIGHT - INV_INTERFACE_HEIGHT) - gusCivQuoteBoxHeight; } } VideoOverlayDesc.sLeft = sX; VideoOverlayDesc.sTop = sY; VideoOverlayDesc.sRight = VideoOverlayDesc.sLeft + gusCivQuoteBoxWidth; VideoOverlayDesc.sBottom = VideoOverlayDesc.sTop + gusCivQuoteBoxHeight; VideoOverlayDesc.sX = VideoOverlayDesc.sLeft; VideoOverlayDesc.sY = VideoOverlayDesc.sTop; VideoOverlayDesc.BltCallback = RenderCivQuoteBoxOverlay; gCivQuoteData.iVideoOverlay = RegisterVideoOverlay( 0, &VideoOverlayDesc ); //Define main region MSYS_DefineRegion( &(gCivQuoteData.MouseRegion), VideoOverlayDesc.sLeft, VideoOverlayDesc.sTop, VideoOverlayDesc.sRight, VideoOverlayDesc.sBottom, MSYS_PRIORITY_HIGHEST, CURSOR_NORMAL, MSYS_NO_CALLBACK, QuoteOverlayClickCallback ); // Add region MSYS_AddRegion( &(gCivQuoteData.MouseRegion) ); gCivQuoteData.bActive = TRUE; gCivQuoteData.uiTimeOfCreation = GetJA2Clock( ); gCivQuoteData.uiDelayTime = min( gTauntsSettings.sMaxDelay , max( gTauntsSettings.sMinDelay, FindDelayForString( gzTauntQuote ) + gTauntsSettings.sModDelay ) ); gCivQuoteData.pCiv = pCiv; } BOOLEAN CivQuoteActive() { return gCivQuoteData.bActive; } STR VoiceTauntFileName[] = { "FIRE_GUN", "FIRE_LAUNCHER", "ATTACK_BLADE", "ATTACK_HTH", "THROW_KNIFE", "THROW_GRENADE", "OUT_OF_AMMO", "RELOAD", "STEAL", // AI routines "CHARGE_BLADE", "CHARGE_HTH", "RUN_AWAY", "SEEK_NOISE", "ALERT", "SUSPICIOUS", "NOTICED_UNSEEN", "SAY_HI", "INFORM_ABOUT", // got_hit_xxx "GOT_HIT", "GOT_HIT_GUNFIRE", "GOT_HIT_BLADE", "GOT_HIT_HTH", "GOT_HIT_FALLROOF", "GOT_HIT_BLOODLOSS", "GOT_HIT_EXPLOSION", "GOT_HIT_GAS", "GOT_HIT_TENTACLES", "GOT_HIT_STRUCTURE_EXPLOSION", "GOT_HIT_OBJECT", "GOT_HIT_THROWING_KNIFE", "GOT_DEAFENED", "GOT_BLINDED", "GOT_ROBBED", // got_missed_xxx "GOT_MISSED", "GOT_MISSED_GUNFIRE", "GOT_MISSED_BLADE", "GOT_MISSED_HTH", "GOT_MISSED_THROWING_KNIFE", // hit_xxx "HIT", "HIT_GUNFIRE", "HIT_BLADE", "HIT_HTH", "HIT_EXPLOSION", "HIT_THROWING_KNIFE", // kill_xxx "KILL", "KILL_GUNFIRE", "KILL_BLADE", "KILL_HTH", "KILL_THROWING_KNIFE", "HEAD_POP", // miss_xxx "MISS", "MISS_GUNFIRE", "TAUNT_MISS_BLADE", "MISS_HTH", "MISS_THROWING_KNIFE", // ripostes to merc quotes "RIPOSTE" }; // sevenfm: voice taunts BOOLEAN PlayVoiceTaunt(SOLDIERTYPE *pCiv, TAUNTTYPE iTauntType, SOLDIERTYPE *pTarget) { CHAR8 filename[1024]; CHAR8 filenameExtra[1024]; CHAR16 noise[1024]; CHAR8 buf[1024]; INT32 iRandomTaunt = 0; UINT8 ubExtraTaunts = 0; CHECKF(pCiv); if (!gTauntsSettings.fTauntVoice) { return FALSE; } // show some information about taunts if (gTauntsSettings.fTauntVoiceShowInfo) ScreenMsg(FONT_MCOLOR_LTGREEN, MSG_INTERFACE, L"Soldier [%d] TauntType %d", pCiv->ubID, iTauntType); // cannot taunt when dead or collapsed if (pCiv->stats.bLife < OKLIFE || pCiv->bCollapsed || pCiv->bBreathCollapsed) { if (gTauntsSettings.fTauntVoiceShowInfo) ScreenMsg(FONT_MCOLOR_LTGREEN, MSG_INTERFACE, L"Bad soldier state (dying or collapsed)"); return FALSE; } if (iTauntType < TAUNT_FIRE_GUN || iTauntType > TAUNT_RIPOSTE) { if (gTauntsSettings.fTauntVoiceShowInfo) ScreenMsg(FONT_MCOLOR_LTGREEN, MSG_INTERFACE, L"Bad taunt"); return FALSE; } strcpy(filename, "VoiceTaunts"); // prepare team if (pCiv->bTeam == ENEMY_TEAM) { strcat(filename, "\\Army\\"); } else if (pCiv->bTeam == MILITIA_TEAM) { strcat(filename, "\\Militia\\"); } else if (pCiv->bTeam == CIV_TEAM && pCiv->ubCivilianGroup != NON_CIV_GROUP) { sprintf(buf, "\\Civgroup%d\\", pCiv->ubCivilianGroup); strcat(filename, buf); } else { if (gTauntsSettings.fTauntVoiceShowInfo) ScreenMsg(FONT_MCOLOR_LTGREEN, MSG_INTERFACE, L"Bad soldier team"); return FALSE; } // prepare gender if (pCiv->ubBodyType <= STOCKYMALE) { strcat(filename, "Male\\"); } else if (pCiv->ubBodyType == REGFEMALE) { strcat(filename, "Female\\"); } else { if (gTauntsSettings.fTauntVoiceShowInfo) ScreenMsg(FONT_MCOLOR_LTGREEN, MSG_INTERFACE, L"Incorrect bodytype"); return FALSE; } // count possible voices UINT8 ubVoiceCount; for (ubVoiceCount = 1; ubVoiceCount < 100; ubVoiceCount++) { sprintf(buf, "%s%02d", filename, ubVoiceCount); strcat(buf, "\\alert.ogg"); // check that folder exists if (!FileExists(buf)) { if (gTauntsSettings.fTauntVoiceShowInfo) { mbstowcs(noise, buf, strlen(buf) + 1); ScreenMsg(FONT_GREEN, MSG_INTERFACE, noise); } break; } } // find last good number ubVoiceCount--; if (ubVoiceCount < 1) { if (gTauntsSettings.fTauntVoiceShowInfo) ScreenMsg(FONT_MCOLOR_LTGREEN, MSG_INTERFACE, L"Could not find any voice folder"); return FALSE; } if (gTauntsSettings.fTauntVoiceShowInfo) ScreenMsg(FONT_MCOLOR_LTGREEN, MSG_INTERFACE, L"found %d voices", ubVoiceCount); // prepare voice folder name sprintf(buf, "%02d", 1 + pCiv->ubID % ubVoiceCount); strcat(filename, buf); strcat(filename, "\\"); // prepare base taunt filename strcat(filename, VoiceTauntFileName[iTauntType]); // count possible extra filenames ubExtraTaunts = 0; for (UINT8 ubCheck = 1; ubCheck <= 10; ubCheck++) { // check extra taunt file strcpy(filenameExtra, filename); sprintf(buf, " %d", ubExtraTaunts); strcat(filenameExtra, buf); strcat(filenameExtra, ".ogg"); if (!FileExists(filenameExtra)) { break; } ubExtraTaunts = ubCheck; } // possibly use extra taunt iRandomTaunt = Random(ubExtraTaunts + 1); if (iRandomTaunt > 0) { sprintf(buf, " %d", iRandomTaunt); strcat(filename, buf); } strcat(filename, ".ogg"); // log taunt file names if (gTauntsSettings.fTauntVoiceShowInfo) { FILE *OutFile; if ((OutFile = fopen("VoiceTauntLog.txt", "a+t")) != NULL) { fprintf(OutFile, "Soldier [%d] TauntType %d %s\n", pCiv->ubID.i, iTauntType, filename); fclose(OutFile); } // show some information about taunts mbstowcs(noise, filename, strlen(filename) + 1); ScreenMsg(FONT_GREEN, MSG_INTERFACE, noise); } // check that taunt file exists if (!FileExists(filename)) { if (gTauntsSettings.fTauntVoiceShowInfo) { mbstowcs(noise, filename, strlen(filename) + 1); ScreenMsg(FONT_GREEN, MSG_INTERFACE, noise); ScreenMsg(FONT_MCOLOR_LTRED, MSG_INTERFACE, L"Taunt: no file %s", noise); } return FALSE; } if (gTauntsSettings.fTauntMakeNoise == TRUE) { // convert char to char16 mbstowcs(noise, filename, strlen(filename) + 1); // use filename as taunt text, play sound later MakeNoise(pCiv->ubID, pCiv->sGridNo, pCiv->pathing.bLevel, pCiv->bOverTerrainType, (UINT8)gTauntsSettings.sVolume, NOISE_VOICE, noise); } else { // play voice taunt if (PlayJA2SampleFromFile(filename, RATE_11025, SoundVolume(HIGHVOLUME, pCiv->sGridNo), 1, SoundDir(pCiv->sGridNo)) == SOUND_ERROR) { if (gTauntsSettings.fTauntVoiceShowInfo) ScreenMsg(FONT_MCOLOR_LTRED, MSG_INTERFACE, L"Failed to play taunt"); return FALSE; } } return TRUE; }