diff --git a/[gameplay]/superman/CHandleSuperman.lua b/[gameplay]/superman/CHandleSuperman.lua index 139b51e9d..c71572f88 100644 --- a/[gameplay]/superman/CHandleSuperman.lua +++ b/[gameplay]/superman/CHandleSuperman.lua @@ -2,44 +2,47 @@ local playerRotation = {} local playerVelocity = {} local streamedPlayers = {} --- local player data - local extraVelocity = {} local lastDirection = {} local currentSpeed = 0 --- static variables - local serverGravity = getGravity() --- utility functions - local function isPlayerFlying(playerElement) local playerFlying = getSupermanData(playerElement, SUPERMAN_FLY_DATA_KEY) - return playerFlying end local function getSupermansFlying() local supermansFlying = {} - for playerElement, _ in pairs(streamedPlayers) do local playerFlying = isPlayerFlying(playerElement) - if (playerFlying) then supermansFlying[#supermansFlying + 1] = playerElement end end - return supermansFlying end local function restorePlayerFromSuperman(playerElement) + if not isElement(playerElement) or not getSupermanData(playerElement, SUPERMAN_FLY_DATA_KEY) then + return + end + setSupermanData(playerElement, SUPERMAN_FLY_DATA_KEY, false) - setPedAnimation(playerElement, false) - setElementVelocity(playerElement, 0, 0, 0) - setElementRotation(playerElement, 0, 0, 0) setElementCollisionsEnabled(playerElement, true) + + local x, y, z = getElementPosition(playerElement) + setElementPosition(playerElement, x, y, z) + + setPedAnimation(playerElement, false) + + local _, _, rz = getElementRotation(playerElement) + setElementRotation(playerElement, 0, 0, rz) + + if playerElement == localPlayer then + setGravity(serverGravity) + end playerRotation[playerElement] = nil playerVelocity[playerElement] = nil @@ -48,7 +51,6 @@ end local function angleDiff(angle1, angle2) angle1, angle2 = angle1 % 360, angle2 % 360 local diff = (angle1 - angle2) % 360 - if diff <= 180 then return diff else @@ -58,11 +60,9 @@ end local function isnan(x) math.inf = 1 / 0 - if x == math.inf or x == -math.inf or x ~= x then return true end - return false end @@ -70,13 +70,10 @@ local function getVector2DAngle(vec) if vec.x == 0 and vec.y == 0 then return 0 end - local angle = math.deg(math.atan(vec.x / vec.y)) + 90 - if vec.y < 0 then angle = angle + 180 end - return angle end @@ -89,20 +86,13 @@ function onClientResourceStartSuperman() addEventHandler("onClientElementStreamIn", root, onClientElementStreamInSuperman) addEventHandler("onClientElementStreamOut", root, onClientElementStreamOutSuperman) addEventHandler("onClientPlayerQuit", root, onClientPlayerQuitClearSupermanData) - bindKey("jump", "down", handleSupermanJump) - addCommandHandler("superman", handleSupermanCommand) - - -- get already streamed players (onClientElementStreamIn won't be called for those who are already streamed in) - local startAt = root local streamedIn = true local playersTable = getElementsByType("player", startAt, streamedIn) - for playerID = 1, #playersTable do local playerElement = playersTable[playerID] - streamedPlayers[playerElement] = true end end @@ -110,14 +100,9 @@ addEventHandler("onClientResourceStart", resourceRoot, onClientResourceStartSupe function onClientResourceStopSuperman() setGravity(serverGravity) - - -- restore all players animations, collisions, etc - local supermansFlying = getSupermansFlying() - for playerID = 1, #supermansFlying do local playerElement = supermansFlying[playerID] - restorePlayerFromSuperman(playerElement) end end @@ -125,11 +110,9 @@ end local function onClientRenderVehicleWarning() local boneX, boneY, boneZ = getPedBonePosition(localPlayer, 6) local screenX, screenY, distanceToBone = getScreenFromWorldPosition(boneX, boneY, boneZ + 0.3) - if (not screenX or not screenY) or (distanceToBone and distanceToBone > 100) then return false end - dxDrawText("You can not warp into a vehicle when superman is activated.", screenX, screenY, screenX, screenY, WARNING_TEXT_COLOR, 1.1, "default-bold", "center") end @@ -146,28 +129,22 @@ end function onClientPlayerDamageSuperman() local playerFlying = isPlayerFlying(localPlayer) - if (not playerFlying) then return false end - cancelEvent() end function onClientElementStreamInSuperman() local validElement = isElement(source) - if (not validElement) then return false end - local elementType = getElementType(source) local playerType = (elementType == "player") - if (not playerType) then return false end - streamedPlayers[source] = true end @@ -185,11 +162,9 @@ end function onClientSupermanDataChange(dataKey, _, newValue) local flyDataKey = (dataKey == SUPERMAN_FLY_DATA_KEY) - if (not flyDataKey) then return false end - if (not newValue) then restorePlayerFromSuperman(source) end @@ -197,22 +172,16 @@ end if (not SUPERMAN_USE_ELEMENT_DATA) then addEvent("onClientSupermanDataChange", false) end addEventHandler(SUPERMAN_USE_ELEMENT_DATA and "onClientElementDataChange" or "onClientSupermanDataChange", root, onClientSupermanDataChange) --- handleSupermanJump: combo to start flight without any command - function handleSupermanJump() local playerFlying = isPlayerFlying(localPlayer) - if (playerFlying) then return false end - local playerTask = getPedSimplestTask(localPlayer) local playerInAir = (playerTask == "TASK_SIMPLE_IN_AIR") - if (not playerInAir) then return false end - setElementVelocity(localPlayer, 0, 0, TAKEOFF_VELOCITY) setTimer(startSupermanFlight, 100, 1) end @@ -220,11 +189,9 @@ end function handleSupermanCommand() local playerInVehicle = isPedInVehicle(localPlayer) local playerFlying = isPlayerFlying(localPlayer) - if (playerInVehicle or playerFlying) then return false end - setElementVelocity(localPlayer, 0, 0, TAKEOFF_VELOCITY) setTimer(startSupermanFlight, TAKEOFF_FLIGHT_DELAY, 1) setSupermanData(localPlayer, SUPERMAN_TAKE_OFF_DATA_KEY, true) @@ -232,70 +199,49 @@ end function startSupermanFlight() local playerFlying = isPlayerFlying(localPlayer) - setSupermanData(localPlayer, SUPERMAN_TAKE_OFF_DATA_KEY, false) - - if (playerFlying) then + if playerFlying then return false end - + setSupermanData(localPlayer, SUPERMAN_FLY_DATA_KEY, true) + setPedAnimation(localPlayer, IDLE_ANIMLIB, IDLE_ANIMATION, -1, IDLE_ANIM_LOOP, false, false, false) setElementVelocity(localPlayer, 0, 0, 0) currentSpeed = 0 extraVelocity = {x = 0, y = 0, z = 0} end --- controls processing - local jump, oldJump = false, false function onClientRenderSupermanProcessControls() local playerFlying = isPlayerFlying(localPlayer) - if (not playerFlying) then jump, oldJump = getPedControlState(localPlayer, "jump"), jump - if (not oldJump and jump) then handleSupermanJump() end - return false end - - -- calculate the requested movement direction - local Direction = newVector3D(0, 0, 0) - if getPedControlState(localPlayer, "forwards") then Direction.y = 1 elseif getPedControlState(localPlayer, "backwards") then Direction.y = -1 end - if getPedControlState(localPlayer, "left") then Direction.x = 1 elseif getPedControlState(localPlayer, "right") then Direction.x = -1 end - Direction = normalizeVector3D(Direction) - - -- calculate the sight direction - local cameraX, cameraY, cameraZ, lookX, lookY, lookZ = getCameraMatrix() local SightDirection = newVector3D((lookX - cameraX), (lookY - cameraY), (lookZ - cameraZ)) - SightDirection = normalizeVector3D(SightDirection) - if getPedControlState(localPlayer, "look_behind") then SightDirection = mulVector3D(SightDirection, -1) end - - -- calculate the current max speed and acceleration values - local maxSpeed = MAX_SPEED local acceleration = ACCELERATION - if getPedControlState(localPlayer, "sprint") then maxSpeed = MAX_SPEED * EXTRA_SPEED_FACTOR acceleration = acceleration * EXTRA_ACCELERATION_FACTOR @@ -303,40 +249,28 @@ function onClientRenderSupermanProcessControls() maxSpeed = MAX_SPEED * LOW_SPEED_FACTOR acceleration = acceleration * LOW_ACCELERATION_FACTOR end - local DirectionModule = moduleVector3D(Direction) - - -- check if we must change the gravity - if DirectionModule == 0 and currentSpeed ~= 0 then setGravity(0) else setGravity(serverGravity) end - - -- calculate the new current speed - if currentSpeed ~= 0 and (DirectionModule == 0 or currentSpeed > maxSpeed) then - currentSpeed = currentSpeed - acceleration -- deccelerate + currentSpeed = currentSpeed - acceleration if currentSpeed < 0 then currentSpeed = 0 end elseif DirectionModule ~= 0 and currentSpeed < maxSpeed then - currentSpeed = currentSpeed + acceleration -- accelerate + currentSpeed = currentSpeed + acceleration if currentSpeed > maxSpeed then currentSpeed = maxSpeed end end - - -- calculate the movement requested direction - if DirectionModule ~= 0 then Direction = newVector3D(SightDirection.x * Direction.y - SightDirection.y * Direction.x, SightDirection.x * Direction.x + SightDirection.y * Direction.y, SightDirection.z * Direction.y) - - lastDirection = Direction -- save the last movement direction for when player releases all direction keys + lastDirection = Direction else - - if lastDirection then -- player is not specifying any direction, use last known direction or the current velocity + if lastDirection then Direction = lastDirection if currentSpeed == 0 then lastDirection = nil @@ -345,22 +279,16 @@ function onClientRenderSupermanProcessControls() Direction = newVector3D(getElementVelocity(localPlayer)) end end - Direction = normalizeVector3D(Direction) Direction = mulVector3D(Direction, currentSpeed) - - if currentSpeed > 0 then -- applicate a smooth direction change, if moving + if currentSpeed > 0 then local VelocityDirection = newVector3D(getElementVelocity(localPlayer)) - VelocityDirection = normalizeVector3D(VelocityDirection) - if math.sqrt(VelocityDirection.x ^ 2 + VelocityDirection.y ^ 2) > 0 then local DirectionAngle = getVector2DAngle(Direction) local VelocityAngle = getVector2DAngle(VelocityDirection) - local diff = angleDiff(DirectionAngle, VelocityAngle) local calculatedAngle - if diff >= 0 then if diff > MAX_ANGLE_SPEED then calculatedAngle = VelocityAngle + MAX_ANGLE_SPEED @@ -368,28 +296,22 @@ function onClientRenderSupermanProcessControls() calculatedAngle = DirectionAngle end else - if diff < MAX_ANGLE_SPEED then + if diff < -MAX_ANGLE_SPEED then calculatedAngle = VelocityAngle - MAX_ANGLE_SPEED else calculatedAngle = DirectionAngle end end calculatedAngle = calculatedAngle % 360 - local DirectionModule2D = math.sqrt(Direction.x ^ 2 + Direction.y ^ 2) Direction.x = -DirectionModule2D * math.cos(math.rad(calculatedAngle)) Direction.y = DirectionModule2D * math.sin(math.rad(calculatedAngle)) end end - if moduleVector3D(Direction) == 0 then extraVelocity = {x = 0, y = 0, z = 0} end - - -- set the new velocity - setElementVelocity(localPlayer, Direction.x + extraVelocity.x, Direction.y + extraVelocity.y, Direction.z + extraVelocity.z) - if extraVelocity.z > 0 then extraVelocity.z = extraVelocity.z - 1 if extraVelocity.z < 0 then @@ -403,100 +325,63 @@ function onClientRenderSupermanProcessControls() end end --- players flight processing - local function processIdleFlight(player) - -- set the proper animation on the player - local animLib, animName = getPedAnimation(player) if animLib ~= IDLE_ANIMLIB or animName ~= IDLE_ANIMATION then - setPedAnimation(player, IDLE_ANIMLIB, IDLE_ANIMATION, -1, IDLE_ANIM_LOOP, false, false) + setPedAnimation(player, IDLE_ANIMLIB, IDLE_ANIMATION, -1, IDLE_ANIM_LOOP, false, false, false) end - setElementCollisionsEnabled(player, false) - - -- if this is myself, calculate the ped rotation depending on the camera rotation - if player == localPlayer then local cameraX, cameraY, cameraZ, lookX, lookY, lookZ = getCameraMatrix() local Sight = newVector3D(lookX - cameraX, lookY - cameraY, lookZ - cameraZ) - Sight = normalizeVector3D(Sight) - if getPedControlState(localPlayer, "look_behind") then Sight = mulVector3D(Sight, -1) end - Sight.z = math.atan(Sight.x / Sight.y) - if Sight.y > 0 then Sight.z = Sight.z + math.pi end - Sight.z = math.deg(Sight.z) + 180 - setElementRotation(localPlayer, 0, 0, Sight.z) else local Zangle = getPedCameraRotation(player) - setElementRotation(player, 0, 0, Zangle) end end local function processMovingFlight(player, Velocity) - -- set the proper animation on the player - local animLib, animName = getPedAnimation(player) - if animLib ~= FLIGHT_ANIMLIB or animName ~= FLIGHT_ANIMATION then - setPedAnimation(player, FLIGHT_ANIMLIB, FLIGHT_ANIMATION, -1, FLIGHT_ANIM_LOOP, true, false) + setPedAnimation(player, FLIGHT_ANIMLIB, FLIGHT_ANIMATION, -1, false, true, false, true) end - local enablePlayerCollision = (player == localPlayer) - setElementCollisionsEnabled(player, enablePlayerCollision) - - -- calculate the player rotation depending on their velocity - local Rotation = newVector3D(0, 0, 0) - if Velocity.x == 0 and Velocity.y == 0 then Rotation.z = getElementRotation(player) else Rotation.z = math.deg(math.atan(Velocity.x / Velocity.y)) - if Velocity.y > 0 then Rotation.z = Rotation.z - 180 end - Rotation.z = (Rotation.z + 180) % 360 end Rotation.x = -math.deg(Velocity.z / moduleVector3D(Velocity) * 1.2) - - -- rotation compensation for the self animation rotation - Rotation.x = Rotation.x - 40 - - -- calculate the Y rotation for barrel rotations - if not playerRotation[player] then playerRotation[player] = 0 end - if not playerVelocity[player] then playerVelocity[player] = newVector3D(0, 0, 0) end - local previousAngle = getVector2DAngle(playerVelocity[player]) local currentAngle = getVector2DAngle(Velocity) local diff = angleDiff(currentAngle, previousAngle) - if isnan(diff) then diff = 0 end - local calculatedYRotation = -diff * MAX_Y_ROTATION / MAX_ANGLE_SPEED - if calculatedYRotation > playerRotation[player] then if calculatedYRotation - playerRotation[player] > ROTATION_Y_SPEED then playerRotation[player] = playerRotation[player] + ROTATION_Y_SPEED @@ -510,7 +395,6 @@ local function processMovingFlight(player, Velocity) playerRotation[player] = calculatedYRotation end end - if playerRotation[player] > MAX_Y_ROTATION then playerRotation[player] = MAX_Y_ROTATION elseif playerRotation[player] < -MAX_Y_ROTATION then @@ -518,35 +402,19 @@ local function processMovingFlight(player, Velocity) elseif math.abs(playerRotation[player]) < ZERO_TOLERANCE then playerRotation[player] = 0 end - Rotation.y = playerRotation[player] - - -- apply the calculated rotation - setElementRotation(player, Rotation.x, Rotation.y, Rotation.z) - - -- save the current velocity - playerVelocity[player] = Velocity end local function processLanding(player, Velocity, distanceToGround) - -- set the proper animation on the player - local animLib, animName = getPedAnimation(player) - if animLib ~= FLIGHT_ANIMLIB or animName ~= FLIGHT_ANIMATION then - setPedAnimation(player, FLIGHT_ANIMLIB, FLIGHT_ANIMATION, -1, FLIGHT_ANIM_LOOP, true, false) + setPedAnimation(player, FLIGHT_ANIMLIB, FLIGHT_ANIMATION, -1, false, true, false, true) end - local enablePlayerCollision = (player == localPlayer) - setElementCollisionsEnabled(player, enablePlayerCollision) - - -- calculate the player rotation depending on their velocity and distance to ground - local Rotation = newVector3D(0, 0, 0) - if Velocity.x == 0 and Velocity.y == 0 then Rotation.z = getElementRotation(player) else @@ -557,30 +425,19 @@ local function processLanding(player, Velocity, distanceToGround) Rotation.z = (Rotation.z + 180) % 360 end Rotation.x = -(85 - (distanceToGround * 85 / LANDING_DISTANCE)) - - -- rotation compensation for the self animation rotation - Rotation.x = Rotation.x - 40 - - -- apply the calculated rotation - setElementRotation(player, Rotation.x, Rotation.y, Rotation.z) end function onClientRenderSupermanProcessFlight() local supermansFlying = getSupermansFlying() - for playerID = 1, #supermansFlying do local playerElement = supermansFlying[playerID] - local Velocity = newVector3D(getElementVelocity(playerElement)) local distanceToBase = getElementDistanceFromCentreOfMassToBaseOfModel(playerElement) local playerPos = newVector3D(getElementPosition(playerElement)) - playerPos.z = playerPos.z - distanceToBase - local distanceToGround - if playerPos.z > 0 then local hit, _, _, hitZ = processLineOfSight(playerPos.x, playerPos.y, playerPos.z, playerPos.x, playerPos.y, playerPos.z - LANDING_DISTANCE - 1, true, true, true, true, true, false, false, false) @@ -588,16 +445,12 @@ function onClientRenderSupermanProcessFlight() distanceToGround = playerPos.z - hitZ end end - + if distanceToGround and distanceToGround < (LANDING_DISTANCE * 1.5) and Velocity.z < -0.5 then + local vx, vy, vz = getElementVelocity(playerElement) + setElementVelocity(playerElement, vx * 0.9, vy * 0.9, vz * 0.9) + end if distanceToGround and distanceToGround < GROUND_ZERO_TOLERANCE then - local isLocalPlayer = (playerElement == localPlayer) - restorePlayerFromSuperman(playerElement) - - if (isLocalPlayer) then - setGravity(serverGravity) - setSupermanData(localPlayer, SUPERMAN_FLY_DATA_KEY, false) - end elseif distanceToGround and distanceToGround < LANDING_DISTANCE then processLanding(playerElement, Velocity, distanceToGround) elseif moduleVector3D(Velocity) < ZERO_TOLERANCE then @@ -606,4 +459,4 @@ function onClientRenderSupermanProcessFlight() processMovingFlight(playerElement, Velocity) end end -end \ No newline at end of file +end diff --git a/[gameplay]/superman/vectors/CVectors.lua b/[gameplay]/superman/vectors/CVectors.lua index 248e0b577..29a548d5f 100644 --- a/[gameplay]/superman/vectors/CVectors.lua +++ b/[gameplay]/superman/vectors/CVectors.lua @@ -1,25 +1,31 @@ function newVector3D(posX, posY, posZ) local newVector = {x = posX or 0.0, y = posY or 0.0, z = posZ or 0.0} - return newVector end function moduleVector3D(vector3D) + if not vector3D or type(vector3D.x) ~= "number" then + return 0 + end return math.sqrt(vector3D.x * vector3D.x + vector3D.y * vector3D.y + vector3D.z * vector3D.z) end function normalizeVector3D(vector3D) + if not vector3D or type(vector3D.x) ~= "number" then + return newVector3D(0, 0, 0) + end local mod = moduleVector3D(vector3D) - if mod ~= 0 then vector3D.x = vector3D.x / mod vector3D.y = vector3D.y / mod vector3D.z = vector3D.z / mod end - return vector3D end function mulVector3D(vector3D, n) + if not vector3D or type(vector3D.x) ~= "number" or type(n) ~= "number" then + return newVector3D(0, 0, 0) + end return newVector3D(vector3D.x * n, vector3D.y * n, vector3D.z * n) -end \ No newline at end of file +end