Skip to content

Add vehicleexplosions world special property - #5123

Open
pube1 wants to merge 4 commits into
multitheftauto:masterfrom
pube1:feature/vehicleexplosions
Open

Add vehicleexplosions world special property#5123
pube1 wants to merge 4 commits into
multitheftauto:masterfrom
pube1:feature/vehicleexplosions

Conversation

@pube1

@pube1 pube1 commented Aug 1, 2026

Copy link
Copy Markdown

Summary

Adds a new world special property vehicleexplosions, which stops the game from
destroying vehicles on its own:
setWorldSpecialPropertyEnabled("vehicleexplosions", false).

  • Added WorldSpecialProperty::VEHICLEEXPLOSIONS enum in Shared.
  • Synced through SWorldSpecialPropertiesStateSync in
    a new data2 block, gated on a new eBitStreamVersion entry
    so peers built before this change keep working.
  • Handled sync via CMapInfoPacket on the server and CPacketHandler on the
    client.
  • Enforced in CClientGame::VehicleDamageHandler, which holds a damaged vehicle
    at VEHICLE_BURNING_HEALTH (250). Below that health the game sets the vehicle
    on fire and blows it up once the delayed explosion timer expires, so keeping
    health at that floor prevents both — for every vehicle type, without patching
    game code.
  • Explicit destruction is unaffected: blowVehicle() still works while the
    property is disabled, since the property only suppresses what the game does by
    itself.

This also fixes a pre-existing bug that the change would otherwise make hot:
CVehicleSA::SetHealth cast every vehicle to CAutomobileSAInterface* in order
to clear m_fBurningTime, a field that only exists on automobiles. For bikes,
boats and trains that wrote past the end of the interface. The automobile-only
field moved to a CAutomobileSA::SetHealth override.

Motivation

Lets gamemode developers keep vehicles alive instead of having them explode and
disappear once they run out of health.

Test plan

  • Tested in-game with a Lua resource calling
    setWorldSpecialPropertyEnabled("vehicleexplosions", false) on resource start,
    firing an RPG and a minigun at cars, planes and boats. Health stops at 250, the
    vehicles neither catch fire nor explode, and damage and deformation still apply
    above that.
  • blowVehicle() still destroys a vehicle while the property is disabled.
  • Updated the SWorldSpecialPropertiesStateSync round-trip test for the extra
    bit and added LegacyPeerOmitsSecondBlock, which covers a peer older than the
    new bitstream version. Full client test suite passes (305 tests).

Checklist

  • Your code should follow the coding guidelines.
  • Smaller pull requests are easier to review. If your pull request is beefy, your pull request should be reviewable commit-by-commit.

Prompt & Goals:
- Add a new world special property 'vehicleexplosions' to allow disabling vehicle explosions.

Motivation & Reasoning:
- Enables gamemode developers to prevent vehicles from exploding upon reaching 0 HP via setWorldSpecialPropertyEnabled('vehicleexplosions', false).

Implementation Details:
- Added WorldSpecialProperty::VEHICLEEXPLOSIONS enum in Shared.
- Added vehicleexplosions bitfield to SWorldSpecialPropertiesStateSync bitstream structure.
- Handled sync via CMapInfoPacket on server and CPacketHandler on client.
- Applied GTA SA memory patch at CVehicle::BlowUpCar (0x6B8F80) using ret 8 (0xC2, 0x08, 0x00) when property is disabled.
@guibzo

guibzo commented Aug 1, 2026

Copy link
Copy Markdown

Seems pretty useful, at least most roleplay servers have to prevent it manually
hope it won't bypass blowVehicle tho

@FileEX FileEX added the enhancement New feature or request label Aug 2, 2026

@FileEX FileEX left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The corresponding changes in SyncMisc_Tests.cpp are missing as well.

Comment thread Client/game_sa/CGameSA.cpp Outdated

if (isEnabled)
{
MemCpy((void*)0x6B8F80, "\x56\x8B\xF1", 3); // CVehicle::BlowUpCar

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0x6B8F80 points to CBike::VehicleDamage. I don't think you tested your PR. That address points to a completely different location in memory, and those bytes will simply cause a crash.

Image

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, thanks for identifying what that address actually is. I dropped the memory patch entirely — details in my comment below.

bool flyingcomponents : 1;
bool vehicleburnexplosions : 1;
bool vehicleEngineAutoStart : 1;
bool vehicleexplosions : 1;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another sign that you didn't test your changes. You need to increase BITCOUNT for this to work.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, though with a version-gated second block rather than bumping BITCOUNT — reasoning in my comment below.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since version 1.7 hasn't had an official release yet, we don't have to worry about the bitstream. So just increase the BITCOUNT and revert the changes in bitstream.h and the tests.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, moved it back into data, BITCOUNT is 21 now, and bitstream.h is reverted. The round-trip test keeps the updated bit count and a check for the new flag; the version-gate test is gone with the gate.

The property was never transmitted: BITCOUNT was left at 20 while the new
bit was added as the 21st, so the client always kept its default. Rather
than bumping BITCOUNT, which would shift the stream by one bit for peers
built before this change, the flag now lives in a separate data2 block
gated on a new eBitStreamVersion entry.

The memory patch at 0x6B8F80 has been dropped. That address is
CBike::VehicleDamage, not CVehicle::BlowUpCar, and BlowUpCar is virtual
anyway, so every overriding class would have needed its own patch. The
property is now enforced in CClientGame::VehicleDamageHandler, which
holds a damaged vehicle at VEHICLE_BURNING_HEALTH. Below that health the
game sets the vehicle on fire and blows it up on its own, so the floor
prevents both for every vehicle type without patching code.

CVehicleSA::SetHealth cast every vehicle to CAutomobileSAInterface* to
clear m_fBurningTime, which only exists on automobiles; for bikes, boats
and trains that wrote past the end of the interface. The health floor
calls SetHealth on every lethal hit, so it hit that path constantly. The
automobile-only field moved to a CAutomobileSA::SetHealth override.

Updated the SWorldSpecialPropertiesStateSync round-trip test for the
extra bit and added a test covering a peer older than the new bitstream
version.
@pube1
pube1 requested a review from FileEX August 2, 2026 14:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants