Advertisement
Guest User

Untitled

a guest
Feb 16th, 2020
103
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 5.27 KB | None | 0 0
  1. local Players = game:GetService('Players')
  2.  
  3. local module = {}
  4. local existingNPC = {}
  5.  
  6. function module.new(npc)
  7.     if not existingNPC[npc.Name] then
  8.         return module:create(npc)
  9.     end
  10.     return existingNPC[npc.Name]
  11. end
  12.  
  13. function module:create(npc)
  14.     local newNPC = {}
  15.     setmetatable(newNPC, self)
  16.    
  17.     self.NPC = npc
  18.     self.Name = self.NPC.Name
  19.     self.Root = self.NPC.PrimaryPart
  20.     self.Humanoid = self.NPC:WaitForChild('Humanoid')
  21.     self.Health = self.Humanoid.Health
  22.     self.MaxHealth = self.Humanoid.MaxHealth
  23.     self.TargetChanged = nil
  24.    
  25.     -- Stats (Things you can change)
  26.     self.Visibility = 100 -- How far it can detect enemies
  27.     self.WalkingSpeed = 9 -- When it doesn't have an enemy, it walks randomly
  28.     self.RunningSpeed = 15 -- When it finds an enemy, it runs toward it
  29.     self.ChargingSpeed = 20 -- Desperate mode when it's low health but also very near
  30.     self.ScareFactor = 0.25 -- Determines the health at which it stops attacking in order to heal.
  31.     self.ChargeFactor = 10 -- Distance at which the npc charges if it's at ScareFactor (instead of heal).
  32.    
  33.     -- Info
  34.     self.Target = nil
  35.     self.TargetPoints = {}
  36.     self.Distance = 0
  37.     self.Path = nil
  38.    
  39.     -- Internal
  40.     self.WalkSpeed = self.Humanoid.WalkSpeed
  41.     self.States = {Walking = self.WalkingSpeed, Running = self.RunningSpeed, Charging = self.ChargingSpeed}
  42.     self.State = self.States.Walking
  43.     self.UnusedStateTypes = {
  44.         Enum.HumanoidStateType.Flying,
  45.         Enum.HumanoidStateType.Seated,
  46.         Enum.HumanoidStateType.StrafingNoPhysics,
  47.         Enum.HumanoidStateType.Climbing,
  48.         Enum.HumanoidStateType.Swimming
  49.     }
  50.    
  51.     -- Utility Functions
  52.     function self.MandateType(object, _type)
  53.         if _type == Vector3 then
  54.             if typeof(object) ~= _type then
  55.                 return object.Position
  56.             end
  57.             return object
  58.         end
  59.         assert(typeof(object) ~= _type, 'Object is not the correct type')
  60.     end
  61.    
  62.     function self:create(func)
  63.         self.MandateType(func, 'function')
  64.         coroutine.resume(coroutine.create(func))
  65.     end
  66.    
  67.     -- Main Functions
  68.     function self.CheckDistance(player, object)
  69.         local character = player.Character or player.CharacterAdded:Wait()
  70.         if object then
  71.             object = self.MandateType(object, Vector3)
  72.             return math.abs((object - character.PrimaryPart.Position).Magnitude)
  73.         end
  74.         return math.abs((self.Root.Position - character.PrimaryPart.Position).Magnitude)
  75.     end
  76.    
  77.     function self.GetNearestPlayer(object)
  78.         local closestDistance = self.Visibility
  79.         local closestPlayer = nil
  80.         for _, player in pairs(Players:GetPlayers()) do
  81.             local dist = 0
  82.             if object then
  83.                 dist = self.CheckDistance(player, object)
  84.             else
  85.                 dist = self.CheckDistance(player)
  86.                 self.TargetPoints[player.Name] = dist
  87.             end
  88.             if dist < closestDistance then
  89.                 closestDistance = dist
  90.                 closestPlayer = player
  91.             end
  92.         end
  93.         return closestPlayer, closestDistance
  94.     end
  95.    
  96.     function self.RayCast(thing, direction)
  97.         local ray = nil
  98.         if thing and not direction then
  99.             thing = self.MandateType(thing, Vector3)
  100.             ray = Ray.new(self.Root.Position, (thing - self.Root.Position).Unit * (self.Visibility + 10))
  101.         elseif direction and not thing then
  102.             ray = Ray.new(self.Root.Position, direction * (self.Visibility + 10))
  103.         end
  104.         local part, pos = workspace:FindPartOnRay(ray, self.NPC)
  105.         local distance = math.abs((self.Root.Position - pos).Magnitude)
  106.         return part, pos, distance
  107.     end
  108.    
  109.     function self.AvoidEnemyPosition()
  110.         local targetPos = self.Target.Character.PrimaryPart.Position
  111.         local unit = (targetPos - self.Root.Position).Unit
  112.         local angle = math.atan2(-unit.Z, -unit.X) + math.rad(Random.new():NextNumber(-90, 90))
  113.         local cframe = CFrame.new(self.Root.Position) * CFrame.Angles(0, angle, 0) * CFrame.new(0, 0, -1)
  114.         return (cframe.Position - self.Root.Position).Unit
  115.     end
  116.    
  117.     -- Once it has the random direction to avoid the closest enemy,
  118.     -- it checks if there is another enemy near that direction
  119.     -- if there is, it should make a different random direction.
  120.     function self.CheckNearTargets(direction)
  121.         if not direction then
  122.             direction = self.AvoidEnemyPosition()
  123.         end
  124.         local part = self.RayCast(nil, direction)
  125.         local nearestPlayer, distance = self.GetNearestPlayer(part)
  126.         if distance < self.Visibility * 0.75 then
  127.             -- It is too close, make another random direction.
  128.             self.CheckNearTargets(self.AvoidEnemyPosition())
  129.             return
  130.         end
  131.     end
  132.    
  133.     function self.Logic()
  134.         -- still doing
  135.     end
  136.    
  137.     function self.Init()
  138.         self.Target, self.Distance = self.GetNearestPlayer()
  139.         if not self.TargetChanged then
  140.             repeat wait() until self.TargetChanged
  141.         end
  142.         self.TargetChanged:Fire(self.Target)
  143.     end
  144.    
  145.     function self.Setup()
  146.         self:create(function()
  147.             for _, stateType in pairs(self.UnusedStateTypes) do
  148.                 self.Humanoid:SetStateEnabled(stateType, false)
  149.             end
  150.         end)
  151.            
  152.         if not script:FindFirstChild(self.Name .. 'Event') then
  153.             self.TargetChanged = Instance.new('BindableEvent')
  154.             self.TargetChanged.Name = self.Name .. 'Event'
  155.             self.TargetChanged.Parent = script
  156.         end
  157.        
  158.         -- Update state/walkspeed
  159.         script[self.Name .. 'Event'].Event:Connect(function(target)
  160.             self.State = (not self.Target and self.States.Walking) or self.States.Running
  161.             self.WalkSpeed = self.State
  162.         end)
  163.     end
  164.  
  165.     existingNPC[self.Name] = self
  166.     self.__tostring = self.Name
  167.     self.__index = self
  168.     self.Setup()
  169.     return self
  170. end
  171.  
  172. return module
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement