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:
Wanne
2012-06-03 16:53:03 +00:00
parent 4750b07f16
commit de09d4e80f
8 changed files with 637 additions and 28 deletions
+412 -22
View File
@@ -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.
+1 -1
View File
@@ -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
View File
@@ -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;
}
}
+3
View File
@@ -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
+65 -1
View File
@@ -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 )
{
+3
View File
@@ -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;
+2 -1
View File
@@ -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)
+134 -1
View File
@@ -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 )