New feature: delayed grenade explosions:

- use transformation menu to change explosion mode
- only Normal/Stun/Flashbang type grenades can be delayed
- AI soldiers will avoid staying close to armed grenades\explosives
- use option DELAYED_GRENADE_EXPLOSION to make all hand\GL grenades work as delayed
- new value AP_GRENADE_MODE = 4 in APBPConstants.ini
- new item flag DELAYED_GRENADE_EXPLOSION

Other changes:
- fixed call to sqrt in DrawExplosionWarning() that prevented compiling in VS2010
- correctly update interface after transformations
- PATHAI: AI soldiers will skip gassed tiles if not in gas already
- InGas check: AI soldiers will avoid mustard gas even when wearing gas mask
- Yellow AI: added check to avoid gas/water/bomb using FindNearestUngassedLand
- gas/flare grenades will always explode instantly even when in bad state
- LegalNPCDestination: locations near armed bombs are not legal
- FindBestNearbyCover: avoid locations near armed bombs

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8324 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
Sevenfm
2016-10-18 12:39:06 +00:00
parent 926b9d9e5c
commit e9a1f4b352
23 changed files with 365 additions and 87 deletions
+1 -1
View File
@@ -636,7 +636,7 @@ void DrawExplosionWarning( INT32 sGridno, INT8 usLevel, INT8 usDelay )
{
for ( INT32 y = yl; y <= yh; ++y )
{
FLOAT diff = std::sqrt( (sScreenX - x) * (sScreenX - x) + 2 * (sScreenY - y) * (sScreenY - y) );
FLOAT diff = std::sqrt( (FLOAT)((sScreenX - x) * (sScreenX - x) + 2 * (sScreenY - y) * (sScreenY - y)) );
if ( radius_inner <= diff && diff <= radius_outer )
{
+136 -3
View File
@@ -82,6 +82,9 @@
#include "Explosion Control.h" // added by Flugente
#include "Food.h" // added by Flugente
#include "Encyclopedia_new.h" //Moa: enc. item visibility
// sevenfm:
#include "Soldier Control.h"
#include "Sound Control.h"
#endif
#include "Multi Language Graphic Utils.h"
@@ -341,6 +344,11 @@ BOOLEAN TransformationMenuPopup_Arm_TestValid(OBJECTTYPE * pObj);
void BombInventoryMessageBoxCallBack( UINT8 ubExitValue );
void BombInventoryDisArmMessageBoxCallBack( UINT8 ubExitValue );
// sevenfm:
// delayed grenade explosion
BOOLEAN TransformationMenuPopup_DelayedGrenadeExplosion_TestValid(OBJECTTYPE* pObj);
void TransformationMenuPopup_DelayedGrenadeExplosion();
// HEADROCK HAM 5: The maximum number of attachment asterisks shown for an item.
UINT32 guiAttachmentAsterisks;
#define MAX_NUM_ASTERISKS 10
@@ -4266,6 +4274,26 @@ void INVRenderItem( UINT32 uiBuffer, SOLDIERTYPE * pSoldier, OBJECTTYPE *pObjec
gprintfinvalidate( sNewX, sNewY, pStr );
}
// sevenfm: display asterisk if grenade is delayed
if( Item[pObject->usItem].usItemClass == IC_GRENADE && (*pObject)[0]->data.sObjectFlag & DELAYED_GRENADE_EXPLOSION )
{
sNewY = sY + sHeight - 10;
SetRGBFontForeground( 120, 120, 120 );
swprintf( pStr, L"*" );
// Get length of string
uiStringLength=StringPixLength(pStr, ITEM_FONT );
sNewX = sX + sWidth - uiStringLength - 4;
if ( uiBuffer == guiSAVEBUFFER )
{
RestoreExternBackgroundRect( sNewX, sNewY, 15, 15 );
}
mprintf( sNewX, sNewY, pStr );
gprintfinvalidate( sNewX, sNewY, pStr );
}
// Flugente: if ammo is used to feed a gun externally, show ammo count left on this ammo
if ( gGameExternalOptions.ubExternalFeeding > 0 && (Item[pObject->usItem].usItemClass & (IC_AMMO)) && ObjectIsExternalFeeder(pSoldier, pObject) )
{
@@ -6844,8 +6872,9 @@ void RenderItemDescriptionBox( )
// - the item is a bomb and has a detonator or remote detonator attached
BOOLEAN renderTransformIcon = FALSE;
if ( ((guiCurrentScreen == GAME_SCREEN || guiCurrentScreen == MAP_SCREEN) && gpItemDescObject->ubNumberOfObjects == 1) &&
((Item[gpItemDescObject->usItem].usItemClass == IC_GRENADE) ||
((Item[gpItemDescObject->usItem].usItemClass == IC_BOMB) && HasAttachmentOfClass( gpItemDescObject, (AC_DETONATOR | AC_REMOTEDET) ))) )
((Item[gpItemDescObject->usItem].usItemClass == IC_GRENADE) ||
((Item[gpItemDescObject->usItem].usItemClass == IC_BOMB) && HasAttachmentOfClass( gpItemDescObject, (AC_DETONATOR | AC_REMOTEDET) )) ||
CanDelayGrenadeExplosion(gpItemDescObject->usItem) && Item[ gpItemDescObject->usItem ].ubCursor == TOSSCURS) )
{
renderTransformIcon = TRUE;
}
@@ -13400,6 +13429,37 @@ void ItemDescTransformRegionCallback( MOUSE_REGION *pRegion, INT32 reason )
// onyl allow transformations if the item is not an armed bomb
if ( !fHaveToDisarm )
{
// Check if grenade can be delayed
// don't allow this transformation if player selected option to make all grenades work in delayed mode
if( !gGameExternalOptions.fDelayedGrenadeExplosion &&
CanDelayGrenadeExplosion(gpItemDescObject->usItem) &&
Item[ gpItemDescObject->usItem ].ubCursor == TOSSCURS )
{
UINT16 usAPCost = APBPConstants[AP_GRENADE_MODE];
CHAR16 MenuRowText[300];
if ( usAPCost > 0 && gTacticalStatus.uiFlags & INCOMBAT && gTacticalStatus.uiFlags & TURNBASED )
{
if( (*gpItemDescObject)[0]->data.sObjectFlag & DELAYED_GRENADE_EXPLOSION )
swprintf (MenuRowText, gzTransformationMessage[13], usAPCost );
else
swprintf (MenuRowText, gzTransformationMessage[14], usAPCost );
}
else
{
if( (*gpItemDescObject)[0]->data.sObjectFlag & DELAYED_GRENADE_EXPLOSION )
swprintf (MenuRowText, gzTransformationMessage[11]);
else
swprintf (MenuRowText, gzTransformationMessage[12]);
}
// Add option
POPUP_OPTION *pOption = new POPUP_OPTION(&std::wstring( MenuRowText ), new popupCallbackFunction<void,void>( &TransformationMenuPopup_DelayedGrenadeExplosion ));
pOption->setAvail(new popupCallbackFunction<bool,OBJECTTYPE*>( &TransformationMenuPopup_DelayedGrenadeExplosion_TestValid, gpItemDescObject ));
gItemDescTransformPopup->addOption( *pOption );
fFoundTransformations = true;
}
for ( INT32 x = 0; x < gMAXITEMS_READ; ++x )
{
if (Transform[x].usItem == (UINT16)-1)
@@ -13564,6 +13624,12 @@ BOOLEAN TransformationMenuPopup_TestValid(TransformInfoStruct * Transform)
// Flugente: This function handles callback when the 'ARM' option in the item transformation menu is clicked
void TransformationMenuPopup_Arm( OBJECTTYPE* pObj )
{
// sevenfm: hide transformation menu
if (gItemDescTransformPopup != NULL && gfItemDescTransformPopupInitialized == TRUE)
{
gItemDescTransformPopup->hide();
}
// cant handle item stacks here
if (gpItemDescObject->ubNumberOfObjects > 1)
{
@@ -13625,6 +13691,11 @@ void TransformationMenuPopup_Arm( OBJECTTYPE* pObj )
DeleteObj( pObj );
// sevenfm: correctly update interface
gfSkipDestroyTransformPopup = TRUE;
DeleteItemDescriptionBox();
gfSkipDestroyTransformPopup = FALSE;
return;
}
@@ -13779,6 +13850,11 @@ void BombInventoryMessageBoxCallBack( UINT8 ubExitValue )
DeleteObj( gpItemDescObject );
// sevenfm: correctly update interface
gfSkipDestroyTransformPopup = TRUE;
DeleteItemDescriptionBox();
gfSkipDestroyTransformPopup = FALSE;
return;
}
}
@@ -13957,6 +14033,11 @@ void BombInventoryDisArmMessageBoxCallBack( UINT8 ubExitValue )
DeleteObj( gpItemDescObject );
// sevenfm: correctly update interface
gfSkipDestroyTransformPopup = TRUE;
DeleteItemDescriptionBox();
gfSkipDestroyTransformPopup = FALSE;
#ifdef JA2TESTVERSION
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Arming failed, explosion here" );
#endif
@@ -14461,4 +14542,56 @@ void UpdateMercBodyRegionHelpText( )
SetRegionFastHelpText( &gSMInvCamoRegion, sString );
}
}
}
BOOLEAN TransformationMenuPopup_DelayedGrenadeExplosion_TestValid(OBJECTTYPE* pObj)
{
if (pObj == NULL)
{
return false;
}
else
{
UINT16 usAPCost = APBPConstants[AP_GRENADE_MODE];
INT32 iBPCost = 0;
if (EnoughPoints( gpItemDescSoldier, usAPCost, iBPCost, false ))
{
return true;
}
else
{
return false;
}
}
}
// sevenfm: change status of delayed grenade explosion
void TransformationMenuPopup_DelayedGrenadeExplosion()
{
UINT16 usAPCost = APBPConstants[AP_GRENADE_MODE];
INT32 iBPCost = 0;
if (gItemDescTransformPopup != NULL && gfItemDescTransformPopupInitialized == TRUE)
{
gItemDescTransformPopup->hide();
}
if(EnoughPoints(gpItemDescSoldier, usAPCost, iBPCost, FALSE))
{
DeductPoints(gpItemDescSoldier, usAPCost, iBPCost);
if( (*gpItemDescObject)[0]->data.sObjectFlag & DELAYED_GRENADE_EXPLOSION )
{
(*gpItemDescObject)[0]->data.sObjectFlag &= ~DELAYED_GRENADE_EXPLOSION;
}
else
{
(*gpItemDescObject)[0]->data.sObjectFlag |= DELAYED_GRENADE_EXPLOSION;
}
PlayJA2Sample( ATTACH_TO_GUN, RATE_11025, SoundVolume( MIDVOLUME, gpItemDescSoldier->sGridNo ), 1, SoundDir( gpItemDescSoldier->sGridNo ) );
RenderItemDescriptionBox();
}
}
+1
View File
@@ -820,6 +820,7 @@ extern OBJECTTYPE gTempObject;
#define TAKEN_BY_MILITIA_TABOO_BLUE 0x0000000800000000 // 34359738368 // this item is taboo for blue militia (have to reset flag for world item upon dropping it)
#define INFECTED 0x0000001000000000 // // this item is infected with disease 0, getting damaged by this will infect you
#define DELAYED_GRENADE_EXPLOSION 0x0000002000000000 // // grenade will blow up on next turn
// Flugente TODO 2012-09-17: next time we break savegame compatibility, extend the flagmasks from UINT32 to UINT64. I didn't do it this time (see double-used flag above), as we try to minimise those breaks. But it is needed.
// ----------------------------------------------------------------
+27 -1
View File
@@ -15568,4 +15568,30 @@ FLOAT GetAttackAPTraitMultiplier( SOLDIERTYPE *pSoldier, OBJECTTYPE *pObj, UINT8
}
return fMultiplier;
}
}
// check if item is grenade and it can use delayed explosion
BOOLEAN CanDelayGrenadeExplosion( UINT16 usItem )
{
// check that item is a grenade
if( Item[ usItem ].usItemClass != IC_GRENADE )
{
return FALSE;
}
// check that grenade type is not gas/smoke/fire or flare
if( Explosive[Item[ usItem ].ubClassIndex].ubType != EXPLOSV_NORMAL &&
Explosive[ Item[ usItem ].ubClassIndex ].ubType != EXPLOSV_STUN &&
Explosive[ Item[ usItem ].ubClassIndex ].ubType != EXPLOSV_FLASHBANG )
{
return FALSE;
}
// check if grenade should always explode on impact
if( Explosive[Item[ usItem ].ubClassIndex].fExplodeOnImpact )
{
return FALSE;
}
return TRUE;
}
+3 -3
View File
@@ -548,7 +548,7 @@ BOOLEAN ObjectIsMilitiaRelevant( OBJECTTYPE *pObj );
FLOAT GetAttackAPTraitMultiplier( SOLDIERTYPE *pSoldier, OBJECTTYPE *pObj, UINT8 ubMode);
// sevenfm: check if this type of grenade can use delayed mode
BOOLEAN CanDelayGrenadeExplosion( UINT16 usItem );
#endif
+11
View File
@@ -60,6 +60,9 @@ class SOLDIERTYPE;
extern UINT16 gubAnimSurfaceIndex[ TOTALBODYTYPES ][ NUMANIMATIONSTATES ];
// sevenfm:
extern BOOLEAN InGas( SOLDIERTYPE *pSoldier, INT32 sGridNo );
//extern UINT8 gubDiagCost[20];
// skiplist has extra level of pointers every 4 elements, so a level 5is optimized for
// 4 to the power of 5 elements, or 2 to the power of 10, 1024
@@ -3128,6 +3131,14 @@ if(!GridNoOnVisibleWorldTile(iDestination))
goto NEXTDIR;
}
// sevenfm: skip gas if not in gas already
if( !(s->flags.uiStatusFlags & SOLDIER_PC) &&
InGas(s, newLoc) &&
!InGas(s, s->sGridNo) )
{
goto NEXTDIR;
}
// WANNE: Know mines (for enemy or player) do not explode - BEGIN
if ( gpWorldLevelData[newLoc].uiFlags & (MAPELEMENT_ENEMY_MINE_PRESENT | MAPELEMENT_PLAYER_MINE_PRESENT) )
{