const DEFORMATION_DAMAGE_THRESHOLD = 0.05;
const MAX_DEFORM_ITERATIONS = 50;
const DEFORMATION_UPDATE_INTERVAL = 1000;
let deformationUpdateTimers = {};
function getVehicleOffsetsForDeformation(vehicle) {
const model = vehicle.model;
const min = mp.game.gameplay.getModelDimensions(model).min;
const max = mp.game.gameplay.getModelDimensions(model).max;
const X = Math.round((max.x - min.x) * 0.5 * 100) / 100;
const Y = Math.round((max.y - min.y) * 0.5 * 100) / 100;
const Z = Math.round((max.z - min.z) * 0.5 * 100) / 100;
const halfY = Math.round(Y * 0.5 * 100) / 100;
return [
new mp.Vector3(-X, Y, 0.0),
new mp.Vector3(-X, Y, Z),
new mp.Vector3(0.0, Y, 0.0),
new mp.Vector3(0.0, Y, Z),
new mp.Vector3(X, Y, 0.0),
new mp.Vector3(X, Y, Z),
new mp.Vector3(-X, halfY, 0.0),
new mp.Vector3(-X, halfY, Z),
new mp.Vector3(0.0, halfY, 0.0),
new mp.Vector3(0.0, halfY, Z),
new mp.Vector3(X, halfY, 0.0),
new mp.Vector3(X, halfY, Z),
new mp.Vector3(-X, 0.0, 0.0),
new mp.Vector3(-X, 0.0, Z),
new mp.Vector3(0.0, 0.0, 0.0),
new mp.Vector3(0.0, 0.0, Z),
new mp.Vector3(X, 0.0, 0.0),
new mp.Vector3(X, 0.0, Z),
new mp.Vector3(-X, -halfY, 0.0),
new mp.Vector3(-X, -halfY, Z),
new mp.Vector3(0.0, -halfY, 0.0),
new mp.Vector3(0.0, -halfY, Z),
new mp.Vector3(X, -halfY, 0.0),
new mp.Vector3(X, -halfY, Z),
new mp.Vector3(-X, -Y, 0.0),
new mp.Vector3(-X, -Y, Z),
new mp.Vector3(0.0, -Y, 0.0),
new mp.Vector3(0.0, -Y, Z),
new mp.Vector3(X, -Y, 0.0),
new mp.Vector3(X, -Y, Z)
];
}
function applyDeformation(vehicle, deformation) {
if (!mp.vehicles.exists(vehicle)) return;
setTimeout(() => {
if (!mp.vehicles.exists(vehicle)) return;
if (deformation && deformation.length > 0) {
setVehicleDeformation(vehicle, deformation);
} else {
vehicle.setFixed();
}
}, 500);
}
function getVehicleDeformation(vehicle) {
if (!mp.vehicles.exists(vehicle)) return [];
const offsets = getVehicleOffsetsForDeformation(vehicle);
const deformationPoints = [];
offsets.forEach(offset => {
const dmg = Math.sqrt(
Math.pow(vehicle.getDeformationAtPos(offset.x, offset.y, offset.z).x, 2) +
Math.pow(vehicle.getDeformationAtPos(offset.x, offset.y, offset.z).y, 2) +
Math.pow(vehicle.getDeformationAtPos(offset.x, offset.y, offset.z).z, 2)
);
if (dmg > DEFORMATION_DAMAGE_THRESHOLD) {
deformationPoints.push({ offset, dmg });
}
});
return deformationPoints;
}
function setVehicleDeformation(vehicle, deformationPoints) {
if (!mp.vehicles.exists(vehicle)) return;
let iteration = 0;
let deform = true;
const fDeformationDamageMult = vehicle.getHandling('fDeformationDamageMult');
let damageMult = 20.0;
if (fDeformationDamageMult <= 0.55) {
damageMult = 1000.0;
} else if (fDeformationDamageMult <= 0.65) {
damageMult = 400.0;
} else if (fDeformationDamageMult <= 0.75) {
damageMult = 200.0;
}
while (deform && iteration < MAX_DEFORM_ITERATIONS) {
deform = false;
deformationPoints.forEach(def => {
const dmg = Math.sqrt(
Math.pow(vehicle.getDeformationAtPos(def.offset.x, def.offset.y, def.offset.z).x, 2) +
Math.pow(vehicle.getDeformationAtPos(def.offset.x, def.offset.y, def.offset.z).y, 2) +
Math.pow(vehicle.getDeformationAtPos(def.offset.x, def.offset.y, def.offset.z).z, 2)
);
if (dmg < def.dmg) {
vehicle.setDamage(
def.offset.x * 2.0, def.offset.y * 2.0, def.offset.z * 2.0,
def.dmg * damageMult,
1000.0,
true
);
deform = true;
}
});
iteration++;
}
}
function isDeformationWorse(newDef, oldDef) {
if (!oldDef || newDef.length > oldDef.length) {
return true;
} else if (newDef.length < oldDef.length) {
return false;
}
for (let i = 0; i < newDef.length; i++) {
let found = false;
for (let j = 0; j < oldDef.length; j++) {
if (newDef[i].offset.x === oldDef[j].offset.x &&
newDef[i].offset.y === oldDef[j].offset.y &&
newDef[i].offset.z === oldDef[j].offset.z) {
found = true;
if (newDef[i].dmg > oldDef[j].dmg) {
return true;
}
}
}
if (!found) {
return true;
}
}
return false;
}
function isDeformationEqual(newDef, oldDef) {
if (oldDef == null && newDef == null) return true;
if (oldDef == null || newDef == null || newDef.length !== oldDef.length) return false;
for (let i = 0; i < newDef.length; i++) {
if (newDef[i].dmg !== oldDef[i].dmg) {
return false;
}
}
return true;
}
function handleDeformationUpdate(vehicle) {
if (!mp.vehicles.exists(vehicle)) return;
if (!deformationUpdateTimers[vehicle.remoteId]) {
deformationUpdateTimers[vehicle.remoteId] = true;
setTimeout(() => {
if (!mp.vehicles.exists(vehicle)) {
deformationUpdateTimers[vehicle.remoteId] = false;
return;
}
const deformation = getVehicleDeformation(vehicle);
const currentDeformation = JSON.parse(vehicle.getVariable('deformation') || '[]');
if (!isDeformationEqual(deformation, currentDeformation) && isDeformationWorse(deformation, currentDeformation)) {
mp.events.callRemote('storeVehicleDeformationState', vehicle.remoteId, JSON.stringify(deformation));
}
deformationUpdateTimers[vehicle.remoteId] = false;
}, DEFORMATION_UPDATE_INTERVAL);
}
}
mp.events.add('playerLeaveVehicle', vehicle => {
if (!vehicle) return;
handleDeformationUpdate(vehicle);
});
mp.events.add('entityStreamIn', entity => {
if (entity.type !== 'vehicle') return;
setTimeout(() => {
const deformation = entity.getVariable('deformation');
if (deformation) {
const parsedDeformation = JSON.parse(deformation);
applyDeformation(entity, parsedDeformation);
}
}, 2000);
});
mp.events.add('playerEnterVehicle', (vehicle, seat) => {
setTimeout(() => {
if (vehicle.getPedInSeat(-1) === mp.players.local.handle) {
const deformation = vehicle.getVariable('deformation');
if (deformation) {
const parsedDeformation = JSON.parse(deformation);
applyDeformation(vehicle, parsedDeformation);
}
}
}, 100);
});
mp.events.add("entityDamage", (entity, damage) => {
if (entity.type === "vehicle") {
handleDeformationUpdate(entity);
}
});