Загрузка данных
script_name('FOV First Person')
script_author('Claude')
script_version('1.0')
require 'lib.moonloader'
local inicfg = require 'inicfg'
local encoding = require 'encoding'
-- Файл должен быть сохранён в UTF-8
encoding.default = 'CP1251'
local u8 = encoding.UTF8
local function t(s) return u8:decode(s) end
local MIN_FOV, MAX_FOV, DEFAULT_FOV = 30, 120, 70
local GAME_FOV = 70.0
local CFG_NAME = 'fov_firstperson.ini'
local cfg = inicfg.load({
main = { fov = 90, enabled = true, dist = 1.3 }
}, CFG_NAME)
local DLG_MENU, DLG_FOV, DLG_DIST, DLG_PRESET = 7200, 7201, 7202, 7203
local presets = { 70, 80, 90, 100, 110, 120 }
local inFirstPerson = false
local function save() inicfg.save(cfg, CFG_NAME) end
local function msg(text)
sampAddChatMessage(t('{00BFFF}[FOV 1-е лицо]{FFFFFF} ' .. text), -1)
end
-- Первое лицо определяем по расстоянию от камеры до игрока:
-- в третьем лице камера всегда заметно дальше, чем в первом.
local function cameraDistance()
local cx, cy, cz = getActiveCameraCoordinates()
local px, py, pz = getCharCoordinates(PLAYER_PED)
local dx, dy, dz = cx - px, cy - py, cz - pz
return math.sqrt(dx * dx + dy * dy + dz * dz)
end
local debugMode = false
local lastDebug = 0
local function isFirstPerson()
return cameraDistance() < cfg.main.dist
end
local function setFov(value)
value = tonumber(value)
if not value then
msg('Введите число, например: /fpfov 90')
return
end
if value < MIN_FOV or value > MAX_FOV then
msg(('Допустимый диапазон: %d-%d'):format(MIN_FOV, MAX_FOV))
return
end
cfg.main.fov = value
cfg.main.enabled = true
save()
msg('FOV для первого лица: {FFD700}' .. value)
end
local function setDist(value)
value = tonumber(value)
if not value or value < 0.5 or value > 3.0 then
msg('Порог должен быть числом от 0.5 до 3.0')
return
end
cfg.main.dist = value
save()
msg('Порог определения первого лица: {FFD700}' .. value)
end
local function openMenu()
local status = cfg.main.enabled and '{00FF00}включён' or '{FF4444}выключен'
local text = table.concat({
'Скрипт: ' .. status,
'Задать FOV (сейчас: {FFD700}' .. cfg.main.fov .. '{FFFFFF})',
'Выбрать готовый пресет',
'Порог определения 1-го лица (сейчас: {FFD700}' .. cfg.main.dist .. '{FFFFFF})',
'Сбросить настройки',
}, '\n')
sampShowDialog(DLG_MENU, t('{00BFFF}FOV от первого лица'), t(text),
t('Выбрать'), t('Закрыть'), 2)
end
local function openFovInput()
sampShowDialog(DLG_FOV, t('{00BFFF}Значение FOV'),
t(('Введите значение от %d до %d:'):format(MIN_FOV, MAX_FOV)),
t('Применить'), t('Назад'), 1)
end
local function openDistInput()
sampShowDialog(DLG_DIST, t('{00BFFF}Порог определения'),
t('Если скрипт путает виды, подберите значение от 0.5 до 3.0.\n' ..
'Больше значение, тем шире зона, где считается «первое лицо».'),
t('Применить'), t('Назад'), 1)
end
local function openPresets()
local lines = {}
for _, p in ipairs(presets) do
local mark = (p == cfg.main.fov) and ' {00FF00}(текущий)' or ''
lines[#lines + 1] = 'FOV ' .. p .. mark
end
sampShowDialog(DLG_PRESET, t('{00BFFF}Пресеты FOV'), t(table.concat(lines, '\n')),
t('Применить'), t('Назад'), 2)
end
local function handleDialogs()
local ok, button, list = sampHasDialogRespond(DLG_MENU)
if ok then
if button == 1 then
if list == 0 then
cfg.main.enabled = not cfg.main.enabled
save()
msg(cfg.main.enabled and 'Скрипт включён.' or 'Скрипт выключен.')
openMenu()
elseif list == 1 then
openFovInput()
elseif list == 2 then
openPresets()
elseif list == 3 then
openDistInput()
elseif list == 4 then
cfg.main.fov, cfg.main.enabled, cfg.main.dist = 90, true, 1.3
save()
msg('Настройки сброшены.')
openMenu()
end
end
return
end
local ok2, button2, _, input2 = sampHasDialogRespond(DLG_FOV)
if ok2 then
if button2 == 1 then setFov(input2) end
openMenu()
return
end
local ok3, button3, _, input3 = sampHasDialogRespond(DLG_DIST)
if ok3 then
if button3 == 1 then setDist(input3) end
openMenu()
return
end
local ok4, button4, list4 = sampHasDialogRespond(DLG_PRESET)
if ok4 then
if button4 == 1 and presets[list4 + 1] then setFov(presets[list4 + 1]) end
openMenu()
end
end
function main()
while not isSampAvailable() do wait(100) end
sampRegisterChatCommand('fpfov', function(arg)
arg = (arg or ''):match('^%s*(.-)%s*$')
if arg == '' then
openMenu()
elseif arg == 'on' then
cfg.main.enabled = true
save()
msg('Скрипт включён.')
elseif arg == 'off' then
cfg.main.enabled = false
save()
msg('Скрипт выключен.')
elseif arg == 'debug' then
debugMode = not debugMode
msg(debugMode and 'Отладка включена.' or 'Отладка выключена.')
elseif arg:match('^dist%s+') then
setDist(arg:match('^dist%s+(.+)$'))
else
setFov(arg)
end
end)
msg('Загружен. Меню: {FFD700}/fpfov{FFFFFF}, быстрая смена: {FFD700}/fpfov [число]')
while true do
wait(0)
if debugMode and os.clock() - lastDebug > 1.0 then
lastDebug = os.clock()
msg(('Дистанция: {FFD700}%.2f{FFFFFF} | порог: %.2f | 1-е лицо: %s'):format(
cameraDistance(), cfg.main.dist, tostring(isFirstPerson())))
end
local firstPerson = cfg.main.enabled and isFirstPerson()
if firstPerson then
cameraSetLerpFov(cfg.main.fov, cfg.main.fov, 1000, true)
inFirstPerson = true
elseif inFirstPerson then
-- вышли из первого лица: возвращаем стандартный FOV один раз
cameraSetLerpFov(GAME_FOV, GAME_FOV, 1000, true)
inFirstPerson = false
end
handleDialogs()
end
end