Files
source/Tactical/Civ Quotes.cpp
T
Sevenfm 74443ca8a9 New option TAUNT_VOICE (FALSE by default): if true, will play sound file instead of showing text.
New option TAUNT_VOICE_SHOW_INFO (FALSE by default): if true, will show debug info when playing voice taunt.
Restored JA2TESTVERSION for SpreadPatternLog.txt
Allow taunts from militia when using voice taunts.
Don't show noise text messages from militia (if on our side).
Fixed incorrect OKLIFE check in CheckStatusNearbyFriendlies().

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8743 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2020-02-07 10:13:16 +00:00

2390 lines
59 KiB
C++

#ifdef PRECOMPILEDHEADERS
#include "Tactical All.h"
#else
#include "builddefines.h"
#include <stdio.h>
#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"
#endif
#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 )
{
SOLDIERTYPE *pTeamSoldier;
INT32 cnt = 0;
if ( ubExitValue == MSG_BOX_RETURN_YES )
{
// CJC Dec 1 2002: fix multiple captures
BeginCaptureSquence();
// Do capture....
cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
for ( pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++,pTeamSoldier++)
{
// Are we active and in sector.....
if ( pTeamSoldier->bActive && pTeamSoldier->bInSector )
{
if ( pTeamSoldier->stats.bLife != 0 )
{
EnemyCapturesPlayerSoldier( pTeamSoldier );
RemoveSoldierFromTacticalSector( pTeamSoldier, TRUE );
}
}
}
EndCaptureSequence( );
gfSurrendered = TRUE;
SetCustomizableTimerCallbackAndDelay( 3000, CaptureTimerCallback, FALSE );
ActionDone( gCivQuoteData.pCiv );
}
else
{
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, UINT32 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; i<num_found_taunt; ++i)
{
// check if attitudes are ok
switch( pCiv->aiData.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%02d\\", 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,
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;
}