What do you want to achieve?
In my upcoming Scrapyard game, one of the enemies I plan to implement into the game would be based on the Junkbots from mrflimflam’s Junkbot raids of November 2020. As such, these enemies would be always hostile to players until given oil.
“But there are players who ARE Junkbots!” That is true, so I intend to implement a character creator that replaces the player’s avatar with a custom character, and in the future, add an ability that temporarily not only transforms the player’s character into their avatar, but also makes them semi-OP.
While randomly searching in the Studio’s Toolbox for “Junkbot”, I came across this 8-month-old model of a somewhat working enemy Junkbot model. After adding it onto a test baseplate, I checked and refined its scripts.
Changes:
- made many variables camelCase
- made constant variables LOUD_SNAKE_CASE
- replaced instances of
if a == true then
andif a ~= nil then
withif a then
- replaced instances of
if a == nil then
withif not a then
- replaced instances similar to
a = a + 1
witha += 1
- uncommented commented-out lines of code that seemed relevant
- added new lines for it to cry out aside from “GIVE OIL”
- many more changes on other scripts
What is the issue?
I did as much modifications as possible, but there is a problem. The enemy AI works fine, but the output constantly keeps writing this error:
attempt to index nil with 'Name’
Even worse, when I kill it during play-testing, it LAGS the testing area very badly.
What solutions have you tried so far?
In the original code, there was this LONG list of conditions for a particular if-then block.
if p:IsA'Part' and not p.Name == bodypartnames and phit and phit.Name ~= bodypartnames and phit:IsA'Part' and rootr:Distance(phit.Position) < 5 then
What I did is that I placed all of these conditions into a table, and then did this:
local c = {
p:IsA"Part",
p.Name ~= bodyPartNames,
pHit,
pHit.Name ~= bodyPartNames,
pHit:IsA"Part",
rootR:Distance(pHit.Position) < 5
}
if c[1] and c[2] and c[3] and c[4] and c[5] and c[6] then
connection = human.Changed:connect(function()
human.Jump = true
end)
else
human.Jump = false
end
If that does not seem enough, here is the full "AI Chase" script:
-- 22/5/2018: So turns out, Baldi without a humanoid body is too skinny to move --
--DuruTeru
--[[
____________________________________________________________________________________________________________________
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
____________________________________________________________________________________________________________________
___ ___
( ) ( ) .-.
.--. .--. | |_ | |_ ( __) ___ .-. .--. .--.
/ _ \ / \ ( __) ( __) (''") ( ) \ / \ / _ \
. .' `. ; | .-. ; | | | | | | | .-. . ; ,-. ' . .' `. ;
| ' | | | | | | | | ___ | | ___ | | | | | | | | | | | ' | |
_\_`.(___) | |/ | | |( ) | |( ) | | | | | | | | | | _\_`.(___)
( ). '. | ' _.' | | | | | | | | | | | | | | | | | | ( ). '.
| | `\ | | .'.-. | ' | | | ' | | | | | | | | | ' | | | | `\ |
; '._,' ' ' `-' / ' `-' ; ' `-' ; | | | | | | ' `-' | ; '._,' '
'.___.' `.__.' `.__. `.__. (___) (___)(___) `.__. | '.___.'
( `-' ;
`.__.
____________________________________________________________________________________________________________________
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
____________________________________________________________________________________________________________________
--]]
SEARCH_DISTANCE = 64 -- How far a player can be before it detects you
ENEMY_DAMAGE = 5 -- How much damage the enemy inficts towards the player
--[[
____________________________________________________________________________________________________________________
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
____________________________________________________________________________________________________________________
-- OPTIONAL --]]
CAN_WANDER = false
WANDER_X,WANDER_Z = 30, 30
-- How many studs the enemy can wander on the x and z axis in studs
--]]
function getHumanoid(model)
for _, v in pairs(model:GetChildren()) do
if v:IsA'Humanoid' then
return v
end
end
end
enemy = script.Parent
human = getHumanoid(enemy)
hRoot = enemy.HumanoidRootPart
eSpeed = hRoot.Velocity.magnitude
PFS = game:GetService("PathfindingService")
function GetPlayerNames()
local players = game:GetService('Players'):GetChildren()
local name
for _, v in pairs(players) do
if v:IsA'Player' then
name = tostring(v.Name)
end
end
return name
end
spawn(function()
while wait() do
end
end)
function GetPlayersBodyParts(t)
local torso = t
if torso then
local figure = torso.Parent
for _, v in pairs(figure:GetChildren()) do
if v:IsA'Part' then
return v.Name
end
end
else
return "HumanoidRootPart"
end
end
function GetTorso(part)
local chars = game.Workspace:GetChildren()
local torso
for _, v in pairs(chars) do
if v:IsA'Model' and v ~= script.Parent and v.Name == GetPlayerNames() then
local charRoot = v:FindFirstChild'HumanoidRootPart'
if (charRoot.Position - part).magnitude < SEARCH_DISTANCE then
torso = charRoot
end
end
end
return torso
end
for _, enemyPart in pairs(enemy:GetChildren()) do
if enemyPart:IsA'Part' then
enemyPart.Touched:connect(function(p)
if p.Parent.Name == GetPlayerNames() and p.Parent.Name ~= enemy.Name then -- damage
local victim = p.Parent
getHumanoid(enemy):TakeDamage(ENEMY_DAMAGE)
end
end)
end
end
-- no touchy
local path
local waypoint
local oldpoints
local isWandering = 0
if CAN_WANDER then
spawn(function()
while isWandering == 0 do
isWandering = 1
local desgx, desgz = hRoot.Position.x + math.random(-WANDER_X, WANDER_X), hRoot.Position.z + math.random(-WANDER_Z, WANDER_Z)
human:MoveTo( Vector3.new(desgx, 0, desgz) )
wait(math.random(4, 6))
isWandering = 0
end
end)
end
while wait() do
local enemytorso = GetTorso(hRoot.Position)
if enemytorso then -- if player detected
isWandering = 1
local function checkw(t)
local ci = 3
if ci > #t then
ci = 3
end
if not t[ci] and ci < #t then
repeat
ci += 1
wait()
until t[ci]
return Vector3.new(1, 0, 0) + t[ci]
else
ci = 3
return t[ci]
end
end
path = PFS:FindPathAsync(hRoot.Position, enemytorso.Position)
waypoint = path:GetWaypoints()
oldpoints = waypoint
local connection;
local direct = Vector3.FromNormalId(Enum.NormalId.Front)
local NCF = hRoot.CFrame * CFrame.new(direct)
direct = NCF.p.unit
local rootR = Ray.new(hRoot.Position, direct)
local pHit, pPos = game.Workspace:FindPartOnRay(rootR, hRoot)
if path and waypoint or checkw(waypoint) then
if checkw(waypoint) and checkw(waypoint).Action == Enum.PathWaypointAction.Walk then
human:MoveTo( checkw(waypoint).Position )
human.Jump = false
end
if checkw(waypoint) and checkw(waypoint).Action == Enum.PathWaypointAction.Jump then
human.Jump = true
connection = human.Changed:connect(function()
human.Jump = true
end)
human:MoveTo( checkw(waypoint).Position )
else
human.Jump = false
end
hRoot.Touched:connect(function(p)
local bodyPartNames = GetPlayersBodyParts(enemytorso)
local c = {
p:IsA"Part",
p.Name ~= bodyPartNames,
pHit,
pHit.Name ~= bodyPartNames,
pHit:IsA"Part",
rootR:Distance(pHit.Position) < 5
}
if c[1] and c[2] and c[3] and c[4] and c[5] and c[6] then
connection = human.Changed:connect(function()
human.Jump = true
end)
else
human.Jump = false
end
end)
if connection then
connection:Disconnect()
end
else
for i = 3, #oldpoints do
human:MoveTo( oldpoints[i].Position )
end
end
elseif enemytorso and CAN_WANDER then -- if player not detected
isWandering = 0
path = nil
waypoint = nil
human.MoveToFinished:Wait()
end
end
-- Edited from @m1sterfufu's "flamingo junkbot" model by TJZ2021playz for game "The Scrapyard"
You could check the original model by @m1sterfufu for reference. If you have any more optimization ideas and suggestions (@m1sterfufu modeled this after supposedly a “Baldi” enemy; this script is 3 years old), please do reply.