Lua Arizona HUD Индикатор HP двигателя на CEF (упрощенный HUD)

z.o_

Новичок
Автор темы
1
2
Версия SA-MP
  1. Любая
  2. Другая

CEF Engine HP

image.png


Решил я тут как-то ночью по приколу добавить то, что разработчики Аризоны почему-то забыли сделать пару лет назад, когда выкатывали упрощенный худ. В общем, вооружился чаем и нейронкой, набросал вот такой простенький «костыльный» скрипт, который выводит здоровье двигателя прямо в 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
 

Вложения

  • CEF Engine HP.lua
    4.3 KB · Просмотры: 2
Последнее редактирование:
  • Нравится
Реакции: Hotring и bfix