diff --git a/[gameplay]/glue/config/ShVehicleAttachConfig.lua b/[gameplay]/glue/config/ShVehicleAttachConfig.lua new file mode 100644 index 000000000..2a55c43b2 --- /dev/null +++ b/[gameplay]/glue/config/ShVehicleAttachConfig.lua @@ -0,0 +1,90 @@ +-- ####################################### +-- ## Project: Glue ## +-- ## Author: MTA contributors ## +-- ## Version: 1.3.1 ## +-- ####################################### + +IS_SERVER = (not triggerServerEvent) -- do not touch — constant; helper bool which checks whether it's a server or client environment + +GLUE_WEAPON_SLOTS = {} -- do not touch — constant; used to verify whether passed weapon slot is in valid range (0-12), this is used to restore currently held weapon, because MTA resets weapon slot on attach +GLUE_CLIENT_ATTACH_DATA_SIZE = 6 -- do not touch, unless you modify data sent from client via triggerServerEvent (onServerVehicleAttachElement) +GLUE_ELEMENT_TYPES_AND_EVENTS = { -- do not touch — constant; controls which events would be added for glue logic based on allowed element type + ["player"] = "onPlayerWasted", + ["vehicle"] = "onVehicleExplode", +} + +GLUE_ALLOWED_ELEMENTS = {"player", "vehicle"} -- elements which could be attached to vehicle (supported: "player", "vehicle") +GLUE_VEHICLE_TYPES = { -- only relevant if GLUE_ALLOWED_ELEMENTS contains "vehicle", specifies which vehicle types are allowed to glue + "Automobile", + --"Plane", + "Bike", + "Helicopter", + --"Boat", + "Train", + "Trailer", + "BMX", + "Monster Truck", + "Quad", +} + +GLUE_VEHICLE_WHITELIST = { -- only relevant if GLUE_ALLOWED_ELEMENTS contains "vehicle", ignores GLUE_VEHICLE_TYPES; specifies which vehicle models are allowed to glue + 500, -- mesa + 411, -- infernus + 443, -- packer + 487, -- maverick +} + +GLUE_ATTACH_OVER_VEHICLE = false -- if true, vehicles will attach over the top of target vehicle. if false, vehicles will attach but will maintain original position (seamless, precise glue'ing) +GLUE_DETACH_ON_VEHICLE_EXPLOSION = true -- specifies whether attached elements would be automatically detached, when attachedTo vehicle explodes +GLUE_ATTACH_ON_TOP_OFFSETS = {0, 0, 1.5} -- only relevant if GLUE_ATTACH_OVER_VEHICLE is set to true, specifies static position for attaching vehicle over vehicle +GLUE_ATTACH_VEHICLE_MAX_DISTANCE = 5 -- specifies maximum distance for nearby vehicle to be glued (also serves anti-cheat purpose, so only nearby vehicle could be attached) +GLUE_ATTACH_PLAYER_MAX_DISTANCE = 10 -- ditto + +GLUE_PREVENT_CONTROLS = false -- prevent players from shooting their guns while attached to vehicle +GLUE_PREVENT_CONTROLS_LIST = {"fire", "action"} -- only relevant GLUE_PREVENT_CONTROLS is set to true, specifies which controls will be toggled on/off + +GLUE_SYNC_CORRECTION = true -- whether attached player positions should be sanity corrected by server, this exists to prevents positional desyncs (e.g: during vehicle teleporting on long distances), which causes player to behave like a ghost (roam freely around SA, while still appearing glued to vehicle for other players) +GLUE_SYNC_CORRECTION_INTERVAL = 3500 -- only relevant if GLUE_SYNC_CORRECTION is set to true, how often glued player position should be corrected, do not set it too low, otherwise you will face weapon aiming interruption (this was constant issue when this variable was set to 1000 before) + +GLUE_MESSAGE_PREFIX = "[Glue]:" -- shown in all glue messages +GLUE_MESSAGE_PREFIX_COLOR = "#c68ff8" -- color used by prefix +GLUE_MESSAGE_HIGHLIGHT_COLOR = "#c68ff8" -- color used in message highlights +GLUE_SHOW_ONE_TIME_HINT = true -- whether player should receive one-time (till resource restart) hint, regarding glue keybindings and settings, upon entering a vehicle + +GLUE_ALLOW_ATTACH_TOGGLING = true -- should players be able to control attach lock on their vehicle (as a driver) +GLUE_ALLOW_DETACHING_ELEMENTS = true -- should players be able to detach already attached elements (as a driver) + +GLUE_ATTACH_DETACH_KEY = "X" -- used to attach/detach yourself/vehicle +GLUE_ATTACH_TOGGLE_KEY = "C" -- only relevant if GLUE_ALLOW_ATTACH_TOGGLING is set to true; specifies whether players can disable attaching to vehicle which are driver of +GLUE_DETACH_ELEMENTS_KEY = "B" -- only relevant if GLUE_ALLOW_DETACHING_ELEMENTS is set to true; controls whether vehicle driver is able to detach elements currently attached to vehicle + +GLUE_ATTACH_DETACH_DELAY = 300 -- how often player can attach/detach yourself/vehicle +GLUE_ATTACH_TOGGLE_DELAY = 300 -- only relevant if GLUE_ALLOW_ATTACH_TOGGLING is set to true; how often player can toggle vehicle attach lock +GLUE_DETACH_ELEMENTS_DELAY = 1000 -- only relevant if GLUE_ALLOW_DETACHING_ELEMENTS is set to true; how often player can detach all currently attached elements + +do + local vehicleModelsWhitelist = {} + local vehicleTypesWhitelist = {} + + for vehicleModelID = 1, #GLUE_VEHICLE_WHITELIST do + local vehicleModel = GLUE_VEHICLE_WHITELIST[vehicleModelID] + + vehicleModelsWhitelist[vehicleModel] = true + end + + for vehicleTypeID = 1, #GLUE_VEHICLE_TYPES do + local vehicleType = GLUE_VEHICLE_TYPES[vehicleTypeID] + + vehicleTypesWhitelist[vehicleType] = true + end + + GLUE_VEHICLE_WHITELIST = vehicleModelsWhitelist + GLUE_VEHICLE_TYPES = vehicleTypesWhitelist + + local weaponSlotMin = 0 + local weaponSlotMax = 12 + + for weaponSlotID = weaponSlotMin, weaponSlotMax do + GLUE_WEAPON_SLOTS[weaponSlotID] = true + end +end \ No newline at end of file diff --git a/[gameplay]/glue/logic/CVehicleAttach.lua b/[gameplay]/glue/logic/CVehicleAttach.lua new file mode 100644 index 000000000..6ec832ab5 --- /dev/null +++ b/[gameplay]/glue/logic/CVehicleAttach.lua @@ -0,0 +1,218 @@ +-- ####################################### +-- ## Project: Glue ## +-- ## Author: MTA contributors ## +-- ## Version: 1.3.1 ## +-- ####################################### + +local glueHintsDisplayed = false + +local function handleGlueAttachAndDetach() + local playerVehicle = getPedOccupiedVehicle(localPlayer) + + if (playerVehicle) then + local playerVehicleToDetach = playerVehicle + local playerVehicleType = getVehicleType(playerVehicle) + local playerVehicleHelicopter = (playerVehicleType == "Helicopter") + + if (playerVehicleHelicopter) then + local playerHelicopterAttachedVehicle = getAttachedVehicle(playerVehicle) + + if (playerHelicopterAttachedVehicle) then + playerVehicleToDetach = playerHelicopterAttachedVehicle + end + end + + if (playerVehicleToDetach) then + local playerVehicleCanDetach = canPlayerDetachElementFromVehicle(localPlayer, playerVehicleToDetach) + + if (playerVehicleCanDetach) then + triggerServerEvent("onServerVehicleDetachElement", localPlayer, playerVehicleToDetach) + + return true + end + end + + local vehicleNearby = getNearestVehicleFromVehicle(playerVehicle) + + if (not vehicleNearby) then + return false + end + + local playerVehicleAttach = (playerVehicleHelicopter and vehicleNearby or playerVehicle) + local playerVehicleAttachTo = (playerVehicleHelicopter and playerVehicle or vehicleNearby) + local playerCanAttachVehicleToVehicle = canPlayerAttachElementToVehicle(localPlayer, playerVehicleAttach, playerVehicleAttachTo) + + if (not playerCanAttachVehicleToVehicle) then + return false + end + + local vehicleAttachX, vehicleAttachY, vehicleAttachZ, vehicleAttachRX, vehicleAttachRY, vehicleAttachRZ = getVehicleAttachData(playerVehicleAttach, playerVehicleAttachTo) + + if (playerVehicleHelicopter) then + local helicopterAttachX, helicopterAttachY, helicopterAttachZ = 0, 0, -1.5 + local helicopterAttachRX, helicopterAttachRY, helicopterAttachRZ = 0, 0, 0 + + vehicleAttachX, vehicleAttachY, vehicleAttachZ = helicopterAttachX, helicopterAttachY, helicopterAttachZ + vehicleAttachRX, vehicleAttachRY, vehicleAttachRZ = helicopterAttachRX, helicopterAttachRY, helicopterAttachRZ + end + + local vehicleAttachData = {vehicleAttachX, vehicleAttachY, vehicleAttachZ, vehicleAttachRX, vehicleAttachRY, vehicleAttachRZ} + + triggerServerEvent("onServerVehicleAttachElement", localPlayer, playerVehicleAttach, playerVehicleAttachTo, vehicleAttachData) + + return false + end + + if (not playerVehicle) then + local playerAttachedTo = getElementAttachedTo(localPlayer) + + if (playerAttachedTo) then + local playerCanDetach = canPlayerDetachElementFromVehicle(localPlayer, localPlayer) + + if (not playerCanDetach) then + return false + end + + triggerServerEvent("onServerVehicleDetachElement", localPlayer, localPlayer) + + return true + end + + local playerContactElement = getPedContactElement(localPlayer) + local playerContactVehicle = isElementType(playerContactElement, "vehicle") + + if (not playerContactVehicle) then + return false + end + + local playerCanAttach = canPlayerAttachElementToVehicle(localPlayer, localPlayer, playerContactElement) + + if (not playerCanAttach) then + return false + end + + local playerX, playerY, playerZ = getElementPosition(localPlayer) + local playerVehicleMatrix = getElementMatrix(playerContactElement) + local playerPosition = {playerX, playerY, playerZ} + local playerAttachX, playerAttachY, playerAttachZ = getOffsetFromXYZ(playerVehicleMatrix, playerPosition) + local playerAttachRX, playerAttachRY, playerAttachRZ = 0, 0, 0 + local playerAttachData = {playerAttachX, playerAttachY, playerAttachZ, playerAttachRX, playerAttachRY, playerAttachRZ} + local playerWeaponSlot = getPedWeaponSlot(localPlayer) + + triggerServerEvent("onServerVehicleAttachElement", localPlayer, localPlayer, playerContactElement, playerAttachData, playerWeaponSlot) + end +end +bindKey(GLUE_ATTACH_DETACH_KEY, "down", handleGlueAttachAndDetach) + +local function handleGlueAttachLock() + local canToggleVehicleAttachLock = canPlayerToggleVehicleAttachLock(localPlayer) + + if (canToggleVehicleAttachLock) then + triggerServerEvent("onServerVehicleToggleAttachLock", localPlayer) + end +end +if (GLUE_ALLOW_ATTACH_TOGGLING) then + bindKey(GLUE_ATTACH_TOGGLE_KEY, "down", handleGlueAttachLock) +end + +local function handleGlueDetachElements() + local canDetachVehicleElements = canPlayerDetachElementsFromVehicle(localPlayer) + + if (canDetachVehicleElements) then + triggerServerEvent("onServerVehicleDetachElements", localPlayer) + end +end +if (GLUE_ALLOW_DETACHING_ELEMENTS) then + bindKey(GLUE_DETACH_ELEMENTS_KEY, "down", handleGlueDetachElements) +end + +local function toggleCombatControls(forcedState) + for controlID = 1, #GLUE_PREVENT_CONTROLS_LIST do + local controlName = GLUE_PREVENT_CONTROLS_LIST[controlID] + local controlState = isControlEnabled(controlName) + local controlStateNeedsUpdate = (controlState ~= forcedState) + + if (controlStateNeedsUpdate) then + toggleControl(controlName, forcedState) + end + end + + return true +end + +local function restoreCombatControlsOnEvent(vehicleElement) + local playerAttachedTo = getElementAttachedTo(localPlayer) + + if (not playerAttachedTo) then + return false + end + + if (vehicleElement) then + local playerAttachedElementMatching = (playerAttachedTo == vehicleElement) + + if (not playerAttachedElementMatching) then + return false + end + end + + toggleCombatControls(true) + + return true +end + +local function restoreCombatControlsOnVehicleDestroy() + restoreCombatControlsOnEvent(source) +end +if (GLUE_PREVENT_CONTROLS) then + addEventHandler("onClientElementDestroy", root, restoreCombatControlsOnVehicleDestroy) +end + +local function restoreCombatControlsOnResourceStop() + restoreCombatControlsOnEvent() +end +if (GLUE_PREVENT_CONTROLS) then + addEventHandler("onClientResourceStop", root, restoreCombatControlsOnResourceStop) +end + +local function displayGlueHintsOnVehicleEnter() + if (glueHintsDisplayed) then + return false + end + + local glueHintCanAttachVehicle = findInTable(GLUE_ALLOWED_ELEMENTS, "vehicle") + local glueHintCanAttachPlayer = findInTable(GLUE_ALLOWED_ELEMENTS, "player") + + if (not glueHintCanAttachVehicle and not glueHintCanAttachPlayer) then + return false + end + + local glueHintAttachVehicles = glueHintCanAttachVehicle and GLUE_MESSAGE_HIGHLIGHT_COLOR.."current/nearby vehicle#ffffff" or "" + local glueHintAttachYourself = glueHintCanAttachPlayer and GLUE_MESSAGE_HIGHLIGHT_COLOR.."yourself#ffffff"..(glueHintCanAttachVehicle and " or " or "") or "" + local glueHintAttachLock = (GLUE_ALLOW_ATTACH_TOGGLING and "#ffffff'"..GLUE_MESSAGE_HIGHLIGHT_COLOR..GLUE_ATTACH_TOGGLE_KEY.."#ffffff' is used to disable/enable attaching to your vehicle. " or "") + local glueHintDetachElements = (GLUE_ALLOW_DETACHING_ELEMENTS and "#ffffff'"..GLUE_MESSAGE_HIGHLIGHT_COLOR..GLUE_DETACH_ELEMENTS_KEY.."#ffffff' to detach all currently attached elements." or "") + + local glueHintA = "Press '"..GLUE_MESSAGE_HIGHLIGHT_COLOR..GLUE_ATTACH_DETACH_KEY.."#ffffff' to attach "..glueHintAttachYourself..glueHintAttachVehicles.." to (nearby/current) vehicle." + local glueHintB = glueHintAttachLock + local glueHintC = glueHintDetachElements + + sendGlueMessage(glueHintA) + sendGlueMessage(glueHintB, nil, "*") + sendGlueMessage(glueHintC, nil, "*") + + glueHintsDisplayed = true +end +if (GLUE_SHOW_ONE_TIME_HINT) then + addEventHandler("onClientPlayerVehicleEnter", localPlayer, displayGlueHintsOnVehicleEnter) +end + +function onClientAttachStateChanged(playerAttached) + if (not GLUE_PREVENT_CONTROLS) then + return false + end + + local toggleControlState = (not playerAttached) + + toggleCombatControls(toggleControlState) +end +addEvent("onClientAttachStateChanged", true) +addEventHandler("onClientAttachStateChanged", localPlayer, onClientAttachStateChanged) \ No newline at end of file diff --git a/[gameplay]/glue/logic/SVehicleAttach.lua b/[gameplay]/glue/logic/SVehicleAttach.lua new file mode 100644 index 000000000..f03facbe4 --- /dev/null +++ b/[gameplay]/glue/logic/SVehicleAttach.lua @@ -0,0 +1,205 @@ +-- ####################################### +-- ## Project: Glue ## +-- ## Author: MTA contributors ## +-- ## Version: 1.3.1 ## +-- ####################################### + +local function performAttachDetachTasksForPlayer(playerElement, attachTo) + local playerType = isElementType(playerElement, "player") + + if (not playerType) then + return false + end + + setGlueSyncCorrectionForPlayer(playerElement, attachTo) + triggerClientEvent(playerElement, "onClientAttachStateChanged", playerElement, attachTo) + + return true +end + +local function processAttachData(attachData) + local attachDataTable = typeCheck(attachData, "table") + + if (not attachDataTable) then + return false + end + + local attachDataSize = (#attachData) + local attachDataSizeMatching = (attachDataSize == GLUE_CLIENT_ATTACH_DATA_SIZE) + + if (not attachDataSizeMatching) then + return false + end + + for attachDataID = 1, attachDataSize do + local attachDataValue = attachData[attachDataID] + local attachDataNumber = typeCheck(attachDataValue, "number") + + if (not attachDataNumber) then + return false + end + end + + return true +end + +local function adjustPlayerWeaponSlot(clientElement, attachElement, playerWeaponSlot) + local playerMatching = (clientElement == attachElement) + + if (not playerMatching) then + return false + end + + local playerWeaponSlotNumber = typeCheck(playerWeaponSlot, "number") + + if (not playerWeaponSlotNumber) then + return false + end + + local playerWeaponSlotValid = GLUE_WEAPON_SLOTS[playerWeaponSlot] + + if (not playerWeaponSlotValid) then + return false + end + + local playerWeaponSlotNow = getPedWeaponSlot(clientElement) + local playerWeaponSlotNeedsUpdate = (playerWeaponSlotNow ~= playerWeaponSlot) + + if (playerWeaponSlotNeedsUpdate) then + setPedWeaponSlot(clientElement, playerWeaponSlot) + + return true + end + + return false +end + +function detachAttachedVehicleElements(vehicleElement, skipGlueTasks) + local vehicleType = isElementType(vehicleElement, "vehicle") + + if (not vehicleType) then + return false + end + + local vehicleAttachedElements = getAttachedElements(vehicleElement) + local vehicleDetachedElementCount = false + + for attachedElementID = 1, #vehicleAttachedElements do + local vehicleAttachedElement = vehicleAttachedElements[attachedElementID] + local vehicleAttachedElementShouldDetach = isElementType(vehicleAttachedElement, GLUE_ALLOWED_ELEMENTS) + + if (vehicleAttachedElementShouldDetach) then + local vehicleElementDetached = detachElements(vehicleAttachedElement, vehicleElement) + + if (vehicleElementDetached) then + local vehicleDetachedElementCountNew = (vehicleDetachedElementCount or 0) + 1 + + vehicleDetachedElementCount = vehicleDetachedElementCountNew + + if (not skipGlueTasks) then + performAttachDetachTasksForPlayer(vehicleAttachedElement, false) + end + end + end + end + + return vehicleDetachedElementCount +end + +function onServerVehicleAttachElement(attachElement, attachToElement, attachData, playerWeaponSlot) + local canAttachElement = canPlayerAttachElementToVehicle(client, attachElement, attachToElement) + + if (not canAttachElement) then + return false + end + + local attachDataProcessed = processAttachData(attachData) + + if (not attachDataProcessed) then + return false + end + + local attachX, attachY, attachZ = attachData[1], attachData[2], attachData[3] + local attachRX, attachRY, attachRZ = attachData[4], attachData[5], attachData[6] + local attachedElement = attachElements(attachElement, attachToElement, attachX, attachY, attachZ, attachRX, attachRY, attachRZ) + + if (attachedElement) then + adjustPlayerWeaponSlot(client, attachElement, playerWeaponSlot) + performAttachDetachTasksForPlayer(attachElement, attachToElement) + end +end +addEvent("onServerVehicleAttachElement", true) +addEventHandler("onServerVehicleAttachElement", root, onServerVehicleAttachElement) + +function onServerVehicleDetachElement(detachElement) + local canDetachElement, detachFromElement = canPlayerDetachElementFromVehicle(client, detachElement) + + if (not canDetachElement) then + return false + end + + local detachedElement = detachElements(detachElement, detachFromElement) + + if (detachedElement) then + performAttachDetachTasksForPlayer(detachElement, false) + end +end +addEvent("onServerVehicleDetachElement", true) +addEventHandler("onServerVehicleDetachElement", root, onServerVehicleDetachElement) + +function onServerVehicleDetachElements() + local vehicleToDetachElements = canPlayerDetachElementsFromVehicle(client) + + if (not vehicleToDetachElements) then + return false + end + + detachAttachedVehicleElements(vehicleToDetachElements) +end +addEvent("onServerVehicleDetachElements", true) +addEventHandler("onServerVehicleDetachElements", root, onServerVehicleDetachElements) + +local function detachElementsOnVehicleDestroy() + detachAttachedVehicleElements(source) +end +addEventHandler("onElementDestroy", root, detachElementsOnVehicleDestroy) + +local function detachElementsOnVehicleExplode() + detachAttachedVehicleElements(source) +end +if (GLUE_DETACH_ON_VEHICLE_EXPLOSION) then + addEventHandler("onVehicleExplode", root, detachElementsOnVehicleExplode) +end + +local function detachElementsOnResourceStop() + local vehiclesTable = getElementsByType("vehicle") + + for vehicleID = 1, #vehiclesTable do + local vehicleElement = vehiclesTable[vehicleID] + + detachAttachedVehicleElements(vehicleElement, true) + end +end +addEventHandler("onResourceStop", resourceRoot, detachElementsOnResourceStop) + +local function detachElementsOnDeath() + local attachToElement = getElementAttachedTo(source) + + if (not attachToElement) then + return false + end + + detachElements(source, attachToElement) + performAttachDetachTasksForPlayer(source, false) +end + +do + for glueElementTypeID = 1, #GLUE_ALLOWED_ELEMENTS do + local glueElementType = GLUE_ALLOWED_ELEMENTS[glueElementTypeID] + local glueDetachEvent = GLUE_ELEMENT_TYPES_AND_EVENTS[glueElementType] + + if (glueDetachEvent) then + addEventHandler(glueDetachEvent, root, detachElementsOnDeath) + end + end +end \ No newline at end of file diff --git a/[gameplay]/glue/logic/SVehicleAttachLock.lua b/[gameplay]/glue/logic/SVehicleAttachLock.lua new file mode 100644 index 000000000..5c86d3c83 --- /dev/null +++ b/[gameplay]/glue/logic/SVehicleAttachLock.lua @@ -0,0 +1,72 @@ +-- ####################################### +-- ## Project: Glue ## +-- ## Author: MTA contributors ## +-- ## Version: 1.3.1 ## +-- ####################################### + +local vehicleAttachLock = {} + +function isVehicleAttachLocked(vehicleElement) + local vehicleAttachLocked = vehicleAttachLock[vehicleElement] + + return vehicleAttachLocked +end + +function onServerVehicleToggleAttachLock() + local vehicleToToggleLock = canPlayerToggleVehicleAttachLock(client) + + if (not vehicleToToggleLock) then + return false + end + + local vehicleLockState = isVehicleAttachLocked(vehicleToToggleLock) + local vehicleLockStateNew = (not vehicleLockState) + local vehicleLockStateMessage = "You have toggled "..(vehicleLockStateNew and "#00ff00on" or "#ff0000off").."#ffffff vehicle attachment lock." + + vehicleAttachLock[vehicleToToggleLock] = vehicleLockStateNew + sendGlueMessage(vehicleLockStateMessage, client, "**") +end +addEvent("onServerVehicleToggleAttachLock", true) +addEventHandler("onServerVehicleToggleAttachLock", root, onServerVehicleToggleAttachLock) + +local function notifyAboutVehicleAttachLock(pPed) + local playerType = isElementType(pPed, "player") + + if (not playerType) then + return false + end + + local vehicleDriver = getVehicleController(source) + local vehicleDriverMatching = (vehicleDriver == pPed) + + if (not vehicleDriverMatching) then + return false + end + + local vehicleAttachLocked = isVehicleAttachLocked(source) + + if (not vehicleAttachLocked) then + return false + end + + local vehicleAttachPlayerAllowed = findInTable(GLUE_ALLOWED_ELEMENTS, "player") + local vehicleAttachVehicleAllowed = findInTable(GLUE_ALLOWED_ELEMENTS, "vehicle") + local vehicleAttachPlayerHint = (vehicleAttachPlayerAllowed and "players can't glue to it" or "") + local vehicleAttachVehicleHint = (vehicleAttachVehicleAllowed and "can't be glued to other vehicles" or "") + local vehicleAttachSeparator = (vehicleAttachPlayerAllowed and vehicleAttachVehicleAllowed) and "/" or "" + local vehicleAttachDescription = (vehicleAttachPlayerAllowed or vehicleAttachVehicleAllowed) and " ("..vehicleAttachPlayerHint..vehicleAttachSeparator..vehicleAttachVehicleHint..")" or "" + + local vehicleLockMessage = "Your vehicle is attach-locked"..vehicleAttachDescription..". Press '"..GLUE_MESSAGE_HIGHLIGHT_COLOR..GLUE_ATTACH_TOGGLE_KEY.."#ffffff' to unlock it." + + sendGlueMessage(vehicleLockMessage, pPed, "**") +end +if (GLUE_ALLOW_ATTACH_TOGGLING) then + addEventHandler("onVehicleEnter", root, notifyAboutVehicleAttachLock) +end + +local function clearVehicleAttachLock() + vehicleAttachLock[source] = nil +end +if (GLUE_ALLOW_ATTACH_TOGGLING) then + addEventHandler("onElementDestroy", root, clearVehicleAttachLock) +end \ No newline at end of file diff --git a/[gameplay]/glue/logic/SVehicleAttachSync.lua b/[gameplay]/glue/logic/SVehicleAttachSync.lua new file mode 100644 index 000000000..1ea8ea002 --- /dev/null +++ b/[gameplay]/glue/logic/SVehicleAttachSync.lua @@ -0,0 +1,102 @@ +-- ####################################### +-- ## Project: Glue ## +-- ## Author: MTA contributors ## +-- ## Version: 1.3.1 ## +-- ####################################### + +local attachSyncTimer = false +local attachSyncCorrection = {} + +local function correctAttachedPlayersPosition() + local attachVehiclesPosition = {} + + for attachedPlayer, attachedToVehicle in pairs(attachSyncCorrection) do + local playerAttachedVehicleElement = isElement(attachedToVehicle) + local playerAttachedVehicleValid = false + + if (playerAttachedVehicleElement) then + local playerAttachedToVehicle = getElementAttachedTo(attachedPlayer) + local playerAttachedVehicleMatching = (playerAttachedToVehicle and playerAttachedToVehicle == attachedToVehicle) + + playerAttachedVehicleValid = playerAttachedVehicleMatching + end + + if (playerAttachedVehicleValid) then + local attachVehiclePosition = attachVehiclesPosition[attachedToVehicle] + + if (not attachVehiclePosition) then + local attachVehiclePosX, attachVehiclePosY, attachVehiclePosZ = getElementPosition(attachedToVehicle) + local attachVehiclePositionData = { + attachVehiclePosX, + attachVehiclePosY, + attachVehiclePosZ, + } + + attachVehiclesPosition[attachedToVehicle] = attachVehiclePositionData + attachVehiclePosition = attachVehiclesPosition[attachedToVehicle] + end + + local attachPositionWarp = false -- warp will reset player animations, we don't want that + local attachPosX, attachPosY, attachPosZ = attachVehiclePosition[1], attachVehiclePosition[2], attachVehiclePosition[3] + + setElementPosition(attachedPlayer, attachPosX, attachPosY, attachPosZ, attachPositionWarp) + else + attachSyncCorrection[attachedPlayer] = nil + end + end + + handleCorrectionSyncTimer() + + return true +end + +function handleCorrectionSyncTimer() + if (not GLUE_SYNC_CORRECTION) then + return false + end + + local toggleOn = next(attachSyncCorrection) + + if (toggleOn) then + + if (attachSyncTimer) then + return false + end + + attachSyncTimer = setTimer(correctAttachedPlayersPosition, GLUE_SYNC_CORRECTION_INTERVAL, 0) + + return true + end + + if (not toggleOn) then + + if (not attachSyncTimer) then + return false + end + + killTimer(attachSyncTimer) + attachSyncTimer = false + + return true + end +end + +function setGlueSyncCorrectionForPlayer(playerElement, attachVehicle) + local playerType = isElementType(playerElement, "player") + + if (not playerType) then + return false + end + + local attachSyncState = (attachVehicle or nil) + + attachSyncCorrection[playerElement] = attachSyncState + handleCorrectionSyncTimer() + + return true +end + +local function clearSyncCorrectionData() + attachSyncCorrection[source] = nil +end +addEventHandler("onPlayerQuit", root, clearSyncCorrectionData) \ No newline at end of file diff --git a/[gameplay]/glue/logic/ShVehicleAttach.lua b/[gameplay]/glue/logic/ShVehicleAttach.lua new file mode 100644 index 000000000..86f4f2311 --- /dev/null +++ b/[gameplay]/glue/logic/ShVehicleAttach.lua @@ -0,0 +1,397 @@ +-- ####################################### +-- ## Project: Glue ## +-- ## Author: MTA contributors ## +-- ## Version: 1.3.1 ## +-- ####################################### + +function canPlayerAttachElementToVehicle(playerElement, attachElement, attachToElement) + local playerType = isElementType(playerElement, "player") + + if (not playerType) then + return false + end + + local playerDead = isPedDead(playerElement) + + if (playerDead) then + return false + end + + local allowedElementToAttach, allowedElementType = isElementType(attachElement, GLUE_ALLOWED_ELEMENTS) + + if (not allowedElementToAttach) then + return false + end + + local attachToElementVehicleType = isElementType(attachToElement, "vehicle") + + if (not attachToElementVehicleType) then + return false + end + + local attachToElementVehicleExploded = isVehicleBlown(attachToElement) + + if (attachToElementVehicleExploded) then + return false + end + + local attachElementPlayer = (allowedElementType == "player") + local attachElementVehicle = (allowedElementType == "vehicle") + + if (attachElementPlayer) then + local attachPlayerSelf = (playerElement == attachElement) + + if (not attachPlayerSelf) then + return false + end + + local attachPlayerVehicle = getPedOccupiedVehicle(attachElement) + + if (attachPlayerVehicle) then + return false + end + + local attachPlayerPosX, attachPlayerPosY, attachPlayerPosZ = getElementPosition(attachElement) + local attachToVehiclePosX, attachToVehiclePosY, attachToVehiclePosZ = getElementPosition(attachToElement) + local attachPlayerDistance = getDistanceBetweenPoints3D(attachToVehiclePosX, attachToVehiclePosY, attachToVehiclePosZ, attachPlayerPosX, attachPlayerPosY, attachPlayerPosZ) + local attachPlayerCloseEnough = (attachPlayerDistance <= GLUE_ATTACH_PLAYER_MAX_DISTANCE) + + if (not attachPlayerCloseEnough) then + return false + end + + local attachPlayerInterior = getElementInterior(attachElement) + local attachPlayerDimension = getElementDimension(attachElement) + local attachToVehicleInterior = getElementInterior(attachToElement) + local attachToVehicleDimension = getElementDimension(attachToElement) + local attachPlayerWorldMatching = (attachPlayerInterior == attachToVehicleInterior) and (attachPlayerDimension == attachToVehicleDimension) + + if (not attachPlayerWorldMatching) then + return false + end + + if (IS_SERVER) then + local vehicleAttachLocked = isVehicleAttachLocked(attachToElement) + + if (vehicleAttachLocked) then + local vehicleAttachLockMessage = "The vehicle you are trying to glue is attach locked." + + sendGlueMessage(vehicleAttachLockMessage, playerElement, "**") + + return false + end + end + + return true + end + + if (attachElementVehicle) then + local vehicleAttachDifferent = (attachElement ~= attachToElement) + + if (not vehicleAttachDifferent) then + return false + end + + local vehicleAttachElementExploded = isVehicleBlown(attachElement) + + if (vehicleAttachElementExploded) then + return false + end + + local vehicleAttachedElement = getAttachedVehicle(attachToElement) + + if (vehicleAttachedElement) then + return false + end + + local vehicleAttachToType = getVehicleType(attachToElement) + local vehicleAttachToHelicopter = (vehicleAttachToType == "Helicopter") + + if (not vehicleAttachToHelicopter) then + local vehicleAttachElementController = getVehicleController(attachElement) + local vehicleAttachElementDriver = (playerElement == vehicleAttachElementController) + + if (not vehicleAttachElementDriver) then + return false + end + end + + local attachVehiclePosX, attachVehiclePosY, attachVehiclePosZ = getElementPosition(attachElement) + local attachToVehiclePosX, attachToVehiclePosY, attachToVehiclePosZ = getElementPosition(attachToElement) + local attachVehicleDistance = getDistanceBetweenPoints3D(attachToVehiclePosX, attachToVehiclePosY, attachToVehiclePosZ, attachVehiclePosX, attachVehiclePosY, attachVehiclePosZ) + local attachVehicleCloseEnough = (attachVehicleDistance <= GLUE_ATTACH_VEHICLE_MAX_DISTANCE) + + if (not attachVehicleCloseEnough) then + return false + end + + local attachVehicleInterior = getElementInterior(attachElement) + local attachVehicleDimension = getElementDimension(attachElement) + local attachToVehicleInterior = getElementInterior(attachToElement) + local attachToVehicleDimension = getElementDimension(attachToElement) + local attachVehicleWorldMatching = (attachVehicleInterior == attachToVehicleInterior) and (attachVehicleDimension == attachToVehicleDimension) + + if (not attachVehicleWorldMatching) then + return false + end + + local vehicleModel = getElementModel(attachElement) + local vehicleModelType = getVehicleType(attachElement) + local vehicleModelAllowed = GLUE_VEHICLE_TYPES[vehicleModelType] or GLUE_VEHICLE_WHITELIST[vehicleModel] + + if (not vehicleModelAllowed) then + local vehicleModelMessage = "This vehicle model or type can't be attached." + + sendGlueMessage(vehicleModelMessage, playerElement, "**") + + return false + end + + if (IS_SERVER) then + local vehicleAttachLocked = isVehicleAttachLocked(attachElement) + local vehicleAttachToLocked = isVehicleAttachLocked(attachToElement) + + if (vehicleAttachLocked or vehicleAttachToLocked) then + local vehicleAttachLockMessage = "The vehicle you are in/nearby is attach locked and can't be glued." + + sendGlueMessage(vehicleAttachLockMessage, playerElement, "**") + + return false + end + end + end + + return true +end + +function canPlayerDetachElementFromVehicle(playerElement, detachElement) + local playerType = isElementType(playerElement, "player") + + if (not playerType) then + return false + end + + local playerDead = isPedDead(playerElement) + + if (playerDead) then + return false + end + + local allowedElement, allowedElementType = isElementType(detachElement, GLUE_ALLOWED_ELEMENTS) + + if (not allowedElement) then + return false + end + + local attachToElement = getElementAttachedTo(detachElement) + + if (not attachToElement) then + return false + end + + local detachElementPlayerType = (allowedElementType == "player") + local detachElementVehicleType = (allowedElementType == "vehicle") + + if (detachElementPlayerType) then + local detachPlayerSelf = (playerElement == detachElement) + + if (not detachPlayerSelf) then + return false + end + end + + if (detachElementVehicleType) then + local detachVehicleExploded = isVehicleBlown(detachElement) + + if (detachVehicleExploded) then + return false + end + + local vehicleAttachToType = getVehicleType(attachToElement) + local vehicleAttachToHelicopter = (vehicleAttachToType == "Helicopter") + + if (not vehicleAttachToHelicopter) then + local vehicleController = getVehicleController(detachElement) + local vehicleDetachToDriver = (playerElement == vehicleController) + + if (not vehicleDetachToDriver) then + return false + end + end + end + + local playerAttachDetachDelayPassed = getOrSetPlayerDelay(playerElement, "attachDetach", GLUE_ATTACH_DETACH_DELAY) + + if (not playerAttachDetachDelayPassed) then + return false + end + + return true, attachToElement +end + +function canPlayerDetachElementsFromVehicle(playerElement) + if (not GLUE_ALLOW_DETACHING_ELEMENTS) then + return false + end + + local playerType = isElementType(playerElement, "player") + + if (not playerType) then + return false + end + + local playerDead = isPedDead(playerElement) + + if (playerDead) then + return false + end + + local playerVehicle = getPedOccupiedVehicle(playerElement) + + if (not playerVehicle) then + return false + end + + local playerVehicleDriver = getVehicleController(playerVehicle) + local playerVehicleDriverMatching = (playerElement == playerVehicleDriver) + + if (not playerVehicleDriverMatching) then + return false + end + + local playerDetachAllDelayPassed = getOrSetPlayerDelay(playerElement, "detachAll", GLUE_DETACH_ELEMENTS_DELAY) + + if (not playerDetachAllDelayPassed) then + return false + end + + if (IS_SERVER) then + local detachedElementCount = detachAttachedVehicleElements(playerVehicle) + + if (detachedElementCount) then + local detachAllMessage = "You have detached ("..GLUE_MESSAGE_HIGHLIGHT_COLOR..detachedElementCount.."#ffffff) elements attached to your vehicle." + + sendGlueMessage(detachAllMessage, playerElement, "**") + end + end + + return true +end + +function canPlayerToggleVehicleAttachLock(playerElement) + if (not GLUE_ALLOW_ATTACH_TOGGLING) then + return false + end + + local playerType = isElementType(playerElement, "player") + + if (not playerType) then + return false + end + + local playerDead = isPedDead(playerElement) + + if (playerDead) then + return false + end + + local playerVehicle = getPedOccupiedVehicle(playerElement) + + if (not playerVehicle) then + return false + end + + local playerVehicleDriver = getVehicleController(playerVehicle) + local playerVehicleDriverMatching = (playerElement == playerVehicleDriver) + + if (not playerVehicleDriverMatching) then + return false + end + + local playerAttachLockToggleDelayPassed = getOrSetPlayerDelay(playerElement, "attachLock", GLUE_ATTACH_TOGGLE_DELAY) + + if (not playerAttachLockToggleDelayPassed) then + return false + end + + return playerVehicle +end + +function getAttachedVehicle(vehicleElement) + local vehicleType = isElementType(vehicleElement, "vehicle") + + if (not vehicleType) then + return false + end + + local attachedElements = getAttachedElements(vehicleElement) + + for attachedElementID = 1, #attachedElements do + local attachedElement = attachedElements[attachedElementID] + local attachedElementVehicle = isElementType(attachedElement, "vehicle") + + if (attachedElementVehicle) then + return attachedElement + end + end + + return false +end + +function getNearestVehicleFromVehicle(vehicleElement) + local vehicleType = isElementType(vehicleElement, "vehicle") + + if (not vehicleType) then + return false + end + + local vehicleX, vehicleY, vehicleZ = getElementPosition(vehicleElement) + local vehicleInterior = getElementInterior(vehicleElement) + local vehicleDimension = getElementDimension(vehicleElement) + local vehiclesInRange = getElementsWithinRange(vehicleX, vehicleY, vehicleZ, GLUE_ATTACH_VEHICLE_MAX_DISTANCE, "vehicle", vehicleInterior, vehicleDimension) + + for vehicleID = 1, #vehiclesInRange do + local vehicleNearby = vehiclesInRange[vehicleID] + local vehicleDifferent = (vehicleElement ~= vehicleNearby) + + if (vehicleDifferent) then + local vehicleExploded = isVehicleBlown(vehicleElement) + + if (not vehicleExploded) then + return vehicleNearby + end + end + end + + return false +end + +function getVehicleAttachData(attachVehicle, attachToVehicle) + local attachVehicleType = isElementType(attachVehicle, "vehicle") + local attachToVehicleType = isElementType(attachToVehicle, "vehicle") + + if (not attachVehicleType or not attachToVehicleType) then + return false + end + + local vehicleStartRX, vehicleStartRY, vehicleStartRZ = getElementRotation(attachVehicle) + local vehicleTargetRX, vehicleTargetRY, vehicleTargetRZ = getElementRotation(attachToVehicle) + local vehicleRX = (vehicleStartRX - vehicleTargetRX) + local vehicleRY = (vehicleStartRY - vehicleTargetRY) + local vehicleRZ = (vehicleStartRZ - vehicleTargetRZ) + + if (GLUE_ATTACH_OVER_VEHICLE) then + local vehicleOffsetTopX = GLUE_ATTACH_ON_TOP_OFFSETS[1] + local vehicleOffsetTopY = GLUE_ATTACH_ON_TOP_OFFSETS[2] + local vehicleOffsetTopZ = GLUE_ATTACH_ON_TOP_OFFSETS[3] + + return vehicleOffsetTopX, vehicleOffsetTopY, vehicleOffsetTopZ, vehicleRX, vehicleRY, vehicleRZ + end + + local vehicleMatrix = getElementMatrix(attachToVehicle) + local vehicleStartX, vehicleStartY, vehicleStartZ = getElementPosition(attachVehicle) + local vehiclePosition = {vehicleStartX, vehicleStartY, vehicleStartZ} + local vehicleAttachX, vehicleAttachY, vehicleAttachZ = getOffsetFromXYZ(vehicleMatrix, vehiclePosition) + + return vehicleAttachX, vehicleAttachY, vehicleAttachZ, vehicleRX, vehicleRY, vehicleRZ +end \ No newline at end of file diff --git a/[gameplay]/glue/meta.xml b/[gameplay]/glue/meta.xml new file mode 100644 index 000000000..e5f666f8c --- /dev/null +++ b/[gameplay]/glue/meta.xml @@ -0,0 +1,9 @@ + + + +