local sx, sy = guiGetScreenSize() local zoom = 1 local baseX = 1900 local minZoom = 2 local streamLib = getResourceFromName('nc_stream') and getResourceState(getResourceFromName('nc_stream')) == 'running' and exports['nc_stream'] or false local colshapeHit = streamLib and streamLib:getStreamColShape('medium') or false local isMarkerStarted = false local math_max = math.max local math_min = math.min local _dxDrawMaterialLine3D = dxDrawMaterialLine3D local getWorldPosition = getScreenFromWorldPosition local getPosition = getElementPosition local getData = getElementData local assets = getResourceFromName('nc_assets') and getResourceState(getResourceFromName('nc_assets')) == 'running' and exports['nc_assets'] or false local LAST_CREATED_RENDER_TARGET = 0 local LAST_CREATED_TEXTURE = 0 local MARKERS_STREAMED = {} local Vec3 = function(x, y, z) return {x = x, y = y, z = z} end local function getDistance3D(x1, y1, z1, x2, y2, z2) local dx = x1 - x2 local dy = y1 - y2 local dz = z1 - z2 return math.sqrt(dx * dx + dy * dy + dz * dz) end local isElementOnScreenEmulated = function(element) if not isElement(element) then return false end if isElementOnScreen(element) then return true end local cx, cy, cz = getCameraMatrix() local x, y, z = getPosition(element) local screenX, screenY = getWorldPosition(x, y, z) return (screenX and screenY) and true or false end addEventHandler('onCustomStreamStarted', root, function() streamLib = getResourceFromName('nc_stream') and getResourceState(getResourceFromName('nc_stream')) == 'running' and exports['nc_stream'] or false colshapeHit = streamLib and streamLib:getStreamColShape('medium') or false end) if sx < baseX then zoom = math_min(minZoom, baseX / sx) end local isElementCustomStreamedIn = function(element) return (isElement(colshapeHit) and isElementWithinColShape(element, colshapeHit) or false) end local prebuildedTexts = { ['exit'] = {'SaĆ­da', 'Volte ao mundo.'} } MarkerStreamer = {} MarkerStreamer.__index = MarkerStreamer function MarkerStreamer:create(...) local instance = {} setmetatable(instance, MarkerStreamer) if instance:constructor(...) then return instance end return false end local fonts = {} function GetFont(name, size, ...) if not fonts[name] then fonts[name] = {}; end if not isElement(fonts[name][size]) then local resource_assets = getResourceFromName('nc_assets') local assets = resource_assets and getResourceState(resource_assets) == 'running' and exports['nc_assets'] if assets and assets:isLoaded() then fonts[name][size] = assets:getFont(name, size, ...) end if isElement(fonts[name][size]) then addEventHandler('onClientElementDestroy', fonts[name][size], function() for name, tables in pairs(fonts) do for size, fontElement in pairs(tables) do if fontElement == source then fonts[name][size] = nil end end end local count = 0 for i, v in pairs(fonts[name]) do count = count + 1 if count >= 1 then break end end if count < 1 then fonts[name] = nil end collectgarbage() end) end end return (fonts[name][size] or 'default-bold') end addEventHandler('onClientResourceStart', resourceRoot, function() assets = getResourceFromName('nc_assets') and getResourceState(getResourceFromName('nc_assets')) == 'running' and exports['nc_assets'] or false if assets then if (assets:isLoaded()) then MarkerStreamer:create() else addEventHandler('onAssetsLoaded', root, function() MarkerStreamer:create() end ) end end end ) function MarkerStreamer:constructor(...) if isMarkerStarted then return end isMarkerStarted = true self.markerCol = isElement(colshapeHit) and colshapeHit or false self.icons = {} self.targets = {} self.streamed = {} self.sizeScale = 0.15 self.maxDist = 60 self.refreshTick = getTickCount() self.refreshTime = 30000 self.animTick = getTickCount() self.animState = 'up' self.anim = 0 GetFont('roboto-medium', 16) GetFont('roboto-regular', 10) self.func = {} self.func.renderer = function(...) self:render(...) end self.func.maximize = function(...) self:cleanUp(...) end addEventHandler('onClientPreRender', root, self.func.renderer) addEventHandler('onClientRestore', root, self.func.maximize) addEventHandler('onElementCustomStreamOut', root, MarkerStreamer.onColshapeLeave) local markers = getElementsByType('marker') for i = 1, #markers do local marker = markers[i] if marker and isElement(marker) and getMarkerType(marker) == 'cylinder' then local data = getData(marker, 'nc.markers') if data and data.icon then local r, g, b = getMarkerColor(marker) setMarkerColor(marker, r, g, b, 0) end end end markers = nil collectgarbage('collect') return true end function MarkerStreamer:onColshapeLeave(marker, _type, colshape) if _type ~= 'medium' then return end if not isElement(source) then return end if getElementType(source) ~= 'marker' then return end MarkerStreamer:destroyMarker(source) end function MarkerStreamer:createIcon(...) if arg[2] then local orgImg = 'files/images/logo.png' if orgImg then self.icons[arg[1]] = dxCreateTexture(orgImg, 'argb', true, 'clamp') return self.icons[arg[1]] end end if not fileExists('files/images/' .. arg[1] .. '.png') then error('o icone '..arg[1]..' nao existe') return false end self.icons[arg[1]] = dxCreateTexture('files/images/' .. arg[1] .. '.png', 'argb', true, 'clamp') return self.icons[arg[1]] end function MarkerStreamer:getIcon(...) local icon, isOrg = self:prepareIconText(arg[1], arg[2]) if self.icons[icon] then return self.icons[icon], icon else return self:createIcon(icon, isOrg), icon end end function MarkerStreamer:prepareIconText(...) if isElement(arg[1]) then arg[1] = arg[2] end if string.find(arg[1], '-') then local icon = split(arg[1], '-') return icon[1] end return arg[1] end function MarkerStreamer:getPrebuildedText(marker, icon, data) local text = prebuildedTexts[icon] if data then return data.title, data.desc end if string.find(icon, '-') then local pre = split(icon, '-') if prebuildedTexts[pre[2]] then text = prebuildedTexts[pre[2]] end end if not text then return false, false end return text[1], text[2] end function MarkerStreamer:createTarget(marker, icon, r, g, b, data) if not marker or not icon then return false end if getTickCount() <= LAST_CREATED_RENDER_TARGET then return false end self.targets[marker] = { target = dxCreateRenderTarget(256, 256, true) } self:updateTarget(marker, icon, r, g, b, data) LAST_CREATED_RENDER_TARGET = getTickCount() + 200 return self.targets[marker].target end function MarkerStreamer:updateTarget(marker, icon, r, g, b, data) if not marker or not icon then return false end local title, desc = self:getPrebuildedText(marker, icon, data) local markerIcon, iconName = self:getIcon(marker, icon) if self.targets[marker] and self.targets[marker].target and dxSetRenderTarget(self.targets[marker].target, true) then if markerIcon then self.targets[marker].icon = iconName dxSetBlendMode('modulate_add') dxDrawImage(91, 0, 73, 73, self:getIcon('marker'), 0, 0, 0, -1) dxDrawImage(110, 16, 37, 37, markerIcon, 0, 0, 0, tocolor(settings.color.r, settings.color.g, settings.color.b, 255)) dxSetBlendMode('blend') end if title then dxSetBlendMode('add') dxDrawText(title, 0, 80, 256, 256, -1, 1, GetFont('roboto-medium', 16), 'center', 'top') dxSetBlendMode('blend') end if desc then dxSetBlendMode('add') dxDrawText(desc, 0, 110, 256, 256, -1, 1, GetFont('roboto-regular', 10), 'center', 'top', true, true) dxSetBlendMode('blend') end dxSetRenderTarget() end end function MarkerStreamer:getMarker(marker, icon, r, g, b, data) if not marker then return false end if self.targets[marker] then return self.targets[marker].target else return self:createTarget(marker, icon, r, g, b, data) end end function MarkerStreamer:destroyMarker(marker) if not marker then return false end if self.targets[marker] then local icon = self.targets[marker].icon if isElement(self.targets[marker].target) then destroyElement(self.targets[marker].target) end self.targets[marker] = nil for i, v in pairs(self.targets) do if v.icon == icon then return end end if isElement(self.icons[icon]) then destroyElement(self.icons[icon]) self.icons[icon] = nil end end end function MarkerStreamer:cleanUp(...) local xp, yp, zp = getElementPosition(localPlayer) for marker, _ in pairs(self.targets) do if isElement(marker) then local x, y, z = getElementPosition(marker) if getDistance3D(xp, yp, zp, x, y, z) <= self.maxDist and isElementOnScreenEmulated(marker) then local r, g, b = getMarkerColor(marker) local data = getData(marker, 'nc.markers') if data.icon then self:updateTarget(marker, data.icon, r, g, b, data) end else self:destroyMarker(marker) end else self:destroyMarker(marker) end end end function MarkerStreamer:clean() if (getTickCount() - self.refreshTick) / self.refreshTime >= 1 then self.refreshTick = getTickCount() self:cleanUp() end end setTimer(function() MARKERS_STREAMED = isElement(colshapeHit) and getElementsWithinColShape(colshapeHit, 'marker') or getElementsWithinRange(localPlayer.position, 80, 'marker') end, 1000, 0) MARKERS_STREAMED = isElement(colshapeHit) and getElementsWithinColShape(colshapeHit, 'marker') or getElementsWithinRange(localPlayer.position, 80, 'marker') function MarkerStreamer:render() self:animate() self:clean() local xLP, yLP, zLP = getElementPosition(localPlayer) for i = 1, #MARKERS_STREAMED do local marker = MARKERS_STREAMED[i]; if marker and isElementOnScreenEmulated(marker) and getMarkerType(marker) == 'cylinder' and localPlayer.interior == marker.interior and localPlayer.dimension == marker.dimension then local x, y, z = getElementPosition(marker) local r, g, b, a = getMarkerColor(marker) local dist = getDistance3D(xLP, yLP, zLP, x, y, z) if a ~= 0 then setMarkerColor(marker, r, g, b, 0) end if dist < self.maxDist then local data = getData(marker, 'nc.markers') local alpha = math_max(math_min((self.maxDist - dist) / 40, 1), 0) local markerImg = data and data.icon and self:getMarker(marker, data.icon, r, g, b, data) if markerImg then dxSetBlendMode('add') _dxDrawMaterialLine3D(x, y, z + 1.5 + self.sizeScale * self.anim, x, y, z + 0 + self.sizeScale * self.anim, markerImg, 1.5, tocolor(255, 255, 255, 255 * alpha)) dxSetBlendMode('blend') end else self:destroyMarker(marker) end else self:destroyMarker(marker) end end end function MarkerStreamer:animate() if self.animState == 'up' then local progress = (getTickCount() - self.animTick) / 2000 self.anim = interpolateBetween(0, 0, 0, 1, 0, 0, progress, 'InOutQuad') if progress >= 1 then self.animState = 'down' self.animTick = getTickCount() end elseif self.animState == 'down' then local progress = (getTickCount() - self.animTick) / 2000 self.anim = interpolateBetween(1, 0, 0, 0, 0, 0, progress, 'InOutQuad') if progress >= 1 then self.animState = 'up' self.animTick = getTickCount() end end end