-- Copyright (c) 2008, Alberto Alonso -- -- All rights reserved. -- -- Redistribution and use in source and binary forms, with or without modification, -- are permitted provided that the following conditions are met: -- -- * Redistributions of source code must retain the above copyright notice, this -- list of conditions and the following disclaimer. -- * Redistributions in binary form must reproduce the above copyright notice, this -- list of conditions and the following disclaimer in the documentation and/or other -- materials provided with the distribution. -- * Neither the name of the superman script nor the names of its contributors may be used -- to endorse or promote products derived from this software without specific prior -- written permission. -- -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --[[THIS IS A MODIFIED VERSION OF SUPERMAN RESOURCE FOR PURPOSES OF THE EDITOR]] local Superman = {} -- Settings local ZERO_TOLERANCE = 0.00001 local MAX_ANGLE_SPEED = 6 -- In degrees per frame local FLIGHT_ANIMLIB = "swim" local FLIGHT_ANIMATION = "Swim_Dive_Under" local FLIGHT_ANIM_LOOP = false local IDLE_ANIMLIB = "cop_ambient" local IDLE_ANIMATION = "Coplook_loop" local IDLE_ANIM_LOOP = true local MAX_Y_ROTATION = 55 local ROTATION_Y_SPEED = 3.8 -- Static global variables local thisResource = getThisResource() local serverGravity = getGravity() local supermanPlayers = {} -- -- Utility functions -- function isPlayerFlying(player) if player == localPlayer then return false end local data = supermanPlayers[player] if not data or data == false then return false else return true end end function setPlayerFlying(player, state) if state == true then state = true else state = nil local self = Superman if isPlayerFlying(player) then self:restorePlayer(player) end end supermanPlayers[player] = state return true end local function iterateFlyingPlayers() local current = 1 local allPlayers = getElementsByType("player") return function() local player repeat player = allPlayers[current] current = current + 1 until not player or (isPlayerFlying(player) and isElementStreamedIn(player)) return player end end function Superman:restorePlayer(player) setPlayerFlying(player, false) setPedAnimation(player, false) setElementVelocity(player, 0, 0, 0) setElementRotation(player, 0, 0, 0) --setPedRotation(player, getPedRotation(player)) setElementCollisionsEnabled(player, true) self.rotations[player] = nil self.previousVelocity[player] = nil end function angleDiff(angle1, angle2) angle1, angle2 = angle1 % 360, angle2 % 360 local diff = (angle1 - angle2) % 360 if diff <= 180 then return diff else return -(360 - diff) end end local function isPedInWater(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 if x == math.inf or x == -math.inf or x ~= x then return true end return false end 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 -- -- Initialization and shutdown functions -- function Superman.Start() local self = Superman -- Register events addEventHandler("onClientResourceStop", resourceRoot, Superman.Stop, false) addEventHandler("onPlayerQuit", root, Superman.onQuit) addEventHandler("onClientRender", root, Superman.processFlight) addEventHandler("onClientElementStreamIn", root, Superman.onStreamIn) addEventHandler("onClientElementStreamOut", root, Superman.onStreamOut) -- Initializate attributes self.rotations = {} self.previousVelocity = {} end addEventHandler("onClientResourceStart", resourceRoot, Superman.Start, false) function Superman.Stop() local self = Superman setGravity(serverGravity) -- Restore all players animations, collisions, etc for player in iterateFlyingPlayers() do self:restorePlayer(player) end end -- -- Join/Quit -- function Superman.onQuit(reason, player) local self = Superman player = player or source if isPlayerFlying(player) then self:restorePlayer(player) end end -- -- onDamage: superman is invulnerable -- -- -- 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 self.rotations[source] = nil self.previousVelocity[source] = nil end end -- -- Players flight processing -- function Superman.processFlight() local self = Superman for player in iterateFlyingPlayers() do local Velocity = Vector3D:new(getElementVelocity(player)) local distanceToBase = getElementDistanceFromCentreOfMassToBaseOfModel(player) local playerPos = Vector3D:new(getElementPosition(player)) playerPos.z = playerPos.z - distanceToBase if Velocity:Module() < ZERO_TOLERANCE then self:processIdleFlight(player) else self:processMovingFlight(player, Velocity) end end end function Superman: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) end setElementCollisionsEnabled(player, false) end function Superman: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) end -- Calculate the player rotation depending on their velocity local Rotation = Vector3D:new(0, 0, 0) if Velocity.x == 0 and Velocity.y == 0 then Rotation.z = getPedRotation(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 / Velocity:Module() * 1.2) -- Rotation compensation for the self animation rotation Rotation.x = Rotation.x - 40 -- Calculate the Y rotation for barrel rotations if not self.rotations[player] then self.rotations[player] = 0 end if not self.previousVelocity[player] then self.previousVelocity[player] = Vector3D:new(0, 0, 0) end local previousAngle = getVector2DAngle(self.previousVelocity[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 > self.rotations[player] then if calculatedYRotation - self.rotations[player] > ROTATION_Y_SPEED then self.rotations[player] = self.rotations[player] + ROTATION_Y_SPEED else self.rotations[player] = calculatedYRotation end else if self.rotations[player] - calculatedYRotation > ROTATION_Y_SPEED then self.rotations[player] = self.rotations[player] - ROTATION_Y_SPEED else self.rotations[player] = calculatedYRotation end end if self.rotations[player] > MAX_Y_ROTATION then self.rotations[player] = MAX_Y_ROTATION elseif self.rotations[player] < -MAX_Y_ROTATION then self.rotations[player] = -MAX_Y_ROTATION elseif math.abs(self.rotations[player]) < ZERO_TOLERANCE then self.rotations[player] = 0 end 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 self.previousVelocity[player] = Velocity end -- -- Vectors -- Vector3D = { new = function(self, x2, y2, z2) local newVector = { x = x2 or 0.0, y = y2 or 0.0, z = z2 or 0.0 } return setmetatable(newVector, { __index = Vector3D }) end, Copy = function(self) return Vector3D:new(self.x, self.y, self.z) end, Normalize = function(self) local mod = self:Module() if mod ~= 0 then self.x = self.x / mod self.y = self.y / mod self.z = self.z / mod end end, Dot = function(self, V) return self.x * V.x + self.y * V.y + self.z * V.z end, Module = function(self) return math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z) end, AddV = function(self, V) return Vector3D:new(self.x + V.x, self.y + V.y, self.z + V.z) end, SubV = function(self, V) return Vector3D:new(self.x - V.x, self.y - V.y, self.z - V.z) end, CrossV = function(self, V) return Vector3D:new(self.y * V.z - self.z * V.y, self.z * V.x - self.x * V.z, self.x * V.y - self.y * V.z) end, Mul = function(self, n) return Vector3D:new(self.x * n, self.y * n, self.z * n) end, Div = function(self, n) return Vector3D:new(self.x / n, self.y / n, self.z / n) end, MulV = function(self, V) return Vector3D:new(self.x * V.x, self.y * V.y, self.z * V.z) end, DivV = function(self, V) return Vector3D:new(self.x / V.x, self.y / V.y, self.z / V.z) end, }