- 1
- 2
- Версия SA-MP
-
- Любая
- Другая
CEF Engine HP
Решил я тут как-то ночью по приколу добавить то, что разработчики Аризоны почему-то забыли сделать пару лет назад, когда выкатывали упрощенный худ. В общем, вооружился чаем и нейронкой, набросал вот такой простенький «костыльный» скрипт, который выводит здоровье двигателя прямо в CEF-спидометр.
Если вы найдёте баги, захотите оптимизировать этот костыль или у вас появятся идеи, как сделать его аккуратнее — пишите прямо в тему, буду рад почитать.
Что делает скрипт:
- Добавляет аккуратную серую иконку двигателя с процентами здоровья слева от скорости (копия оригинального индикатора топлива).
- Информирует о состоянии цветом:
- Серый — двигатель полностью исправен (не мозолит глаза).
- Желтый — машина повреждена (меньше 800 HP).
- Красный — критическое состояние, авто вот-вот заглохнет (меньше 360 HP).
- Полная автоматика: появляется только когда вы за рулем, и бесследно исчезает при выходе из машины.
Установка:
Перенести скачанный
.lua файл в папку moonloader.
Lua:
-- Author: z.o_
local lastVehicleHandle = nil
local vehicleMaxHealth = nil
local BREAKDOWN_HEALTH = 360
local WARNING_HEALTH = 800
function main()
repeat wait(0) until isSampAvailable()
while true do
local health, percent = getVehicleHp()
if health then
ensureAndUpdateVehicleHp(health, percent)
else
removeVehicleHp()
end
wait(200)
end
end
function ensureAndUpdateVehicleHp(health, percent)
evalanon(([[
const wrapper = document.querySelector('.speedometer-simplified__main-info-wrapper');
if (!wrapper) return;
let hpBlock = document.getElementById('arzaddon__hud__vehiclehp');
if (!hpBlock) {
const fuelBlock = wrapper.querySelector('.speedometer-simplified__fuel-indicator');
if (!fuelBlock) return;
hpBlock = fuelBlock.cloneNode(true);
hpBlock.id = 'arzaddon__hud__vehiclehp';
const iconEl = hpBlock.querySelector('.circle-indicator__icon');
if (iconEl) {
iconEl.classList.remove('ui-fuel');
iconEl.classList.add('ui-engine');
}
wrapper.insertBefore(hpBlock, fuelBlock);
}
hpBlock.style.order = '1';
hpBlock.style.position = 'relative';
hpBlock.style.zIndex = '3';
hpBlock.style.marginRight = '-66px';
hpBlock.style.marginLeft = '0px';
const speedBlock = wrapper.firstElementChild;
if (speedBlock && speedBlock !== hpBlock) {
speedBlock.style.order = '2';
speedBlock.style.position = 'relative';
speedBlock.style.zIndex = '2';
}
let fuelBlock = null;
const fuelIndicators = wrapper.querySelectorAll('.speedometer-simplified__fuel-indicator');
for (let el of fuelIndicators) {
if (el.id !== 'arzaddon__hud__vehiclehp') {
fuelBlock = el;
break;
}
}
if (fuelBlock) {
fuelBlock.style.order = '3';
fuelBlock.style.position = 'relative';
fuelBlock.style.zIndex = '3';
}
const hpCircle = hpBlock.querySelector('.circle-indicator');
if (hpCircle) {
const svgZone = hpCircle.querySelector('.circle-indicator__svg-zone');
const countEl = hpCircle.querySelector('.circle-indicator__count');
if (svgZone && countEl) {
const pct = %d;
const hl = %d;
countEl.innerHTML = pct + '<span class="circle-indicator__units">%%</span>';
const dashArray = parseFloat(svgZone.style.getPropertyValue('--dasharray'));
const p = pct < 0 ? 1 : pct > 100 ? 0 : 1 - (pct / 100);
svgZone.style.setProperty('--dashoffset', dashArray * p);
let color = '#8E9296';
if (hl <= %d && hl > %d) color = '#e0b400';
if (hl <= %d) color = '#e02d2d';
hpCircle.style.setProperty('--icon-color', color);
hpCircle.style.setProperty('--progress-color', color);
}
}
]]):format(percent, health, WARNING_HEALTH, BREAKDOWN_HEALTH, BREAKDOWN_HEALTH))
end
function removeVehicleHp()
evalanon "const el = document.getElementById('arzaddon__hud__vehiclehp'); if (el) el.remove();"
end
function evalanon(code)
evalcef(("(() => {%s})()"):format(code))
end
function evalcef(code, encoded)
encoded = encoded or 0
local bs = raknetNewBitStream()
raknetBitStreamWriteInt8(bs, 17)
raknetBitStreamWriteInt32(bs, 0)
raknetBitStreamWriteInt16(bs, #code)
raknetBitStreamWriteInt8(bs, encoded)
raknetBitStreamWriteString(bs, code)
raknetEmulPacketReceiveBitStream(220, bs)
raknetDeleteBitStream(bs)
end
function getVehicleHp()
if not isCharInAnyCar(PLAYER_PED) then
lastVehicleHandle = nil
vehicleMaxHealth = nil
return nil
end
local car = storeCarCharIsInNoSave(PLAYER_PED)
if not car or car == 0 then return nil end
if car ~= lastVehicleHandle then
lastVehicleHandle = car
vehicleMaxHealth = nil
end
local health = getCarHealth(car)
if not vehicleMaxHealth then
if health > 5000 then vehicleMaxHealth = 10000
elseif health > 1500 then vehicleMaxHealth = 5000
else vehicleMaxHealth = 1500 end
end
local activeHealth = health - BREAKDOWN_HEALTH
local activeMax = vehicleMaxHealth - BREAKDOWN_HEALTH
local percent = 0
if activeHealth > 0 and activeMax > 0 then
percent = math.floor((activeHealth / activeMax) * 100)
end
return health, math.max(0, math.min(100, percent))
end
Вложения
Последнее редактирование: