Skip to content

Instantly share code, notes, and snippets.

@Elmuti
Created August 28, 2015 12:26
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/7b817605d95e7b84bf35 to your computer and use it in GitHub Desktop.
Save Elmuti/7b817605d95e7b84bf35 to your computer and use it in GitHub Desktop.
monster_ai.lua
local char = script.Parent
local head = char:WaitForChild("Head")
local torso = char:WaitForChild("Torso")
local hum = char:WaitForChild("Humanoid")
local destReachRange = 3
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 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()
timeouts = 0
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 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)
if (torso.Position - 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, _G.ignorelist)
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
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
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
timeouts = timeouts + 1
if timeouts >= 3 then
warn("NPC stuck, teleporting..")
resetNpc()
break
end
warn("-> Timed out!")
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
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment