Skip to content

Make onVehicleExplode event cancellable #4295

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions Client/mods/deathmatch/logic/CClientExplosionManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,6 @@ bool CClientExplosionManager::Hook_ExplosionCreation(CEntity* pGameExplodingEnti
{
auto vehicle = reinterpret_cast<CClientVehicle*>(pResponsible);
pOriginSource = vehicle;

// Create an explosion, if the vehicle was not blown by us directly (CClientVehicle::Blow)
if (vehicle->GetBlowState() == VehicleBlowState::INTACT)
{
vehicle->SetBlowState(VehicleBlowState::AWAITING_EXPLOSION_SYNC);
}
}
// If theres other players, sync it relative to the closest (lag compensation)
else if (m_pManager->GetPlayerManager()->Count() > 1)
Expand All @@ -157,7 +151,7 @@ bool CClientExplosionManager::Hook_ExplosionCreation(CEntity* pGameExplodingEnti
}

// Request a new explosion
g_pClientGame->SendExplosionSync(vecPosition, explosionType, pOriginSource);
g_pClientGame->SendExplosionSync(vecPosition, explosionType, pOriginSource, VehicleBlowState::AWAITING_EXPLOSION_SYNC);
return false;
}

Expand Down
4 changes: 2 additions & 2 deletions Client/mods/deathmatch/logic/CClientGame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5262,7 +5262,7 @@ bool CClientGame::StaticProcessPacket(unsigned char ucPacketID, NetBitStreamInte
return false;
}

void CClientGame::SendExplosionSync(const CVector& vecPosition, eExplosionType Type, CClientEntity* pOrigin)
void CClientGame::SendExplosionSync(const CVector& vecPosition, eExplosionType Type, CClientEntity* pOrigin, std::optional<VehicleBlowState> vehicleBlowState)
{
SPositionSync position(false);
position.data.vecPosition = vecPosition;
Expand All @@ -5285,7 +5285,7 @@ void CClientGame::SendExplosionSync(const CVector& vecPosition, eExplosionType T
{
auto vehicle = reinterpret_cast<CClientVehicle*>(pOrigin);
pBitStream->WriteBit(1);
pBitStream->WriteBit(vehicle->GetBlowState() == VehicleBlowState::BLOWN);
pBitStream->WriteBit(vehicleBlowState.value_or(vehicle->GetBlowState()) == VehicleBlowState::BLOWN);
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion Client/mods/deathmatch/logic/CClientGame.h
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ class CClientGame
bool VerifySADataFiles(int iEnableClientChecks = 0);
void DebugElementRender();

void SendExplosionSync(const CVector& vecPosition, eExplosionType Type, CClientEntity* pOrigin = NULL);
void SendExplosionSync(const CVector& vecPosition, eExplosionType Type, CClientEntity* pOrigin = nullptr, std::optional<VehicleBlowState> vehicleBlowState = std::nullopt);
void SendFireSync(CFire* pFire);
void SendProjectileSync(CClientProjectile* pProjectile);

Expand Down
43 changes: 36 additions & 7 deletions Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2866,17 +2866,46 @@ bool CStaticFunctionDefinitions::BlowVehicle(CClientEntity& Entity, std::optiona
{
RUN_CHILDREN(BlowVehicle(**iter, withExplosion))

if (IS_VEHICLE(&Entity))
{
CClientVehicle& vehicle = static_cast<CClientVehicle&>(Entity);
if (!IS_VEHICLE(&Entity))
return false;

VehicleBlowFlags blow;
blow.withExplosion = withExplosion.value_or(true);
CClientVehicle& vehicle = static_cast<CClientVehicle&>(Entity);
VehicleBlowFlags blow;

blow.withExplosion = withExplosion.value_or(true);

if (vehicle.IsLocalEntity())
{
vehicle.Blow(blow);
return true;
}
else
{
CVector position;
vehicle.GetPosition(position);

return false;
const auto type = vehicle.GetType();
const auto state = (blow.withExplosion ? VehicleBlowState::AWAITING_EXPLOSION_SYNC : VehicleBlowState::BLOWN);
eExplosionType explosion;

switch (type)
{
case CLIENTVEHICLE_CAR:
explosion = EXP_TYPE_CAR;
break;
case CLIENTVEHICLE_HELI:
explosion = EXP_TYPE_HELI;
break;
case CLIENTVEHICLE_BOAT:
explosion = EXP_TYPE_BOAT;
break;
default:
explosion = EXP_TYPE_CAR;
}

g_pClientGame->SendExplosionSync(position, explosion, &Entity, state);
}

return true;
}

bool CStaticFunctionDefinitions::GetVehicleVariant(CClientVehicle* pVehicle, unsigned char& ucVariant, unsigned char& ucVariant2)
Expand Down
10 changes: 6 additions & 4 deletions Server/mods/deathmatch/logic/CGame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2833,18 +2833,20 @@ void CGame::Packet_ExplosionSync(CExplosionSyncPacket& Packet)

if (previousBlowState != VehicleBlowState::BLOWN)
{
vehicle->SetBlowState(VehicleBlowState::BLOWN);
vehicle->SetEngineOn(false);

// NOTE(botder): We only trigger this event if we didn't blow up a vehicle with `blowVehicle`
if (previousBlowState == VehicleBlowState::INTACT)
{
CLuaArguments arguments;
arguments.PushBoolean(!Packet.m_blowVehicleWithoutExplosion);
arguments.PushElement(clientSource);
vehicle->CallEvent("onVehicleExplode", arguments);

if (!vehicle->CallEvent("onVehicleExplode", arguments))
return;
}

vehicle->SetBlowState(VehicleBlowState::BLOWN);
vehicle->SetEngineOn(false);

syncToPlayers = vehicle->GetBlowState() == VehicleBlowState::BLOWN && !vehicle->IsBeingDeleted();
}
else
Expand Down
4 changes: 3 additions & 1 deletion Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5485,7 +5485,9 @@ bool CStaticFunctionDefinitions::BlowVehicle(CElement* pElement, std::optional<b

CLuaArguments arguments;
arguments.PushBoolean(createExplosion); // withExplosion
vehicle->CallEvent("onVehicleExplode", arguments);

if (!vehicle->CallEvent("onVehicleExplode", arguments))
return false;

// Abort if vehicle got fixed or destroyed
if (!vehicle->IsBlown() || vehicle->IsBeingDeleted())
Expand Down
Loading