mirror of
https://github.com/Michatec/ptelevision.git
synced 2026-03-31 23:46:30 +02:00
Update v1.1
This commit is contained in:
85
client/cursor.lua
Normal file
85
client/cursor.lua
Normal file
@@ -0,0 +1,85 @@
|
||||
local scale = 1.5
|
||||
local screenWidth = math.floor(1920 / scale)
|
||||
local screenHeight = math.floor(1080 / scale)
|
||||
shouldDraw = false
|
||||
|
||||
function SetInteractScreen(bool)
|
||||
if (not shouldDraw and bool) then
|
||||
shouldDraw = bool
|
||||
Citizen.CreateThread(function ()
|
||||
-- Create screen
|
||||
local nX = 0
|
||||
local nY = 0
|
||||
|
||||
local w, h = screenWidth, screenHeight
|
||||
|
||||
local minX, maxX = ((w - (w / 2)) / 2), (w - (w / 4))
|
||||
local totalX = minX - maxX
|
||||
|
||||
local minY, maxY = ((h - (h / 2)) / 2), (h - (h / 4))
|
||||
local totalY = minY - maxY
|
||||
|
||||
RequestTextureDictionary('fib_pc')
|
||||
|
||||
-- Update controls while active
|
||||
while shouldDraw do
|
||||
nX = GetControlNormal(0, 239) * screenWidth
|
||||
nY = GetControlNormal(0, 240) * screenHeight
|
||||
DisableControlAction(0, 1, true) -- Disable looking horizontally
|
||||
DisableControlAction(0, 2, true) -- Disable looking vertically
|
||||
DisablePlayerFiring(PlayerPedId(), true) -- Disable weapon firing
|
||||
DisableControlAction(0, 142, true) -- Disable aiming
|
||||
DisableControlAction(0, 106, true) -- Disable in-game mouse controls
|
||||
-- Update mouse position when changed
|
||||
DrawSprite("ptelevision_b_dict", "ptelevision_b_txd", 0.5, 0.5, 0.5, 0.5, 0.0, 255, 255, 255, 255)
|
||||
if nX ~= mX or nY ~= mY then
|
||||
mX = nX; mY = nY
|
||||
local duiX = -screenWidth * ((mX - minX) / totalX)
|
||||
local duiY = -screenHeight * ((mY - minY) / totalY)
|
||||
BlockWeaponWheelThisFrame()
|
||||
if not (mX > 325) then
|
||||
mX = 325
|
||||
end
|
||||
if not (mX < 965) then
|
||||
mX = 965
|
||||
end
|
||||
if not (mY > 185) then
|
||||
mY = 185
|
||||
end
|
||||
if not (mY < 545) then
|
||||
mY = 545
|
||||
end
|
||||
SendDuiMouseMove(duiObj, math.floor(duiX), math.floor(duiY))
|
||||
end
|
||||
DrawSprite('fib_pc', 'arrow', mX / screenWidth, mY / screenHeight, 0.005, 0.01, 0.0, 255, 255, 255, 255)
|
||||
|
||||
-- Send scroll and click events to dui
|
||||
|
||||
if IsControlPressed(0, 177) then
|
||||
SetInteractScreen(false)
|
||||
OpenTVMenu()
|
||||
end -- scroll up
|
||||
if IsControlPressed(0, 172) then
|
||||
SendDuiMouseWheel(duiObj, 10, 0) end -- scroll up
|
||||
if IsControlPressed(0, 173) then
|
||||
SendDuiMouseWheel(duiObj, -10, 0) end -- scroll down
|
||||
|
||||
if IsDisabledControlJustPressed(0, 24) then
|
||||
SendDuiMouseDown(duiObj, 'left')
|
||||
elseif IsDisabledControlJustReleased(0, 24) then
|
||||
SendDuiMouseUp(duiObj, 'left')
|
||||
SendDuiMouseUp(duiObj, "right")
|
||||
end
|
||||
if IsDisabledControlJustPressed(0, 25) then
|
||||
SendDuiMouseDown(duiObj, "right")
|
||||
elseif IsDisabledControlJustReleased(0, 24) then
|
||||
SendDuiMouseUp(duiObj, "right")
|
||||
end
|
||||
|
||||
Wait(0)
|
||||
end
|
||||
end)
|
||||
else
|
||||
shouldDraw = bool
|
||||
end
|
||||
end
|
||||
185
client/main.lua
Normal file
185
client/main.lua
Normal file
@@ -0,0 +1,185 @@
|
||||
DEFAULT_URL = "https://cfx-nui-ptelevision/html/index.html"
|
||||
duiUrl = DEFAULT_URL
|
||||
duiObj = nil
|
||||
tvObj = nil
|
||||
volume = 0.5
|
||||
CURRENT_SCREEN = nil
|
||||
|
||||
function getDuiURL()
|
||||
return duiUrl
|
||||
end
|
||||
|
||||
function SetVolume(coords, num)
|
||||
volume = num
|
||||
SetTelevisionLocal(coords, "volume", num)
|
||||
end
|
||||
|
||||
function GetVolume(dist, range)
|
||||
if not volume then return 0 end
|
||||
local rem = (dist / range)
|
||||
rem = rem > volume and volume or rem
|
||||
local _vol = math.floor((volume - rem) * 100)
|
||||
return _vol
|
||||
end
|
||||
|
||||
function setDuiURL(url)
|
||||
duiUrl = url
|
||||
SetDuiUrl(duiObj, duiUrl)
|
||||
end
|
||||
|
||||
local sfName = 'generic_texture_renderer'
|
||||
|
||||
local width = 1280
|
||||
local height = 720
|
||||
|
||||
local sfHandle = nil
|
||||
local txdHasBeenSet = false
|
||||
|
||||
|
||||
function loadScaleform(scaleform)
|
||||
local scaleformHandle = RequestScaleformMovie(scaleform)
|
||||
|
||||
while not HasScaleformMovieLoaded(scaleformHandle) do
|
||||
scaleformHandle = RequestScaleformMovie(scaleform)
|
||||
Citizen.Wait(0)
|
||||
end
|
||||
return scaleformHandle
|
||||
end
|
||||
|
||||
function ShowScreen(data)
|
||||
CURRENT_SCREEN = data
|
||||
sfHandle = loadScaleform(sfName)
|
||||
runtimeTxd = 'ptelevision_b_dict'
|
||||
|
||||
local txd = CreateRuntimeTxd('ptelevision_b_dict')
|
||||
duiObj = CreateDui(duiUrl, width, height)
|
||||
local dui = GetDuiHandle(duiObj)
|
||||
local tx = CreateRuntimeTextureFromDuiHandle(txd, 'ptelevision_b_txd', dui)
|
||||
|
||||
Citizen.Wait(10)
|
||||
|
||||
PushScaleformMovieFunction(sfHandle, 'SET_TEXTURE')
|
||||
|
||||
PushScaleformMovieMethodParameterString('ptelevision_b_dict')
|
||||
PushScaleformMovieMethodParameterString('ptelevision_b_txd')
|
||||
|
||||
PushScaleformMovieFunctionParameterInt(0)
|
||||
PushScaleformMovieFunctionParameterInt(0)
|
||||
PushScaleformMovieFunctionParameterInt(width)
|
||||
PushScaleformMovieFunctionParameterInt(height)
|
||||
|
||||
PopScaleformMovieFunctionVoid()
|
||||
Citizen.CreateThread(function()
|
||||
local tvObj = data.entity
|
||||
|
||||
local _, status = GetTelevision(data.coords)
|
||||
local _, lstatus = GetTelevisionLocal(data.coords)
|
||||
local screenModel = Config.Models[data.model]
|
||||
if status and status["ptv_status"] then
|
||||
local status = status["ptv_status"]
|
||||
Citizen.Wait(1000)
|
||||
if status.type == "play" and lstatus then
|
||||
if (status.channel and Channels[status.channel]) then
|
||||
PlayVideo({url = Channels[status.channel].url, channel = status.channel})
|
||||
elseif (status.url) then
|
||||
local time
|
||||
if (lstatus.start_time) then
|
||||
time = math.floor((GetGameTimer() - lstatus.start_time) / 1000)
|
||||
end
|
||||
PlayVideo({url = status.url, time = time})
|
||||
end
|
||||
elseif (status.type == "browser") then
|
||||
PlayBrowser({ url = status.url })
|
||||
end
|
||||
end
|
||||
while duiObj do
|
||||
if (tvObj and sfHandle ~= nil and HasScaleformMovieLoaded(sfHandle)) then
|
||||
local pos = GetEntityCoords(tvObj)
|
||||
local scale = screenModel.Scale
|
||||
local offset = GetOffsetFromEntityInWorldCoords(tvObj, -1.02, -0.055, 1.04)
|
||||
local hz = GetEntityHeading(tvObj)
|
||||
DrawScaleformMovie_3dNonAdditive(sfHandle, offset.x, offset.y, offset.z, 0.0, -hz, 0.0, 2.0, 2.0, 2.0, scale * 1, scale * (9/16), 1, 2)
|
||||
end
|
||||
Citizen.Wait(0)
|
||||
end
|
||||
end)
|
||||
Citizen.CreateThread(function()
|
||||
local screen = CURRENT_SCREEN
|
||||
local modelData = Config.Models[screen.model]
|
||||
local coords = screen.coords
|
||||
local range = modelData.Range
|
||||
local _, lstatus = GetTelevisionLocal(coords)
|
||||
if (lstatus and lstatus.volume) then
|
||||
SetVolume(coords, lstatus.volume)
|
||||
else
|
||||
SetVolume(coords, modelData.DefaultVolume)
|
||||
end
|
||||
while duiObj do
|
||||
local pcoords = GetEntityCoords(PlayerPedId())
|
||||
local dist = #(coords - pcoords)
|
||||
SendDuiMessage(duiObj, json.encode({
|
||||
setVolume = true,
|
||||
data = GetVolume(dist, range, volume)
|
||||
}))
|
||||
Citizen.Wait(100)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function HideScreen()
|
||||
if (duiObj) then
|
||||
DestroyDui(duiObj)
|
||||
SetScaleformMovieAsNoLongerNeeded(sfHandle)
|
||||
duiObj = nil
|
||||
sfHandle = nil
|
||||
end
|
||||
end
|
||||
|
||||
function GetClosestScreen()
|
||||
local objPool = GetGamePool('CObject')
|
||||
local closest = {dist = -1}
|
||||
local pcoords = GetEntityCoords(PlayerPedId())
|
||||
for i=1, #objPool do
|
||||
local entity = objPool[i]
|
||||
local model = GetEntityModel(entity)
|
||||
local data = Config.Models[model]
|
||||
if (data) then
|
||||
local coords = GetEntityCoords(entity)
|
||||
local dist = #(pcoords-coords)
|
||||
if (dist < closest.dist or closest.dist < 0) and dist < data.Range then
|
||||
closest = {dist = dist, coords = coords, model = model, entity = entity}
|
||||
end
|
||||
end
|
||||
end
|
||||
return (closest.entity and closest or nil)
|
||||
end
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
Citizen.Wait(2000)
|
||||
TriggerServerEvent("ptelevision:requestUpdate")
|
||||
while true do
|
||||
local wait = 2500
|
||||
local data = GetClosestScreen()
|
||||
if (data and not duiObj) then
|
||||
ShowScreen(data)
|
||||
elseif ((not data or #(vec3(CURRENT_SCREEN.coords) - vec3(data.coords)) > 0.01 ) and duiObj) then
|
||||
HideScreen()
|
||||
end
|
||||
Citizen.Wait(wait)
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent("ptelevision:requestUpdate", function(data)
|
||||
Televisions = data.Televisions
|
||||
Channels = data.Channels
|
||||
end)
|
||||
|
||||
RegisterNUICallback("pageLoaded", function()
|
||||
waitForLoad = false
|
||||
end)
|
||||
|
||||
AddEventHandler('onResourceStop', function(name)
|
||||
if name == GetCurrentResourceName() then
|
||||
HideScreen()
|
||||
end
|
||||
end)
|
||||
226
client/tv.lua
Normal file
226
client/tv.lua
Normal file
@@ -0,0 +1,226 @@
|
||||
TelevisionsLocal = {}
|
||||
|
||||
function SetChannel(index)
|
||||
TriggerServerEvent("ptelevision:event", CURRENT_SCREEN, "ptv_status", {
|
||||
type = "play",
|
||||
channel = index,
|
||||
})
|
||||
end
|
||||
|
||||
function GetChannelList()
|
||||
if not Channels then return {} end
|
||||
local channel_list = {}
|
||||
local menu_list = {}
|
||||
local current = 1
|
||||
local screen = CURRENT_SCREEN
|
||||
local ent = screen.entity
|
||||
local _, status = GetTelevision(screen.coords)
|
||||
local channel = nil
|
||||
if (status) then
|
||||
channel = status.channel
|
||||
end
|
||||
for index,value in pairs(Channels) do
|
||||
table.insert(channel_list, {index = index, url = value.url})
|
||||
table.insert(menu_list, "Channel #" .. index .. " (".. value.name ..")")
|
||||
if channel ~= nil and channel == index then
|
||||
current = #channel_list
|
||||
end
|
||||
end
|
||||
return {list = channel_list, display = menu_list, current = current}
|
||||
end
|
||||
|
||||
function BroadcastMenu()
|
||||
local _source = GetPlayerServerId(PlayerId())
|
||||
for k,v in pairs(Channels) do
|
||||
if (v.source == _source) then
|
||||
TriggerServerEvent("ptelevision:broadcast", nil)
|
||||
return
|
||||
end
|
||||
end
|
||||
local input = lib.inputDialog('Live Broadcast', {'Channel Name:', 'Broadcast URL:'})
|
||||
if (input[1] and input[2]) then
|
||||
TriggerServerEvent("ptelevision:broadcast", {name = input[1], url = input[2]})
|
||||
end
|
||||
end
|
||||
|
||||
function WebBrowserMenu()
|
||||
lib.hideMenu()
|
||||
local input = lib.inputDialog('Web Browser', {'URL:'})
|
||||
|
||||
if input then
|
||||
TriggerServerEvent("ptelevision:event", CURRENT_SCREEN, "ptv_status", {
|
||||
type = "browser",
|
||||
url = input[1]
|
||||
})
|
||||
end
|
||||
Citizen.Wait(300)
|
||||
OpenTVMenu()
|
||||
end
|
||||
|
||||
function VideoMenu()
|
||||
lib.hideMenu()
|
||||
local input = lib.inputDialog('Video Player', {'URL:'})
|
||||
if input then
|
||||
TriggerServerEvent("ptelevision:event", CURRENT_SCREEN, "ptv_status", {
|
||||
type = "play",
|
||||
url = input[1]
|
||||
})
|
||||
end
|
||||
Citizen.Wait(300)
|
||||
OpenTVMenu()
|
||||
end
|
||||
|
||||
function VolumeMenu()
|
||||
lib.hideMenu()
|
||||
local input = lib.inputDialog('Volume', {'Set Volume (0-100):'})
|
||||
if (tonumber(input[1])) then
|
||||
local coords = CURRENT_SCREEN.coords
|
||||
SetVolume(coords, tonumber(input[1])/100)
|
||||
end
|
||||
Citizen.Wait(300)
|
||||
OpenTVMenu()
|
||||
end
|
||||
|
||||
function OpenTVMenu()
|
||||
local screen = CURRENT_SCREEN
|
||||
if not screen then return end
|
||||
lib.hideMenu()
|
||||
local ChannelList = GetChannelList()
|
||||
lib.registerMenu({
|
||||
id = 'ptelevision-menu',
|
||||
title = 'Television',
|
||||
position = 'top-right',
|
||||
onSideScroll = function(selected, scrollIndex, args)
|
||||
if (selected == 3) then
|
||||
SetChannel(ChannelList.list[scrollIndex].index)
|
||||
end
|
||||
end,
|
||||
onSelected = function(selected, scrollIndex, args)
|
||||
end,
|
||||
onClose = function(keyPressed)
|
||||
end,
|
||||
options = {
|
||||
{label = 'Videos', description = 'Play a video or stream on the screen.'},
|
||||
{label = 'Web Browser', description = 'Access the web via your TV.'},
|
||||
{label = 'TV Channels', values = ChannelList.display, description = 'Live TV Channels in San Andreas!', defaultIndex = ChannelList.current},
|
||||
{label = 'Interact With Screen', description = 'Allows you to control on-screen elements.'},
|
||||
{label = 'Set Volume', description = 'Sets your TV\'s volume (For yourself).'},
|
||||
{label = 'Close Menu', close = true},
|
||||
}
|
||||
}, function(selected, scrollIndex, args)
|
||||
if (selected == 1) then
|
||||
VideoMenu()
|
||||
elseif (selected == 2) then
|
||||
WebBrowserMenu()
|
||||
elseif (selected == 3) then
|
||||
SetChannel(ChannelList.list[scrollIndex].index)
|
||||
OpenTVMenu()
|
||||
elseif selected == 4 then
|
||||
SetInteractScreen(true)
|
||||
elseif selected == 5 then
|
||||
VolumeMenu()
|
||||
end
|
||||
end)
|
||||
lib.showMenu('ptelevision-menu')
|
||||
end
|
||||
|
||||
function PlayBrowser(data)
|
||||
while not IsDuiAvailable(duiObj) do Wait(10) end
|
||||
setDuiURL(data.url)
|
||||
end
|
||||
|
||||
function PlayVideo(data)
|
||||
while not IsDuiAvailable(duiObj) do Wait(10) end
|
||||
if (getDuiURL() ~= DEFAULT_URL) then
|
||||
waitForLoad = true
|
||||
setDuiURL(DEFAULT_URL)
|
||||
while waitForLoad do Wait(10) end
|
||||
end
|
||||
SendDuiMessage(duiObj, json.encode({
|
||||
setVideo = true,
|
||||
data = data
|
||||
}))
|
||||
end
|
||||
|
||||
function ResetDisplay()
|
||||
setDuiURL(DEFAULT_URL)
|
||||
end
|
||||
|
||||
function GetTelevisionLocal(coords)
|
||||
for k,v in pairs(TelevisionsLocal) do
|
||||
if #(vec3(v.coords) - vec3(coords)) < 0.01 then
|
||||
return k, v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SetTelevisionLocal(coords, key, value)
|
||||
local index, data = GetTelevisionLocal(coords)
|
||||
if (index ~= nil) then
|
||||
if (TelevisionsLocal[index] == nil) then
|
||||
TelevisionsLocal[index] = {}
|
||||
end
|
||||
TelevisionsLocal[index][key] = value
|
||||
else
|
||||
index = GetGameTimer()
|
||||
while TelevisionsLocal[index] do
|
||||
index = index + 1
|
||||
Citizen.Wait(0)
|
||||
end
|
||||
if (TelevisionsLocal[index] == nil) then
|
||||
TelevisionsLocal[index] = {}
|
||||
end
|
||||
TelevisionsLocal[index][key] = value
|
||||
end
|
||||
TelevisionsLocal[index].coords = coords
|
||||
return index
|
||||
end
|
||||
|
||||
RegisterNetEvent("ptelevision:event", function(data, index, key, value)
|
||||
Televisions = data
|
||||
local data = Televisions[index]
|
||||
local screen = CURRENT_SCREEN
|
||||
if (screen and #(vec3(screen.coords) - vec3(data.coords)) < 0.001) then
|
||||
local index, data = GetTelevision(screen.coords)
|
||||
if (index) then
|
||||
local event = value
|
||||
if (event.type == "play") then
|
||||
local data = { url = event.url }
|
||||
if (event.channel) then
|
||||
data = Channels[event.channel]
|
||||
data.channel = event.channel
|
||||
end
|
||||
PlayVideo(data)
|
||||
elseif (event.type == "browser") then
|
||||
PlayBrowser({ url = event.url })
|
||||
end
|
||||
end
|
||||
end
|
||||
SetTelevisionLocal(Televisions[index].coords, "start_time", GetGameTimer())
|
||||
end)
|
||||
|
||||
RegisterNetEvent("ptelevision:broadcast", function(data, index)
|
||||
Channels = data
|
||||
if getDuiURL() == DEFAULT_URL then
|
||||
local screen = CURRENT_SCREEN
|
||||
local tvObj = screen.entity
|
||||
local _, status = GetTelevision(screen.coords)
|
||||
if (status and status.channel == index and data[index] == nil) then
|
||||
ResetDisplay()
|
||||
Citizen.Wait(10)
|
||||
end
|
||||
SendDuiMessage(duiObj, json.encode({
|
||||
showNotification = true,
|
||||
channel = index,
|
||||
data = data[index]
|
||||
}))
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterCommand('tv', function()
|
||||
OpenTVMenu()
|
||||
end)
|
||||
|
||||
RegisterCommand("broadcast", function(source, args, raw)
|
||||
BroadcastMenu()
|
||||
end)
|
||||
32
client/utils.lua
Normal file
32
client/utils.lua
Normal file
@@ -0,0 +1,32 @@
|
||||
function CreateNamedRenderTargetForModel(name, model)
|
||||
local handle = 0
|
||||
if not IsNamedRendertargetRegistered(name) then
|
||||
RegisterNamedRendertarget(name, 0)
|
||||
end
|
||||
if not IsNamedRendertargetLinked(model) then
|
||||
LinkNamedRendertarget(model)
|
||||
end
|
||||
if IsNamedRendertargetRegistered(name) then
|
||||
handle = GetNamedRendertargetRenderId(name)
|
||||
end
|
||||
|
||||
return handle
|
||||
end
|
||||
|
||||
function RequestTextureDictionary (dict)
|
||||
RequestStreamedTextureDict(dict)
|
||||
|
||||
while not HasStreamedTextureDictLoaded(dict) do Wait(0) end
|
||||
|
||||
return dict
|
||||
end
|
||||
|
||||
function LoadModel (model)
|
||||
if not IsModelInCdimage(model) then return end
|
||||
|
||||
RequestModel(model)
|
||||
|
||||
while not HasModelLoaded(model) do Wait(0) end
|
||||
|
||||
return model
|
||||
end
|
||||
Reference in New Issue
Block a user