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 @@
+
@@ -30,6 +31,7 @@
+