· 5 years ago · Jan 08, 2021, 01:46 AM
1local Players = game:GetService("Players")
2local Tool = script.Parent
3
4local GravityAccelerationConstant = 9.81 * 20 -- For every 20 studs is one meter on ROBLOX. 9.81 is the common accepted acceleration of gravity per a kg on earth, and is used on ROBLOX
5local PercentGravity = 25 -- Percentage of countered acceleration due to gravity by the coil.
6
7
8local function WaitForChild(Parent, Name, TimeLimit)
9 -- Waits for a child to appear. Not efficient, but it shoudln't have to be. It helps with debugging.
10 -- Useful when ROBLOX lags out, and doesn't replicate quickly.
11 -- @param TimeLimit If TimeLimit is given, then it will return after the timelimit, even if it hasn't found the child.
12
13 assert(Parent ~= nil, "Parent is nil")
14 assert(type(Name) == "string", "Name is not a string.")
15
16 local Child = Parent:FindFirstChild(Name)
17 local StartTime = tick()
18 local Warned = false
19
20 while not Child and Parent do
21 wait(0)
22 Child = Parent:FindFirstChild(Name)
23 if not Warned and StartTime + (TimeLimit or 5) <= tick() then
24 Warned = true
25 warn("Infinite yield possible for WaitForChild(" .. Parent:GetFullName() .. ", " .. Name .. ")")
26 if TimeLimit then
27 return Parent:FindFirstChild(Name)
28 end
29 end
30 end
31
32 if not Parent then
33 warn("Parent became nil.")
34 end
35
36 return Child
37end
38
39
40local function CallOnChildren(Instance, FunctionToCall)
41 -- Calls a function on each of the children of a certain object, using recursion.
42
43 FunctionToCall(Instance)
44
45 for _, Child in next, Instance:GetChildren() do
46 CallOnChildren(Child, FunctionToCall)
47 end
48end
49
50local function GetBricks(StartInstance)
51 -- Returns a list of bricks (will include StartInstance)
52
53 local List = {}
54
55 CallOnChildren(StartInstance, function(Item)
56 if Item:IsA("BasePart") then
57 List[#List+1] = Item;
58 end
59 end)
60
61 return List
62end
63
64--[[Maid
65Manages the cleaning of events and other things.
66
67API:
68 HireMaid() Returns a new Maid object.
69
70 Maid[key] = (function) Adds a task to perform when cleaning up.
71 Maid[key] = (event connection) Manages an event connection. Anything that isn't a function is assumed to be this.
72 Maid[key] = nil Removes a named task. If the task is an event, it is disconnected.
73
74 Maid:GiveTask(task) Same as above, but uses an incremented number as a key.
75 Maid:DoCleaning() Disconnects all managed events and performs all clean-up tasks.
76]]
77local MakeMaid do
78 local index = {
79 GiveTask = function(self, task)
80 local n = #self.Tasks+1
81 self.Tasks[n] = task
82 return n
83 end;
84 DoCleaning = function(self)
85 local tasks = self.Tasks
86 for name,task in pairs(tasks) do
87 if type(task) == 'function' then
88 task()
89 else
90 task:disconnect()
91 end
92 tasks[name] = nil
93 end
94 -- self.Tasks = {}
95 end;
96 };
97
98 local mt = {
99 __index = function(self, k)
100 if index[k] then
101 return index[k]
102 else
103 return self.Tasks[k]
104 end
105 end;
106 __newindex = function(self, k, v)
107 local tasks = self.Tasks
108 if v == nil then
109 -- disconnect if the task is an event
110 if type(tasks[k]) ~= 'function' and tasks[k] then
111 tasks[k]:disconnect()
112 end
113 elseif tasks[k] then
114 -- clear previous task
115 self[k] = nil
116 end
117 tasks[k] = v
118 end;
119 }
120
121 function MakeMaid()
122 return setmetatable({Tasks={},Instances={}},mt)
123 end
124end
125
126local function GetCharacter(Descendant)
127 -- Returns the Player and Charater that a descendent is part of, if it is part of one.
128 -- @param Descendant A child of the potential character.
129
130 local Charater = Descendant
131 local Player = Players:GetPlayerFromCharacter(Charater)
132
133 while not Player do
134 if Charater.Parent then
135 Charater = Charater.Parent
136 Player = Players:GetPlayerFromCharacter(Charater)
137 else
138 return nil
139 end
140 end
141
142 -- Found the player, character must be true.
143 return Charater, Player
144end
145
146--- Load and create constants
147local AntiGravityForce = Instance.new("BodyForce")
148AntiGravityForce.Name = "GravityCoilEffect"
149AntiGravityForce.Archivable = false
150
151local Handle = WaitForChild(Tool, "Handle")
152local GravityMaid = MakeMaid() -- Will contain and maintain events
153
154local function UpdateGravityEffect(Character)
155 -- Updates the AntiGravityForce to match the force of gravity on the character
156
157 local Bricks
158 if Character:IsDescendantOf(game) and Character:FindFirstChild("HumanoidRootPart") and Character.HumanoidRootPart:IsA("BasePart") then
159 local BasePart = Character.HumanoidRootPart
160 Bricks = BasePart:GetConnectedParts(true) -- Recursive
161 else
162 warn("[UpdateGravityEffect] - Character failed to have a HumanoidRootPart or something")
163 Bricks = GetBricks(Character)
164 end
165
166 local TotalMass = 0
167
168 -- Calculate total mass of player
169 for _, Part in pairs(Bricks) do
170 TotalMass = TotalMass + Part:GetMass()
171 end
172
173 -- Force = Mass * Acceleration
174 local ForceOnCharacter = GravityAccelerationConstant * TotalMass
175 local CounteringForceMagnitude = (1 - 0.25) * ForceOnCharacter
176
177 -- Set the actual value...
178 AntiGravityForce.force = Vector3.new(0, CounteringForceMagnitude, 0)
179end
180
181
182-- Connect events for player interaction
183Tool.Equipped:connect(function()
184 local Character, Player = GetCharacter(Tool)
185
186 if Character then
187 -- Connect events to recalculate gravity when hats are added or removed. Of course, this is not a perfect solution,
188 -- as connected parts are not necessarily part of the character, but ROBLOX has no API to handle the changing of joints, and
189 -- scanning the whole game for potential joints is really not worth the efficiency cost.
190 GravityMaid.DescendantAddedConnection = Character.DescendantAdded:connect(function()
191 UpdateGravityEffect(Character)
192 end)
193
194 GravityMaid.DecendantRemovingConnection = Character.DescendantRemoving:connect(function()
195 UpdateGravityEffect(Character)
196 end)
197
198 UpdateGravityEffect(Character)
199 -- Add in the force
200 AntiGravityForce.Parent = Handle
201 else
202 warn("[GravityCoil] - Somehow inexplicity failed to retrieve character")
203 end
204end)
205
206Tool.Unequipped:connect(function()
207 -- Remove force and clean up events
208 AntiGravityForce.Parent = nil
209 GravityMaid:DoCleaning()
210end)