Created
August 28, 2015 18:34
-
-
Save Elmuti/be362b54a1996b3a0105 to your computer and use it in GitHub Desktop.
monster_ai v3
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 = 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