· 8 years ago · Feb 13, 2018, 01:56 AM
1local mathc = {}
2local settings = {}
3local sound = {}
4local gui = {}
5local ik = {}
6local springs = {}
7local input = {}
8local physics = {}
9local triangle = {}
10local character = {}
11local camera = {}
12local weapons = {}
13local replication = {}
14local run = {}
15--Le Remote'a Folder.
16local Remotes = game:service'ReplicatedStorage'.Remotes
17--Cached Functions
18local Rnew = Ray.new
19local cf = CFrame.new
20local nc = cf()
21local ca = CFrame.Angles
22local v3 = Vector3.new
23local nv = v3()
24local v2 = Vector2.new
25local Inew = Instance.new
26local ray = Rnew
27local tinsert = table.insert
28local tremove = table.remove
29local tsort = table.sort
30
31local rad = math.rad
32local deg = math.deg
33local min = math.min
34local max = math.max
35local atan2 = math.atan2
36local abs = math.abs
37local sin = math.sin
38local cos = math.cos
39local cosh = math.cosh
40local acos = math.acos
41local floor = math.floor
42local ceil = math.ceil
43local random = math.random
44local pi = math.pi
45
46local player = game.Players.LocalPlayer
47
48local timeSpeed = 1
49
50local gravity = v3(0,-0.05,0)
51local characterHeight = 4.8
52local characterHitboxSize = 2
53local walkAnimSpeed = 0.8
54local diagonalCounter = .96
55
56game.StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.All,false)
57
58--Math scope
59do
60 local self = mathc
61
62 self.roundToNearest = function(a,b) --ROUND A TO NEAREST MULTIPLE OF B
63 return floor((a/b)+.5)*b
64 end
65 self.lerp = function(a,b,t) --HAS THE SAME EFFECT AS LERP BUT NOT AS ACCURATE
66 return a+(t*(b-a))
67 end
68 self.wrapValue = function(a,b) --IF A > B THEN A = 1
69 return a%b~=0 and a%b or b
70 end
71 self.removeRotation = function(c)
72 return (c*((c-c.p):inverse()))
73 end
74end
75
76--Settings scope
77do
78 local self = settings
79 --Animation
80 self.walkAnimSize = .65
81 --Character
82 self.walkSpeed = .027
83 self.sprintSpeed = .042
84 --Camera
85 self.camSensitivity = .185
86 --CLEAN UP CLIENT (UNUSED DEFAULT SCRIPTS)
87 script.Parent.ChildAdded:connect(function(child)
88 child.Disabled = true
89 wait()
90 child:Destroy()
91 end)
92end
93
94--Sound scope
95do
96 local self = sound
97 self.memory = {}
98 self.loadIntoMemory = function(id) --CREATE A NEW SOUND AND ADD IT TO THE table
99 --CREATE SOUND
100 local s = Inew("Sound",player)
101 s.SoundId = "rbxassetid://"..tostring(id)
102 s.PlaybackSpeed = timeSpeed
103 --ADD SOUND TO TABLE
104 tinsert(self.memory,s)
105 --RETURN SOUND INDEX
106 return #self.memory
107 end
108 self.playSound = function(index) --PLAY SAVED SOUND
109 self.memory[index]:Play()
110 end
111 self.playCloneSound = function(index) --PLAY CLONED SOUND
112 --CLONE SOUND
113 local s = self.memory[index]:clone()
114 s.Parent = self.memory[index].Parent
115 s.PlaybackSpeed = timeSpeed
116 --PLAY NEW SOUND
117 s:Play()
118 --DESTROY SOUND
119 game.Debris:AddItem(s,s.TimeLength / timeSpeed+0.05)
120 end
121end
122
123--GUI scope
124do
125 local self = gui
126 local sg = game:WaitForChild("StarterGui")
127 local main = nil
128 local mainGui = {}
129 function init()
130 for i,v in pairs(sg:GetChildren()) do
131 local c = v:clone()
132 c.Parent = game.Players.LocalPlayer:WaitForChild("PlayerGui")
133 if c.Name == "Main" then
134 main = c
135 end
136 end
137
138 mainGui.Game = {}
139 mainGui.GameGui = main:WaitForChild("Game")
140 mainGui.Game.Crosshair = mainGui.GameGui:WaitForChild("Crosshair")
141 mainGui.Game.CrosshairOffset = 15
142 end
143
144 function positionCrosshair(offset,tran)
145 offset = offset + 15
146 mainGui.Game.CrosshairOffset = mathc.lerp(mainGui.Game.CrosshairOffset,offset,.1)
147 mainGui.Game.CrosshairOffset = math.max(mainGui.Game.CrosshairOffset,15)
148 local o = mainGui.Game.CrosshairOffset
149 for i,v in pairs(mainGui.Game.Crosshair:GetChildren()) do
150 v.BackgroundTransparency = tran
151 if v.Name == "Top" then
152 v.Position = UDim2.new(.5,0,.5,-o)
153 elseif v.Name == "Right" then
154 v.Position = UDim2.new(.5,o,.5,0)
155 elseif v.Name == "Bottom" then
156 v.Position = UDim2.new(.5,0,.5,o)
157 elseif v.Name == "Left" then
158 v.Position = UDim2.new(.5,-o,.5,0)
159 end
160 end
161 end
162
163 self.step = function()
164 local offset = physics.char.velocity.magnitude*200
165 offset = offset + weapons.aim
166 offset = offset + abs(input.deltaX) * 2.5
167 offset = offset + abs(input.deltaY) * 2.5
168 local tran = weapons.aim / 50
169 positionCrosshair(offset,tran)
170 end
171
172 init()
173end
174
175--IK scope
176do
177 local self = ik
178 self.newChain = function(length0,length1) --CREATE A NEW IK CHAIN
179 --CREATE A TABLE TO STORE THE CHAIN
180 local chain = {}
181
182 --SET VARIABLES TODO WITH CHAIN
183 chain.l0 = length0
184 chain.l1 = length1
185 chain.start = nv
186 chain.goal = nv
187 chain.elbowRot = 0
188
189 chain.update = function() --UPDATE THE CHAIN TO CALCULATE THE CFRAMES
190 --WORK OUT VECTOR BETWEEN POINTS AND CALCULATE DISTANCE
191 local t = chain.goal-chain.start
192 local d = ((t.X)^2+(t.Y)^2+(t.Z)^2)^.5
193 if d >= chain.l0 + chain.l1 then
194 local ecf = cf(chain.start,chain.goal) * cf(0,0,-chain.l0)
195 return cf(chain.start,chain.goal) * cf(0,0,-chain.l0/2),
196 cf(ecf.p,chain.goal) * cf(0,0,-chain.l1/2),
197 ecf
198 end
199 --USE HERONS FORMULA AND TRIANGLE LOGIC TO CALCULATE TRIANGLE HEIGHT
200 local ed = (chain.l0/(chain.l0+chain.l1))*d
201 local s = (chain.l0+chain.l1+d)/2
202 local a = (s*(s-chain.l0)*(s-chain.l1)*(s-d))^.5
203 local h = (a*2)/d
204 --GENERATE A CFRAME FOR THE ELBOW
205 local ecf = cf(chain.start,chain.goal)
206 *cf(0,0,-ed)
207 *ca(rad(90),chain.elbowRot,0)
208 *cf(0,0,-h)
209 --GENERATE CFRAMES BASED ON ELBOW CFRAME
210 return cf(chain.start,ecf.p)*cf(0,0,-chain.l0/2),
211 cf(ecf.p,chain.goal)*cf(0,0,-chain.l1/2),
212 ecf
213 end
214 return chain
215 end
216end
217
218--Springs scope
219do
220 springs.createNumberSpring = function(start,target,speed,k,d,mult) --CREATE A SPRING SOLVER WHICH TWEENS NUMBERS
221 --CREATE A TABLE TO STORE THE SPRING
222 local spring = {}
223
224 --SET VARIABLES FOR SPRING
225 spring.current = start
226 spring.speed = speed or 0
227 spring.target = target
228 spring.k = k or 0.2
229 spring.d = d or 0.4
230
231 spring.update = function(dt) --UPDATE SPRING CURRENT VALUE
232 --WORK OUT DISTANCE BETWEEN NUMBERS
233 local x = spring.target - spring.current
234
235 --USE HOOKES LAW TO FIND THE FORCE THE SPRING EXERTS
236 local f = x * spring.k
237
238 --CHANGE SPRING SPEED BASED ON DISTANCE AND FORCE
239 spring.speed = (spring.speed * (1-spring.d)) + f
240
241 --CHANGE THE VALUE OF THE SPRING BASED ON THE SPEED
242 spring.current = spring.current + spring.speed * mult * timeSpeed * dt
243 return spring.current
244 end
245 return spring
246 end
247 springs.createCSpring = function(startCF,targetCF,k,d,mult) --USE 12 NUMBER SPRINGS TO TWEEN A CFrame
248 --CREATE A TABLE TO STORE THE SPRING
249 local spring = {}
250
251 --SET SPRING VARIABLES
252 spring.current = startCF
253 spring.target = targetCF
254 spring.k = k or 0.2
255 spring.d = d or 0.4
256 spring.mult = mult or .85
257
258 --GET COMPONENTS OF CFRAMES
259 local x,y,z,r00,r01,r02,r10,r11,r12,r20,r21,r22 = spring.current:components()
260 local tarx,tary,tarz,tarr00,tarr01,tarr02,tarr10,tarr11,tarr12,tarr20,tarr21,tarr22 = spring.target:components()
261
262 --CREATE NUMBER SPRINGS FOR EACH COMPONENT OF CFRAME
263 local numberSprings = {
264 springs.createNumberSpring(x ,tarx ,0,spring.k,spring.d,spring.mult),
265 springs.createNumberSpring(y ,tary ,0,spring.k,spring.d,spring.mult),
266 springs.createNumberSpring(z ,tarz ,0,spring.k,spring.d,spring.mult),
267 springs.createNumberSpring(r00,tarr00,0,spring.k,spring.d,spring.mult),
268 springs.createNumberSpring(r01,tarr01,0,spring.k,spring.d,spring.mult),
269 springs.createNumberSpring(r02,tarr02,0,spring.k,spring.d,spring.mult),
270 springs.createNumberSpring(r10,tarr10,0,spring.k,spring.d,spring.mult),
271 springs.createNumberSpring(r11,tarr11,0,spring.k,spring.d,spring.mult),
272 springs.createNumberSpring(r12,tarr12,0,spring.k,spring.d,spring.mult),
273 springs.createNumberSpring(r20,tarr20,0,spring.k,spring.d,spring.mult),
274 springs.createNumberSpring(r21,tarr21,0,spring.k,spring.d,spring.mult),
275 springs.createNumberSpring(r22,tarr22,0,spring.k,spring.d,spring.mult)
276 }
277
278 --CONVERT A CFRAME TO A TABLE
279 local function convertCFrameToTable(cframe)
280 local x,y,z,r00,r01,r02,r10,r11,r12,r20,r21,r22 = cframe:components()
281 return {x,y,z,r00,r01,r02,r10,r11,r12,r20,r21,r22}
282 end
283 --CONVERT A TABLE TO A CFRAME
284 local function convertTableToCFrame(tab)
285 return cf(tab[1],tab[2],tab[3],tab[4],tab[5],tab[6],tab[7],tab[8],tab[9],tab[10],tab[11],tab[12])
286 end
287
288 --A FUNCTION TO SET THE SPRINGS TARGET CFRAME
289 spring.setTarget = function(cframe)
290 local tab = convertCFrameToTable(cframe)
291 for i = 1,#tab do
292 numberSprings[i].target = tab[i]
293 end
294 end
295
296 spring.setCurrent = function(cframe)
297 local tab = convertCFrameToTable(cframe)
298 for i = 1,#tab do
299 numberSprings[i].current = tab[i]
300 end
301 end
302
303 --A FUNCTION TO MULTIPLY THE SPEED OF EACH NUMBER SPRING BY A GIVEN NUMBER
304 spring.multiplySpeeds = function(x)
305 for i = 1,#numberSprings do
306 numberSprings[i].speed = numberSprings[i].speed * x
307 end
308 end
309
310 --A FUNCTION TO UPDATE EACH SPRING AND RETURN A NEW CFRAME
311 spring.update = function(dt)
312 local tab = {}
313 for i = 1,#numberSprings do
314 tinsert(tab,numberSprings[i].update(dt))
315 end
316 return convertTableToCFrame(tab)
317 end
318 return spring
319 end
320end
321
322--Input scope
323do
324 local self = input
325 --GET/STORE SOME STUFF
326 local uis = game:GetService("UserInputService")
327 local mouse = player:GetMouse()
328 self.currentInput = {}
329 self.deltaX = 0 --FIXME
330 self.deltaY = 0 --FIXME
331
332 --BIND UIS KEYDOWN + KEYUP TO CHANGING THE TABLE
333 uis.InputBegan:connect(function(key,gpe)
334 if gpe then return end
335 self.currentInput[key.KeyCode.Value] = true
336 end)
337 uis.InputEnded:connect(function(key,gpe)
338 if gpe then return end
339 self.currentInput[key.KeyCode.Value] = false
340 end)
341
342 --BIND UIS MOVEMOVE TO CHANGE DELTAX AND DELTAY
343 uis.InputChanged:connect(function(io,gpe)
344 if gpe then return end
345 if io.UserInputType == Enum.UserInputType.MouseMovement then
346 self.deltaX = max(min(io.Delta.X,50),-50)
347 self.deltaY = max(min(io.Delta.Y,50),-50)
348 end
349 end)
350
351 --BIND MOUSE BUTTONS TO CHANGING THE TABLE
352 mouse.Button1Down:connect(function()
353 self.currentInput["Mouse1"] = true
354 end)
355 mouse.Button1Up:connect(function()
356 self.currentInput["Mouse1"] = false
357 end)
358 mouse.Button2Down:connect(function()
359 self.currentInput["Mouse2"] = true
360 end)
361 mouse.Button2Up:connect(function()
362 self.currentInput["Mouse2"] = false
363 end)
364
365 --LOCK THE MOUSE TO THE CENTER AND HIDE IT
366 self.step = function() --FIXME (do this once on spawn)
367 uis.MouseBehavior = Enum.MouseBehavior.LockCenter
368 uis.MouseIconEnabled = false
369 end
370end
371
372--Physics scope
373do
374 local self = physics
375 self.char = {}
376 --CHARACTER PHYSICS SUBSCOPE
377 do
378 local self = self.char
379 self.velocity = nv --THE VELOCITY ADDED ONTO THE CHARACTER EACH PHYSICS UPDATE
380 self.move = cf(37, 31, -175) --THE POSITION OF THE CHARACTER
381 self.grounded = true --IF THE CHARACTER IS ON THE GROUND OR NOT
382 self.lastJump = -5 --THE TIME OF OUR LAST JUMP (NEGATIVE AT THE START)
383 self.walk = 0 --FOR GUN WALK ANIM FORWARDS/BACKWARDS
384 self.side = 0 --FOR GUN WALK ANIM SIDEWAYS
385 self.walkVel = 0
386 self.sideVel = 0
387 self.walk2 = 0
388 self.side2 = 0
389 self.lastWalk = nil
390 self.lastSide = nil
391 self.sideTurn = 0 --FOR GUN SWAY
392 self.rayStep = 1 --STEP FOR FORLOOP WHEN DOING GROUNDED + ABOVE RAYS (LOWER IS MORE RAYS + MORE ACCURACY)
393 self.ignore = {weapons.current,character.arms,weapons.bulletsFolder} --STUFF RAYS SHOULD IGNORE
394 self.collisions = function(dt) --CASTS LOTS OF RAYS TO DETECT COLLISIONS
395 --STORE A VARIABLE FOR DEBUGGING NUMBER OF RAYS
396 local numRays = 0
397
398 --GET ALL VERTICES OF THE PLAYERS HITBOX
399 local pos = self.move * ca(0,camera.getHeading()+rad(180),0)
400 local top = pos * cf(0,1,0)
401 local tr = top * cf(characterHitboxSize,0,0)
402 local trf = tr * cf(0,0,-(characterHitboxSize+weapons.config.hitBoxExtension))
403 local trb = tr * cf(0,0,characterHitboxSize)
404 local tl = top * cf(-characterHitboxSize,0,0)
405 local tlf = tl * cf(0,0,-(characterHitboxSize+weapons.config.hitBoxExtension))
406 local tlb = tl * cf(0,0,characterHitboxSize)
407 local bottom = pos * cf(0,(-characterHeight)+3.8,0)
408 local br = bottom * cf(characterHitboxSize,0,0)
409 local brf = br * cf(0,0,-(characterHitboxSize+weapons.config.hitBoxExtension))
410 local brb = br * cf(0,0,characterHitboxSize)
411 local bl = bottom * cf(-characterHitboxSize,0,0)
412 local blf = tl * cf(0,0,-(characterHitboxSize+weapons.config.hitBoxExtension))
413 local blb = tl * cf(0,0,characterHitboxSize)
414
415 --PUT ALL VERTICIES INTO A TABLE
416 local corners = {trf.p,trb.p,tlf.p,tlb.p,brf.p,brb.p,blf.p,blb.p}
417 --STORE VERTICIES LOCATIONS AS SIMPLE LOCAL SPACE DATA
418 local cornerPositions = {
419 v3(1,1,-1),
420 v3(1,1,1),
421 v3(-1,1,-1),
422 v3(-1,1,1),
423 v3(1,-1,-1),
424 v3(1,-1,1),
425 v3(-1,-1,-1),
426 v3(-1,-1,1),
427 }
428
429 --CREATE AN EMPTY TABLE TO STORE ALL RAYS
430 local rays = {}
431
432 --A FUNCTION TO DO A RAYCAST FROM 2 POSITIONS
433 local function doRay(sP,tP)
434 numRays = numRays + 1 --debug
435 local ray = Rnew(sP,(tP - sP).unit*(tP-sP).magnitude)
436 local h,p = game.Workspace:FindPartOnRayWithIgnoreList(ray,self.ignore)
437 tinsert(rays,{h,p})
438 end
439
440 --FOR EACH VERTICIE OF THE HITBOX
441 for i = 1,#corners do
442 --GET THE CURRENT CORNER
443 local startPos = corners[i]
444 local startPosLocal = cornerPositions[i]
445
446 --FOR EACH CORNER, LOOP THROUGH ALL CORNERS
447 for ii = 1,#corners do
448 --IF THE CURRENT CORNER IS NOT THE NEW CORNER
449 if ii ~= i then
450 --GET THE NEW CORNER
451 local targetPos = corners[ii]
452 local targetPosLocal = cornerPositions[ii]
453
454 --WORK OUT THE NUMBER OF POSITION COMPONENTS WHICH ARE THE SAME
455 local numSame = 0
456 if startPosLocal.X == targetPosLocal.X then numSame = numSame + 1 end
457 if startPosLocal.Y == targetPosLocal.Y then numSame = numSame + 1 end
458 if startPosLocal.Z == targetPosLocal.Z then numSame = numSame + 1 end
459
460 --IF THE CORNER HAS ONLY 1 OR LESS SAME COMPONENTS THEN CAST A RAY
461 if numSame <= 1 then
462 doRay(startPos,targetPos)
463 end
464 end
465 end
466 end
467
468 --FOR EACH RAY WE CAST WORK OUT A BOUNCE DIRECTION
469 for i = 1,#rays do
470 --GET THE HIT AND POSITION FROM THE RAY
471 local h,p,n = rays[i][1],rays[i][2]
472 if h and h.CanCollide == true then
473 --IF THE RAY HIT SOMETHING (OPTIMISATION here maybe (dont add rays to table with no hits))
474 --CALCULATE THE BOUNCE DIRECTION
475 local bounceDir = (p - self.move.p) * 0.005
476 self.move = self.move - v3(bounceDir.X,0,bounceDir.Z) * 0.5
477 self.velocity = self.velocity - v3(bounceDir.X,0,bounceDir.Z)
478 end
479 end
480
481 --CHECK IF THE CHARACTER IS GROUNDED
482 local gRays = {}
483 --LOOP FROM -1 TO 1 OVER RAY STEP
484 for x = -1,1,self.rayStep do
485 --LOOP FROM -1 TO 1 OVER RAY STEP
486 for z = -1,1,self.rayStep do
487 --GENERATE AN OFFSET FROM THE X AND Z FROM THE LOOP
488 local v = v3(x,0,z)
489 --FIND RAY POSITION BASED ON CURRENT POS AND OFFSET
490 local pos = self.move.p + v
491 numRays = numRays + 1 --debug
492 --CAST A RAY DOWN AT THE POSITION
493 local groundRay = Rnew(
494 pos,
495 (pos - (pos + v3(0,-1,0))).unit*-(characterHeight+.1)
496 )
497 --FIND THE RAY HIT POINT AND ADD IT TO THE TABLE
498 local h,p = game.Workspace:FindPartOnRayWithIgnoreList(groundRay,self.ignore)
499 tinsert(gRays,{h,p})
500 end
501 end
502
503 --STORE THE HIGHEST ALTITUDE POINT THE GROUND RAYS HIT
504 local highest
505 local didHit = false
506 --FOR EACH RAY WE CAST
507 for i = 1,#gRays do
508 --GET THE HIT AND POSITION FROM THE RAY
509 local h,p = gRays[i][1],gRays[i][2]
510
511 --IF THE RAY HIT SOMETHING
512 if h and h.CanCollide == true then
513 didHit = true
514 if highest then
515 --IF THIS IS NO THE FIRST RAY
516 --CHECK IF THIS RAY HIT SOMETHING HIGHER UP
517 if p.Y > highest.Y then
518 --SET THE HIGHEST TO THE HIT POSITION
519 highest = p
520 end
521 else
522 --IF THIS IS THE FIRST RAY SET THE HIGHEST TO THE HIT POSITION
523 highest = p
524 end
525 end
526 end
527
528 --IF WE HIT SOMETHING
529 if didHit and highest then
530 --MAKE OUR GROUNDED VARIABLE TRUE
531 self.grounded = true
532
533 --WORK OUT HOW HIGH OFF THE GROUND WE ARE
534 local dis = (self.move.p - highest).magnitude
535 if dis < 4 then
536 --MOVE SO WE ARE ON THE GROUND
537 self.move = self.move + v3(0,(4.8-dis) * 0.2 ,0)
538 end
539 else
540 self.grounded = false
541 end
542
543 --CHECK IF WE HIT OUR HEAD WHILST JUMPING
544 --SAME CODE AS GROUNDED JUST RAYS GO UP
545 local uRays = {}
546 for x = -1,1,self.rayStep do
547 for z = -1,1,self.rayStep do
548 local v = v3(x,1,z)
549 local pos = self.move.p + v
550 numRays = numRays + 1
551 local upRay = Rnew(
552 pos,
553 (pos - (pos + v3(0,1,0))).unit*2.5
554 )
555 local h,p = game.Workspace:FindPartOnRayWithIgnoreList(upRay,self.ignore)
556 tinsert(uRays,{h,p})
557 end
558 end
559 --FOR EACH RAY
560 for i = 1,#uRays do
561 if uRays[i][1] and uRays[i][1].CanCollide == true then
562 --IF THE RAY HIT SOMETHING
563 --SLOW OUR UPWARDS VELOCITY A BIT
564 self.move = cf(self.move.X,self.move.Y-.1*timeSpeed*dt,self.move.Z)
565 self.velocity = v3(self.velocity.X,self.velocity.Y-.08*timeSpeed*dt,self.velocity.Z)
566 end
567 end
568 end
569 self.update = function(dt) --STEPS THE PHYSICS SYSTEM
570 --UPDATE THE RAY IGNORE LIST
571 self.ignore = {weapons.current,character.arms,weapons.bulletsFolder}
572
573 --GET THE CFRAME OF THE CAMERA WITH ONLY ROTATION ON THE Y AXIS
574 local camCFrame = cf(camera.cameraCFrame.p) * ca(0,camera.getHeading()+rad(180),0)
575
576 --GET THE Z AND X DIRECTIONS FROM THE WAY WE ARE LOOKING
577 local forwards = camCFrame.lookVector
578 local sideWays = camCFrame.rightVector
579
580 --THE NUMBER OF BUTTONS CONTROLLING CORRESPONDING DIRECTION BEING PRESSED
581 local x = 0
582 local z = 0
583
584 --GET A MULTIPLIER FOR MOVEMENT SPEED FROM SETTINGS
585 local mult = settings.walkSpeed
586
587 --CHECK INPUT
588 if input.currentInput[304] then
589 --Shift
590 mult = settings.sprintSpeed
591 end
592 if input.currentInput[119] then
593 z = z + 1
594 --W
595 if self.lastWalk == "S" then
596 self.walk2 = 0
597 end
598 self.velocity = self.velocity + forwards * mult
599 self.walk = self.walk + rad(400 * mult * walkAnimSpeed * dt)
600 self.walkVel = self.walkVel + rad(400 * mult * walkAnimSpeed ) * .2
601 self.lastWalk = "W"
602 end
603 if input.currentInput[115] then
604 z = z + 1
605 --S
606 if self.lastWalk == "W" then
607 self.walk2 = 0
608 end
609 self.velocity = self.velocity - forwards * mult
610 self.walk = self.walk - rad(400 * mult * walkAnimSpeed * dt)
611 self.walkVel = self.walkVel - rad(400 * mult * walkAnimSpeed ) * .2
612 self.lastWalk = "S"
613 end
614 if input.currentInput[97] then
615 x = x + 1
616 --A
617 if self.lastSide == "D" then
618 self.side2 = 0
619 end
620 self.velocity = self.velocity - sideWays * mult
621 self.side = self.side + rad(400 * mult * walkAnimSpeed * dt)
622 self.sideVel = self.sideVel + rad(400 * mult * walkAnimSpeed ) * .2
623 self.sideTurn = self.sideTurn - .3
624 self.lastSide = "A"
625 end
626 if input.currentInput[100] then
627 x = x + 1
628 --D
629 if self.lastSide == "A" then
630 self.side2 = 0
631 end
632 self.velocity = self.velocity + sideWays * mult
633 self.side = self.side - rad(400 * mult * walkAnimSpeed * dt)
634 self.sideVel = self.sideVel - rad(400 * mult * walkAnimSpeed ) * .2
635 self.sideTurn = self.sideTurn + .3
636 self.lastSide = "D"
637 end
638 if input.currentInput[32] then
639 --Space
640 if self.grounded and tick() - self.lastJump > .065 then
641 self.lastJump = tick()
642 self.velocity = self.velocity + v3(0,.6 ,0)
643 self.grounded = false
644 end
645 end
646
647 self.walkVel = self.walkVel * .9
648 self.sideVel = self.sideVel * .9
649
650 self.walk2 = self.walk2 + self.walkVel
651 self.side2 = self.side2 + self.sideVel
652
653 --CHECK IF NOT PRESSING KEYS
654 if z == 0 then
655 --IF WE ARE NOT PRESSING W OR S THEN LERP OUR WALK VARIABLE TO A MULTIPLE OF 180
656 self.walk = mathc.lerp(self.walk,mathc.roundToNearest(self.walk,rad(180)),.1 )
657 self.walk2 = mathc.lerp(self.walk2,mathc.roundToNearest(self.walk2,rad(180)),.1 )
658 end
659 if x == 0 then
660 --IF WE ARE NOT PRESSING A OR D THEN LERP OUR SIDE VARIABLE TO A MULTIPLE OF 180
661 self.side = mathc.lerp(self.side,mathc.roundToNearest(self.side,rad(180)),.1 )
662 self.side2 = mathc.lerp(self.side2,mathc.roundToNearest(self.side2,rad(180)),.1 )
663 end
664
665 --IF WE OUR GROUNDED STOP Y MOVEMENT OTHERWISE FALL
666 if self.grounded then
667 self.velocity = v3(self.velocity.X,0,self.velocity.Z)
668 else
669 self.velocity = self.velocity + gravity
670 end
671
672 --COUNTER MOVING QUICKLY DIAGONALLY
673 local mult = 1
674 if x >= 1 and z >=1 then
675 mult = diagonalCounter
676 end
677
678 --APPLY DRAG TO SIDETURN VARIABLE (used in anims)
679 self.sideTurn = self.sideTurn * .85
680
681 --APPLY DRAG TO VELOCITY
682 self.velocity = v3(
683 (self.velocity.X * 0.9)*mult,
684 self.velocity.Y * 0.99,
685 (self.velocity.Z * 0.9)*mult
686 )
687
688 --WORK OUT THE NEW POSITION
689 local newPos = self.move * cf(self.velocity*timeSpeed*dt)
690 --COUNTER BOUNCING ON SLOPES
691 local rayDown1 = Rnew(
692 self.move.p,
693 ((self.move.p + v3(0,-1,0)) - self.move.p).unit * (characterHeight + (self.velocity.magnitude/1.35))
694 )
695 local rayDown2 = Rnew(
696 newPos.p,
697 ((newPos.p + v3(0,-1,0)) - newPos.p).unit * (characterHeight + (self.velocity.magnitude/1.35))
698 )
699 local dh1,dp1 = game.Workspace:FindPartOnRayWithIgnoreList(rayDown1,self.ignore)
700 local dh2,dp2 = game.Workspace:FindPartOnRayWithIgnoreList(rayDown2,self.ignore)
701 local down = cf(0,0,0)
702 if dh1 and dh2 then
703 down = cf(0,max(min(dp2.Y-dp1.Y,.28),-.28),0)
704 end
705 --MOVE TO CALCULATED POSITION
706 self.move = newPos * down
707 --CALCULATE COLLISIONS
708 self.collisions(dt)
709 end
710 end
711
712 --CREATE A NEW BULLET (CUSTOM PHYSICS)
713 self.createBullet = function(start,speed,drag)
714 --CREATE A TABLE TO STORE IT
715 local bullet = {}
716 --ASSIGN BULLET SOME VARIABLES
717 bullet.pos = start
718 bullet.speed = speed
719 bullet.drag = drag
720 bullet.forward = -1 * bullet.speed
721 bullet.drop = 0
722 bullet.downVelocity = 0
723 --CREATE A FUNCTION FOR MOVING THE BULLET
724 bullet.update = function(dt)
725 --CALCULATE NEW FORWARD + DROP FOR THE BULLET
726 bullet.downVelocity = (bullet.downVelocity * bullet.drag ) + 0.002/bullet.speed
727 bullet.forward = bullet.forward * bullet.drag
728 bullet.drop = bullet.drop - bullet.downVelocity
729 --WORK OUT POSITIONS BASED ON NEW DATA
730 local newPos = bullet.pos * cf(0,0,bullet.forward*timeSpeed*dt) + v3(0,bullet.drop*timeSpeed*dt,0)
731 local oldPos = bullet.pos
732 --CAST A RAY TO CHECK FOR COLLISIONS
733 local distance = (newPos.p - oldPos.p).magnitude
734 local r = ray(oldPos.p,(newPos.p - oldPos.p).unit * distance)
735 local hit,pos,nor = game.Workspace:FindPartOnRayWithIgnoreList(r,self.char.ignore)
736 --UPDATE THE BULLETS POSITION
737 bullet.pos = newPos
738 --RETURN NEW BULLET DATA
739 return newPos,oldPos,hit,pos,nor,distance
740 end
741 return bullet
742 end
743end
744
745--Triangle scope
746do
747 local self = triangle
748 self.setPosition = function(a,b,c,t) --A FUNCTION TO SET SIZE AND POSITION OF TRIANGLE
749 --CALCULATE SIZE AND POSITION OF THE TRIANGLE
750 if not a or not b or not c or not t then return end
751 local w0 = t[1]
752 local w1 = t[2]
753 local edges = {
754 {longest = (c - b), other = (a - b), position = b},
755 {longest = (a - c), other = (b - c), position = c},
756 {longest = (b - a), other = (c - a), position = a},
757 }
758 tsort(edges, function(a, b) return a.longest.magnitude > b.longest.magnitude end)
759 local edge = edges[1]
760 local theta = acos(edge.longest.unit:Dot(edge.other.unit))
761 local s1 = v2(edge.other.magnitude * math.cos(theta), edge.other.magnitude * sin(theta))
762 local s2 = v2(edge.longest.magnitude - s1.x, s1.y)
763 local p1 = edge.position + edge.other * 0.5
764 local p2 = edge.position + edge.longest + (edge.other - edge.longest) * 0.5
765 local right = edge.longest:Cross(edge.other).unit
766 local up = right:Cross(edge.longest).unit
767 local back = edge.longest.unit
768 local cf1 = cf(
769 p1.x, p1.y, p1.z,
770 -right.x, up.x, back.x,
771 -right.y, up.y, back.y,
772 -right.z, up.z, back.z
773 )
774 local cf2 = cf(
775 p2.x, p2.y, p2.z,
776 right.x, up.x, -back.x,
777 right.y, up.y, -back.y,
778 right.z, up.z, -back.z
779 )
780 --CALCULATE MESH SCALES FOR W0
781 local meshY = 1
782 local meshZ = 1
783 if s1.y < 0.2 then
784 meshY = s1.y/0.2
785 end
786 if s1.x < 0.2 then
787 meshZ = s1.x/0.2
788 end
789 w0.Mesh.Scale = v3(w0.Mesh.Scale,meshY,meshZ)
790
791 --CALCULATE MESH SCALES FOR W1
792 meshY = 1
793 meshZ = 1
794 if s2.y < 0.2 then
795 meshY = s2.y/0.2
796 end
797 if s2.x < 0.2 then
798 meshZ = s2.x/0.2
799 end
800 w1.Mesh.Scale = v3(w1.Mesh.Scale,meshY,meshZ)
801
802 --SET SIZE AND CFRAME OF ALL PARTS
803 w0.Size = v3(0.2, s1.y, s1.x)
804 w1.Size = v3(0.2, s2.y, s2.x)
805 w0.CFrame = cf1
806 w1.CFrame = cf2
807 end
808 self.create = function(p,bc) --CREATE A TRIANGLE (2 wedge parts)
809 local w0 = Inew("WedgePart",p)
810 w0.Anchored = true
811 w0.TopSurface = "Smooth"
812 w0.BottomSurface = "Smooth"
813 w0.BrickColor = bc
814 w0.Material = "SmoothPlastic"
815 local sm = Inew("SpecialMesh",w0)
816 sm.MeshType = "Wedge"
817 sm.Scale = v3(.001,1,1)
818 local w1 = Inew("WedgePart",p)
819 w1.Anchored = true
820 w1.TopSurface = "Smooth"
821 w1.BottomSurface = "Smooth"
822 w1.BrickColor = bc
823 w1.Material = "SmoothPlastic"
824 sm = Inew("SpecialMesh",w1)
825 sm.MeshType = "Wedge"
826 sm.Scale = v3(.001,1,1)
827 return {w0,w1} --RETURN TRIANGLE AS A TABLE
828 end
829end
830
831--Character scope
832do
833 local self = character
834 --INIT SOME VARIABLES FOR THE CHARACTER (model)
835 self.hands = nil
836 self.arms = nil
837 self.armData = {}
838 self.armThickness = .6
839 self.armLength = 2.7
840 self.triangles = nil
841 self.doTriangles = true
842 self.createArms = function() --A FUNCTION TO CREATE THE 4 PARTS FOR ARMS (optimisation (make it not ik))
843 --CREATE A NEW MODEL AND 2 EMPTY VARIABLES
844 local m = Inew("Model",game.Workspace)
845 local ra
846 local la
847 do --Right
848 --CREATE ALL PARTS FOR RIGHT ARM
849 local upper = Inew("Part",m)
850 upper.Size = v3(self.armThickness,self.armThickness,self.armLength)
851 upper.Anchored = true
852 upper.TopSurface = "Smooth"
853 upper.BottomSurface = "Smooth"
854 upper.Material = "SmoothPlastic"
855 upper.BrickColor = BrickColor.new("Cool yellow")
856 upper.Name = "UR"
857 local lower = Inew("Part",m)
858 lower.Size = v3(self.armThickness,self.armThickness,self.armLength)
859 lower.Anchored = true
860 lower.TopSurface = "Smooth"
861 lower.BottomSurface = "Smooth"
862 lower.Material = "SmoothPlastic"
863 upper.Name = "LR"
864 lower.BrickColor = BrickColor.new("Cool yellow")
865 --STORE NEW PARTS IN A TABLE AND CREATE A NEW IK CHAIN
866 ra = {upper,lower,ik.newChain(self.armLength,self.armLength)}
867 end
868 do --Left
869 --CREATE ALL PARTS FOR LEFT ARM
870 local upper = Inew("Part",m)
871 upper.Size = v3(self.armThickness,self.armThickness,self.armLength)
872 upper.Anchored = true
873 upper.TopSurface = "Smooth"
874 upper.BottomSurface = "Smooth"
875 upper.Material = "SmoothPlastic"
876 upper.BrickColor = BrickColor.new("Cool yellow")
877 upper.Name = "UL"
878 local lower = Inew("Part",m)
879 lower.Size = v3(self.armThickness,self.armThickness,self.armLength)
880 lower.Anchored = true
881 lower.TopSurface = "Smooth"
882 lower.BottomSurface = "Smooth"
883 lower.Material = "SmoothPlastic"
884 upper.Name = "LL"
885 lower.BrickColor = BrickColor.new("Cool yellow")
886 --STORE NEW PARTS IN A TABLE AND CREATE A NEW IK CHAIN
887 la = {upper,lower,ik.newChain(self.armLength,self.armLength)}
888 end
889 --SET PREINIT VARIABLES TO THE NEW MODEL/TABLE
890 self.arms = m
891 self.armData = {ra,la}
892 end
893 self.createHands = function() --A FUNCTION TO CREATE 2 HAND PARTS
894 local m = self.arms
895 local right = Inew("Part",m)
896 right.Size = v3(self.armThickness,self.armThickness,1)
897 right.Anchored = true
898 right.TopSurface = "Smooth"
899 right.BottomSurface = "Smooth"
900 right.Material = "SmoothPlastic"
901 right.BrickColor = BrickColor.new("Cool yellow")
902 right.Name = "RH"
903 local left = Inew("Part",m)
904 left.Size = v3(self.armThickness,self.armThickness,1)
905 left.Anchored = true
906 left.TopSurface = "Smooth"
907 left.BottomSurface = "Smooth"
908 left.Material = "SmoothPlastic"
909 left.Name = "LH"
910 left.BrickColor = BrickColor.new("Cool yellow")
911 self.hands = {right,left}
912 end
913 self.createTriangles = function() --A FUNCTION THAT CREATES ALL TRIANGLES FOR THE WRIST JOINT
914 --CREATE LOTS OF TABLES
915 local m = self.arms
916 local lt = {}
917 local rt = {}
918 local lfront = {}
919 local lback = {}
920 local rfront = {}
921 local rback = {}
922 local bc = BrickColor.new("Cool yellow")
923 --MAKE A NEW THREAD SO CODE CAN CONTAIN WAITS
924 spawn(function()
925 local rs = game:GetService("RunService")
926 --CREATE 2 TRIANGLES THEN WAIT 1 FRAME
927 for i = 1,4 do
928 tinsert(lfront,triangle.create(m,bc))
929 tinsert(rfront,triangle.create(m,bc))
930 rs.Stepped:wait()
931 end
932 --CREATE 2 TRIANGLES THEN WAIT 1 FRAME
933 for i = 1,4 do
934 tinsert(lback,triangle.create(m,bc))
935 tinsert(rback,triangle.create(m,bc))
936 rs.Stepped:wait()
937 end
938 lt = {lfront,lback}
939 rt = {rfront,rback}
940 self.triangles = {lt,rt}
941 end)
942 end
943 self.updateTriangles = function(p0,p1,leftOrRight) --A FUNCTION TO UPDATE TRIANGLES ON THE LEFT WRIST
944 --GET TRIANGLES
945 local tris = self.triangles
946
947 --GET 4 VERTICIES FROM P0
948 local c0 = p0.CFrame
949 local back0 = c0 * cf(0,0,p0.Size.Z/2)
950 local up0 = cf(0,p0.Size.Y/2,0)
951 local down0 = cf(0,-p0.Size.Y/2,0)
952 local left0 = cf(-p0.Size.X/2,0,0)
953 local right0 = cf(p0.Size.X/2,0,0)
954 --PUT NEW VERTICIES IN A TABLE
955 local v0 = {
956 back0 * up0 * right0,
957 back0 * down0 * right0,
958 back0 * down0 * left0,
959 back0 * up0 * left0,
960 }
961
962 --GET 4 VERTICIES FROM P1
963 local c1 = p1.CFrame
964 local front1 = c1 * cf(0,0,-p1.Size.Z/2)
965 local up1 = cf(0,p1.Size.Y/2,0)
966 local down1 = cf(0,-p1.Size.Y/2,0)
967 local left1 = cf(-p1.Size.X/2,0,0)
968 local right1 = cf(p1.Size.X/2,0,0)
969 --PUT NEW VERTICIES IN A TABLE
970 local v1 = {
971 front1 * up1 * right1,
972 front1 * down1 * right1,
973 front1 * down1 * left1,
974 front1 * up1 * left1,
975 }
976
977 --PAIR VERTICES FROM V0 and V1 together (optimisation skip this step?)
978 local vPairs = {}
979 for i = 1,#v0 do
980 tinsert(vPairs,{v0[i].p,v1[i].p})
981 end
982
983 --POSITION TRIANGLES
984 local t = tris[leftOrRight]
985 local front = t[1]
986 local back = t[2]
987 --FOR EACH OF THE 4 TRIANGLES WITH 2 VERTICIES ON V0
988 for i = 1,#front do
989 local pair = vPairs[i]
990 local wI = mathc.wrapValue(i+1,#v0)
991 local a = pair[1]
992 local b = pair[2]
993 local c = v1[wI].p
994 triangle.setPosition(a,b,c,front[i])
995 end
996 --FOR EACH OF THE 4 TRIANGLES WITH 2 VERTICIES ON V1
997 for i = 1,#back do
998 local fI = (#back - i)+1
999 local pair = vPairs[fI]
1000 local wI = mathc.wrapValue(fI-1,#v1)
1001 local a = pair[1]
1002 local b = pair[2]
1003 local c = v0[wI].p
1004 triangle.setPosition(a,b,c,back[fI])
1005 end
1006 end
1007 self.createArms() --FIXME (run this on spawn)
1008 self.createHands()
1009 if self.doTriangles then
1010 self.createTriangles()
1011 end
1012 self.step = function()
1013 --Update arms
1014 if weapons.current then
1015 local rh = self.hands[1]
1016 local lh = self.hands[2]
1017 local ra = self.armData[1]
1018 local la = self.armData[2]
1019 local ras = ra[3]
1020 local las = la[3]
1021 --MOVE ARMS
1022 local charPos = physics.char.move * ca(0,camera.getHeading(),0)
1023 ras.start = (charPos * cf(-1,-2,-.8)).p
1024 las.start = (charPos * cf(1,-2,-.8)).p
1025 ras.goal = weapons.gunData[5].CFrame.p
1026 las.goal = weapons.gunData[4].CFrame.p
1027 las.elbowRot = rad(155)
1028 ras.elbowRot = rad(-155)
1029 local rc0,rc1 = ras.update()
1030 local lc0,lc1 = las.update()
1031 ra[1].CFrame = rc0
1032 ra[2].CFrame = rc1
1033 la[1].CFrame = lc0
1034 la[2].CFrame = lc1
1035 --MOVE HANDS
1036 local rhS = (rc1 * cf(0,0,-self.armLength/2)).p
1037 local rhT = weapons.gunData[9].CFrame.p
1038 local rhD = (rhS - rhT).magnitude
1039
1040 local lhS = (lc1 * cf(0,0,-self.armLength/2)).p
1041 local lhT = weapons.gunData[8].CFrame.p
1042 local lhD = (lhS - lhT).magnitude
1043
1044 local roff = weapons.config.rHandOffset or nc
1045 local loff = weapons.config.lHandOffset or nc
1046
1047 rh.Size = v3(rh.Size.X,rh.Size.Y,rhD-.25)
1048 rh.CFrame = cf(rhS,rhT) * cf(0,0,-rhD/2) * roff
1049
1050 lh.Size = v3(lh.Size.X,lh.Size.Y,lhD-.25)
1051 lh.CFrame = cf(lhS,lhT) * cf(0,0,-lhD/2) * loff
1052
1053 --MOVE WRISTS
1054 if self.triangles and self.doTriangles then
1055 self.updateTriangles(lh,la[2],1)
1056 self.updateTriangles(rh,ra[2],2)
1057 end
1058 end
1059 end
1060end
1061
1062--Camera scope
1063do
1064 local self = camera
1065 --GET THE CURRENT CAMERA
1066 self.current = game.Workspace.CurrentCamera
1067 --CREATE SOME VARIABLES
1068 self.cameraCFrame = nc
1069 local rotX = 0
1070 local rotY = 0
1071 local rXVel = 0
1072 local rYVel = 0
1073 local rX = 0
1074 local rY = 0
1075 local breath = 0
1076 self.rotX = 0
1077 self.rotY = 0
1078 self.step = function(dt)
1079 --BREATHING ANIMATION
1080 breath = breath + (rad(2.75) * dt)
1081 self.kick(
1082 (sin(breath)/3)*.00065,
1083 (sin(breath/2)/2.8)*.00065
1084 )
1085 --CAMERA BOB
1086 local mult = 1
1087 if input.currentInput["304"] then mult = 2 end
1088 local val = physics.char.walk
1089 if not input.currentInput[119] and not input.currentInput[115] then
1090 val = physics.char.side
1091 end
1092 self.kick(
1093 (sin(val*timeSpeed*2)/3)*mult*.002,
1094 (sin(val*timeSpeed)/2.8)*mult*.002
1095 )
1096 --MAIN CAMERA MODE (LOOKING AROUND)
1097 rotX = rotX - rad(input.deltaY) * timeSpeed * dt
1098 rotY = rotY - rad(input.deltaX) * timeSpeed * dt
1099 rXVel = rXVel * .95
1100 rYVel = rYVel * .95
1101 rX = rX + rXVel
1102 rY = rY + rYVel
1103 if rotX > rad(360) then rotX = rad(360) end
1104 if rotX < -rad(280) then rotX = -rad(280) end
1105 self.cameraCFrame = physics.char.move
1106 * ca(0,rotY*settings.camSensitivity,0)
1107 * ca(rotX*settings.camSensitivity,0,0)
1108 * ca(rX,rY,0)
1109 rX = rX * .7
1110 rY = rY * .7
1111 self.current.CFrame = self.cameraCFrame
1112 self.current.CameraType = Enum.CameraType.Scriptable --FIXME
1113 self.rotX = rotX
1114 self.rotY = rotY
1115 end
1116 self.kick = function(xRot,yRot) --ADD A FORCE ONTO CAMERA ROTATION
1117 if not xRot then xRot = 0 end
1118 if not yRot then yRot = 0 end
1119 rXVel = rXVel + xRot
1120 rYVel = rYVel + yRot
1121 end
1122 self.centerRay = function(jump) --CAST A RAY IN THE DIRECTION OF THE CAMERA
1123 if not jump then jump = 0 end
1124 local r = Rnew((self.cameraCFrame * cf(0,0,-jump)).p,self.cameraCFrame.lookVector * 5000)
1125 local h,p,n = game.Workspace:FindPartOnRayWithIgnoreList(r,physics.char.ignore)
1126 return h,p,n
1127 end
1128 self.getHeading = function() --GETS THE Y ROTATION OF THE CAMERA USING ATAN2
1129 local direction = self.cameraCFrame.lookVector
1130 local heading = atan2(direction.X,direction.Z)
1131 return heading
1132 end
1133end
1134
1135--Weapons scope
1136do
1137 local self = weapons
1138 --WAIT FOR SOME STUFF
1139 local guns = game.ReplicatedStorage:WaitForChild("Guns")
1140 local pistols = guns:WaitForChild("Pistols")
1141 local assaultRifles = guns:WaitForChild("Assault Rifles")
1142 local assaultRifle = assaultRifles:WaitForChild("Assault Rifle")
1143 local pistol = pistols:WaitForChild("Pistol") --FIXME
1144 self.bulletsFolder = game.Workspace:WaitForChild("Bullets")
1145 --CFREATE LOTS OF VARIABLES
1146 self.bullets = {}
1147 self.primary = assaultRifle
1148 self.secondary = pistol
1149 self.equipped = 1
1150 self.current = nil
1151 self.config = nil
1152 self.gunLoaded = false
1153 self.gunSpring = nil
1154 self.gunData = {}
1155 self.recoilX = 0
1156 self.recoilY = 0
1157 self.recoil = 0
1158 self.lastShot = nil
1159 self.aim = 0
1160
1161 local ding = sound.loadIntoMemory(241852639)--FIXME TEMPROARY
1162
1163 function getTarget(dt) --GET THE TARGET FOR THE GUN SPRING
1164 --LOAD OFFSETS FROM THE GUNS CONFIG FILE
1165 local posOffset = self.config.holdOffset
1166 local aimOffset = self.config.aimOffset
1167
1168 --GENERATE ROTATION OFFSET
1169 local rotOffset = ca(input.deltaY*0.005,input.deltaX*0.005,input.deltaX*0.005)
1170 * ca(-physics.char.velocity.Y,physics.char.sideTurn/11,physics.char.sideTurn/16)
1171
1172 --GET A TARGET FOR THE GUN TO FACE FROM THE CAMERA
1173 local _,tgt = camera.centerRay(5)
1174
1175 --CALCULATE HOW FAR BACK TO MOVE THE GUN SO IT DOESNT GO INTO WALLS
1176 local gunBack = 0
1177 if self.gunData[3] then
1178 local gRay = Rnew((self.gunData[3].CFrame * cf(0,0,1)).p,self.gunData[3].CFrame.lookVector * 5000)
1179 local _,gTGT = game.Workspace:FindPartOnRayWithIgnoreList(gRay,physics.char.ignore)
1180 gunBack = min((gTGT-camera.cameraCFrame.p).magnitude-4,0)/-2
1181 end
1182
1183 --LOAD DEFAULTS VARIABLES
1184 local walkAnimSize = settings.walkAnimSize
1185 local mult = 2
1186
1187 --CHANGE DEFAULTS UNDER SPECIFIC CIRCUMASTANCES
1188 if input.currentInput["Mouse2"] then
1189 walkAnimSize = walkAnimSize * .25 mult = 1
1190 --ALSO CHANGE THE CROSSHAIR ANIM THINGY
1191 self.aim = mathc.lerp(self.aim,50,.2)
1192 else
1193 self.aim = mathc.lerp(self.aim,0,.2)
1194 end
1195
1196 --GENERATE WALK ANIM CFRAME (FIXME (check if we need to))
1197 local walk = physics.char.walk * timeSpeed-- * dt * 50
1198 local side = physics.char.side * timeSpeed-- * dt * 50
1199 local walkAnim = cf(
1200 ( sin(walk ) / 4 ) * walkAnimSize,
1201 ( cos(walk*2) / 8 ) * walkAnimSize,
1202 (-cos(walk*2) / 9 ) * walkAnimSize
1203 )
1204 * ca(
1205 ( sin(walk/2) / 6 ) * walkAnimSize,
1206 (-sin(walk ) / 6 ) * walkAnimSize,
1207 (-sin(walk ) / 18 )* walkAnimSize
1208 )
1209 --GENERATE SIDE ANIM CFRAME (FIXME (check if we need to))
1210 local sideAnim = cf(
1211 ( sin(side ) / 5 ) * walkAnimSize,
1212 ( cos(side*2) / 6 ) * walkAnimSize,
1213 (-cos(side*2) / 8 ) * walkAnimSize
1214 )
1215 * ca(
1216 ( sin(side/2) / 8 ) *walkAnimSize,
1217 (-sin(side ) / 4 ) *walkAnimSize,
1218 (-sin(side ) / 10 )*walkAnimSize
1219 )
1220
1221 --GENERATE A RECOIL CFRAME
1222 local recoil = cf(0,0,self.recoil)
1223 * ca(rad(self.recoilX)*mult,rad(self.recoilY)*mult,0)
1224
1225 --IF WE ARE NOT MOVING IN A DIRECTION THEN REMOVE THAT ANIM
1226 if not input.currentInput[119] and not input.currentInput[115] then
1227 walkAnim = nc
1228 else
1229 sideAnim = nc
1230 end
1231
1232 --IF WE ARE AIMING (FIXME only have this code once)
1233 if input.currentInput["Mouse2"] then
1234 --GENERATE CFRAME FOR THE GUN USING AIM OFFSET
1235 local pos = (camera.cameraCFrame * aimOffset * cf(0,0,gunBack/4)).p
1236 local pointed = cf(pos,tgt) * walkAnim * sideAnim
1237 return pointed * rotOffset * recoil
1238 else
1239 --GENERATE CFRAME FOR THE GUN USING POS OFFSET
1240 local pos = (camera.cameraCFrame * posOffset * cf(0,0,gunBack)).p
1241 local pointed = cf(pos,tgt) * walkAnim * sideAnim
1242 return pointed * rotOffset * recoil
1243 end
1244 end
1245
1246 function equip(weaponType) --EQUIP A GUN (1 = PRIMARY, 2 = SECONDARY)
1247 --RESET SOME VARIABLES
1248 if self.current then
1249 self.current:Destroy()
1250 end
1251 self.gunLoaded = false
1252 self.config = nil
1253 self.gunData = {}
1254
1255 local gun
1256 --LOAD THE GUN MODEL
1257 if weaponType == 1 then
1258 gun = self.primary
1259 elseif weaponType == 2 then
1260 gun = self.secondary
1261 end
1262
1263 --CLONE THE GUN MODEL AND PUT IT IN THE WORKSPACE
1264 self.current = gun:clone()
1265 self.current.Parent = game.Workspace
1266
1267 --LOAD THE GUNS CONFIG
1268 self.config = require(self.current:WaitForChild("Config"))
1269
1270 --LOAD GUN DATA
1271 self.gunData[1] = sound.loadIntoMemory(self.config.shootSound)
1272 local dis = self.current:FindFirstChild("Discharger")
1273 if dis then
1274 self.gunData[2] = require(dis)
1275 else
1276 self.gunData[2] = "NoDischarger"
1277 end
1278 self.gunData[3] = self.current:WaitForChild("FirePos")
1279 self.gunData[4] = self.current:WaitForChild("LeftArm")
1280 self.gunData[5] = self.current:WaitForChild("RightArm")
1281 self.gunData[6] = self.gunData[3]:WaitForChild("SpotLight") --FIXME (gun might not have light)
1282 self.gunData[7] = sound.loadIntoMemory(156286438)
1283 self.gunData[8] = self.current:WaitForChild("LeftHand")
1284 self.gunData[9] = self.current:WaitForChild("RightHand")
1285
1286 self.current:SetPrimaryPartCFrame(getTarget(1))
1287
1288 self.gunSpring = springs.createCSpring(self.current.PrimaryPart.CFrame,getTarget(1),0.18,0.42,.75)
1289
1290 --TELL THE REST OF THE CODE THE GUN HAS LOADED
1291 self.gunLoaded = true
1292 end
1293
1294 equip(self.equipped) --FIXME (do this on spawn)
1295
1296 self.step = function(dt)
1297 --IF WE HAVE A GUN EQUIPPED
1298 if self.gunLoaded then
1299 if input.currentInput[113] then
1300 input.currentInput[113] = false
1301 self.equipped = mathc.wrapValue(self.equipped + 1,2)
1302 equip(self.equipped)
1303 end
1304
1305 --UPDATE THE GUN SPRING
1306 self.gunSpring.setTarget(getTarget(dt))
1307 local newCF = self.gunSpring.update(dt)
1308
1309 --SLIGHTLY LERP THE SPRING TO MAKE IT LESS SPRINGY
1310 self.gunSpring.multiplySpeeds(0.95)
1311 self.current:SetPrimaryPartCFrame(newCF:lerp(getTarget(dt),0.2))
1312
1313 --IF WE ARE PRESSING MOUSE1 (SHOOT)
1314 if input.currentInput["Mouse1"] then
1315 local function fire() --FIRES THE GUN
1316 --SET THE COOLDOWN TIMER TO THE CURRENT TIME
1317 self.lastShot = tick()
1318
1319 --IF WE ARE SEMI AUTOMATIC, AUTO RELEASE THE MOUSE BUTTON
1320 if self.config.gunType == "SemiAutomatic" then
1321 input.currentInput["Mouse1"] = false
1322 end
1323
1324 --SET A DEFAULT VALUE FOR A MULTIPLIER
1325 local mult = .1 --FIXME (load from config)
1326 if input.currentInput["Mouse2"] then
1327 --IF WE ARE AIMING, SET THE MULTIPLIER LOWER
1328 mult = .05 --FIXME (load from config)
1329 end
1330
1331 --GENERATE AND SET THE RECOIL VALUE
1332 self.recoil = math.min(self.recoil+self.config.recoilBack,1)
1333
1334 --GENERATE RANDOM RECOIL OFFSETS
1335 local xAdd = random(
1336 -self.config.recoilXMax*25,
1337 self.config.recoilXMax*25
1338 )/25
1339 local yAdd = random(
1340 -self.config.recoilYMax*25,
1341 self.config.recoilYMax*25
1342 )/25
1343
1344 --APPLY OFFSETS TO THE GUNS RECOIL
1345 self.recoilX = self.recoilX + xAdd
1346 self.recoilY = self.recoilY + yAdd
1347
1348 --APPLY RECOIL TO THE CAMERA
1349 camera.kick(rad(xAdd)/12,rad(yAdd)/12)
1350
1351 --CREATE A NEW BULLET
1352 local bullet = physics.createBullet(
1353 self.gunData[3].CFrame * ca(
1354 rad(self.recoilX)*mult,
1355 rad(self.recoilY)*mult,
1356 0
1357 ),
1358 7.25,
1359 0.99999
1360 )
1361
1362 --CREATE A PART TO VISUALISE THE BULLET
1363 local part = Inew("Part",self.bulletsFolder)
1364 part.Anchored = true
1365 part.Name = "0"
1366 part.Material = "Neon"
1367 part.BrickColor = BrickColor.White()
1368
1369 --INSERT THE BULLET AND THE PART INTO THE BULLETS TABLE
1370 tinsert(self.bullets,{bullet,part})
1371
1372 --TRIGGER THE DISCHARGER ANIMATION
1373 if self.gunData[2] ~= "NoDischarger" then
1374 self.gunData[2].fire()
1375 end
1376
1377 --PLAY THE FIRE SOUND
1378 sound.playCloneSound(self.gunData[1])
1379 end
1380
1381 if self.lastShot and tick() - self.lastShot > self.config.shotCooldown then
1382 --CHECK IF WE CAN SHOOT
1383 fire()
1384 elseif not self.lastShot then
1385 --IF THERE IS NOTHING TO CHECK AGAINST, SHOOT ANYWAY
1386 fire()
1387 end
1388 end
1389
1390 --APPLY 'DRAG' TO THE GUNS RECOIL
1391 self.recoilX = self.recoilX * .5
1392 self.recoilY = self.recoilY * .5
1393 self.recoil = self.recoil * .8
1394
1395 --IF WE PRESS THE F KEY
1396 if input.currentInput[102] then
1397 --TOGGLE THE LIGHT --FIXME (MAKE THE LIGHT AN ATTACHMENT ?)
1398 input.currentInput[102] = false
1399 self.gunData[6].Enabled = not self.gunData[6].Enabled
1400 sound.playCloneSound(self.gunData[7])
1401 end
1402
1403 --STEP THE DISCHARGER ANIMATION
1404 if self.gunData[2] ~= "NoDischarger" then
1405 self.gunData[2].update(dt)
1406 end
1407 end
1408
1409 --FOR EACH BULLET WHICH IS CURRENTLY IN THE AIR
1410 for i = 1,#self.bullets do
1411 --CHECK THE BULLET EXISTS
1412 if self.bullets[i] then
1413 --UPDATE THE BULLET
1414 local bullet = self.bullets[i][1]
1415 local part = self.bullets[i][2]
1416 local newPos,oldPos,hit,pos,normal,distance = bullet.update(dt)
1417
1418 --IF THE BULLET HIT SOMETHING
1419 if hit and hit.CanCollide == true then --FIXME (other players might be uncancollide)
1420 ---------TEMPORARY------------
1421 if hit.Name == "tgt" then
1422 spawn(function()
1423 sound.playCloneSound(ding)
1424 hit.Material = "Neon"
1425 wait(.25)
1426 hit.Material = "Plastic"
1427 end)
1428 end
1429 ---------TEMPORARY------------
1430
1431 --REMOVE THE PART AND THE BULLET
1432 part:Destroy()
1433 tremove(self.bullets,i)
1434
1435 elseif newPos.Y < -500 then
1436 --IF THE PART FELL OFF THE MAP
1437 --REMOVE THE PART AND THE BULLET
1438 part:Destroy()
1439 tremove(self.bullets,i)
1440 else
1441 if oldPos then
1442 --IF WE DIDN'T HIT ANYTHING
1443 --UPDATE THE BULLETS PART
1444 part.Size = v3(.2,.2,distance)
1445 part.CFrame = cf(oldPos.p,newPos.p) * cf(0,0,-distance/2)
1446 part.Name = tostring(tonumber(part.Name)+.25)
1447 part.Transparency = 1 - tonumber(part.Name)
1448 --local p = part:clone()
1449 --p.Parent = part.Parent
1450 end
1451 end
1452 end
1453 end
1454 end
1455end
1456
1457--Replication scope
1458do
1459 local self = replication
1460
1461 self.charData = {}
1462 self.characters = {}
1463 self.playerData = {}
1464
1465 local remotes = game.ReplicatedStorage:WaitForChild("Remotes")
1466 local transceiver = remotes:WaitForChild("SendPlayerData")
1467 local mainEvent = remotes:WaitForChild("MainEvent")
1468 --local send = remotes:WaitForChild("GetPlayerData")
1469
1470 self.GetDataToSendServer = function()
1471 local data = {
1472 physics.char.move * ca(0,camera.getHeading(),0),
1473 }
1474 return data
1475 end
1476
1477 mainEvent.OnClientEvent:connect(function(data)
1478 if data[1] == "PlayerLeft" then
1479 local leavingPlayerName = data[2]
1480 self.killCharacter(leavingPlayerName)
1481 end
1482 end)
1483
1484 transceiver.OnClientEvent:connect(function(newData)
1485 local didChange = false
1486 for i = 1,#self.playerData do
1487 if self.playerData[i][1] == newData[1] then
1488 self.playerData[i] = newData
1489 didChange = true
1490 break
1491 end
1492 end
1493 if not didChange then
1494 tinsert(self.playerData,newData)
1495 end
1496 end)
1497 --[[
1498 function send.OnClientInvoke()
1499 return self.GetDataToSendServer()
1500 end
1501 ]]--
1502
1503 self.replicateStep = function()
1504 transceiver:FireServer(self.GetDataToSendServer())
1505 end
1506
1507 self.killCharacter = function(playerName)
1508 for i = 1,#self.playerData do
1509 if self.playerData[i][1] == playerName then
1510 tremove(self.playerData,i)
1511 break
1512 end
1513 end
1514 local char = self.charData[playerName]
1515 if char then
1516 char.destroy()
1517 self.charData[playerName] = nil
1518 end
1519 end
1520 self.createCharacter = function()
1521 local char = {}
1522 --char.moveSpring = springs.createCSpring(nc,nc,0.18,0.425,.775)
1523
1524 char.oldPos = nc
1525
1526 char.walk = 0
1527 char.side = 0
1528
1529 char.velocity = nv
1530
1531 char.model = Inew("Model",game.Workspace.Bullets)
1532 char.parts = {}
1533 local bc = BrickColor.new("Cool yellow")
1534 local torso = Inew("Part",char.model)
1535 torso.Size = v3(2,2,1)
1536 torso.Anchored = true
1537 torso.BrickColor = bc
1538 --torso.Transparency = .5
1539 local lLeg = Inew("Part",char.model)
1540 lLeg.Name = "Left Leg"
1541 lLeg.Size = v3(1,2,1)
1542 lLeg.Anchored = true
1543 lLeg.BrickColor = bc
1544 local rLeg = Inew("Part",char.model)
1545 rLeg.Name = "Right Leg"
1546 rLeg.Size = v3(1,2,1)
1547 rLeg.Anchored = true
1548 rLeg.BrickColor = bc
1549
1550
1551 tinsert(char.parts,torso)
1552 tinsert(char.parts,lLeg)
1553 tinsert(char.parts,rLeg)
1554
1555 char.destroy = function()
1556 char.model:Destroy()
1557 end
1558 Remotes.PlayerCharacter:FireServer(self.charData[name])
1559 return char
1560 end
1561
1562 self.step = function(dt)
1563 for i = 1,#self.playerData do
1564 local name = self.playerData[i][1]
1565 local data = self.playerData[i][2]
1566 if not self.charData[name] then
1567 self.charData[name] = self.createCharacter()
1568
1569 end
1570 local char = self.charData[name]
1571
1572 local torso = char.parts[1]
1573 local lLeg = char.parts[2]
1574 local rLeg = char.parts[3]
1575
1576 --char.moveSpring.setTarget(data[1] * cf(0,-1.8,0))
1577 --torso.CFrame = char.moveSpring.update(dt)
1578 --char.moveSpring.multiplySpeeds(.9)
1579 local tgt = data[1] * cf(0,-1.8,0)
1580 torso.CFrame = torso.CFrame:lerp(tgt,.5)
1581
1582 local newPos = torso.CFrame
1583 local oldPos = char.oldPos
1584
1585 local pointed = cf(oldPos.p,newPos.p)
1586 local distance = math.min((newPos.p - oldPos.p).magnitude,1)
1587 local angle = atan2(pointed.lookVector.X,pointed.lookVector.Z) - atan2(oldPos.lookVector.X,oldPos.lookVector.Z)
1588 local xMove,zMove = 0,0
1589 if angle > .1 or angle < -.1 then
1590 xMove = cos((-pi/2)+angle) * distance * 20
1591 zMove = cos(angle) * distance * 20
1592 else
1593 zMove = distance * 20
1594 end
1595
1596 char.walk = char.walk + zMove/24
1597 char.side = char.side + xMove/24
1598
1599 char.velocity = char.velocity + v3(xMove,0,zMove)
1600 char.velocity = char.velocity * 0.25
1601
1602 lLeg.CFrame = newPos
1603 * cf(-.5,-1,0)
1604 * ca(sin(((char.walk))+pi/2),0,0)
1605 * cf(0,-1,0)
1606
1607 rLeg.CFrame = newPos
1608 * cf(.5,-1,0)
1609 * ca(sin(((char.walk))-pi/2),0,0)
1610 * cf(0,-1,0)
1611
1612 local x = char.velocity.X
1613 local z = char.velocity.Z
1614 if x < .1 and x > -.1 then x = 0 end
1615 if z < .1 and z > -.1 then z = 0 end
1616 char.velocity = v3(x,0,z)
1617
1618 char.oldPos = newPos
1619 end
1620 for i = 1,#self.characters do
1621 if not self.characters[i] then break end
1622 local charName = self.characters[i]
1623 if not self.playerData[charName] then
1624 if self.charData[charName] then
1625 self.charData[charName].destroy()
1626 end
1627 self.charData[charName] = nil
1628 end
1629 self.characters[i] = nil
1630 end
1631 end
1632 Remotes.PlayerCharacter:FireServer(self)
1633end
1634
1635--Run scope
1636do
1637 local self = run
1638 local rs = game:GetService("RunService")
1639 local renderLast = tick()
1640 local heartbeatLast = tick()
1641 --FUNCTIONS THAT RUN ON EACH RENDERSTEPPED
1642 local renderLoop = {
1643 input.step,
1644 camera.step,
1645 weapons.step,
1646 character.step,
1647 gui.step,
1648 replication.step
1649 }
1650 --FUNCTIONS THAT RUN OF EACH HEARTBEAT
1651 local heartbeatLoop = {
1652 physics.char.update,
1653 }
1654 rs:BindToRenderStep("MainLoop",201,function()
1655 --RUN RENDERSTEP FUNCTIONS
1656 local dt = (tick() - renderLast) * 45
1657 renderLast = tick()
1658 for i = 1,#renderLoop do
1659 renderLoop[i](dt)
1660 end
1661 input.deltaY = 0
1662 input.deltaX = 0
1663 end)
1664 --LOWER FRAMERATE TEST RENDER LOOP
1665 --[[
1666 spawn(function()
1667 while true do
1668 --RUN RENDERSTEP FUNCTIONS
1669 local dt = (tick() - renderLast) * 45
1670 renderLast = tick()
1671 for i = 1,#renderLoop do
1672 renderLoop[i](dt)
1673 end
1674 input.deltaY = 0
1675 input.deltaX = 0
1676 wait(1/30)
1677 end
1678 end)
1679 ]]--
1680 spawn(function()
1681 while true do
1682 replication.replicateStep()
1683 wait(1/33)
1684 end
1685 end)
1686 spawn(function()
1687 while true do
1688 --RUN HEARTBEAT FUNCTIONS
1689 local dt = (tick() - heartbeatLast) * 45
1690 heartbeatLast = tick()
1691 for i = 1,#heartbeatLoop do
1692 heartbeatLoop[i](dt)
1693 end
1694 rs.Heartbeat:wait()
1695 end
1696 end)
1697end