diff --git a/[gameplay]/superman/client.lua b/[gameplay]/superman/CHandleSuperman.lua similarity index 82% rename from [gameplay]/superman/client.lua rename to [gameplay]/superman/CHandleSuperman.lua index ea2a1bdc7..046a5820c 100644 --- a/[gameplay]/superman/client.lua +++ b/[gameplay]/superman/CHandleSuperman.lua @@ -1,6 +1,7 @@ local Superman = {} -- Settings + local ZERO_TOLERANCE = 0.00001 local MAX_ANGLE_SPEED = 6 -- In degrees per frame local MAX_SPEED = 2.5 @@ -21,37 +22,21 @@ local IDLE_ANIMATION = "Coplook_loop" local IDLE_ANIM_LOOP = true local MAX_Y_ROTATION = 70 local ROTATION_Y_SPEED = 3.8 +local warningTextColor = tocolor(255, 0, 0, 255) + +-- Static variables --- Static global variables -local thisResource = getThisResource() local serverGravity = getGravity() -- -- Utility functions -- -local function isPlayerFlying(player) - local data = getElementData(player, "superman:flying") - if not data or data == false then - return false - else - return true - end -end +local function isPlayerFlying(playerElement) + local playerFlying = getSupermanData(playerElement, SUPERMAN_FLY_DATA_KEY) -local function setPlayerFlying(player, state) - if not player then return end - - if state == true then - state = true - else - state = false - end - - setElementData(player, "superman:flying", state) + return playerFlying end -addEvent("setPlayerFlyingC", true) -addEventHandler("setPlayerFlyingC", root, setPlayerFlying) local function iterateFlyingPlayers() local current = 1 @@ -70,14 +55,14 @@ local function iterateFlyingPlayers() end end -function Superman:restorePlayer(player) - setPlayerFlying(player, false) - setPedAnimation(player, false) - setElementVelocity(player, 0, 0, 0) - setElementRotation(player, 0, 0, 0) - setElementCollisionsEnabled(player, true) - self.rotations[player] = nil - self.previousVelocity[player] = nil +function Superman:restorePlayer(playerElement) + setSupermanData(playerElement, SUPERMAN_FLY_DATA_KEY, false) + setPedAnimation(playerElement, false) + setElementVelocity(playerElement, 0, 0, 0) + setElementRotation(playerElement, 0, 0, 0) + setElementCollisionsEnabled(playerElement, true) + self.rotations[playerElement] = nil + self.previousVelocity[playerElement] = nil end function angleDiff(angle1, angle2) @@ -91,22 +76,6 @@ function angleDiff(angle1, angle2) end end -local function isElementInWater(ped) - local pedPosition = Vector3D:new(getElementPosition(ped)) - - if pedPosition.z <= 0 then - return true - end - - local waterLevel = getWaterLevel(pedPosition.x, pedPosition.y, pedPosition.z) - - if not isElementStreamedIn(ped) or not waterLevel or waterLevel < pedPosition.z then - return false - else - return true - end -end - local function isnan(x) math.inf = 1 / 0 @@ -136,15 +105,12 @@ function Superman.Start() local self = Superman -- Register events - addEventHandler("onClientResourceStop", getResourceRootElement(thisResource), Superman.Stop, false) - addEventHandler("onPlayerJoin", root, Superman.onJoin) - addEventHandler("onPlayerQuit", root, Superman.onQuit) + + addEventHandler("onClientResourceStop", resourceRoot, Superman.Stop, false) addEventHandler("onClientRender", root, Superman.processControls) addEventHandler("onClientRender", root, Superman.processFlight) addEventHandler("onClientPlayerDamage", localPlayer, Superman.onDamage, false) addEventHandler("onClientPlayerVehicleEnter", localPlayer, Superman.onEnter) - addEventHandler("onClientElementDataChange", root, Superman.onDataChange) - addEventHandler("onClientElementStreamIn", root, Superman.onStreamIn) addEventHandler("onClientElementStreamOut", root, Superman.onStreamOut) bindKey("jump", "down", Superman.onJump) @@ -152,10 +118,11 @@ function Superman.Start() addCommandHandler("superman", Superman.cmdSuperman) -- Initializate attributes + self.rotations = {} self.previousVelocity = {} end -addEventHandler("onClientResourceStart", getResourceRootElement(thisResource), Superman.Start, false) +addEventHandler("onClientResourceStart", resourceRoot, Superman.Start, false) function Superman.Stop() local self = Superman @@ -168,22 +135,6 @@ function Superman.Stop() end end -function Superman.onJoin(player) - local self = Superman - local playerElement = player or source - - setPlayerFlying(playerElement, false) -end - -function Superman.onQuit(reason, player) - local self = Superman - local playerElement = player or source - - if isPlayerFlying(playerElement) then - self:restorePlayer(playerElement) - end -end - -- onEnter: superman cant enter vehicles -- There's a fix (serverside) for players glitching other players' vehicles by warping into them while superman is active, causing them to flinch into air and get stuck. function showWarning() @@ -191,7 +142,7 @@ function showWarning() local sx, sy, dist = getScreenFromWorldPosition(x, y, z + 0.3) if sx and sy and dist and dist < 100 then - dxDrawText("You can not warp into a vehicle when superman is activated.", sx, sy, sx, sy, tocolor(255, 0, 0, 255), 1.1, "default-bold", "center") + dxDrawText("You can not warp into a vehicle when superman is activated.", sx, sy, sx, sy, warningTextColor, 1.1, "default-bold", "center") end end @@ -200,46 +151,44 @@ function hideWarning() end function Superman.onEnter() - if (isPlayerFlying(localPlayer) or getElementData(localPlayer, "superman:takingOff")) and not isTimer(warningTimer) then + if (isPlayerFlying(localPlayer) or getSupermanData(localPlayer, SUPERMAN_TAKE_OFF_DATA_KEY)) and not isTimer(warningTimer) then addEventHandler("onClientRender", root, showWarning) warningTimer = setTimer(hideWarning, 5000, 1) end end function Superman.onDamage() - local self = Superman - if isPlayerFlying(localPlayer) then cancelEvent() end end --- onStreamIn: Reset rotation attribute for player -function Superman.onStreamIn() - local self = Superman -end - function Superman.onStreamOut() local self = Superman - if source and isElement(source) and getElementType(source) == "player" and isPlayerFlying(source) then + if isPlayerFlying(source) then self.rotations[source] = nil self.previousVelocity[source] = nil end end --- onDataChange: Check if somebody who is out of stream stops being superman -function Superman.onDataChange(dataName, oldValue) - local self = Superman +function onClientSupermanDataChange(dataKey, _, newValue) + local flyDataKey = (dataKey == SUPERMAN_FLY_DATA_KEY) + + if (not flyDataKey) then + return false + end - if dataName == "superman:flying" and isElement(source) and getElementType(source) == "player" and oldValue ~= getElementData(source, dataName) and oldValue == true and getElementData(source, dataName) == false then - self:restorePlayer(source) + if (not newValue) then + Superman:restorePlayer(source) end end +if (not SUPERMAN_USE_ELEMENT_DATA) then addEvent("onClientSupermanDataChange", false) end +addEventHandler(SUPERMAN_USE_ELEMENT_DATA and "onClientElementDataChange" or "onClientSupermanDataChange", root, onClientSupermanDataChange) -- onJump: Combo to start flight without any command -function Superman.onJump(key, keyState) - local self = Superman + +function Superman.onJump() local task = getPedSimplestTask(localPlayer) if not isPlayerFlying(localPlayer) then @@ -251,34 +200,32 @@ function Superman.onJump(key, keyState) end function Superman.cmdSuperman() - local self = Superman - if isPedInVehicle(localPlayer) or isPlayerFlying(localPlayer) then return end setElementVelocity(localPlayer, 0, 0, TAKEOFF_VELOCITY) setTimer(Superman.startFlight, TAKEOFF_FLIGHT_DELAY, 1) - setElementData(localPlayer, "superman:takingOff", true) + setSupermanData(localPlayer, SUPERMAN_TAKE_OFF_DATA_KEY, true) end function Superman.startFlight() local self = Superman - setElementData(localPlayer, "superman:takingOff", false) + setSupermanData(localPlayer, SUPERMAN_TAKE_OFF_DATA_KEY, false) if isPlayerFlying(localPlayer) then return end - triggerServerEvent("superman:start", localPlayer) - setPlayerFlying(localPlayer, true) + setSupermanData(localPlayer, SUPERMAN_FLY_DATA_KEY, true) setElementVelocity(localPlayer, 0, 0, 0) self.currentSpeed = 0 self.extraVelocity = {x = 0, y = 0, z = 0} end -- Controls processing + local jump, oldJump = false, false function Superman.processControls() @@ -449,7 +396,7 @@ function Superman.processFlight() self:restorePlayer(player) if player == localPlayer then setGravity(serverGravity) - triggerServerEvent("superman:stop", localPlayer) + setSupermanData(localPlayer, SUPERMAN_FLY_DATA_KEY, false) end elseif distanceToGround and distanceToGround < LANDING_DISTANCE then self:processLanding(player, Velocity, distanceToGround) @@ -488,11 +435,10 @@ function Superman:processIdleFlight(player) Sight.z = math.deg(Sight.z) + 180 - setPedRotation(localPlayer, Sight.z) setElementRotation(localPlayer, 0, 0, Sight.z) else local Zangle = getPedCameraRotation(player) - setPedRotation(player, Zangle) + setElementRotation(player, 0, 0, Zangle) end end @@ -515,7 +461,7 @@ function Superman:processMovingFlight(player, Velocity) local Rotation = Vector3D:new(0, 0, 0) if Velocity.x == 0 and Velocity.y == 0 then - Rotation.z = getPedRotation(player) + Rotation.z = getElementRotation(player) else Rotation.z = math.deg(math.atan(Velocity.x / Velocity.y)) @@ -574,7 +520,7 @@ function Superman:processMovingFlight(player, Velocity) Rotation.y = self.rotations[player] -- Apply the calculated rotation - setPedRotation(player, Rotation.z) + setElementRotation(player, Rotation.x, Rotation.y, Rotation.z) -- Save the current velocity @@ -600,7 +546,7 @@ function Superman:processLanding(player, Velocity, distanceToGround) local Rotation = Vector3D:new(0, 0, 0) if Velocity.x == 0 and Velocity.y == 0 then - Rotation.z = getPedRotation(player) + Rotation.z = getElementRotation(player) else Rotation.z = math.deg(math.atan(Velocity.x / Velocity.y)) if Velocity.y > 0 then @@ -614,7 +560,7 @@ function Superman:processLanding(player, Velocity, distanceToGround) Rotation.x = Rotation.x - 40 -- Apply the calculated rotation - setPedRotation(player, Rotation.z) + setElementRotation(player, Rotation.x, Rotation.y, Rotation.z) end diff --git a/[gameplay]/superman/SHandleSuperman.lua b/[gameplay]/superman/SHandleSuperman.lua new file mode 100644 index 000000000..3a618e34b --- /dev/null +++ b/[gameplay]/superman/SHandleSuperman.lua @@ -0,0 +1,29 @@ +local function supermanCancelAirKill() + local supermanFlying = getSupermanData(source, SUPERMAN_FLY_DATA_KEY) + local supermanTakingOff = getSupermanData(source, SUPERMAN_TAKE_OFF_DATA_KEY) + + if (not supermanFlying and not supermanTakingOff) then + return false + end + + cancelEvent() +end +addEventHandler("onPlayerStealthKill", root, supermanCancelAirKill) + +-- Fix for players glitching other players' vehicles by warping into them while superman is active, causing them to flinch into air and get stuck. + +local function supermanEnterVehicle() + local supermanFlying = getSupermanData(source, SUPERMAN_FLY_DATA_KEY) + local supermanTakingOff = getSupermanData(source, SUPERMAN_TAKE_OFF_DATA_KEY) + + if (not supermanFlying and not supermanTakingOff) then + return false + end + + removePedFromVehicle(source) + + local playerX, playerY, playerZ = getElementPosition(source) + + setElementPosition(source, playerX, playerY, playerZ) +end +addEventHandler("onPlayerVehicleEnter", root, supermanEnterVehicle) \ No newline at end of file diff --git a/[gameplay]/superman/config/ShSupermanConfig.lua b/[gameplay]/superman/config/ShSupermanConfig.lua new file mode 100644 index 000000000..e0ad32507 --- /dev/null +++ b/[gameplay]/superman/config/ShSupermanConfig.lua @@ -0,0 +1,2 @@ +SUPERMAN_FLY_DATA_KEY = "superman:flying" +SUPERMAN_TAKE_OFF_DATA_KEY = "superman:takingOff" \ No newline at end of file diff --git a/[gameplay]/superman/data/CData.lua b/[gameplay]/superman/data/CData.lua new file mode 100644 index 000000000..391282a5f --- /dev/null +++ b/[gameplay]/superman/data/CData.lua @@ -0,0 +1,15 @@ +local function onClientSupermanSync(supermanServerData) + syncSupermansData(supermanServerData) +end +if (not SUPERMAN_USE_ELEMENT_DATA) then + addEvent("onClientSupermanSync", true) + addEventHandler("onClientSupermanSync", localPlayer, onClientSupermanSync) +end + +local function onClientSupermanSetData(dataKey, dataValue) + setSupermanData(source, dataKey, dataValue) +end +if (not SUPERMAN_USE_ELEMENT_DATA) then + addEvent("onClientSupermanSetData", true) + addEventHandler("onClientSupermanSetData", root, onClientSupermanSetData) +end \ No newline at end of file diff --git a/[gameplay]/superman/data/SData.lua b/[gameplay]/superman/data/SData.lua new file mode 100644 index 000000000..58d00254a --- /dev/null +++ b/[gameplay]/superman/data/SData.lua @@ -0,0 +1,107 @@ +local supermanReceivers = {} + +local function tableToElementsArray(tableWithElements) + local arrayTable = {} + local elementID = 0 + + for elementReference, _ in pairs(tableWithElements) do + local validElement = isElement(elementReference) + + if (validElement) then + local newElementID = (elementID + 1) + + arrayTable[elementID] = elementReference + elementID = newElementID + end + end + + return arrayTable +end + +local function canElementDataBeChanged(clientElement, sourceElement, dataKey, newValue) + local matchingPlayer = (clientElement == sourceElement) + + if (not matchingPlayer) then + return false + end + + local supermanDataKey = SUPERMAN_ALLOWED_DATA_KEYS[dataKey] + + if (not supermanDataKey) then + return true + end + + local newValueDataType = type(newValue) + local newValueBool = (newValueDataType == "boolean") + + return newValueBool +end + +local function onServerSupermanSetData(dataKey, dataValue) + if (not client) then + return false + end + + setSupermanData(client, dataKey, dataValue) +end +if (not SUPERMAN_USE_ELEMENT_DATA) then + addEvent("onServerSupermanSetData", true) + addEventHandler("onServerSupermanSetData", root, onServerSupermanSetData) +end + +local function onElementDataChangeSuperman(dataKey, oldValue, newValue) + if (not client) then + return false + end + + local allowElementDataChange = canElementDataBeChanged(client, source, dataKey, newValue) + + if (not allowElementDataChange) then + local removeChangedData = (oldValue == nil) + + if (removeChangedData) then + removeElementData(source, dataKey) + else + setElementData(source, dataKey, oldValue) + end + end +end +if (SUPERMAN_USE_ELEMENT_DATA) then addEventHandler("onElementDataChange", root, onElementDataChangeSuperman) end + +local function onPlayerResourceStartSyncSuperman(startedResource) + local matchingResource = (startedResource == resource) + + if (not matchingResource) then + return false + end + + local supermansData = getSupermansData() + + triggerClientEvent(source, "onClientSupermanSync", source, supermansData) + supermanReceivers[source] = true +end +if (not SUPERMAN_USE_ELEMENT_DATA) then addEventHandler("onPlayerResourceStart", root, onPlayerResourceStartSyncSuperman) end + +local function onResourceStopClearSupermanElementData() + local playersTable = getElementsByType("player") + + for playerID = 1, #playersTable do + local playerElement = playersTable[playerID] + + for dataKey, _ in pairs(SUPERMAN_ALLOWED_DATA_KEYS) do + removeElementData(playerElement, dataKey) + end + end +end +if (SUPERMAN_USE_ELEMENT_DATA) then addEventHandler("onResourceStop", resourceRoot, onResourceStopClearSupermanElementData) end + +local function onPlayerQuitClearSupermanReceiver() + supermanReceivers[source] = nil +end +if (not SUPERMAN_USE_ELEMENT_DATA) then addEventHandler("onPlayerQuit", root, onPlayerQuitClearSupermanReceiver) end + +function getSupermanReceivers() + local supermanListeners = tableToElementsArray(supermanReceivers) + + return supermanListeners +end \ No newline at end of file diff --git a/[gameplay]/superman/data/ShData.lua b/[gameplay]/superman/data/ShData.lua new file mode 100644 index 000000000..b5a293079 --- /dev/null +++ b/[gameplay]/superman/data/ShData.lua @@ -0,0 +1,146 @@ +local isServer = (not triggerServerEvent) +local supermansData = {} + +SUPERMAN_USE_ELEMENT_DATA = false -- decides whether script will use built-in MTA data system (setElementData) or custom one, shipped with superman resource + +-- in general element data is bad, and shouldn't be used, hence it should be set to false, unless you want to have backwards compatibility + +SUPERMAN_ALLOWED_DATA_KEYS = { + [SUPERMAN_FLY_DATA_KEY] = true, + [SUPERMAN_TAKE_OFF_DATA_KEY] = true, +} + +function getSupermanData(playerElement, dataKey) + local validElement = isElement(playerElement) + + if (not validElement) then + return false + end + + local elementType = getElementType(playerElement) + local playerType = (elementType == "player") + + if (not playerType) then + return false + end + + local dataKeyType = type(dataKey) + local dataKeyString = (dataKeyType == "string") + + if (not dataKeyString) then + return false + end + + local allowedDataKey = SUPERMAN_ALLOWED_DATA_KEYS[dataKey] + + if (not allowedDataKey) then + return false + end + + if (SUPERMAN_USE_ELEMENT_DATA) then + return getElementData(playerElement, dataKey) + end + + local supermanData = supermansData[playerElement] + + if (not supermanData) then + return false + end + + local playerSupermanData = supermanData[dataKey] + + return playerSupermanData +end + +function setSupermanData(playerElement, dataKey, dataValue) + local validElement = isElement(playerElement) + + if (not validElement) then + return false + end + + local elementType = getElementType(playerElement) + local playerType = (elementType == "player") + + if (not playerType) then + return false + end + + local dataKeyType = type(dataKey) + local dataKeyString = (dataKeyType == "string") + + if (not dataKeyString) then + return false + end + + local allowedDataKey = SUPERMAN_ALLOWED_DATA_KEYS[dataKey] + + if (not allowedDataKey) then + return false + end + + local dataValueType = type(dataValue) + local dataValueBool = (dataValueType == "boolean") + + if (not dataValueBool) then + return false + end + + if (SUPERMAN_USE_ELEMENT_DATA) then + local oldElementData = getElementData(playerElement, dataKey) + local updateElementData = (oldElementData ~= dataValue) + + if (updateElementData) then + local syncElementData = (isServer or not isServer and localPlayer == playerElement) + + return setElementData(playerElement, dataKey, dataValue, syncElementData) + end + + return false + end + + local supermanData = supermansData[playerElement] + + if (not supermanData) then + supermansData[playerElement] = {} + supermanData = supermansData[playerElement] + end + + local oldDataValue = supermanData[dataKey] + local updateDataValue = (oldDataValue ~= dataValue) + + if (not updateDataValue) then + return false + end + + supermanData[dataKey] = dataValue + + if (isServer) then + local supermanListeners = getSupermanReceivers() + + triggerClientEvent(supermanListeners, "onClientSupermanSetData", playerElement, dataKey, dataValue) + else + local syncToServer = (playerElement == localPlayer) + + triggerEvent(isServer and "onSupermanDataChange" or "onClientSupermanDataChange", playerElement, dataKey, oldDataValue, dataValue) + + if (syncToServer) then + triggerServerEvent("onServerSupermanSetData", localPlayer, dataKey, dataValue) + end + end + + return true +end + +function getSupermansData() + return supermansData +end + +function syncSupermansData(supermansServerData) + supermansData = supermansServerData +end + +local function onClientServerPlayerQuitClearSupermanData() + supermansData[source] = nil +end +if (not SUPERMAN_USE_ELEMENT_DATA) then addEventHandler(isServer and "onPlayerQuit" or "onClientPlayerQuit", root, onClientServerPlayerQuitClearSupermanData) end \ No newline at end of file diff --git a/[gameplay]/superman/meta.xml b/[gameplay]/superman/meta.xml index 7eee34f1f..06206124a 100644 --- a/[gameplay]/superman/meta.xml +++ b/[gameplay]/superman/meta.xml @@ -1,6 +1,29 @@ + -