Created
August 28, 2015 12:26
-
-
Save Elmuti/7b817605d95e7b84bf35 to your computer and use it in GitHub Desktop.
monster_ai.lua
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
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