Skip to content

Instantly share code, notes, and snippets.

@Elmuti
Created August 28, 2015 18:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Elmuti/be362b54a1996b3a0105 to your computer and use it in GitHub Desktop.
Save Elmuti/be362b54a1996b3a0105 to your computer and use it in GitHub Desktop.
monster_ai v3
local char = script.Parent
local head = char:WaitForChild("Head")
local torso = char:WaitForChild("Torso")
local hum = char:WaitForChild("Humanoid")
local destReachRange = 2
local losRange = 128
local hearRange = 48
local ai_max_range = math.huge
local attackRange = 2 --65
local runSpeed = 16
local patrolSpeed = 8
local areanode_radius = 6
local timeouts = 0
local Orakel = require(game.ReplicatedStorage.Orakel.Main)
local npcLib = Orakel.LoadModule("NpcLib")
local pathLib = Orakel.LoadModule("PathLib")
local assetLib = Orakel.LoadModule("AssetLib")
local physLib = Orakel.LoadModule("PhysLib")
local map = workspace.Map
local masterTable, mnt_index = pathLib.CollectNodes(map.Nodes)
local sndEvent = workspace.Game.PlaySoundClient
math.randomseed(os.time())
local gyro = Instance.new("BodyGyro", script.Parent.Torso)
gyro.D = 100
gyro.P = 400
gyro.MaxTorque = Vector3.new(0, 400, 0)
function resetNpc()
local nodelist = map.Nodes:GetChildren()
local randNode = nodelist[math.random(1,#nodelist)]
torso.CFrame = CFrame.new(randNode.Position + Vector3.new(0, 3, 0))
end
function rotateTowards(goal)
if gyro ~= nil then
gyro.CFrame = CFrame.new(torso.Position, goal) - torso.Position
end
end
function checkObstacles(dir)
local walkspeed = hum.WalkSpeed
local ray = Ray.new(torso.Position,(dir - torso.Position).unit * 4)
local hit, pos = workspace:FindPartOnRayWithIgnoreList(ray, {char, workspace.Map.AreaNodes, workspace.Map.Nodes})
if hit ~= nil then
if hit.Name == "Door" then
print("DOOR BLOCKING ME")
hum.WalkSpeed = 0
wait(1)
sndEvent:Fire("3d", "monster_roar", "ROAR ID", 1, 1, false, 5, head)
wait(2)
local mat = assetLib.RealMaterial:Get(hit)
local snd = assetLib.Sounds.Destroy[mat]
sndEvent:Fire("3d", "door_break", snd, 1, 1, false, 5, head)
physLib.SpawnGibs(hit.Position, mat, hit.Size, nil)
hit:Destroy()
wait(1)
hum.WalkSpeed = walkspeed
print("DOOR IS DEAD :)")
end
end
end
function shuffleTable(tab)
local t = tab
local rand = math.random
assert( t, "shuffleTable() expected a table, got nil" )
local iterations = #t
local j
for i = iterations, 2, -1 do
j = rand(i)
t[i], t[j] = t[j], t[i]
end
return t
end
function hasReachedDestination(dest)
local torsoPos = torso.Position - Vector3.new(0, 2, 0)
if (torsoPos - dest.Position).magnitude <= destReachRange then
return true
else
return false
end
end
function lineOfSight(a, b)
local los = false
local vis = npcLib.LineOfSight(a, b, losRange, {char, workspace.Map.AreaNodes, workspace.Map.Nodes})
local behind = (b.CFrame:toObjectSpace(a.CFrame).p.Z < 0)
if vis and not behind then
los = true
end
return los
end
function attack()
end
function chooseNodeInArea(areanode)
local validNodes = pathLib.GetNodesVisibleInRadius(areanode.Position, areanode_radius, true, workspace.Map.Nodes, masterTable)
return validNodes[math.random(1,#validNodes)]
end
function chooseTarget()
--[[
local targets = {}
--Find Player targets
for _, plrInGame in pairs(game.Players:GetPlayers()) do
local plr = plrInGame.Character
if plr ~= nil then
if plr.Humanoid.Health > 0 then
local los = lineOfSight(head, plr.Head)
if los then
table.insert(targets, plr)
else
if plr.EmittingSound.Value then
local dist = (torso.Position - plr.Torso.Position).magnitude
if dist <= hearRange then
table.insert(targets, plr)
end
end
end
end
end
end
if #targets > 1 then
local nearest = targets[1]
for t = 2, #targets do
local old = (nearest.Torso.Position - torso.Position).magnitude
local new = (targets[t].Torso.Position - torso.Position).magnitude
if new < old then
nearest = targets[t]
end
end
elseif #targets == 1 then
return targets[1]
end
]]
return nil
end
function pathFind(target, startPos, goalPos)
local startID = pathLib.GetNearestNode(startPos, false, map.Nodes, masterTable)
local goalID = pathLib.GetNearestNode(goalPos, false, map.Nodes, masterTable)
local path = pathLib.AStar(masterTable, startID, goalID)
for nodeNum, node in pairs(path) do
local eta = npcLib.EstimatedPathTime(torso.Position, node.Position, hum.WalkSpeed)
local timeWalked = 0
timeouts = 0
while true do
hum:MoveTo(node.Position)
local rotTarget
local nextNode = path[nodeNum + 1]
if nextNode == nil then
rotTarget = node.Position
else
rotTarget = nextNode.Position
end
checkObstacles(node.Position)
rotateTowards(rotTarget)
local act = wait(1/20)
timeWalked = timeWalked + act
local newTarget = chooseTarget()
--Reached node, Move to next node
if hasReachedDestination(node) then
break
end
--Target changed!
if newTarget ~= target and newTarget ~= nil then
warn("-> Target changed from "..tostring(target).." to "..tostring(newTarget)..", aborting pathfind!")
return false
end
--Timed out! Return false, acquire new path
if timeWalked > eta then
warn("-> Timed out!")
timeouts = timeouts + 1
if timeouts >= 3 then
timeouts = 0
warn("NPC stuck, teleporting..")
resetNpc()
break
end
return false
end
--Got line of sight on target, Stop finding the path and just walk there
if target.Parent:FindFirstChild("Humanoid") then
local los = npcLib.LineOfSight(head, target.Torso, losRange, _G.ignorelist)
local cdist = (torso.Position - target.Torso.Position).magnitude
if los and cdist <= 15 then
char.PlayerSeen.Value = true
warn("-> Got line of sight on target, Stop pathfinding")
return false
elseif los and not char.PlayerSeen.Value then
char.PlayerSeen.Value = true
sndEvent:Fire("3d", "monster_roar", "ROAR ID", 1, 1, false, 5, head)
return false
else
char.PlayerSeen.Value = false
end
end
end
end
return true
end
warn("NPC loaded")
while true do
wait(1/30)
local target-- = chooseTarget()
if target == nil then
--Area nodes are basically positions of rooms, each room will have one area node
local areanodes = map.AreaNodes:GetChildren()
areanodes = shuffleTable(areanodes)
hum.WalkSpeed = patrolSpeed
for node = 1, #areanodes do
print("Patrolling area #"..node)
local patrolLocation = chooseNodeInArea(areanodes[node])
local success = pathFind(patrolLocation, torso.Position, patrolLocation.Position)
if not success then
warn("Pathfind was not a success")
break
else
wait(math.random(3, 6))
end
end
else
local los = lineOfSight(torso.Position, target.Torso)
local dist = (target - torso.position).magnitude
if dist <= attackRange then
attack(target)
end
--pathfind to target
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment