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 @@
+
-
-
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/[gameplay]/superman/server.lua b/[gameplay]/superman/server.lua
deleted file mode 100644
index 4bca45d1f..000000000
--- a/[gameplay]/superman/server.lua
+++ /dev/null
@@ -1,51 +0,0 @@
-local Superman = {}
-
---
--- Start/stop functions
---
-function Superman.Start()
- local self = Superman
-
- addEvent("superman:start", true)
- addEvent("superman:stop", true)
-
- addEventHandler("superman:start", root, self.clientStart)
- addEventHandler("superman:stop", root, self.clientStop)
- addEventHandler("onPlayerJoin", root, Superman.setStateOff)
- addEventHandler("onPlayerVehicleEnter", root, self.enterVehicle)
-end
-addEventHandler("onResourceStart", resourceRoot, Superman.Start, false)
-
-function Superman.clientStart()
- setElementData(client, "superman:flying", true)
-end
-
-function Superman.clientStop()
- setElementData(client, "superman:flying", false)
-end
-
-function Superman.setStateOff(state)
- triggerClientEvent("setPlayerFlyingC", source, false)
- setElementData(source or client, "superman:flying", false)
-end
-
-function Superman.setStateOn(state)
- triggerClientEvent("setPlayerFlyingC", source, true)
- setElementData(source or client, "superman:flying", true)
-end
-
-function cancelAirkill()
- if getElementData(source, "superman:flying") or getElementData(source, "superman:takingOff") then
- cancelEvent()
- end
-end
-addEventHandler("onPlayerStealthKill", root, cancelAirkill)
-
--- Fix for players glitching other players' vehicles by warping into them while superman is active, causing them to flinch into air and get stuck.
-function Superman.enterVehicle()
- if getElementData(source, "superman:flying") or getElementData(source, "superman:takingOff") then
- removePedFromVehicle(source)
- local x, y, z = getElementPosition(source)
- setElementPosition(source, x, y, z)
- end
-end
\ No newline at end of file