diff --git a/README.md b/README.md index 5bcd5b0..9658e5c 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Compatibility is quite high: - Almost all SA-MP **scripting functions** and **callbacks** are implemented. - **Database** functions (`db_*`) are implemented. -- SA-MP server **plugins** work unmodified. +- SA-MP server **plugins** work unmodified, if they don't use memory hacking. - SA-MP style **rcon** commands are available from the server console and the ingame console. @@ -83,6 +83,9 @@ features: - You can **load plugins dynamically**, while the server is running. Use the `loadplugin` console command for this. +- There is no hard-coded max filterscript count, the **number of + running filterscripts is unlimited**. + ## Installation *amx* consists of a binary server module (.dll/.so) and a Lua resource. @@ -203,7 +206,7 @@ Information about this is lined out below. The meta.xml files of gamemodes and filterscripts are slightly different. Two resources, amx-test and amx-fs-test, are included - with the *amx* download as examples. Most times you can simply + in the *amx* repository as examples. Most times you can simply copy-paste these to a new resource and adjust the names in it. - To specify what **filterscripts to autostart** when *amx* loads, @@ -224,10 +227,10 @@ Information about this is lined out below. plugins to start, separated by spaces. For example: ```xml - + ``` - This will load irc.dll and sampmysql.dll on Windows, or .so on + This will load irc.dll and mysql.dll on Windows, or .so on Linux. - jbeta's mapcycler resource (shipped with the MTA server) is used for @@ -675,12 +678,18 @@ Even though *amx* offers a high level of compatibility, not everything is perfect. Below is a list of limitations that may or may not be addressed in later versions of *amx* and Multi Theft Auto. +- The following scripting functions will require certain resources + installed to have effect when called: DisableInteriorEnterExits + (disable enex markers implemented by "interiors" resource, + otherwise they are always disabled), SendDeathMessage (invoke + graphical death messages from "killmessages" resource). + - The following scripting functions are currently not implemented and - will have no effect when called: DisableInteriorEnterExits, - EnableStuntBonusForAll, EnableStuntBonusForPlayer, EnableTirePopping - (tire popping is always on), PlayerPlaySound, SendDeathMessage (use - the "killmessages" resource on your server instead for graphical - death messages), SetNameTagDrawDistance, TextDrawSetProportional. + will have no effect when called: DisableRemoteVehicleCollisions, + EnablePlayerCameraTarget, EnableStuntBonusForAll, + EnableStuntBonusForPlayer, EnableTirePopping (tire popping is + always on), EnableVehicleFriendlyFire, SetObjectNoCameraCol, + SetPlayerShopName, TextDrawSetProportional, TextDrawSetSelectable. ## Credits diff --git a/amx-test/pawno/include/a_amx.inc b/amx-test/pawno/include/a_amx.inc index 445eef6..5bda332 100644 --- a/amx-test/pawno/include/a_amx.inc +++ b/amx-test/pawno/include/a_amx.inc @@ -34,13 +34,22 @@ forward OnPlayerKeyUp(playerid, key); // World native Float:GetGameSpeed(); native SetGameSpeed(Float:speed); +native Float:GetRainLevel(); +native SetRainLevel(Float:level); +native ResetRainLevel(); native GetSkyGradient(&topRed, &topGreen, &topBlue, &bottomRed, &bottomGreen, &bottomBlue); native SetSkyGradient(topRed = 0, topGreen = 0, topBlue = 0, bottomRed = 0, bottomGreen = 0, bottomBlue = 0); native ResetSkyGradient(); +native Float:GetFogDistance(); +native SetFogDistance(Float:distance); +native ResetFogDistance(); native GetCloudsEnabled(); native SetCloudsEnabled(bool:enable); +native SetWeatherBlended(weatherid); native GetInteriorSoundsEnabled(); native SetInteriorSoundsEnabled(bool:enable); +native GetOcclusionsEnabled(); +native SetOcclusionsEnabled(bool:enable); native IsGarageOpen(garageid); native SetGarageOpen(garageid, bool:open); native IsGlitchEnabled(const name[]); @@ -51,6 +60,7 @@ native Float:GetAircraftMaxHeight(); native SetAircraftMaxHeight(Float:height); native Float:GetJetpackMaxHeight(); native SetJetpackMaxHeight(Float:height); +native GetWeaponSlot(weaponid); native GetFPSLimit(); native SetFPSLimit(fps); native GetRandomPlayer(); @@ -127,6 +137,8 @@ native IsPlayerHeadless(playerid); native SetPlayerHeadless(playerid, bool:headless); native GetPlayerBlurLevel(playerid); native SetPlayerBlurLevel(playerid, level); +native IsPlayerMapForced(playerid); +native ForcePlayerMap(playerid, bool:forceOn); native FadePlayerCamera(playerid, bool:fadeIn, Float:timeToFade = 1.0, red = 0, green = 0, blue = 0); native SetPlayerControlState(playerid, const control[], bool:controlState); native IsPlayerCursorShowing(playerid); @@ -241,7 +253,7 @@ native SetBotAlpha(botid, alpha); // Scoreboard native AddScoreBoardColumn(const column[]); -native SetPlayerScoreBoardData(playerid, const column[], const data[]); +native SetPlayerScoreBoardData(playerid, const column[], const value[]); native RemoveScoreBoardColumn(const column[]); // Player Data diff --git a/amx/client/client.lua b/amx/client/client.lua index 78aa72a..a1207b5 100644 --- a/amx/client/client.lua +++ b/amx/client/client.lua @@ -51,13 +51,6 @@ addEventHandler('onClientResourceStart', resourceRoot, false ) -addEventHandler('onClientResourceStop', resourceRoot, - function() - TogglePlayerClock(false, true) - end, - false -) - function enableDebug() local state = not isDebugViewActive() setDebugViewActive(state) @@ -139,7 +132,7 @@ function startClassSelection(classInfo) g_ClassSelectionInfo.selectedclass = 0 end g_ClassSelectionInfo.gui = { - img = guiCreateStaticImage(35, screenHeight - 410, 205, 236, 'client/logo_small.png', false), + img = guiCreateStaticImage(35, screenHeight - 410, 205, 236, 'images/logo_small.png', false), btnLeft = guiCreateButton(screenWidth / 2 - 145 - 70, screenHeight - 100, 140, 20, '<<<', false), btnRight = guiCreateButton(screenWidth / 2 - 70, screenHeight - 100, 140, 20, '>>>', false), btnSpawn = guiCreateButton(screenWidth / 2 + 145 - 70, screenHeight - 100, 140, 20, 'Spawn', false) @@ -216,6 +209,7 @@ end addEventHandler('onClientResourceStop', resourceRoot, function() destroyClassSelGUI() + TogglePlayerClock(true) removeEventHandler('onClientRender', root, renderTextDraws) removeEventHandler('onClientRender', root, renderMenu) end @@ -275,10 +269,9 @@ function pickupOnInteriorChangeLoop() end local function clientPlayerPickupHit(thePickup, matchingDimension) - if source ~= localPlayer then return end triggerServerEvent('OnPlayerPickUpPickup_Ev', localPlayer, thePickup) end -addEventHandler('onClientPlayerPickupHit', root, clientPlayerPickupHit) +addEventHandler('onClientPlayerPickupHit', localPlayer, clientPlayerPickupHit) ----------------------------- -- Interior related @@ -310,7 +303,7 @@ ca.maxZ = math.rad(89) ca.minZ = math.rad(-45) function removeCamAttachHandler() - outputConsole('removeCamAttachHandler was called') + --outputConsole('removeCamAttachHandler was called') if (ca.active == 1) then outputConsole('Destroying cam attach handler...') ca.active = 0 @@ -372,7 +365,7 @@ function cursorMouseMoveHandler(curX, curY, absX, absY) end function AttachCameraToObject(camObj) - outputConsole('AttachCameraToObject was called') + --outputConsole('AttachCameraToObject was called') if not isElement(camObj) then return @@ -386,7 +379,7 @@ function AttachCameraToObject(camObj) end function AttachCameraToPlayerObject(camobjID) - outputConsole('AttachCameraToPlayerObject was called') + --outputConsole('AttachCameraToPlayerObject was called') if not isElement(g_PlayerObjects[camobjID]) then return @@ -406,7 +399,7 @@ sm.moov = 0 sm.objCamPos, sm.objLookAt = nil, nil function removeInterpCamHandler() - outputConsole('removeInterpCamHandler was called') + --outputConsole('removeInterpCamHandler was called') if (sm.moov == 1) then outputConsole('Destroying cam handler...') sm.moov = 0 @@ -550,15 +543,16 @@ function StopPlayerObject(objID) stopObject(obj) end ----------------------------- --- Audio +-- Audio & SFX local pAudioStreamSound = nil -- SA-MP can only do one stream at a time anyway function PlayAudioStreamForPlayer(url, posX, posY, posZ, distance, usepos) --outputConsole(string.format("PlayAudioStreamForPlayer called with args %s %f %f %f %f %d", url, posX, posY, posZ, distance, usepos)) - if pAudioStreamSound ~= nil then -- If there's one already playing, stop it + if pAudioStreamSound and isElement(pAudioStreamSound) then -- If there's one already playing, stop it --outputConsole("PlayAudioStreamForPlayer is stopping an audio stream") - StopAudioStreamForPlayer() + stopSound(pAudioStreamSound) end + if not usepos then --outputConsole(string.format("PlayAudioStreamForPlayer now playing non-3d sound %s", url)) pAudioStreamSound = playSound(url) @@ -567,15 +561,52 @@ function PlayAudioStreamForPlayer(url, posX, posY, posZ, distance, usepos) pAudioStreamSound = playSound3D(url, posX, posY, posZ) setSoundMaxDistance(pAudioStreamSound, distance) end - if pAudioStreamSound ~= nil then + + if pAudioStreamSound and isElement(pAudioStreamSound) then setSoundVolume(pAudioStreamSound, 1.0) end end function StopAudioStreamForPlayer() - if pAudioStreamSound == nil then return end + if not pAudioStreamSound then return end + if not isElement(pAudioStreamSound) then return end + stopSound(pAudioStreamSound) end + +local sfxSound = nil +function PlayerPlaySound(soundid, posX, posY, posZ) + if sfxSound and isElement(sfxSound) then + stopSound(sfxSound) + end + + -- only 'script' container supported + if not getSFXStatus('script') then return end + + for bankId = 1, #SFX_Offset do + local bank = SFX_Offset[bankId] + local first = bank.first + local last = bank.last or first + + if soundid >= first and soundid <= last then + local bankIdx = bankId - 1 + local audioEvent = soundid - first + + if posX ~= 0.0 or posY ~= 0.0 or posZ ~= 0.0 then + --outputConsole(string.format("PlayerPlaySound now playing 3d sound %d (bank %d)", audioEvent, bankIdx)) + sfxSound = playSFX3D('script', bankIdx, audioEvent, posX, posY, posZ) + else + --outputConsole(string.format("PlayerPlaySound now playing non-3d sound %d (bank %d)", audioEvent, bankIdx)) + sfxSound = playSFX('script', bankIdx, audioEvent) + end + break + end + end + + if sfxSound and isElement(sfxSound) then + setSoundVolume(sfxSound, 1.0) + end +end ----------------------------- -- Checkpoints @@ -791,14 +822,17 @@ addEventHandler('onClientElementStreamOut', root, local function clientVehicleDamage(attacker, weapon, loss, x, y, z, tire) if not isElement(source) then return end - local driver = getVehicleOccupant(source) + -- get the driver from either players or peds + local occupants = getVehicleOccupants(source) + local driver = occupants and occupants[0] -- seat 0 + if not driver then - -- Block any damage for unoccupied vehicles like SA-MP does + -- block any damage for unoccupied vehicles like SA-MP does return cancelEvent() end if driver ~= localPlayer then return end - triggerServerEvent('OnVehicleDamageStatusUpdate_Ev', localPlayer, source) + serverAMXEvent('OnVehicleDamageStatusUpdate', getElemID(source), g_PlayerID) end addEventHandler('onClientVehicleDamage', root, clientVehicleDamage) @@ -1212,12 +1246,9 @@ function renderTextLabels() local pX, pY, pZ, _, _, _ = getCameraMatrix() --getElementPosition(localPlayer) local dist = getDistanceBetweenPoints3D(pX, pY, pZ, textlabel.X, textlabel.Y, textlabel.Z) local vw = getElementDimension(localPlayer) - local LOS = isLineOfSightClear(pX, pY, pZ, textlabel.X, textlabel.Y, textlabel.Z, true, false, false) if screenX and dist <= textlabel.dist and (vw == textlabel.vw or textlabel.vw == -1) then -- Because player textlabels don't have VW's, since we're processing both here - if not textlabel.los then - dxDrawText(textlabel.text, screenX, screenY, screenX, screenY, tocolor(textlabel.color.r, textlabel.color.g, textlabel.color.b, textlabel.color.a), 1.0, 'default-bold', 'center', 'top', false, false, false, true) - elseif LOS then + if not textlabel.los or isLineOfSightClear(pX, pY, pZ, textlabel.X, textlabel.Y, textlabel.Z, true, false, false) then dxDrawText(textlabel.text, screenX, screenY, screenX, screenY, tocolor(textlabel.color.r, textlabel.color.g, textlabel.color.b, textlabel.color.a), 1.0, 'default-bold', 'center', 'top', false, false, false, true) end end @@ -1422,7 +1453,7 @@ function ShowMenuForPlayer(menuID) if not prevMenu then g_CurrentMenu.alpha = 0 - g_CurrentMenu.closebtn = guiCreateStaticImage(g_CurrentMenu.x + g_CurrentMenu.width - closebtnSide, g_CurrentMenu.y, closebtnSide, closebtnSide, 'client/closebtn.png', false, nil) + g_CurrentMenu.closebtn = guiCreateStaticImage(g_CurrentMenu.x + g_CurrentMenu.width - closebtnSide, g_CurrentMenu.y, closebtnSide, closebtnSide, 'images/closebtn.png', false, nil) guiSetAlpha(g_CurrentMenu.closebtn, 0) addEventHandler('onClientMouseEnter', g_CurrentMenu.closebtn, function() @@ -1432,7 +1463,7 @@ function ShowMenuForPlayer(menuID) false ) - g_CurrentMenu.closebtnhover = guiCreateStaticImage(g_CurrentMenu.x + g_CurrentMenu.width - closebtnSide, g_CurrentMenu.y, closebtnSide, closebtnSide, 'client/closebtn_hover.png', false, nil) + g_CurrentMenu.closebtnhover = guiCreateStaticImage(g_CurrentMenu.x + g_CurrentMenu.width - closebtnSide, g_CurrentMenu.y, closebtnSide, closebtnSide, 'images/closebtn_hover.png', false, nil) guiSetVisible(g_CurrentMenu.closebtnhover, false) guiSetAlpha(g_CurrentMenu.closebtnhover, .75) addEventHandler('onClientMouseLeave', g_CurrentMenu.closebtnhover, @@ -1446,6 +1477,7 @@ function ShowMenuForPlayer(menuID) addEventHandler('onClientGUIClick', g_CurrentMenu.closebtnhover, function() if not g_CurrentMenu.anim then + playSoundFrontEnd(2) HideMenuForPlayer() end end, @@ -1530,8 +1562,8 @@ function closeMenu() end function exitMenu() - closeMenu() serverAMXEvent('OnPlayerExitedMenu', g_PlayerID) + closeMenu() end function renderMenu() @@ -1606,14 +1638,18 @@ function menuClickHandler(button, state, clickX, clickY) local selectedRow = math.floor((clickY - g_CurrentMenu.y - MENU_TOP_PADDING) / MENU_ITEM_HEIGHT) if not (g_CurrentMenu.disabledrows and table.find(g_CurrentMenu.disabledrows, selectedRow)) then + playSoundFrontEnd(1) serverAMXEvent('OnPlayerSelectedMenuRow', g_PlayerID, selectedRow) closeMenu() + else + playSoundFrontEnd(4) end end function OnKeyPress(key, keyState) if (keyState == 'down') then - exitMenu() + playSoundFrontEnd(2) + HideMenuForPlayer() end end ----------------------------- @@ -1623,9 +1659,31 @@ function SetPlayerPosFindZ(x, y, z) setElementPosition(localPlayer, x, y, getGroundPosition(x, y, z) + 1) end +local function clientPlayerDamage(attacker, weapon, bodypart, loss) + local issuer = attacker + + if issuer then + if getElementType(issuer) == 'vehicle' then + issuer = getVehicleOccupant(issuer) + end + if issuer == source then + issuer = nil + end + end + + if issuer == localPlayer then -- give damage + triggerServerEvent('OnPlayerDamage_Ev', issuer, source, true, loss, weapon, bodypart) + end + + if source == localPlayer then -- take damage + if issuer and getElementType(issuer) ~= 'player' then issuer = nil end + triggerServerEvent('OnPlayerDamage_Ev', source, issuer, false, loss, weapon, bodypart) + end +end +addEventHandler('onClientPlayerDamage', root, clientPlayerDamage) + local function clientPlayerWeaponFire(weapon, ammo, ammoInClip, hitX, hitY, hitZ, hitElement, startX, startY, startZ) if weapon < 22 or (weapon > 34 and weapon ~= 38) then return end - if source ~= localPlayer then return end local hitId, hitType = 65535, 0 local offsetX, offsetY, offsetZ = hitX, hitY, hitZ @@ -1659,17 +1717,24 @@ local function clientPlayerWeaponFire(weapon, ammo, ammoInClip, hitX, hitY, hitZ triggerServerEvent('OnPlayerWeaponShot_Ev', localPlayer, weapon, hitType, hitId, startX, startY, startZ, hitX, hitY, hitZ, offsetX, offsetY, offsetZ) end -addEventHandler('onClientPlayerWeaponFire', root, clientPlayerWeaponFire) +addEventHandler('onClientPlayerWeaponFire', localPlayer, clientPlayerWeaponFire) local function clientPedDamage(attacker, weapon, bodypart, loss) if getElementType(source) == 'ped' and getElementData(source, 'ActorPed') then local issuer = attacker + if issuer and getElementType(issuer) == 'vehicle' then - issuer = getVehicleOccupant(issuer) + local driver = getVehicleOccupant(issuer) + + if driver and getElementType(driver) == 'player' then + issuer = driver + else + issuer = nil + end end if issuer == localPlayer and not getElementData(source, 'Invulnerable') then - triggerServerEvent('OnPlayerGiveDamageActor_Ev', localPlayer, source, loss, weapon, bodypart) + triggerServerEvent('OnPlayerGiveDamageActor_Ev', issuer, source, loss, weapon, bodypart) end -- Actor damage controlled by the server in any case @@ -1680,7 +1745,7 @@ addEventHandler('onClientPedDamage', root, clientPedDamage) function enableWeaponSyncing(enable) if enable and not g_WeaponSyncTimer then - g_WeaponSyncTimer = setTimer(sendWeapons, 1000, 0) + g_WeaponSyncTimer = setTimer(sendWeapons, 500, 0) elseif not enable and g_WeaponSyncTimer then killTimer(g_WeaponSyncTimer) g_WeaponSyncTimer = nil @@ -1930,7 +1995,7 @@ function OnListDialogButton1Click(button, state) if button == 'left' then local row, column = guiGridListGetSelectedItem(listGrid) local text = guiGridListGetItemText(listGrid, row, column) - serverAMXEvent('OnDialogResponse', getElemID(localPlayer), listDialog, 1, row, text) + serverAMXEvent('OnDialogResponse', g_PlayerID, listDialog, 1, row, text) guiSetVisible(listWindow, false) showCursor(false) listDialog = nil @@ -1942,7 +2007,7 @@ function OnListDialogButton2Click(button, state) if button == 'left' then local row, column = guiGridListGetSelectedItem(listGrid) local text = guiGridListGetItemText(listGrid, row, column) - serverAMXEvent('OnDialogResponse', getElemID(localPlayer), listDialog, 0, row, text) + serverAMXEvent('OnDialogResponse', g_PlayerID, listDialog, 0, row, text) guiSetVisible(listWindow, false) showCursor(false) listDialog = nil @@ -1952,7 +2017,7 @@ end function OnInputDialogButton1Click(button, state) if button == 'left' then - serverAMXEvent('OnDialogResponse', getElemID(localPlayer), inputDialog, 1, 0, guiGetText(inputEdit)) + serverAMXEvent('OnDialogResponse', g_PlayerID, inputDialog, 1, 0, guiGetText(inputEdit)) guiSetVisible(inputWindow, false) showCursor(false) inputDialog = nil @@ -1961,7 +2026,7 @@ end function OnInputDialogButton2Click(button, state) if button == 'left' then - serverAMXEvent('OnDialogResponse', getElemID(localPlayer), inputDialog, 0, 0, guiGetText(inputEdit)) + serverAMXEvent('OnDialogResponse', g_PlayerID, inputDialog, 0, 0, guiGetText(inputEdit)) guiSetVisible(inputWindow, false) showCursor(false) inputDialog = nil @@ -1970,7 +2035,7 @@ end function OnMessageDialogButton1Click(button, state) if button == 'left' then - serverAMXEvent('OnDialogResponse', getElemID(localPlayer), msgDialog, 1, 0, '') + serverAMXEvent('OnDialogResponse', g_PlayerID, msgDialog, 1, 0, '') guiSetVisible(msgWindow, false) showCursor(false) msgDialog = nil @@ -1979,7 +2044,7 @@ end function OnMessageDialogButton2Click(button, state) if button == 'left' then - serverAMXEvent('OnDialogResponse', getElemID(localPlayer), msgDialog, 0, 0, '') + serverAMXEvent('OnDialogResponse', g_PlayerID, msgDialog, 0, 0, '') guiSetVisible(msgWindow, false) showCursor(false) msgDialog = nil @@ -2146,6 +2211,6 @@ end -- depends on scoreboard resource local function clientPlayerScoreboardClick(selected, cX, cY, clickedColumn) if getElementType(source) ~= 'player' then return end - serverAMXEvent('OnPlayerClickPlayer', getElemID(localPlayer), getElemID(source), 0) + serverAMXEvent('OnPlayerClickPlayer', g_PlayerID, getElemID(source), 0) end addEventHandler('onClientPlayerScoreboardClick', root, clientPlayerScoreboardClick) diff --git a/amx/client/map_waypoint.lua b/amx/client/map_waypoint.lua new file mode 100644 index 0000000..2030bc1 --- /dev/null +++ b/amx/client/map_waypoint.lua @@ -0,0 +1,61 @@ +local targetBlipIcon = 41 +local targetBlip + +local screenSize = Vector2(guiGetScreenSize()) +local wasLMBPressed, wasRMBPressed = false, false +local wasMapOpened = false + +local function handleMapTargetBlip() + if not isPlayerMapVisible() then + if wasMapOpened then + showCursor(false, false) + wasLMBPressed, wasRMBPressed = false, false + wasMapOpened = false + end + return + end + + if not isCursorShowing() then + showCursor(true, false) + end + + local isLMBPressed = getKeyState('mouse1') + if isLMBPressed and isLMBPressed ~= wasLMBPressed then + local cursorPos, mapMin, mapMax = Vector2(getCursorPosition()) + cursorPos.x, cursorPos.y = cursorPos.x * screenSize.x, cursorPos.y * screenSize.y + + do + local mx, my, Mx, My = getPlayerMapBoundingBox() + mapMin = Vector2(mx, my) + mapMax = Vector2(Mx, My) + end + + if cursorPos.x >= mapMin.x and cursorPos.y >= mapMin.y and cursorPos.x <= mapMax.x and cursorPos.y <= mapMax.y then + local relPos = Vector2((cursorPos.x - mapMin.x) / (mapMax.x - mapMin.x), (cursorPos.y - mapMin.y) / (mapMax.y - mapMin.y)) + local worldPlanePos = Vector2(6000 * (relPos.x - 0.5), 3000 - (relPos.y * 6000)) + local worldPos = Vector3(worldPlanePos.x, worldPlanePos.y, getGroundPosition(worldPlanePos.x, worldPlanePos.y, 3000)) + + triggerServerEvent('OnPlayerClickMap_Ev', localPlayer, worldPos.x, worldPos.y, worldPos.z) + playSoundFrontEnd(1) + + if not targetBlip then + targetBlip = createBlip(worldPos, targetBlipIcon) + else + setElementPosition(targetBlip, worldPos) + end + end + end + + local isRMBPressed = getKeyState('mouse2') + if targetBlip then + if isRMBPressed and isRMBPressed ~= wasRMBPressed then + playSoundFrontEnd(2) + destroyElement(targetBlip) + targetBlip = nil + end + end + + wasLMBPressed, wasRMBPressed = isLMBPressed, isRMBPressed + wasMapOpened = true +end +addEventHandler('onClientRender', root, handleMapTargetBlip) diff --git a/amx/client/samp_nametags.lua b/amx/client/samp_nametags.lua index 1ebbd93..b607599 100644 --- a/amx/client/samp_nametags.lua +++ b/amx/client/samp_nametags.lua @@ -1,3 +1,8 @@ +local showNameTags = true +local nameTagsRadius = 70 +local nameTagsLOS = true +local nameTagShowing = {} + local font = 'arial' -- default font HealthBarBorderVertices = @@ -24,24 +29,17 @@ HealthBarInnerVertices = { x = 0, y = 0, z = 0, c = tocolor(185, 34, 40, 255) } } -function applyColorAlpha(color, alpha) - if color < 0 then - color = 0x100000000 + color +function drawNameTag(position, nameText, r, g, b, health, armor, distance) + if not r or not g or not b then + r = 255 + g = 255 + b = 255 end - local rgb = color % 0x1000000 - local a = (color - rgb) / 0x1000000 * alpha - a = a - a % 1 - return rgb + a * 0x1000000 -end -function drawNameTag(position, nameText, health, armor, distance) position.z = (distance * 0.025) + position.z + 0.3 local screenCoordsX, screenCoordsY = getScreenFromWorldPosition(position.x, position.y, position.z) - - if not screenCoordsX then - return - end + if not screenCoordsX then return end local rect = {left = screenCoordsX, top = screenCoordsY, right = screenCoordsX + 1, bottom = screenCoordsY + 1} local textSizeX, textSizeY = dxGetTextSize(nameText, 0, 1, 1, font) @@ -87,7 +85,7 @@ function drawNameTag(position, nameText, health, armor, distance) dxDrawText( nameText, rect.left, rect.top, rect.right, rect.bottom, - tocolor(255, 255, 255, 255), 1, 1, + tocolor(r, g, b, 255), 1, 1, font, 'left', 'top', false, false, false, false, false, @@ -195,19 +193,62 @@ end addEventHandler('onClientRender', root, function() + if not showNameTags then return end local playerPosX, playerPosY, playerPosZ = getElementPosition(localPlayer) + for k, player in pairs(getElementsByType('player')) do if player ~= localPlayer and isElementOnScreen(player) then - --local fPosX, fPosY, fPosZ = getElementPosition(player) - local fPosX, fPosY, fPosZ = getPedBonePosition(player, 8) - local distance = getDistanceBetweenPoints3D(playerPosX, playerPosY, playerPosZ, fPosX, fPosY, fPosZ) - if distance < 45 then - local cx, cy, cz = getCameraMatrix(localPlayer) - if isLineOfSightClear(cx, cy, cz, fPosX, fPosY, fPosZ, true, true, false, true, true, false, false) then - drawNameTag({x = fPosX, y = fPosY, z = fPosZ}, getPlayerName(player) .. ' (' .. getElemID(player) .. ')', getElementHealth(player), getPedArmor(player), distance) + if nameTagShowing[player] ~= false then + --local fPosX, fPosY, fPosZ = getElementPosition(player) + local fPosX, fPosY, fPosZ = getPedBonePosition(player, 8) + local distance = getDistanceBetweenPoints3D(playerPosX, playerPosY, playerPosZ, fPosX, fPosY, fPosZ) + + if distance < nameTagsRadius then + local cx, cy, cz = getCameraMatrix() + + if not nameTagsLOS or isLineOfSightClear(cx, cy, cz, fPosX, fPosY, fPosZ, true, false, false, true, true, false, false) then + local r, g, b = getPlayerNametagColor(player) + drawNameTag({x = fPosX, y = fPosY, z = fPosZ}, getPlayerName(player) .. ' (' .. getElemID(player) .. ')', r, g, b, getElementHealth(player), getPedArmor(player), distance) + end + end + end + end + end + + for k, bot in pairs(getElementsByType('ped')) do + if isElementOnScreen(bot) then + if getElementData(bot, 'ShowNameTag') then + --local fPosX, fPosY, fPosZ = getElementPosition(bot) + local fPosX, fPosY, fPosZ = getPedBonePosition(bot, 8) + local distance = getDistanceBetweenPoints3D(playerPosX, playerPosY, playerPosZ, fPosX, fPosY, fPosZ) + + if distance < nameTagsRadius then + local cx, cy, cz = getCameraMatrix() + + if not nameTagsLOS or isLineOfSightClear(cx, cy, cz, fPosX, fPosY, fPosZ, true, false, false, true, true, false, false) then + local botName = getElementData(bot, 'BotName') or 'Bot' + drawNameTag({x = fPosX, y = fPosY, z = fPosZ}, botName .. ' (' .. getElemID(bot) .. ')', 255, 255, 255, getElementHealth(bot), getPedArmor(bot), distance) + end end end end end end ) + +function updateNameTagGlobals(settings) + if settings.status ~= nil then + showNameTags = settings.status + end + if settings.radius ~= nil then + nameTagsRadius = settings.radius + end + if settings.los ~= nil then + nameTagsLOS = settings.los + end +end + +-- ShowPlayerNameTagForPlayer +function updateNameTagShowing(playerToShow, show) + nameTagShowing[playerToShow] = show +end diff --git a/amx/client/closebtn.png b/amx/images/closebtn.png similarity index 100% rename from amx/client/closebtn.png rename to amx/images/closebtn.png diff --git a/amx/client/closebtn_hover.png b/amx/images/closebtn_hover.png similarity index 100% rename from amx/client/closebtn_hover.png rename to amx/images/closebtn_hover.png diff --git a/amx/client/logo_small.png b/amx/images/logo_small.png similarity index 100% rename from amx/client/logo_small.png rename to amx/images/logo_small.png diff --git a/amx/meta.xml b/amx/meta.xml index 35e33e1..200e446 100644 --- a/amx/meta.xml +++ b/amx/meta.xml @@ -5,6 +5,7 @@