Skip to content

Commit

Permalink
feat: in-game TV support
Browse files Browse the repository at this point in the history
  • Loading branch information
bitpredator committed Sep 17, 2024
1 parent b5fb0f3 commit 1a1ba6c
Show file tree
Hide file tree
Showing 17 changed files with 1,210 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fx_version "cerulean"
game "gta5"
lua54 'yes'
version "1.0.2"
Binary file not shown.
Binary file not shown.
27 changes: 27 additions & 0 deletions server-data/resources/[esx_addons]/ptelevision/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<div align='center'><img src='https://user-images.githubusercontent.com/111543470/198868741-d78353cf-0576-4f2e-8554-1f7e4ef4c986.png'/></div>
<div align='center'><h3><a href='https://picklemods.com/'>More Information & Scripts can be found here!</a></h3></div>

## What is this?

Basically, this serves as a free resource for servers that need a television script.

With this resource, you will be able to do the following:

- Watch Youtube & Twitch Videos / Streams.
- Broadcast your Youtube / Twitch Stream as a server-wide channel.
- Display & Browse the Web.
- Display Images and Videos (via direct link in the browser).

## What do I need?

You will need the following for this script to work.

- [Ox Lib](https://github.com/overextended/ox_lib/releases) (works with any framework)

- [Renderer](https://forum.cfx.re/t/release-generic-dui-2d-3d-renderer/131208) (works with any framework)

If you want to make a fork, please obtain permission with a valid reason.

## Need Support?

<a href='https://pickle-mods.tebex.io/contact'>Click here!</a>
88 changes: 88 additions & 0 deletions server-data/resources/[esx_addons]/ptelevision/client/cursor.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
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
44 changes: 44 additions & 0 deletions server-data/resources/[esx_addons]/ptelevision/client/dui.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
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

function RenderScaleformTV(renderTarget, scaleform, entity)
SetTextRenderId(renderTarget) -- set render target
Set_2dLayer(4)
SetScriptGfxDrawBehindPausemenu(1)
--DrawRect(0.5, 0.5, 1.0, 0.5, 255, 0, 0, 255); -- WOAH!
local coords = GetEntityCoords(entity)
local rot = GetEntityRotation(entity)
DrawSprite("ptelevision_b_dict", "ptelevision_b_txd", 0.5, 0.5, 1.0, 1.0, 0.0, 255, 255, 255, 255)
SetTextRenderId(GetDefaultScriptRendertargetRenderId()) -- reset
SetScriptGfxDrawBehindPausemenu(0)
end
228 changes: 228 additions & 0 deletions server-data/resources/[esx_addons]/ptelevision/client/main.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
DEFAULT_URL = "https://cfx-nui-ptelevision/html/index.html"
duiUrl = DEFAULT_URL
duiObj = nil
tvObj = nil
volume = 0.5
CURRENT_SCREEN = nil

local Locations = Config.Locations

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()
TriggerServerEvent("ptelevision:requestSync", data.coords)
local tvObj = data.entity
local screenModel = Config.Models[data.model]
while duiObj do
if tvObj and sfHandle ~= nil and HasScaleformMovieLoaded(sfHandle) then
local pos = GetEntityCoords(tvObj)
local scale = screenModel.Scale
local offset = GetOffsetFromEntityInWorldCoords(tvObj, screenModel.Offset.x, screenModel.Offset.y, screenModel.Offset.z)
if screenModel.Target then
local id = CreateNamedRenderTargetForModel(screenModel.Target, data.model)
if id ~= -1 then
RenderScaleformTV(id, sfHandle, tvObj)
end
else
local hz = GetEntityHeading(tvObj)
DrawScaleformMovie_3dSolid(sfHandle, offset, 0.0, 0.0, -hz, 2.0, 2.0, 2.0, scale * 1, scale * (9 / 16), 2)
end
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()
CURRENT_SCREEN = nil
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 #(v3(CURRENT_SCREEN.coords) - v3(data.coords)) > 0.01) and duiObj then
HideScreen()
end
Citizen.Wait(wait)
end
end)

Citizen.CreateThread(function()
while true do
local wait = 2500
for i = 1, #Locations do
local data = Locations[i]
local dist = #(GetEntityCoords(PlayerPedId()) - v3(data.Position))
if not Locations[i].obj and dist < 20.0 then
LoadModel(data.Model)
Locations[i].obj = CreateObject(data.Model, data.Position.x, data.Position.y, data.Position.z)
SetEntityHeading(Locations[i].obj, data.Position.w)
FreezeEntityPosition(Locations[i].obj, true)
elseif Locations[i].obj and dist > 20.0 then
DeleteEntity(Locations[i].obj)
Locations[i].obj = nil
end
end
Citizen.Wait(wait)
end
end)

RegisterNetEvent("ptelevision:requestUpdate", function(data)
Televisions = data.Televisions
Channels = data.Channels
end)

RegisterNetEvent("ptelevision:requestSync", function(coords, data)
local tvObj = data.entity

local _, status = GetTelevision(coords)
local screenModel = Config.Models[data.model]
if status and status["ptv_status"] then
local update_time = status.update_time
local status = status["ptv_status"]
Citizen.Wait(1000)
if status.type == "play" then
if status.channel and Channels[status.channel] then
PlayVideo({ url = Channels[status.channel].url, channel = status.channel })
elseif status.url then
local time = math.floor(data.current_time - update_time)
PlayVideo({ url = status.url, time = time })
end
elseif status.type == "browser" then
PlayBrowser({ url = status.url })
end
end
end)

RegisterNUICallback("pageLoaded", function(cb)
waitForLoad = false
if cb then
cb()
end
end)

AddEventHandler("onResourceStop", function(name)
if name == GetCurrentResourceName() then
HideScreen()
for i = 1, #Locations do
DeleteEntity(Locations[i].obj)
end
end
end)
Loading

0 comments on commit 1a1ba6c

Please sign in to comment.