mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
New Feature: Bombs can now be armed from the inventory. This requires an attached detonator or remote detonator. (by Flugente)
- they can then be thrown, dropped or whatever else you can think of. - to arm/disarm a bomb, enter the Enhanced description box and click on the item's picture. - timed bombs will blow up once the time is up, wether they are in the sector or in your inventory. Remote bombs can be also detonated in your inventory. - see this thread for more info: http://www.bears-pit.com/board/ubbthreads.php/topics/305534.html#Post305534 git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@5327 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
+412
-22
@@ -80,6 +80,10 @@
|
||||
#include "popup_callback.h"
|
||||
// BOB : quick attachment popup
|
||||
#include "popup_class.h"
|
||||
#include "Campaign.h" // added by Flugente
|
||||
#include "SkillCheck.h" // added by Flugente
|
||||
#include "random.h" // added by Flugente
|
||||
#include "Explosion Control.h" // added by Flugente
|
||||
#endif
|
||||
|
||||
#ifdef JA2UB
|
||||
@@ -330,6 +334,12 @@ void TransformationMenuPopup_SplitCrateInInventory( );
|
||||
void TransformFromItemDescBox( TransformInfoStruct * Transform);
|
||||
void ConfirmTransformationMessageBoxCallBack( UINT8 bExitValue );
|
||||
|
||||
// Flugente:
|
||||
void TransformationMenuPopup_Arm( OBJECTTYPE* pObj );
|
||||
BOOLEAN TransformationMenuPopup_Arm_TestValid(OBJECTTYPE * pObj);
|
||||
void BombInventoryMessageBoxCallBack( UINT8 ubExitValue );
|
||||
void BombInventoryDisArmMessageBoxCallBack( UINT8 ubExitValue );
|
||||
|
||||
// HEADROCK HAM 5: The maximum number of attachment asterisks shown for an item.
|
||||
UINT32 guiAttachmentAsterisks;
|
||||
#define MAX_NUM_ASTERISKS 10
|
||||
@@ -6280,7 +6290,8 @@ void RenderItemDescriptionBox( )
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (Transform[x].usItem == gpItemDescObject->usItem)
|
||||
|
||||
if ( (Transform[x].usItem == gpItemDescObject->usItem) || ( (guiCurrentScreen == GAME_SCREEN) || (guiCurrentScreen == MAP_SCREEN) ) && Item[gpItemDescObject->usItem].usItemClass == IC_BOMB && gpItemDescObject->ubNumberOfObjects == 1 && HasAttachmentOfClass( gpItemDescObject, (AC_DETONATOR | AC_REMOTEDET) ) )
|
||||
{
|
||||
BltVideoObjectFromIndex( guiSAVEBUFFER, guiTransformIconGraphic, 0, (ITEMDESC_ITEM_X+ITEMDESC_ITEM_WIDTH)-13, (ITEMDESC_ITEM_Y+ITEMDESC_ITEM_HEIGHT)-17, VO_BLT_SRCTRANSPARENCY, NULL );
|
||||
}
|
||||
@@ -12162,36 +12173,96 @@ void ItemDescTransformRegionCallback( MOUSE_REGION *pRegion, INT32 reason )
|
||||
|
||||
// Scan the Transformation list for the current item. Create pop-up options as required.
|
||||
INT32 iTransformIndex = -1;
|
||||
for (INT32 x = 0; x < MAXITEMS; x++)
|
||||
{
|
||||
if (Transform[x].usItem == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (Transform[x].usItem == gpItemDescObject->usItem)
|
||||
{
|
||||
iTransformIndex++;
|
||||
|
||||
CHAR16 MenuRowText[300];
|
||||
if ( Transform[x].usAPCost > 0 && gTacticalStatus.uiFlags & INCOMBAT && gTacticalStatus.uiFlags & TURNBASED )
|
||||
// Flugente: we can also arm/disarm bombs in our inventory via this menu
|
||||
BOOLEAN fHaveToDisarm = FALSE; // important check: if item is an armed bomb, we have to disarm it prior to any transformation
|
||||
if ( ( (guiCurrentScreen == GAME_SCREEN) || (guiCurrentScreen == MAP_SCREEN) ) && Item[gpItemDescObject->usItem].usItemClass == IC_BOMB && gpItemDescObject->ubNumberOfObjects == 1 && HasAttachmentOfClass( gpItemDescObject, (AC_DETONATOR | AC_REMOTEDET) ) )
|
||||
{
|
||||
iTransformIndex++;
|
||||
|
||||
UINT16 apcost = 20;
|
||||
|
||||
if ( (guiCurrentScreen == GAME_SCREEN) || (guiCurrentScreen == MAP_SCREEN) )
|
||||
apcost = 15;
|
||||
|
||||
// test wether item is already armed
|
||||
INT8 detonatortype;
|
||||
INT8 setting;
|
||||
INT8 defusefrequency;
|
||||
CheckBombSpecifics( gpItemDescObject, &detonatortype, &setting, &defusefrequency );
|
||||
|
||||
CHAR16 MenuRowText[300];
|
||||
|
||||
if ( detonatortype == BOMB_TIMED || detonatortype == BOMB_REMOTE )
|
||||
{
|
||||
fHaveToDisarm = TRUE;
|
||||
|
||||
if ( apcost > 0 && gTacticalStatus.uiFlags & INCOMBAT && gTacticalStatus.uiFlags & TURNBASED )
|
||||
{
|
||||
swprintf (MenuRowText, L"%s (%d AP)", Transform[x].szMenuRowText, Transform[x].usAPCost );
|
||||
swprintf (MenuRowText, L"Disarm (%d AP)", apcost );
|
||||
}
|
||||
else
|
||||
{
|
||||
swprintf (MenuRowText, Transform[x].szMenuRowText);
|
||||
swprintf (MenuRowText, L"Disarm");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( apcost > 0 && gTacticalStatus.uiFlags & INCOMBAT && gTacticalStatus.uiFlags & TURNBASED )
|
||||
{
|
||||
swprintf (MenuRowText, L"Arm (%d AP)", apcost );
|
||||
}
|
||||
else
|
||||
{
|
||||
swprintf (MenuRowText, L"Arm");
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a new option for the menu
|
||||
POPUP_OPTION *pOption = new POPUP_OPTION(&std::wstring( MenuRowText ), new popupCallbackFunction<void,TransformInfoStruct*>( &TransformationMenuPopup_Transform, &Transform[x] ) );
|
||||
// Set the function that tests whether it's valid at the moment.
|
||||
pOption->setAvail(new popupCallbackFunction<bool,TransformInfoStruct*>( &TransformationMenuPopup_TestValid, &Transform[x] ));
|
||||
// Add the option to the menu.
|
||||
gItemDescTransformPopup->addOption( *pOption );
|
||||
// Set this flag so we know we have at least one Transformation available.
|
||||
fFoundTransformations = true;
|
||||
// Generate a new option for the menu
|
||||
POPUP_OPTION *pOption = new POPUP_OPTION(&std::wstring( MenuRowText ), new popupCallbackFunction<void, OBJECTTYPE*>( &TransformationMenuPopup_Arm, gpItemDescObject ) );
|
||||
// Set the function that tests whether it's valid at the moment.
|
||||
pOption->setAvail(new popupCallbackFunction<bool,OBJECTTYPE*>( &TransformationMenuPopup_Arm_TestValid, gpItemDescObject ));
|
||||
// Add the option to the menu.
|
||||
gItemDescTransformPopup->addOption( *pOption );
|
||||
// Set this flag so we know we have at least one Transformation available.
|
||||
fFoundTransformations = true;
|
||||
}
|
||||
|
||||
// onyl allow transformations if the item is not an armed bomb
|
||||
if ( !fHaveToDisarm )
|
||||
{
|
||||
for (INT32 x = 0; x < MAXITEMS; x++)
|
||||
{
|
||||
if (Transform[x].usItem == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (Transform[x].usItem == gpItemDescObject->usItem)
|
||||
{
|
||||
iTransformIndex++;
|
||||
|
||||
CHAR16 MenuRowText[300];
|
||||
if ( Transform[x].usAPCost > 0 && gTacticalStatus.uiFlags & INCOMBAT && gTacticalStatus.uiFlags & TURNBASED )
|
||||
{
|
||||
swprintf (MenuRowText, L"%s (%d AP)", Transform[x].szMenuRowText, Transform[x].usAPCost );
|
||||
}
|
||||
else
|
||||
{
|
||||
swprintf (MenuRowText, Transform[x].szMenuRowText);
|
||||
}
|
||||
|
||||
// Generate a new option for the menu
|
||||
POPUP_OPTION *pOption = new POPUP_OPTION(&std::wstring( MenuRowText ), new popupCallbackFunction<void,TransformInfoStruct*>( &TransformationMenuPopup_Transform, &Transform[x] ) );
|
||||
// Set the function that tests whether it's valid at the moment.
|
||||
pOption->setAvail(new popupCallbackFunction<bool,TransformInfoStruct*>( &TransformationMenuPopup_TestValid, &Transform[x] ));
|
||||
// Add the option to the menu.
|
||||
gItemDescTransformPopup->addOption( *pOption );
|
||||
// Set this flag so we know we have at least one Transformation available.
|
||||
fFoundTransformations = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fFoundTransformations)
|
||||
{
|
||||
POPUP_OPTION * pOption = new POPUP_OPTION( &std::wstring( gzTransformationMessage[ 0 ] ), new popupCallbackFunction<void,TransformInfoStruct*>( &TransformationMenuPopup_Transform, NULL ) );
|
||||
@@ -12303,6 +12374,15 @@ BOOLEAN TransformationMenuPopup_TestValid(TransformInfoStruct * Transform)
|
||||
|
||||
if (EnoughPoints( gpItemDescSoldier, usAPCost, iBPCost, false ))
|
||||
{
|
||||
// Flugente: If item is an armed bomb, do not allow any transformation!
|
||||
if ( Item[gpItemDescObject->usItem].usItemClass == IC_BOMB && gpItemDescObject->ubNumberOfObjects == 1 && HasAttachmentOfClass( gpItemDescObject, (AC_DETONATOR | AC_REMOTEDET) ) )
|
||||
{
|
||||
if ( (*gpItemDescObject)[0]->data.misc.bDetonatorType == BOMB_TIMED || (*gpItemDescObject)[0]->data.misc.bDetonatorType == BOMB_REMOTE )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -12312,6 +12392,316 @@ 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 )
|
||||
{
|
||||
// cant handle item stacks here
|
||||
if (gpItemDescObject->ubNumberOfObjects > 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
INT8 screen = guiCurrentScreen;
|
||||
if ( screen != GAME_SCREEN && screen != MAP_SCREEN )
|
||||
return;
|
||||
|
||||
// test wether item is already armed
|
||||
INT8 detonatortype;
|
||||
INT8 setting;
|
||||
INT8 defusefrequency;
|
||||
CheckBombSpecifics( gpItemDescObject, &detonatortype, &setting, &defusefrequency );
|
||||
|
||||
if ( detonatortype == BOMB_TIMED || detonatortype == BOMB_REMOTE )
|
||||
{
|
||||
// TODO
|
||||
DoMessageBox( MSG_BOX_BASIC_STYLE, TacticalStr[ DISARM_BOOBYTRAP_PROMPT ], screen, ( UINT8 )MSG_BOX_FLAG_YESNO, BombInventoryDisArmMessageBoxCallBack, NULL );
|
||||
}
|
||||
else if ( HasAttachmentOfClass( gpItemDescObject, (AC_DEFUSE ) ) )
|
||||
{
|
||||
if ( HasAttachmentOfClass( gpItemDescObject, (AC_DETONATOR ) ) )
|
||||
{
|
||||
DoMessageBox( MSG_BOX_BASIC_SMALL_BUTTONS, TacticalStr[ CHOOSE_DETONATE_AND_REMOTE_DEFUSE_FREQUENCY_STR ], screen, MSG_BOX_FLAG_SIXTEEN_NUMBERED_BUTTONS, BombInventoryMessageBoxCallBack, NULL );
|
||||
}
|
||||
else if ( HasAttachmentOfClass( gpItemDescObject, (AC_REMOTEDET ) ) )
|
||||
{
|
||||
DoMessageBox( MSG_BOX_BASIC_SMALL_BUTTONS, TacticalStr[ CHOOSE_REMOTE_DETONATE_AND_REMOTE_DEFUSE_FREQUENCY_STR ], screen, MSG_BOX_FLAG_SIXTEEN_NUMBERED_BUTTONS, BombInventoryMessageBoxCallBack, NULL );
|
||||
}
|
||||
}
|
||||
else if ( HasAttachmentOfClass( gpItemDescObject, (AC_DETONATOR ) ) )
|
||||
{
|
||||
DoMessageBox( MSG_BOX_BASIC_SMALL_BUTTONS, TacticalStr[ CHOOSE_TIMER_STR ], screen, MSG_BOX_FLAG_FOUR_NUMBERED_BUTTONS, BombInventoryMessageBoxCallBack, NULL );
|
||||
}
|
||||
else if ( HasAttachmentOfClass( gpItemDescObject, (AC_REMOTEDET ) ) )
|
||||
{
|
||||
DoMessageBox( MSG_BOX_BASIC_SMALL_BUTTONS, TacticalStr[ CHOOSE_REMOTE_FREQUENCY_STR ], screen, MSG_BOX_FLAG_FOUR_NUMBERED_BUTTONS, BombInventoryMessageBoxCallBack, NULL );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOLEAN TransformationMenuPopup_Arm_TestValid(OBJECTTYPE * pObj)
|
||||
{
|
||||
if (pObj == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
UINT16 usAPCost = 20;
|
||||
INT32 iBPCost = 1;
|
||||
|
||||
if (EnoughPoints( gpItemDescSoldier, usAPCost, iBPCost, false ))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BombInventoryMessageBoxCallBack( UINT8 ubExitValue )
|
||||
{
|
||||
if (gpItemDescSoldier)
|
||||
{
|
||||
// no planting tripwire in our inventory...
|
||||
if ( Item[ gpItemDescObject->usItem ].tripwire == 1 )
|
||||
return;
|
||||
|
||||
INT32 iResult;
|
||||
|
||||
if ( HasAttachmentOfClass( gpItemDescObject, AC_REMOTEDET ) )
|
||||
{
|
||||
iResult = SkillCheck( gpItemDescSoldier, PLANTING_REMOTE_BOMB_CHECK, 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
iResult = SkillCheck( gpItemDescSoldier, PLANTING_BOMB_CHECK, 0 );
|
||||
}
|
||||
|
||||
if ( iResult >= 0 )
|
||||
{
|
||||
// EXPLOSIVES GAIN (25): Place a bomb, or buried and armed a mine
|
||||
StatChange( gpItemDescSoldier, EXPLODEAMT, 25, FALSE );
|
||||
}
|
||||
else
|
||||
{
|
||||
// EXPLOSIVES GAIN (10): Failed to place a bomb, or bury and arm a mine
|
||||
StatChange( gpItemDescSoldier, EXPLODEAMT, 10, FROM_FAILURE );
|
||||
|
||||
// oops! How badly did we screw up?
|
||||
if ( iResult >= -20 )
|
||||
{
|
||||
// messed up the setting
|
||||
if ( ubExitValue == 0 )
|
||||
{
|
||||
ubExitValue = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// change up/down by 1
|
||||
ubExitValue = (UINT8) (ubExitValue + Random( 3 ) - 1);
|
||||
}
|
||||
// and continue
|
||||
}
|
||||
else
|
||||
{
|
||||
gpItemDescSoldier->DoMercBattleSound( BATTLE_SOUND_CURSE1 );
|
||||
|
||||
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Arming of bomb failed. Resulting explosion damages %s's inventory and health", gpItemDescSoldier->name );
|
||||
|
||||
INT8 screen = guiCurrentScreen;
|
||||
if ( screen == GAME_SCREEN )
|
||||
{
|
||||
// ignite explosions manually - this item is not in the WorldBombs-structure, so we can't add it to the queue
|
||||
IgniteExplosion( (*gpItemDescObject)[0]->data.misc.ubBombOwner - 2, gpItemDescSoldier->sX, gpItemDescSoldier->sY, (INT16) (gpWorldLevelData[gpItemDescSoldier->sGridNo].sHeight), gpItemDescSoldier->sGridNo, gpItemDescObject->usItem, gpItemDescSoldier->pathing.bLevel, gpItemDescSoldier->ubDirection );
|
||||
|
||||
DeleteObj( gpItemDescObject );
|
||||
}
|
||||
else if ( (screen == MAP_SCREEN) || (screen == MSG_BOX_SCREEN) )
|
||||
{
|
||||
// no explosions in map screen - instead we simply damage the inventory and harm our health
|
||||
gpItemDescSoldier->InventoryExplosion();
|
||||
|
||||
DeleteObj( gpItemDescObject );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ArmBomb( gpItemDescObject, ubExitValue ) )
|
||||
{
|
||||
// SANDRO - STOMP traits - Demolitions bonus to trap level
|
||||
if ( gGameOptions.fNewTraitSystem && HAS_SKILL_TRAIT( gpItemDescSoldier, DEMOLITIONS_NT ))
|
||||
{
|
||||
// increase trap level for Demolitions trait
|
||||
gpItemDescSoldier->inv[ HANDPOS ][0]->data.bTrap = __min( max( 10, (8 + gSkillTraitValues.ubDEPlacedBombLevelBonus)), (( EffectiveExplosive( gpItemDescSoldier ) / 20) + (EffectiveExpLevel( gpItemDescSoldier ) / 3) + gSkillTraitValues.ubDEPlacedBombLevelBonus) );
|
||||
}
|
||||
else
|
||||
{
|
||||
gpItemDescSoldier->inv[ HANDPOS ][0]->data.bTrap = __min( 10, ( EffectiveExplosive( gpItemDescSoldier ) / 20) + (EffectiveExpLevel( gpItemDescSoldier ) / 3) );
|
||||
}
|
||||
|
||||
// Flugente: We armed a bomb in our inventory. We will NOT add it to the item pool and the world bombs.
|
||||
// Instead, we will count down the delay time every turn and eventually ignite the bomb from our inventory.
|
||||
// Same for remote detonators, when sending a signal we will have to also check everyone's inventory and eventually ignite bombs in inventories
|
||||
|
||||
// HACK IMMINENT!
|
||||
// value of 1 is stored in maps for SIDE of bomb owner... when we want to use IDs!
|
||||
// so we add 2 to all owner IDs passed through here and subtract 2 later
|
||||
//if (gpItemDescSoldier->inv[HANDPOS].MoveThisObjectTo(gTempObject, 1) == 0)
|
||||
{
|
||||
gTempObject[0]->data.misc.ubBombOwner = gpItemDescSoldier->ubID + 2;
|
||||
gTempObject[0]->data.ubDirection = gpItemDescSoldier->ubDirection; // Flugente: direction of bomb is direction of soldier
|
||||
|
||||
//AddItemToPool( gsTempGridNo, &gTempObject, VISIBLE, gpItemDescSoldier->pathing.bLevel, WORLD_ITEM_ARMED_BOMB, 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BombInventoryDisArmMessageBoxCallBack( UINT8 ubExitValue )
|
||||
{
|
||||
if ( !gpItemDescSoldier )
|
||||
return;
|
||||
|
||||
if ( !gpItemDescObject )
|
||||
return;
|
||||
|
||||
if (ubExitValue == MSG_BOX_RETURN_YES)
|
||||
{
|
||||
INT32 iCheckResult;
|
||||
|
||||
INT8 trapdifficulty = (*gpItemDescObject)[0]->data.bTrap;
|
||||
|
||||
// Snap: make it easier to disarm our own traps.
|
||||
// If we succede - we get exp, but if we fail - we pay fair and square!
|
||||
|
||||
//CHRISL: first things first. If we're in combat, we need to spend some APs to disarm the device
|
||||
if((gTacticalStatus.uiFlags & INCOMBAT) || (gTacticalStatus.fEnemyInSector))
|
||||
{
|
||||
// SANDRO was here, AP_DISARM_MINE changed to GetAPsToDisarmMine
|
||||
if(EnoughPoints(gpItemDescSoldier, GetAPsToDisarmMine( gpItemDescSoldier ), APBPConstants[BP_DISARM_MINE], TRUE))
|
||||
DeductPoints(gpItemDescSoldier, GetAPsToDisarmMine( gpItemDescSoldier ), APBPConstants[BP_DISARM_MINE], AFTERACTION_INTERRUPT);
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
// NB owner grossness... bombs 'owned' by the enemy are stored with side value 1 in
|
||||
// the map. So if we want to detect a bomb placed by the player, owner is > 1, and
|
||||
// owner - 2 gives the ID of the character who planted it
|
||||
if ( (*gpItemDescObject)[0]->data.misc.ubBombOwner > 1 && ( (INT32)(*gpItemDescObject)[0]->data.misc.ubBombOwner - 2 >= gTacticalStatus.Team[ OUR_TEAM ].bFirstID && (*gpItemDescObject)[0]->data.misc.ubBombOwner - 2 <= gTacticalStatus.Team[ OUR_TEAM ].bLastID ) )
|
||||
{
|
||||
// Flugente: get a tripwire-related bonus if we have a wire cutter in our hands
|
||||
INT8 wirecutterbonus = 0;
|
||||
if ( ( (&gpItemDescSoldier->inv[HANDPOS])->exists() && Item[ gpItemDescSoldier->inv[HANDPOS].usItem ].wirecutters == 1 ) || ( (&gpItemDescSoldier->inv[SECONDHANDPOS])->exists() && Item[ gpItemDescSoldier->inv[SECONDHANDPOS].usItem ].wirecutters == 1 ) )
|
||||
{
|
||||
// + 10 if item gets activated by tripwire
|
||||
if ( Item[gpItemDescObject->usItem].tripwireactivation == 1 )
|
||||
wirecutterbonus += 10;
|
||||
|
||||
// + 10 if item is tripwire
|
||||
if ( Item[gpItemDescObject->usItem].tripwire == 1 )
|
||||
wirecutterbonus += 10;
|
||||
}
|
||||
|
||||
if ( (*gpItemDescObject)[0]->data.misc.ubBombOwner - 2 == gpItemDescSoldier->ubID )
|
||||
{
|
||||
// my own boobytrap!
|
||||
iCheckResult = SkillCheck( gpItemDescSoldier, DISARM_TRAP_CHECK, 40 + wirecutterbonus );
|
||||
}
|
||||
else
|
||||
{
|
||||
// our team's boobytrap!
|
||||
iCheckResult = SkillCheck( gpItemDescSoldier, DISARM_TRAP_CHECK, 20 + wirecutterbonus );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
iCheckResult = SkillCheck( gpItemDescSoldier, DISARM_TRAP_CHECK, 0 );
|
||||
}
|
||||
|
||||
if (iCheckResult >= 0)
|
||||
{
|
||||
|
||||
if ( (*gpItemDescObject)[0]->data.misc.ubBombOwner > 1 && ( (INT32)(*gpItemDescObject)[0]->data.misc.ubBombOwner - 2 >= gTacticalStatus.Team[ OUR_TEAM ].bFirstID && (*gpItemDescObject)[0]->data.misc.ubBombOwner - 2 <= gTacticalStatus.Team[ OUR_TEAM ].bLastID ) )
|
||||
{
|
||||
if ( (*gpItemDescObject)[0]->data.misc.ubBombOwner - 2 == gpItemDescSoldier->ubID )
|
||||
{
|
||||
// disarmed my own boobytrap!
|
||||
StatChange( gpItemDescSoldier, EXPLODEAMT, (UINT16) (2 * trapdifficulty), FALSE );
|
||||
}
|
||||
else
|
||||
{
|
||||
// disarmed our team's boobytrap!
|
||||
StatChange( gpItemDescSoldier, EXPLODEAMT, (UINT16) (4 * trapdifficulty), FALSE );
|
||||
|
||||
// SANDRO - merc records - trap removal count (don't count our own traps)
|
||||
gMercProfiles[ gpItemDescSoldier->ubProfile ].records.usTrapsRemoved++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// disarmed a boobytrap!
|
||||
StatChange( gpItemDescSoldier, EXPLODEAMT, (UINT16) (6 * trapdifficulty), FALSE );
|
||||
|
||||
// SANDRO - merc records - trap removal count
|
||||
gMercProfiles[ gpItemDescSoldier->ubProfile ].records.usTrapsRemoved++;
|
||||
}
|
||||
|
||||
// have merc say this is good
|
||||
gpItemDescSoldier->DoMercBattleSound( BATTLE_SOUND_COOL1 );
|
||||
|
||||
(*gpItemDescObject)[0]->data.ubWireNetworkFlag = 0;
|
||||
(*gpItemDescObject)[0]->data.bDefuseFrequency = 0;
|
||||
(*gpItemDescObject)[0]->data.misc.bDetonatorType = 0;
|
||||
(*gpItemDescObject)[0]->data.misc.bDelay = 0;
|
||||
(*gpItemDescObject)[0]->data.misc.bFrequency = 0;
|
||||
(*gpItemDescObject)[0]->data.bTrap = 0;
|
||||
|
||||
if ( (*gpItemDescObject).fFlags & OBJECT_KNOWN_TO_BE_TRAPPED )
|
||||
gpItemDescObject->fFlags &= ~( OBJECT_KNOWN_TO_BE_TRAPPED );
|
||||
|
||||
if ( (*gpItemDescObject).fFlags & OBJECT_ARMED_BOMB )
|
||||
gpItemDescObject->fFlags &= ~( OBJECT_ARMED_BOMB );
|
||||
|
||||
if ( (*gpItemDescObject).fFlags & OBJECT_KNOWN_TO_BE_TRAPPED )
|
||||
gpItemDescObject->fFlags &= ~( OBJECT_KNOWN_TO_BE_TRAPPED );
|
||||
}
|
||||
else
|
||||
{
|
||||
// oops! trap goes off
|
||||
StatChange( gpItemDescSoldier, EXPLODEAMT, (INT8) (3 * trapdifficulty ), FROM_FAILURE );
|
||||
|
||||
gpItemDescSoldier->DoMercBattleSound( BATTLE_SOUND_CURSE1 );
|
||||
|
||||
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Disarming of bomb failed. Resulting explosion damages %s's inventory and health", gpItemDescSoldier->name );
|
||||
|
||||
INT8 screen = guiCurrentScreen;
|
||||
if ( screen == GAME_SCREEN )
|
||||
{
|
||||
// ignite explosions manually - this item is not in the WorldBombs-structure, so we can't add it to the queue
|
||||
IgniteExplosion( (*gpItemDescObject)[0]->data.misc.ubBombOwner - 2, gpItemDescSoldier->sX, gpItemDescSoldier->sY, (INT16) (gpWorldLevelData[gpItemDescSoldier->sGridNo].sHeight), gpItemDescSoldier->sGridNo, gpItemDescObject->usItem, gpItemDescSoldier->pathing.bLevel, gpItemDescSoldier->ubDirection );
|
||||
|
||||
DeleteObj( gpItemDescObject );
|
||||
}
|
||||
else if ( (screen == MAP_SCREEN) || (screen == MSG_BOX_SCREEN) )
|
||||
{
|
||||
// no explosions in map screen - instead we simply damage the inventory and harm our health
|
||||
gpItemDescSoldier->InventoryExplosion();
|
||||
|
||||
DeleteObj( gpItemDescObject );
|
||||
}
|
||||
|
||||
#ifdef JA2TESTVERSION
|
||||
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Arming failed, explosion here" );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HEADROCK HAM 5: This Transformation Menu Callback will attempt to unjam a jammed gun. If the
|
||||
// soldier lacks APs to do so, it will fail with a screen message. Otherwise the gun is unjammed,
|
||||
// causing the normal effects.
|
||||
|
||||
@@ -472,7 +472,7 @@ public:
|
||||
// these variables should belong to a different position. However, I am forced to put them here, otherwise loading of WF maps and other old data would not work properly
|
||||
FLOAT bTemperature; // Flugente FTW 1.2: temperature of gun
|
||||
|
||||
// shoul belong to misc, but was moved here because of the odl maps issue
|
||||
// should belong to misc, but was moved here because of the old maps issue
|
||||
// added by Flugente 12-04-15
|
||||
UINT8 ubDirection; // direction the bomb faces (for directional explosives)
|
||||
UINT32 ubWireNetworkFlag; // flags for the tripwire network
|
||||
|
||||
+17
-2
@@ -7656,7 +7656,6 @@ BOOLEAN ArmBomb( OBJECTTYPE * pObj, INT8 bSetting )
|
||||
return( FALSE );
|
||||
}
|
||||
|
||||
// Flugente TODO determine correct frequency and detonate/defuse stuff
|
||||
// Flugente: decide how to interpret the bSetting we just got.
|
||||
// Due to limitations in the message system, we only receive a single value to interpret, as we currently can't have a message box return 2 values
|
||||
// It might be possible to have proper checkboxes, but I'll rather not research this right now.
|
||||
@@ -7669,7 +7668,7 @@ BOOLEAN ArmBomb( OBJECTTYPE * pObj, INT8 bSetting )
|
||||
// d) if we have a remote detonator plus a remote defuse: frequency on which the bomb will blow plus frequency to defuse: 1-16
|
||||
//
|
||||
// I we are placing tripwire, consider this:
|
||||
// e) if we palce tripwire: the tripwire network plus the hierachy in that network: 1-16
|
||||
// e) if we place tripwire: the tripwire network plus the hierachy in that network: 1-16
|
||||
//
|
||||
// It is clear that we only have to reinterpret the values if a defuse is equiped, or we are placing tripwire
|
||||
INT8 detonatesetting = bSetting;
|
||||
@@ -13860,3 +13859,19 @@ UINT64 GetAvailableAttachmentPoint (OBJECTTYPE * pObject, UINT8 subObject)
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
// Flugente: check if and how a bomb has been set up
|
||||
void CheckBombSpecifics( OBJECTTYPE * pObj, INT8* detonatortype, INT8* setting, INT8* defusefrequency )
|
||||
{
|
||||
if ( pObj && pObj->exists() )
|
||||
{
|
||||
*detonatortype = (*pObj)[0]->data.misc.bDetonatorType;
|
||||
|
||||
if ( *detonatortype == BOMB_TIMED )
|
||||
*setting = (*pObj)[0]->data.misc.bDelay;
|
||||
else
|
||||
*setting = (*pObj)[0]->data.misc.bFrequency;
|
||||
|
||||
*defusefrequency = (*pObj)[0]->data.bDefuseFrequency;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,4 +459,7 @@ bool IsAttachmentPointAvailable( UINT32 itemID, UINT32 attachmentID );
|
||||
bool IsAttachmentPointAvailable( UINT64 point, UINT32 attachmentID, BOOLEAN onlyCheckAttachments );
|
||||
UINT64 GetAvailableAttachmentPoint ( OBJECTTYPE * pObject, UINT8 subObject );
|
||||
|
||||
// Flugente: check if and how a bomb has been set up
|
||||
void CheckBombSpecifics( OBJECTTYPE * pObj, INT8* detonatortype, INT8* setting, INT8* defusefrequency );
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11529,7 +11529,7 @@ void SOLDIERTYPE::EVENT_SoldierBeginPunchAttack( INT32 sGridNo, UINT8 ubDirectio
|
||||
if ( pTSoldier->stats.bLife - damage < 0 )
|
||||
damage = oldlife;
|
||||
|
||||
// We've got a problem if we kill someone outright ithout him collapsing properly...
|
||||
// We've got a problem if we kill someone outright without him collapsing properly...
|
||||
// FIX: We'll adjust our damage, so if we'd kill someone without collapsing first, we lower our damage, to let him collapse
|
||||
// After all, this whole thing's a rig up, so there shouldn't be a problem with that
|
||||
if ( oldlife >= OKLIFE && oldlife <= damage )
|
||||
@@ -13508,6 +13508,70 @@ void SOLDIERTYPE::ResetExtraStats()
|
||||
bExtraExpLevel = 0;
|
||||
}
|
||||
|
||||
// Flugente: inventory bombs can ignite while in mapscreen. Workaround: Damage items and health
|
||||
void SOLDIERTYPE::InventoryExplosion( void )
|
||||
{
|
||||
INT8 invsize = (INT8)this->inv.size(); // remember inventorysize, so we don't call size() repeatedly
|
||||
|
||||
for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop) // ... for all items in our inventory ...
|
||||
{
|
||||
if (this->inv[bLoop].exists() == true )
|
||||
{
|
||||
OBJECTTYPE * pObj = &(this->inv[bLoop]); // ... get pointer for this item ...
|
||||
|
||||
if ( pObj != NULL ) // ... if pointer is not obviously useless ...
|
||||
{
|
||||
for(INT16 i = 0; i < pObj->ubNumberOfObjects; ++i) // ... there might be multiple items here (item stack), so for each one ...
|
||||
{
|
||||
INT16 status = (*pObj)[0]->data.objectStatus;
|
||||
(*pObj)[0]->data.objectStatus = max(1, (INT16)(status/2));
|
||||
|
||||
// for every objects, we also have to check wether there are weapon attachments (eg. underbarrel grenade launchers), and cool them down too
|
||||
attachmentList::iterator iterend = (*pObj)[i]->attachments.end();
|
||||
for (attachmentList::iterator iter = (*pObj)[i]->attachments.begin(); iter != iterend; ++iter)
|
||||
{
|
||||
if ( iter->exists() )
|
||||
{
|
||||
INT16 status = (*iter)[0]->data.objectStatus;
|
||||
(*iter)[0]->data.objectStatus = max(1, (INT16)(status/2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now damage our health
|
||||
INT8 oldlife = stats.bLife;
|
||||
|
||||
INT16 damage = (INT16)(30 + Random(20));
|
||||
if ( stats.bLife - damage < 0 )
|
||||
damage = oldlife;
|
||||
|
||||
// We've got a problem if we kill someone outright without him collapsing properly...
|
||||
// FIX: We'll adjust our damage, so if we'd kill someone without collapsing first, we lower our damage, to let him collapse
|
||||
// After all, this whole thing's a rig up, so there shouldn't be a problem with that
|
||||
if ( oldlife >= OKLIFE && oldlife <= damage )
|
||||
damage -= (INT16)((5 + Random(5)));
|
||||
|
||||
INT16 breathdamage = (INT16)(500 + Random(1500));
|
||||
if ( bBreath - breathdamage < 0 )
|
||||
breathdamage = bBreath;
|
||||
|
||||
SoldierTakeDamage( 0, damage, 0, breathdamage, TAKE_DAMAGE_EXPLOSION, this->ubID, sGridNo, 0, TRUE );
|
||||
|
||||
if ( stats.bLife <= 0 )
|
||||
{
|
||||
// FINISH HIM!
|
||||
HandleTakeDamageDeath( this, oldlife, TAKE_DAMAGE_BLOODLOSS );
|
||||
}
|
||||
else if ( stats.bLife < OKLIFE && !bCollapsed )
|
||||
{
|
||||
// let the target collapse...
|
||||
SoldierCollapse(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
INT32 CheckBleeding( SOLDIERTYPE *pSoldier )
|
||||
{
|
||||
|
||||
@@ -1367,6 +1367,9 @@ public:
|
||||
|
||||
// reset the extra stat variables
|
||||
void ResetExtraStats();
|
||||
|
||||
// Flugente: inventory bombs can ignite while in mapscreen. Workaround: Damage items and health
|
||||
void InventoryExplosion( void );
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
}; // SOLDIERTYPE;
|
||||
|
||||
@@ -449,7 +449,8 @@ INT32 AddItemToWorld( INT32 sGridNo, OBJECTTYPE *pObject, UINT8 ubLevel, UINT16
|
||||
gWorldItems[ iItemIndex ].object = *pObject;
|
||||
|
||||
// Add a bomb reference if needed
|
||||
if (usFlags & WORLD_ITEM_ARMED_BOMB)
|
||||
// Flugente: we can arm bombs in our inventory and then throw them out, which will cause them to be added to the world. Only way to identify those items is via a check for their bDetonatorType
|
||||
if (usFlags & WORLD_ITEM_ARMED_BOMB || ( Item[pObject->usItem].usItemClass & (IC_BOMB) && ( (*pObject)[0]->data.misc.bDetonatorType == BOMB_TIMED ) || ( (*pObject)[0]->data.misc.bDetonatorType == BOMB_REMOTE ) ) )
|
||||
{
|
||||
iReturn = AddBombToWorld( iItemIndex );
|
||||
if (iReturn == -1)
|
||||
|
||||
@@ -3841,7 +3841,7 @@ void DecayBombTimers( void )
|
||||
OBJECTTYPE * pObj;
|
||||
|
||||
uiTimeStamp = GetJA2Clock();
|
||||
|
||||
|
||||
// Go through all the bombs in the world, and look for timed ones
|
||||
for (uiWorldBombIndex = 0; uiWorldBombIndex < guiNumWorldBombs; uiWorldBombIndex++)
|
||||
{
|
||||
@@ -3881,6 +3881,59 @@ void DecayBombTimers( void )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Flugente: we have to check every inventory for armed bombs and do the countdown for them, too
|
||||
// Flugente: new stuff: we can now also arm bombs in our inventory, and detonate/defuse those bombs remotely
|
||||
// So we have to look at every item in every inventory in this sector
|
||||
for (UINT32 cnt = 0; cnt < guiNumMercSlots; cnt++ )
|
||||
{
|
||||
SOLDIERTYPE* pSoldier = MercSlots[ cnt ];
|
||||
|
||||
if ( pSoldier != NULL )
|
||||
{
|
||||
if ( pSoldier->bInSector && pSoldier->bActive )
|
||||
{
|
||||
INT8 invsize = (INT8)pSoldier->inv.size(); // remember inventorysize, so we don't call size() repeatedly
|
||||
|
||||
for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop) // ... for all items in our inventory ...
|
||||
{
|
||||
// ... if Item is a bomb ...
|
||||
if (pSoldier->inv[bLoop].exists() == true && ( Item[pSoldier->inv[bLoop].usItem].usItemClass & (IC_BOMB) ) )
|
||||
{
|
||||
OBJECTTYPE * pObj = &(pSoldier->inv[bLoop]); // ... get pointer for this item ...
|
||||
|
||||
if ( (*pObj)[0]->data.misc.bDetonatorType == BOMB_TIMED )
|
||||
{
|
||||
// Found a timed bomb, so decay its delay value and see if it goes off
|
||||
(*pObj)[0]->data.misc.bDelay--;
|
||||
if ((*pObj)[0]->data.misc.bDelay == 0)
|
||||
{
|
||||
// ATE: CC black magic....
|
||||
if ( (*pObj)[0]->data.misc.ubBombOwner > 1 )
|
||||
{
|
||||
gubPersonToSetOffExplosions = (UINT8) ((*pObj)[0]->data.misc.ubBombOwner - 2);
|
||||
// SANDRO - merc records - detonating explosives
|
||||
if ( MercPtrs[ gubPersonToSetOffExplosions ]->ubProfile != NO_PROFILE && MercPtrs[ gubPersonToSetOffExplosions ]->bTeam == gbPlayerNum )
|
||||
{
|
||||
gMercProfiles[ MercPtrs[ gubPersonToSetOffExplosions ]->ubProfile ].records.usExpDetonated++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gubPersonToSetOffExplosions = NOBODY;
|
||||
}
|
||||
|
||||
// ignite explosions manually - this item is not in the WorldBombs-structure, so we can't add it to the queue
|
||||
IgniteExplosion( gubPersonToSetOffExplosions, pSoldier->sX, pSoldier->sY, (INT16) (gpWorldLevelData[pSoldier->sGridNo].sHeight), pSoldier->sGridNo, pObj->usItem, pSoldier->pathing.bLevel, pSoldier->ubDirection );
|
||||
|
||||
DeleteObj( pObj );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetOffBombsByFrequency( UINT8 ubID, INT8 bFrequency )
|
||||
@@ -3985,6 +4038,86 @@ void SetOffBombsByFrequency( UINT8 ubID, INT8 bFrequency )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Flugente: new stuff: we can now also arm bombs in our inventory, and detonate/defuse those bombs remotely
|
||||
// So we have to look at every item in every inventory in this sector
|
||||
for (UINT32 cnt = 0; cnt < guiNumMercSlots; cnt++ )
|
||||
{
|
||||
SOLDIERTYPE* pSoldier = MercSlots[ cnt ];
|
||||
|
||||
if ( pSoldier != NULL )
|
||||
{
|
||||
if ( pSoldier->bInSector && pSoldier->bActive )
|
||||
{
|
||||
INT8 invsize = (INT8)pSoldier->inv.size(); // remember inventorysize, so we don't call size() repeatedly
|
||||
|
||||
for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop) // ... for all items in our inventory ...
|
||||
{
|
||||
// ... if Item is a bomb ...
|
||||
if (pSoldier->inv[bLoop].exists() == true && ( Item[pSoldier->inv[bLoop].usItem].usItemClass & (IC_BOMB) ) )
|
||||
{
|
||||
OBJECTTYPE * pObj = &(pSoldier->inv[bLoop]); // ... get pointer for this item ...
|
||||
|
||||
if ( (*pObj)[0]->data.misc.bDetonatorType == BOMB_REMOTE )
|
||||
{
|
||||
// Found a remote bomb, so check to see if it has the same frequency
|
||||
if ( fDetonate ) // detonate bombs
|
||||
{
|
||||
// Found a remote bomb, so check to see if it has the same frequency
|
||||
if ((*pObj)[0]->data.misc.bFrequency == bFrequency)
|
||||
{
|
||||
// SANDRO - added merc records and some exp
|
||||
if ( ((*pObj)[0]->data.misc.ubBombOwner) > 1 )
|
||||
{
|
||||
if ( MercPtrs[((*pObj)[0]->data.misc.ubBombOwner - 2)]->ubProfile != NO_PROFILE &&
|
||||
MercPtrs[((*pObj)[0]->data.misc.ubBombOwner - 2)]->bTeam == gbPlayerNum )
|
||||
{
|
||||
gMercProfiles[MercPtrs[((*pObj)[0]->data.misc.ubBombOwner - 2)]->ubProfile].records.usExpDetonated++;
|
||||
|
||||
StatChange( MercPtrs[((*pObj)[0]->data.misc.ubBombOwner - 2)], EXPLODEAMT, ( 5 ), FALSE );
|
||||
}
|
||||
}
|
||||
|
||||
gubPersonToSetOffExplosions = ubID;
|
||||
|
||||
// ignite explosions manually - this item is not in the WorldBobms-structure, so we can't add it to the queue
|
||||
IgniteExplosion( ubID, pSoldier->sX, pSoldier->sY, (INT16) (gpWorldLevelData[pSoldier->sGridNo].sHeight), pSoldier->sGridNo, pObj->usItem, pSoldier->pathing.bLevel, pSoldier->ubDirection );
|
||||
|
||||
DeleteObj( pObj );
|
||||
}
|
||||
}
|
||||
else // defuse bombs
|
||||
{
|
||||
// check for frequency
|
||||
if ((*pObj)[0]->data.bDefuseFrequency == bFrequency)
|
||||
{
|
||||
// check for a defuse
|
||||
if ( HasAttachmentOfClass(pObj, AC_DEFUSE) )
|
||||
{
|
||||
(*pObj)[0]->data.bTrap = 0;
|
||||
|
||||
if ( (*pObj).fFlags & OBJECT_KNOWN_TO_BE_TRAPPED )
|
||||
pObj->fFlags &= ~( OBJECT_KNOWN_TO_BE_TRAPPED );
|
||||
|
||||
if ( (*pObj).fFlags & OBJECT_ARMED_BOMB )
|
||||
pObj->fFlags &= ~( OBJECT_ARMED_BOMB );
|
||||
|
||||
// set back ubWireNetworkFlag and bDefuseFrequency, but not the direction... bomb is still aimed, it is just turned off
|
||||
(*pObj)[0]->data.ubWireNetworkFlag = 0;
|
||||
(*pObj)[0]->data.bDefuseFrequency = 0;
|
||||
(*pObj)[0]->data.misc.bDetonatorType = 0;
|
||||
(*pObj)[0]->data.misc.bDelay = 0;
|
||||
(*pObj)[0]->data.misc.bFrequency = 0;
|
||||
(*pObj)[0]->data.bTrap = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetOffPanicBombs( UINT8 ubID, INT8 bPanicTrigger )
|
||||
|
||||
Reference in New Issue
Block a user