Created
August 28, 2015 23:27
-
-
Save Elmuti/e7843f7b2743f4b57942 to your computer and use it in GitHub Desktop.
asdads
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--Some vars | |
local char = script.Parent | |
local head = char:WaitForChild("Head") | |
local torso = char:WaitForChild("Torso") | |
local hum = char:WaitForChild("Humanoid") | |
local map = workspace.Map | |
local sndEvent = workspace.Game.PlaySoundClient | |
local timeouts = 0 | |
--Some constants | |
local destReachRange = 1.5 | |
local losRange = 128 | |
local hearRange = 50 --Accurate hearing range | |
local hearRangeMax = 120 --Hearing range | |
local hearAccuracy = 30 --Hearing accuracy at max range in studs | |
local ai_max_range = math.huge | |
local attackRange = 2 | |
local runSpeed = 16 | |
local patrolSpeed = 8 | |
local areanode_radius = 6 | |
local maxTimeouts = 3 --the amount of times the NPC can fail to walk to a node | |
--Dependencies | |
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") | |
--Sounds | |
local roar_angry = Orakel.FindSound("id/24484284") | |
local idleroars = { | |
Orakel.FindSound("id/24484284"), | |
Orakel.FindSound("id/24484284") | |
} | |
--Node grid | |
local masterTable, mnt_index = pathLib.CollectNodes(map.Nodes) | |
--Random seed for table shuffle(patrol routes) | |
math.randomseed(os.time()) | |
--Make a bodygyro for npc rotation | |
local gyro = Instance.new("BodyGyro", script.Parent.Torso) | |
gyro.D = 100 | |
gyro.P = 400 | |
gyro.MaxTorque = Vector3.new(0, 400, 0) | |
--Activities will be investigated in order from this list | |
local investigateQueue = {} | |
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 | |
--Checks for obstacles such as locked doors (fires a ray towards next node) | |
function checkObstacles(dir) | |
local walkspeed = hum.WalkSpeed | |
local ray = Ray.new(torso.Position,(dir - torso.Position).unit * 2) | |
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_angry, 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 | |
--Returns true if A has line of sight to B | |
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 | |
--Chooses random node in an area node's presence | |
function chooseNodeInArea(areanode) | |
local validNodes = pathLib.GetNodesVisibleInRadius(areanode.Position, areanode_radius, true, workspace.Map.Nodes, masterTable) | |
return validNodes[math.random(1,#validNodes)] | |
end | |
--Adds possible signs of player to the investigation queue | |
function updateSenses() | |
for _, p in pairs(workspace.Map.Sounds) do | |
local dist = (p.Position - torso.Position).magnitude | |
local snd = p:GetChildren()[1] | |
local rng = hearRangeMax * snd.Volume | |
if dist <= hearRangeMax then | |
table.insert(investigateQueue, p.Position) | |
end | |
end | |
end | |
--Makes NPC travel the shortest path between startPos and goalPos | |
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 >= maxTimeouts 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_angry, 1, 1, false, 5, head) | |
return false | |
else | |
char.PlayerSeen.Value = false | |
end | |
end | |
end | |
end | |
return true | |
end | |
warn("NPC loaded") | |
--Secondary AI loop | |
spawn(function() | |
while true do | |
wait(1/10) | |
updateSenses() | |
end | |
end) | |
--Main AI loop | |
while true do | |
wait(1/30) | |
local target-- = chooseTarget() | |
if target == nil then | |
if #investigateQueue == 0 then | |
char.Mode.Value = "Patrolling" | |
--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 | |
char.Mode.Value = "Investigating" | |
for num, pos in pairs(investigateQueue) do | |
local dist = (pos - torso.Position).magnitude | |
local node | |
if dist >= hearRangeMax then | |
local nodes = pathLib.GetNodesVisibleInRadius(pos, hearAccuracy, true, map.Nodes, masterTable) | |
node = nodes[math.random(1, #nodes)] | |
else | |
node = pathLib.GetNearestNode(pos, true, map.Nodes, masterTable) | |
end | |
local success = pathFind(node, torso.Position, node.Position) | |
if not success then | |
warn("Pathfind was not a success") | |
break | |
end | |
end | |
end | |
else | |
char.Mode.Value = "Chasing" | |
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