Error:
[ERROR] gamemodes/darkrp/gamemode/modules/base/sh_createitems.lua:185: attempt to index a string value with bad key (‘function: 0x31e1ad80’ is not part of the string library)
1. checkValid - gamemodes/darkrp/gamemode/modules/base/sh_createitems.lua:185
2. createCategory - gamemodes/darkrp/gamemode/modules/base/sh_createitems.lua:999
Pastebin of my sh_createitems folder: http://pastebin.com/5daXSUvh
local plyMeta = FindMetaTable("Player")
-- automatically block players from doing certain things with their DarkRP entities
local blockTypes = {"Physgun1", "Spawning1", "Toolgun1"}
-- Assert function, asserts a property and returns the error if false.
-- Allows f to override err and hints by simply returning them
local ass = function(f, err, hints) return function(...)
local res = {f(...)}
table.insert(res, err)
table.insert(res, hints)
return unpack(res)
end end
-- Returns whether a value is nil
local isnil = fn.Curry(fn.Eq, 2)(nil)
-- Optional value, when filled in it must meet the conditions
local optional = function(...) return fn.FOr{isnil, ...} end
-- Check the correctness of a model
local checkModel = isstring
-- A table of which each element must meet condition f
local tableOf = function(f) return function(tbl)
if not istable(tbl) then return false end
for k,v in pairs(tbl) do if not f(v) then return false end end
return true
end end
-- Any of the given elements
local oneOf = function(f) return fp{table.HasValue, f} end
-- A table that is nonempty, wrap around tableOf
local nonempty = function(f) return function(tbl) return istable(tbl) and #tbl > 0 and f(tbl) end end
-- A value must be unique amongst all `kind`. Uses optional `hash` function to create custom hashes in the internal table
local uniqueEntity = function(cmd, tbl)
for k, v in pairs(DarkRPEntities) do
if v.cmd ~= cmd then continue end
return false, "This entity does not have a unique command.", {"There must be some other end that has the same thing for 'cmd'.", "Fix this by changing the 'cmd' field of your entity to something else."}
end
return true
end
local uniqueJob = function(v, tbl)
local job = DarkRP.getJobByCommand(v)
if job then return false, "This job does not have a unique command.", {"There must be some other job that has the same command.", "Fix this by changing the 'command' of your job to something else."} end
return true
end
-- Template for a correct job
local requiredTeamItems = {
color = ass(tableOf(isnumber), "The color must be a Color value.", {"Color values look like this: Color(r, g, b, a), where r, g, b and a are numbers between 0 and 255."}),
model = ass(fn.FOr{checkModel, nonempty(tableOf(checkModel))}, "The model must either be a table of correct model strings or a single correct model string.", {"This error could happens when the model does not exist on the server.", "Are you sure the model path is right?", "Is the model from an addon that is not properly installed?"}),
description = ass(isstring, "The description must be a string."),
weapons = ass(optional(tableOf(isstring)), "The weapons must be a valid table of strings.", {"Example: weapons = {\"med_kit\", \"weapon_bugbait\"},"}),
command = ass(fn.FAnd{isstring, uniqueJob}, "The command must be a string."),
max = ass(fn.FAnd{isnumber, fp{fn.Lte, 0}}, "The max must be a number greater than or equal to zero.", {"Zero means infinite.", "A decimal between 0 and 1 is seen as a percentage."}),
salary = ass(fn.FAnd{isnumber, fp{fn.Lte, 0}}, "The salary must be a number greater than zero."),
admin = ass(fn.FAnd{isnumber, fp{fn.Lte, 0}, fp{fn.Gte, 2}}, "The admin value must be a number greater than or equal to zero and smaller than three."),
vote = ass(optional(isbool), "The vote must be either true or false."),
-- Optional advanced stuff
category = ass(optional(isstring), "The category must be the name of an existing category!"),
sortOrder = ass(optional(isnumber), "The sortOrder must be a number."),
buttonColor = ass(optional(tableOf(isnumber)), "The buttonColor must be a Color value."),
label = ass(optional(isstring), "The label must be a valid string."),
ammo = ass(optional(tableOf(isnumber)), "The ammo must be a table containing numbers.", {"See example on http://wiki.darkrp.com/index.php/DarkRP:CustomJobFields"}),
hasLicense = ass(optional(isbool), "The hasLicense must be either true or false."),
NeedToChangeFrom = ass(optional(tableOf(isnumber), isnumber), "The NeedToChangeFrom must be either an existing team or a table of existing teams", {"Is there a job here that doesn't exist (anymore)?"}),
customCheck = ass(optional(isfunction), "The customCheck must be a function."),
CustomCheckFailMsg = ass(optional(isstring, isfunction), "The CustomCheckFailMsg must be either a string or a function."),
modelScale = ass(optional(isnumber), "The modelScale must be a number."),
maxpocket = ass(optional(isnumber), "The maxPocket must be a number."),
maps = ass(optional(tableOf(isstring)), "The maps value must be a table of valid map names."),
candemote = ass(optional(isbool), "The candemote value must be either true or false."),
mayor = ass(optional(isbool), "The mayor value must be either true or false."),
chief = ass(optional(isbool), "The chief value must be either true or false."),
medic = ass(optional(isbool), "The medic value must be either true or false."),
cook = ass(optional(isbool), "The cook value must be either true or false."),
hobo = ass(optional(isbool), "The hobo value must be either true or false."),
CanPlayerSuicide = ass(optional(isfunction), "The CanPlayerSuicide must be a function."),
PlayerCanPickupWeapon = ass(optional(isfunction), "The PlayerCanPickupWeapon must be a function."),
PlayerDeath = ass(optional(isfunction), "The PlayerDeath must be a function."),
PlayerLoadout = ass(optional(isfunction), "The PlayerLoadout must be a function."),
PlayerSelectSpawn = ass(optional(isfunction), "The PlayerSelectSpawn must be a function."),
PlayerSetModel = ass(optional(isfunction), "The PlayerSetModel must be a function."),
PlayerSpawn = ass(optional(isfunction), "The PlayerSpawn must be a function."),
PlayerSpawnProp = ass(optional(isfunction), "The PlayerSpawnProp must be a function."),
RequiresVote = ass(optional(isfunction), "The RequiresVote must be a function."),
ShowSpare1 = ass(optional(isfunction), "The ShowSpare1 must be a function."),
ShowSpare2 = ass(optional(isfunction), "The ShowSpare2 must be a function."),
canStartVote = ass(optional(isfunction), "The canStartVote must be a function."),
canStartVoteReason = ass(optional(isstring, isfunction), "The canStartVoteReason must be either a string or a function."),
}
-- Template for correct shipment
local validShipment = {
model = ass(checkModel, "The model of the shipment must be a valid model.", {"This error could happens when the model does not exist on the server.", "Are you sure the model path is right?", "Is the model from an addon that is not properly installed?"}),
entity = ass(isstring, "The entity of the shipment must be a string."),
price = ass(function(v, tbl) return isnumber(v) or isfunction(tbl.getPrice) end, "The price must be an existing number or (for advanced users) the getPrice field must be a function."),
amount = ass(fn.FAnd{isnumber, fp{fn.Lte, 0}}, "The amount must be a number greater than zero."),
seperate = ass(optional(isbool), "the seperate field must be either true or false.", {"It's spelled as 'seperate' because of a really old mistake."}),
pricesep = ass(function(v, tbl) return not tbl.seperate or isnumber(v) and v >= 0 end, "The pricesep must be a number greater than or equal to zero."),
allowed = ass(optional(tableOf(isnumber), isnumber), "The allowed field must be either an existing team or a table of existing teams", {"Is there a job here that doesn't exist (anymore)?"}),
category = ass(optional(isstring), "The category must be the name of an existing category!"),
sortOrder = ass(optional(isnumber), "The sortOrder must be a number."),
buttonColor = ass(optional(tableOf(isnumber)), "The buttonColor must be a Color value."),
label = ass(optional(isstring), "The label must be a valid string."),
noship = ass(optional(isbool), "The noship must be either true or false."),
shipmodel = ass(optional(checkModel), "The shipmodel must be a valid model.", {"This error could happens when the model does not exist on the server.", "Are you sure the model path is right?", "Is the model from an addon that is not properly installed?"}),
customCheck = ass(optional(isfunction), "The customCheck must be a function."),
CustomCheckFailMsg = ass(optional(isstring, isfunction), "The CustomCheckFailMsg must be either a string or a function."),
weight = ass(optional(isnumber), "The weight must be a number."),
spareammo = ass(optional(isnumber), "The spareammo must be a number."),
clip1 = ass(optional(isnumber), "The clip1 must be a number."),
clip2 = ass(optional(isnumber), "The clip2 must be a number."),
shipmentClass = ass(optional(isstring), "The shipmentClass must be a string."),
onBought = ass(optional(isfunction), "The onBought must be a function."),
getPrice = ass(optional(isfunction), "The getPrice must be a function."),
}
-- Template for correct vehicle
local validVehicle = {
name = ass(isstring, "The name of the vehicle must be a string."),
model = ass(checkModel, "The model of the vehicle must be a valid model.", {"This error could happens when the model does not exist on the server.", "Are you sure the model path is right?", "Is the model from an addon that is not properly installed?"}),
price = ass(function(v, tbl) return isnumber(v) or isfunction(tbl.getPrice) end, "The price must be an existing number or (for advanced users) the getPrice field must be a function."),
allowed = ass(optional(tableOf(isnumber), isnumber), "The allowed field must be either an existing team or a table of existing teams", {"Is there a job here that doesn't exist (anymore)?"}),
category = ass(optional(isstring), "The category must be the name of an existing category!"),
sortOrder = ass(optional(isnumber), "The sortOrder must be a number."),
distance = ass(optional(isnumber), "The distance must be a number."),
angle = ass(optional(isangle), "The distance must be a valid Angle."),
buttonColor = ass(optional(tableOf(isnumber)), "The buttonColor must be a Color value."),
label = ass(optional(isstring), "The label must be a valid string."),
customCheck = ass(optional(isfunction), "The customCheck must be a function."),
CustomCheckFailMsg = ass(optional(isstring, isfunction), "The CustomCheckFailMsg must be either a string or a function."),
getPrice = ass(optional(isfunction), "The getPrice must be a function."),
}
-- Template for correct entity
local validEntity = {
ent = ass(isstring, "The name of the entity must be a string."),
model = ass(checkModel, "The model of the entity must be a valid model.", {"This error could happens when the model does not exist on the server.", "Are you sure the model path is right?", "Is the model from an addon that is not properly installed?"}),
price = ass(function(v, tbl) return isnumber(v) or isfunction(tbl.getPrice) end, "The price must be an existing number or (for advanced users) the getPrice field must be a function."),
max = ass(function(v, tbl) return isnumber(v) or isfunction(tbl.getMax) end, "The max must be an existing number or (for advanced users) the getMax field must be a function."),
cmd = ass(fn.FAnd{isstring, uniqueEntity}, "The cmd must be a valid string."),
name = ass(isstring, "The name must be a valid string."),
category = ass(optional(isstring), "The category must be the name of an existing category!"),
sortOrder = ass(optional(isnumber), "The sortOrder must be a number."),
buttonColor = ass(optional(tableOf(isnumber)), "The buttonColor must be a Color value."),
label = ass(optional(isstring), "The label must be a valid string."),
customCheck = ass(optional(isfunction), "The customCheck must be a function."),
CustomCheckFailMsg = ass(optional(isstring, isfunction), "The CustomCheckFailMsg must be either a string or a function."),
getPrice = ass(optional(isfunction), "The getPrice must be a function."),
}
local validAgenda = {
Title = ass(isstring, "The title must be a string."),
Manager = ass(fn.FOr{isnumber, nonempty(tableOf(isnumber))}, "The Manager must either be a single team or a non-empty table of existing teams.", {"Is there a job here that doesn't exist (anymore)?"}),
Listeners = ass(nonempty(tableOf(isnumber)), "The Listeners must be a non-empty table of existing teams.",
{
"Is there a job here that doesn't exist (anymore)?",
"Are you trying to have multiple manager jobs in this agenda? In that case you must put the list of manager jobs in curly braces.",
[[Like so: DarkRP.createAgenda("Some agenda", {TEAM_MANAGER1, TEAM_MANAGER2}, {TEAM_LISTENER1, TEAM_LISTENER2})]]
})
}
local validCategory = {
name = ass(isstring, "The name must be a string."),
categorises = ass(oneOf{"jobs", "entities", "shipments", "weapons", "vehicles", "ammo"},
[[The categorises must be one of "jobs", "entities", "shipments", "weapons", "vehicles", "ammo"]],
{"Mind that this is case sensitive.", "Also mind the quotation marks."}),
startExpanded = ass(isbool, "The startExpanded must be either true or false."),
color = ass(tableOf(isnumber), "The color must be a Color value."),
canSee = ass(optional(isfunction), "The canSee must be a function."),
sortOrder = ass(optional(isnumber), "The sortOrder must be a number."),
}
-- Check template against actual implementation
local env = {} -- environment used to be check propositions between multiple tables
local function checkValid(tbl, requiredItems, oEnv) -- Allow override environment
for k,v in pairs(requiredItems) do
local correct, err, hints = tbl[v] ~= nil
if isfunction(v) then correct, err, hints = v(tbl[k], tbl, oEnv or env) end
err = err or string.format("Element '%s' is corrupt!", k)
if not correct then return correct, err, hints end
end
return true
end
-----------------------------------------------------------
-- Job commands --
-----------------------------------------------------------
local function declareTeamCommands(CTeam)
local k = 0
for num,v in pairs(RPExtraTeams) do
if v.command == CTeam.command then
k = num
end
end
if CTeam.vote or CTeam.RequiresVote then
DarkRP.declareChatCommand{
command = "vote"..CTeam.command,
description = "Vote to become " .. CTeam.name .. ".",
delay = 1.5,
condition = fn.FAnd
{
fn.If(
fn.Curry(isfunction, 2)(CTeam.RequiresVote),
fn.Curry(fn.Flip(fn.FOr{fn.Curry(fn.Const, 2)(CTeam.RequiresVote), fn.Curry(fn.Const, 2)(-1)}()), 2)(k),
fn.Curry(fn.Const, 2)(true)
)(),
fn.If(
fn.Curry(isnumber, 2)(CTeam.NeedToChangeFrom),
fn.Compose{fn.Curry(fn.Eq, 2)(CTeam.NeedToChangeFrom), plyMeta.Team},
fn.If(
fn.Curry(istable, 2)(CTeam.NeedToChangeFrom),
fn.Compose{fn.Curry(table.HasValue, 2)(CTeam.NeedToChangeFrom), plyMeta.Team},
fn.Curry(fn.Const, 2)(true)
)()
)(),
fn.If(
fn.Curry(isfunction, 2)(CTeam.customCheck),
CTeam.customCheck,
fn.Curry(fn.Const, 2)(true)
)(),
fn.Compose{fn.Curry(fn.Neq, 2)(k), plyMeta.Team},
fn.FOr {
fn.Curry(fn.Lte, 3)(CTeam.admin)(0),
fn.FAnd{fn.Curry(fn.Eq, 3)(CTeam.admin)(1), plyMeta.IsAdmin},
fn.FAnd{fn.Curry(fn.Gte, 3)(CTeam.admin)(2), plyMeta.IsSuperAdmin}
}
}
}
DarkRP.declareChatCommand{
command = CTeam.command,
description = "Become " .. CTeam.name .. " and skip the vote.",
delay = 1.5,
condition = fn.FAnd {
fn.FOr {
fn.Curry(fn.Flip(plyMeta.hasDarkRPPrivilege), 2)("rp_"..CTeam.command),
fn.FAnd {
fn.FOr {
fn.Curry(fn.Lte, 3)(CTeam.admin)(0),
fn.FAnd{fn.Curry(fn.Eq, 3)(CTeam.admin)(1), plyMeta.IsAdmin},
fn.FAnd{fn.Curry(fn.Gte, 3)(CTeam.admin)(2), plyMeta.IsSuperAdmin}
},
fn.If(
fn.Curry(isfunction, 2)(CTeam.RequiresVote),
fn.Curry(fn.Flip(fn.FOr{fn.Curry(fn.Const, 2)(CTeam.RequiresVote), fn.Curry(fn.Const, 2)(-1)}()), 2)(k),
fn.FOr {
fn.FAnd{fn.Curry(fn.Eq, 3)(CTeam.admin)(0), plyMeta.IsAdmin},
fn.FAnd{fn.Curry(fn.Eq, 3)(CTeam.admin)(1), plyMeta.IsSuperAdmin}
}
)()
}
},
fn.Compose{fn.Not, plyMeta.isArrested},
fn.If(
fn.Curry(isnumber, 2)(CTeam.NeedToChangeFrom),
fn.Compose{fn.Curry(fn.Eq, 2)(CTeam.NeedToChangeFrom), plyMeta.Team},
fn.If(
fn.Curry(istable, 2)(CTeam.NeedToChangeFrom),
fn.Compose{fn.Curry(table.HasValue, 2)(CTeam.NeedToChangeFrom), plyMeta.Team},
fn.Curry(fn.Const, 2)(true)
)()
)(),
fn.If(
fn.Curry(isfunction, 2)(CTeam.customCheck),
CTeam.customCheck,
fn.Curry(fn.Const, 2)(true)
)(),
fn.Compose{fn.Curry(fn.Neq, 2)(k), plyMeta.Team}
}
}
else
DarkRP.declareChatCommand{
command = CTeam.command,
description = "Become " .. CTeam.name .. ".",
delay = 1.5,
condition = fn.FAnd
{
fn.Compose{fn.Not, plyMeta.isArrested},
fn.If(
fn.Curry(isnumber, 2)(CTeam.NeedToChangeFrom),
fn.Compose{fn.Curry(fn.Eq, 2)(CTeam.NeedToChangeFrom), plyMeta.Team},
fn.If(
fn.Curry(istable, 2)(CTeam.NeedToChangeFrom),
fn.Compose{fn.Curry(table.HasValue, 2)(CTeam.NeedToChangeFrom), plyMeta.Team},
fn.Curry(fn.Const, 2)(true)
)()
)(),
fn.If(
fn.Curry(isfunction, 2)(CTeam.customCheck),
CTeam.customCheck,
fn.Curry(fn.Const, 2)(true)
)(),
fn.Compose{fn.Curry(fn.Neq, 2)(k), plyMeta.Team},
fn.FOr {
fn.Curry(fn.Lte, 3)(CTeam.admin)(0),
fn.FAnd{fn.Curry(fn.Eq, 3)(CTeam.admin)(1), plyMeta.IsAdmin},
fn.FAnd{fn.Curry(fn.Gte, 3)(CTeam.admin)(2), plyMeta.IsSuperAdmin}
}
}
}
end
end
local function addTeamCommands(CTeam, max)
if CLIENT then return end
if not GAMEMODE:CustomObjFitsMap(CTeam) then return end
local k = 0
for num,v in pairs(RPExtraTeams) do
if v.command == CTeam.command then
k = num
end
end
if CTeam.vote or CTeam.RequiresVote then
DarkRP.defineChatCommand("vote"..CTeam.command, function(ply)
if CTeam.RequiresVote and not CTeam.RequiresVote(ply, k) then
DarkRP.notify(ply, 1,4, DarkRP.getPhrase("job_doesnt_require_vote_currently"))
return ""
end
if CTeam.canStartVote and not CTeam.canStartVote(ply) then
local reason = isfunction(CTeam.canStartVoteReason) and CTeam.canStartVoteReason(ply, CTeam) or CTeam.canStartVoteReason or ""
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("unable", "/vote"..CTeam.command, reason))
return ""
end
if CTeam.admin == 1 and not ply:IsAdmin() then
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("need_admin", "/".."vote"..CTeam.command))
return ""
elseif CTeam.admin > 1 and not ply:IsSuperAdmin() then
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("need_sadmin", "/".."vote"..CTeam.command))
return ""
end
if type(CTeam.NeedToChangeFrom) == "number" and ply:Team() ~= CTeam.NeedToChangeFrom then
DarkRP.notify(ply, 1,4, DarkRP.getPhrase("need_to_be_before", team.GetName(CTeam.NeedToChangeFrom), CTeam.name))
return ""
elseif type(CTeam.NeedToChangeFrom) == "table" and not table.HasValue(CTeam.NeedToChangeFrom, ply:Team()) then
local teamnames = ""
for a,b in pairs(CTeam.NeedToChangeFrom) do teamnames = teamnames.." or "..team.GetName(b) end
DarkRP.notify(ply, 1,4, DarkRP.getPhrase("need_to_be_before", string.sub(teamnames, 5), CTeam.name))
return ""
end
if CTeam.customCheck and not CTeam.customCheck(ply) then
local message = isfunction(CTeam.CustomCheckFailMsg) and CTeam.CustomCheckFailMsg(ply, CTeam) or
CTeam.CustomCheckFailMsg or
DarkRP.getPhrase("unable", team.GetName(t), "")
DarkRP.notify(ply, 1, 4, message)
return ""
end
if not ply:changeAllowed(k) then
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("unable", "/vote"..CTeam.command, DarkRP.getPhrase("banned_or_demoted")))
return ""
end
if ply:Team() == k then
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("unable", CTeam.command, ""))
return ""
end
local max = CTeam.max
if max ~= 0 and ((max % 1 == 0 and team.NumPlayers(k) >= max) or (max % 1 ~= 0 and (team.NumPlayers(k) + 1) / #player.GetAll() > max)) then
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("team_limit_reached", CTeam.name))
return ""
end
if ply.LastJob and 10 - (CurTime() - ply.LastJob) >= 0 then
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("have_to_wait", math.ceil(10 - (CurTime() - ply.LastJob)), GAMEMODE.Config.chatCommandPrefix..CTeam.command))
return ""
end
if #player.GetAll() == 1 then
DarkRP.notify(ply, 0, 4, DarkRP.getPhrase("vote_alone"))
ply:changeTeam(k)
return ""
end
if CurTime() - ply:GetTable().LastVoteCop < 80 then
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("have_to_wait", math.ceil(80 - (CurTime() - ply:GetTable().LastVoteCop)), GAMEMODE.Config.chatCommandPrefix..CTeam.command))
return ""
end
DarkRP.createVote(DarkRP.getPhrase("wants_to_be", ply:Nick(), CTeam.name), "job", ply, 20, function(vote, choice)
local ply = vote.target
if not IsValid(ply) then return end
if choice >= 0 then
ply:changeTeam(k)
else
DarkRP.notifyAll(1, 4, DarkRP.getPhrase("has_not_been_made_team", ply:Nick(), CTeam.name))
end
end, nil, nil, {targetTeam = k})
ply:GetTable().LastVoteCop = CurTime()
return ""
end)
DarkRP.defineChatCommand(CTeam.command, function(ply)
if ply:hasDarkRPPrivilege("rp_"..CTeam.command) then
ply:changeTeam(k)
return ""
end
local a = CTeam.admin
if a > 0 and not ply:IsAdmin()
or a > 1 and not ply:IsSuperAdmin()
then
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("need_admin", CTeam.name))
return ""
end
if not CTeam.RequiresVote and
(a == 0 and not ply:IsAdmin()
or a == 1 and not ply:IsSuperAdmin()
or a == 2)
or CTeam.RequiresVote and CTeam.RequiresVote(ply, k)
then
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("need_to_make_vote", CTeam.name))
return ""
end
ply:changeTeam(k)
return ""
end)
else
DarkRP.defineChatCommand(CTeam.command, function(ply)
if CTeam.admin == 1 and not ply:IsAdmin() then
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("need_admin", "/"..CTeam.command))
return ""
end
if CTeam.admin > 1 and not ply:IsSuperAdmin() then
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("need_sadmin", "/"..CTeam.command))
return ""
end
ply:changeTeam(k)
return ""
end)
end
concommand.Add("rp_"..CTeam.command, function(ply, cmd, args)
if ply:EntIndex() ~= 0 and not ply:IsAdmin() then
ply:PrintMessage(HUD_PRINTCONSOLE, DarkRP.getPhrase("need_admin", cmd))
return
end
if CTeam.admin > 1 and not ply:IsSuperAdmin() and ply:EntIndex() ~= 0 then
ply:PrintMessage(HUD_PRINTCONSOLE, DarkRP.getPhrase("need_sadmin", cmd))
return
end
if CTeam.vote then
if CTeam.admin >= 1 and ply:EntIndex() ~= 0 and not ply:IsSuperAdmin() then
ply:PrintMessage(HUD_PRINTCONSOLE, DarkRP.getPhrase("need_sadmin", cmd))
return
elseif CTeam.admin > 1 and ply:IsSuperAdmin() and ply:EntIndex() ~= 0 then
ply:PrintMessage(HUD_PRINTCONSOLE, DarkRP.getPhrase("need_to_make_vote", CTeam.name))
return
end
end
if not args or not args[1] then
DarkRP.printConsoleMessage(ply, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
return
end
local target = DarkRP.findPlayer(args[1])
if (target) then
target:changeTeam(k, true)
local nick
if (ply:EntIndex() ~= 0) then
nick = ply:Nick()
else
nick = "Console"
end
DarkRP.notify(target, 0, 4, DarkRP.getPhrase("x_made_you_a_y", nick, CTeam.name))
else
DarkRP.printConsoleMessage(ply, DarkRP.getPhrase("could_not_find", tostring(args[1])))
end
end)
end
local function addEntityCommands(tblEnt)
DarkRP.declareChatCommand{
command = tblEnt.cmd,
description = "Purchase a " .. tblEnt.name,
delay = 2,
condition = fn.FAnd
{
fn.Compose{fn.Not, plyMeta.isArrested},
fn.If(
fn.Curry(istable, 2)(tblEnt.allowed),
fn.Compose{fn.Curry(table.HasValue, 2)(tblEnt.allowed), plyMeta.Team},
fn.Curry(fn.Const, 2)(true)
)(),
fn.If(
fn.Curry(isfunction, 2)(tblEnt.customCheck),
tblEnt.customCheck,
fn.Curry(fn.Const, 2)(true)
)(),
fn.Curry(fn.Flip(plyMeta.canAfford), 2)(tblEnt.price)
}
}
if CLIENT then return end
-- Default spawning function of an entity
-- used if tblEnt.spawn is not defined
local function defaultSpawn(ply, tr, tblEnt)
local ent = ents.Create(tblEnt.ent)
if not ent:IsValid() then error("Entity '"..tblEnt.ent.."' does not exist or is not valid.") end
ent.dt = ent.dt or {}
ent.dt.owning_ent = ply
if ent.Setowning_ent then ent:Setowning_ent(ply) end
ent:SetPos(tr.HitPos)
-- These must be set before :Spawn()
ent.SID = ply.SID
ent.allowed = tblEnt.allowed
ent.DarkRPItem = tblEnt
ent:Spawn()
local phys = ent:GetPhysicsObject()
if phys:IsValid() then phys:Wake() end
return ent
end
local function buythis(ply, args)
if ply:isArrested() then return "" end
if type(tblEnt.allowed) == "table" and not table.HasValue(tblEnt.allowed, ply:Team()) then
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("incorrect_job", tblEnt.cmd))
return ""
end
if tblEnt.customCheck and not tblEnt.customCheck(ply) then
local message = isfunction(tblEnt.CustomCheckFailMsg) and tblEnt.CustomCheckFailMsg(ply, tblEnt) or
tblEnt.CustomCheckFailMsg or
DarkRP.getPhrase("not_allowed_to_purchase")
DarkRP.notify(ply, 1, 4, message)
return ""
end
if ply:customEntityLimitReached(tblEnt) then
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("limit", tblEnt.cmd))
return ""
end
local canbuy, suppress, message, price = hook.Call("canBuyCustomEntity", nil, ply, tblEnt)
local cost = price or tblEnt.getPrice and tblEnt.getPrice(ply, tblEnt.price) or tblEnt.price
if not ply:canAfford(cost) then
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("cant_afford", tblEnt.cmd))
return ""
end
if canbuy == false then
if not suppress and message then DarkRP.notify(ply, 1, 4, message) end
return ""
end
ply:addMoney(-cost)
local trace = {}
trace.start = ply:EyePos()
trace.endpos = trace.start + ply:GetAimVector() * 85
trace.filter = ply
local tr = util.TraceLine(trace)
local ent = (tblEnt.spawn or defaultSpawn)(ply, tr, tblEnt)
ent.onlyremover = true
-- Repeat these properties to alleviate work in tblEnt.spawn:
ent.SID = ply.SID
ent.allowed = tblEnt.allowed
ent.DarkRPItem = tblEnt
hook.Call("playerBoughtCustomEntity", nil, ply, tblEnt, ent, cost)
DarkRP.notify(ply, 0, 4, DarkRP.getPhrase("you_bought", tblEnt.name, DarkRP.formatMoney(cost), ""))
ply:addCustomEntity(tblEnt)
return ""
end
DarkRP.defineChatCommand(tblEnt.cmd, buythis)
end
RPExtraTeams = {}
local jobByCmd = {}
DarkRP.getJobByCommand = function(cmd)
if not jobByCmd[cmd] then return nil, nil end
return RPExtraTeams[jobByCmd[cmd]], jobByCmd[cmd]
end
plyMeta.getJobTable = fn.FOr{fn.Compose{fn.Curry(fn.Flip(fn.GetValue), 2)(RPExtraTeams), plyMeta.Team}, fn.Curry(fn.Id, 2)({})}
local jobCount = 0
function DarkRP.createJob(Name, colorOrTable, model, Description, Weapons, command, maximum_amount_of_this_class, Salary, admin, Vote, Haslicense, NeedToChangeFrom, CustomCheck)
local tableSyntaxUsed = not IsColor(colorOrTable)
local CustomTeam = tableSyntaxUsed and colorOrTable or
{color = colorOrTable, model = model, description = Description, weapons = Weapons, command = command,
max = maximum_amount_of_this_class, salary = Salary, admin = admin or 0, vote = tobool(Vote), hasLicense = Haslicense,
NeedToChangeFrom = NeedToChangeFrom, customCheck = CustomCheck
}
CustomTeam.name = Name
CustomTeam.default = DarkRP.DARKRP_LOADING
-- Disabled job
if DarkRP.DARKRP_LOADING and DarkRP.disabledDefaults["jobs"][CustomTeam.command] then return end
local valid, err, hints = checkValid(CustomTeam, requiredTeamItems)
if not valid then DarkRP.error(string.format("Corrupt team: %s!
%s", CustomTeam.name or "", err), 3, hints) end
jobCount = jobCount + 1
CustomTeam.team = jobCount
CustomTeam.salary = math.floor(CustomTeam.salary)
CustomTeam.customCheck = CustomTeam.customCheck and fp{DarkRP.simplerrRun, CustomTeam.customCheck}
CustomTeam.CustomCheckFailMsg = isfunction(CustomTeam.CustomCheckFailMsg) and fp{DarkRP.simplerrRun, CustomTeam.CustomCheckFailMsg} or CustomTeam.CustomCheckFailMsg
CustomTeam.CanPlayerSuicide = CustomTeam.CanPlayerSuicide and fp{DarkRP.simplerrRun, CustomTeam.CanPlayerSuicide}
CustomTeam.PlayerCanPickupWeapon = CustomTeam.PlayerCanPickupWeapon and fp{DarkRP.simplerrRun, CustomTeam.PlayerCanPickupWeapon}
CustomTeam.PlayerDeath = CustomTeam.PlayerDeath and fp{DarkRP.simplerrRun, CustomTeam.PlayerDeath}
CustomTeam.PlayerLoadout = CustomTeam.PlayerLoadout and fp{DarkRP.simplerrRun, CustomTeam.PlayerLoadout}
CustomTeam.PlayerSelectSpawn = CustomTeam.PlayerSelectSpawn and fp{DarkRP.simplerrRun, CustomTeam.PlayerSelectSpawn}
CustomTeam.PlayerSetModel = CustomTeam.PlayerSetModel and fp{DarkRP.simplerrRun, CustomTeam.PlayerSetModel}
CustomTeam.PlayerSpawn = CustomTeam.PlayerSpawn and fp{DarkRP.simplerrRun, CustomTeam.PlayerSpawn}
CustomTeam.PlayerSpawnProp = CustomTeam.PlayerSpawnProp and fp{DarkRP.simplerrRun, CustomTeam.PlayerSpawnProp}
CustomTeam.RequiresVote = CustomTeam.RequiresVote and fp{DarkRP.simplerrRun, CustomTeam.RequiresVote}
CustomTeam.ShowSpare1 = CustomTeam.ShowSpare1 and fp{DarkRP.simplerrRun, CustomTeam.ShowSpare1}
CustomTeam.ShowSpare2 = CustomTeam.ShowSpare2 and fp{DarkRP.simplerrRun, CustomTeam.ShowSpare2}
CustomTeam.canStartVote = CustomTeam.canStartVote and fp{DarkRP.simplerrRun, CustomTeam.canStartVote}
jobByCmd[CustomTeam.command] = table.insert(RPExtraTeams, CustomTeam)
DarkRP.addToCategory(CustomTeam, "jobs", CustomTeam.category)
team.SetUp(#RPExtraTeams, Name, CustomTeam.color)
local Team = #RPExtraTeams
timer.Simple(0, function()
declareTeamCommands(CustomTeam)
addTeamCommands(CustomTeam, CustomTeam.max)
end)
// Precache model here. Not right before the job change is done
if type(CustomTeam.model) == "table" then
for k,v in pairs(CustomTeam.model) do util.PrecacheModel(v) end
else
util.PrecacheModel(CustomTeam.model)
end
return Team
end
AddExtraTeam = DarkRP.createJob
local function removeCustomItem(tbl, category, hookName, reloadF4, i)
local item = tbl*
tbl* = nil
if category then DarkRP.removeFromCategory(item, category) end
if istable(item) and (item.command or item.cmd) then DarkRP.removeChatCommand(item.command or item.cmd) end
hook.Run(hookName, i, item)
if CLIENT and reloadF4 and ValidPanel(DarkRP.getF4MenuPanel()) then DarkRP.getF4MenuPanel():Remove() end -- Rebuild entire F4 menu frame
end
function DarkRP.removeJob(i)
local job = RPExtraTeams*
jobByCmd[job.command] = nil
jobCount = jobCount - 1
DarkRP.removeChatCommand("vote" .. job.command)
removeCustomItem(RPExtraTeams, "jobs", "onJobRemoved", true, i)
end
RPExtraTeamDoors = {}
function DarkRP.createEntityGroup(name, ...)
if DarkRP.DARKRP_LOADING and DarkRP.disabledDefaults["doorgroups"][name] then return end
RPExtraTeamDoors[name] = {...}
RPExtraTeamDoors[name].name = name
end
AddDoorGroup = DarkRP.createEntityGroup
DarkRP.removeEntityGroup = fp{removeCustomItem, RPExtraTeamDoors, nil, "onEntityGroupRemoved", false}
CustomVehicles = {}
CustomShipments = {}
local shipByName = {}
DarkRP.getShipmentByName = function(name)
name = string.lower(name or "")
if not shipByName[name] then return nil, nil end
return CustomShipments[shipByName[name]], shipByName[name]
end
function DarkRP.createShipment(name, model, entity, price, Amount_of_guns_in_one_shipment, Sold_seperately, price_seperately, noshipment, classes, shipmodel, CustomCheck)
local tableSyntaxUsed = type(model) == "table"
local price = tonumber(price)
local shipmentmodel = shipmodel or "models/Items/item_item_crate.mdl"
local customShipment = tableSyntaxUsed and model or
{model = model, entity = entity, price = price, amount = Amount_of_guns_in_one_shipment,
seperate = Sold_seperately, pricesep = price_seperately, noship = noshipment, allowed = classes,
shipmodel = shipmentmodel, customCheck = CustomCheck, weight = 5}
if customShipment.separate ~= nil then
customShipment.seperate = customShipment.separate
end
if customShipment.allowed == nil then
customShipment.allowed = {}
for k,v in pairs(team.GetAllTeams()) do
table.insert(customShipment.allowed, k)
end
end
customShipment.name = name
customShipment.default = DarkRP.DARKRP_LOADING
if DarkRP.DARKRP_LOADING and DarkRP.disabledDefaults["shipments"][customShipment.name] then return end
local valid, err, hints = checkValid(customShipment, validShipment)
if not valid then DarkRP.error(string.format("Corrupt shipment: %s!
%s", name or "", err), 3, hints) end
customShipment.allowed = isnumber(customShipment.allowed) and {customShipment.allowed} or customShipment.allowed
customShipment.customCheck = customShipment.customCheck and fp{DarkRP.simplerrRun, customShipment.customCheck}
CustomVehicles.CustomCheckFailMsg = isfunction(CustomVehicles.CustomCheckFailMsg) and fp{DarkRP.simplerrRun, CustomVehicles.CustomCheckFailMsg} or CustomVehicles.CustomCheckFailMsg
if not customShipment.noship then DarkRP.addToCategory(customShipment, "shipments", customShipment.category) end
if customShipment.seperate then DarkRP.addToCategory(customShipment, "weapons", customShipment.category) end
shipByName[string.lower(name or "")] = table.insert(CustomShipments, customShipment)
util.PrecacheModel(customShipment.model)
end
AddCustomShipment = DarkRP.createShipment
function DarkRP.removeShipment(i)
local ship = CustomShipments*
shipByName[ship.name] = nil
removeCustomItem(CustomShipments, "shipments", "onShipmentRemoved", true, i)
end
function DarkRP.createVehicle(Name_of_vehicle, model, price, Jobs_that_can_buy_it, customcheck)
local vehicle = istable(Name_of_vehicle) and Name_of_vehicle or
{name = Name_of_vehicle, model = model, price = price, allowed = Jobs_that_can_buy_it, customCheck = customcheck}
vehicle.default = DarkRP.DARKRP_LOADING
if DarkRP.DARKRP_LOADING and DarkRP.disabledDefaults["vehicles"][vehicle.name] then return end
local found = false
for k,v in pairs(DarkRP.getAvailableVehicles()) do
if string.lower(k) == string.lower(vehicle.name) then found = true break end
end
local valid, err, hints = checkValid(vehicle, validVehicle)
if not valid then DarkRP.error(string.format("Corrupt vehicle: %s!
%s", vehicle.name or "", err), 3, hints) end
if not found then DarkRP.error("Vehicle invalid: " .. vehicle.name .. ". Unknown vehicle name.", 3) end
CustomVehicles.customCheck = CustomVehicles.customCheck and fp{DarkRP.simplerrRun, CustomVehicles.customCheck}
CustomVehicles.CustomCheckFailMsg = isfunction(CustomVehicles.CustomCheckFailMsg) and fp{DarkRP.simplerrRun, CustomVehicles.CustomCheckFailMsg} or CustomVehicles.CustomCheckFailMsg
table.insert(CustomVehicles, vehicle)
DarkRP.addToCategory(vehicle, "vehicles", vehicle.category)
end
AddCustomVehicle = DarkRP.createVehicle
DarkRP.removeVehicle = fp{removeCustomItem, CustomVehicles, "vehicles", "onVehicleRemoved", true}
/*---------------------------------------------------------------------------
Decides whether a custom job or shipmet or whatever can be used in a certain map
---------------------------------------------------------------------------*/
function GM:CustomObjFitsMap(obj)
if not obj or not obj.maps then return true end
local map = string.lower(game.GetMap())
for k,v in pairs(obj.maps) do
if string.lower(v) == map then return true end
end
return false
end
DarkRPEntities = {}
function DarkRP.createEntity(name, entity, model, price, max, command, classes, CustomCheck)
local tableSyntaxUsed = type(entity) == "table"
local tblEnt = tableSyntaxUsed and entity or
{ent = entity, model = model, price = price, max = max,
cmd = command, allowed = classes, customCheck = CustomCheck}
tblEnt.name = name
tblEnt.default = DarkRP.DARKRP_LOADING
if DarkRP.DARKRP_LOADING and DarkRP.disabledDefaults["entities"][tblEnt.name] then return end
if type(tblEnt.allowed) == "number" then
tblEnt.allowed = {tblEnt.allowed}
end
local valid, err, hints = checkValid(tblEnt, validEntity)
if not valid then DarkRP.error(string.format("Corrupt entity: %s!
%s", name or "", err), 3, hints) end
tblEnt.customCheck = tblEnt.customCheck and fp{DarkRP.simplerrRun, tblEnt.customCheck}
tblEnt.CustomCheckFailMsg = isfunction(tblEnt.CustomCheckFailMsg) and fp{DarkRP.simplerrRun, tblEnt.CustomCheckFailMsg} or tblEnt.CustomCheckFailMsg
tblEnt.getPrice = tblEnt.getPrice and fp{DarkRP.simplerrRun, tblEnt.getPrice}
tblEnt.getMax = tblEnt.getMax and fp{DarkRP.simplerrRun, tblEnt.getMax}
tblEnt.spawn = tblEnt.spawn and fp{DarkRP.simplerrRun, tblEnt.spawn}
-- if SERVER and FPP then
-- FPP.AddDefaultBlocked(blockTypes, tblEnt.ent)
-- end
table.insert(DarkRPEntities, tblEnt)
DarkRP.addToCategory(tblEnt, "entities", tblEnt.category)
timer.Simple(0, function() addEntityCommands(tblEnt) end)
end
AddEntity = DarkRP.createEntity
DarkRP.removeEntity = fp{removeCustomItem, DarkRPEntities, "entities", "onEntityRemoved", true}
-- here for backwards compatibility
DarkRPAgendas = {}
local agendas = {}
-- Returns the agenda managed by the player
plyMeta.getAgenda = fn.Compose{fn.Curry(fn.Flip(fn.GetValue), 2)(DarkRPAgendas), plyMeta.Team}
-- Returns the agenda this player is member of
function plyMeta:getAgendaTable()
return agendas[self:Team()]
end
DarkRP.getAgendas = fp{fn.Id, agendas}
function DarkRP.createAgenda(Title, Manager, Listeners)
if DarkRP.DARKRP_LOADING and DarkRP.disabledDefaults["agendas"][Title] then return end
local agenda = {Manager = Manager, Title = Title, Listeners = Listeners, ManagersByKey = {}}
agenda.default = DarkRP.DARKRP_LOADING
local valid, err, hints = checkValid(agenda, validAgenda)
if not valid then DarkRP.error(string.format("Corrupt agenda: %s!
%s", agenda.Title or "", err), 2, hints) end
for k,v in pairs(Listeners) do
agendas[v] = agenda
end
for k,v in pairs(istable(Manager) and Manager or {Manager}) do
agendas[v] = agenda
DarkRPAgendas[v] = agenda -- backwards compat
agenda.ManagersByKey[v] = true
end
if SERVER then
timer.Simple(0, function()
-- Run after scripts have loaded
agenda.text = hook.Run("agendaUpdated", nil, agenda, "")
end)
end
end
AddAgenda = DarkRP.createAgenda
function DarkRP.removeAgenda(title)
local agenda
for k,v in pairs(agendas) do
if v.Title == title then
agenda = v
agendas[k] = nil
end
end
for k,v in pairs(DarkRPAgendas) do
if v.Title == title then agendas[k] = nil end
end
hook.Run("onAgendaRemoved", title, agenda)
end
GM.DarkRPGroupChats = {}
local groupChatNumber = 0
function DarkRP.createGroupChat(funcOrTeam, ...)
local gm = GM or GAMEMODE
gm.DarkRPGroupChats = gm.DarkRPGroupChats or {}
if DarkRP.DARKRP_LOADING then
groupChatNumber = groupChatNumber + 1
if DarkRP.disabledDefaults["groupchat"][groupChatNumber] then return end
end
-- People can enter either functions or a list of teams as parameter(s)
if type(funcOrTeam) == "function" then
table.insert(gm.DarkRPGroupChats, fp{DarkRP.simplerrRun, funcOrTeam})
else
local teams = {funcOrTeam, ...}
table.insert(gm.DarkRPGroupChats, function(ply) return table.HasValue(teams, ply:Team()) end)
end
end
GM.AddGroupChat = function(GM, ...) DarkRP.createGroupChat(...) end
DarkRP.removeGroupChat = fp{removeCustomItem, GM.DarkRPGroupChats, nil, "onGroupChatRemoved", false}
DarkRP.getGroupChats = fp{fn.Id, GM.DarkRPGroupChats}
GM.AmmoTypes = {}
function DarkRP.createAmmoType(ammoType, name, model, price, amountGiven, customCheck)
local gm = GM or GAMEMODE
gm.AmmoTypes = gm.AmmoTypes or {}
local ammo = istable(name) and name or {
name = name,
model = model,
price = price,
amountGiven = amountGiven,
customCheck = customCheck
}
ammo.ammoType = ammoType
ammo.default = DarkRP.DARKRP_LOADING
if DarkRP.DARKRP_LOADING and DarkRP.disabledDefaults["ammo"][ammo.name] then return end
ammo.customCheck = ammo.customCheck and fp{DarkRP.simplerrRun, ammo.customCheck}
ammo.CustomCheckFailMsg = isfunction(ammo.CustomCheckFailMsg) and fp{DarkRP.simplerrRun, ammo.CustomCheckFailMsg} or ammo.CustomCheckFailMsg
ammo.id = table.insert(gm.AmmoTypes, ammo)
DarkRP.addToCategory(ammo, "ammo", ammo.category)
end
GM.AddAmmoType = function(GM, ...) DarkRP.createAmmoType(...) end
DarkRP.removeAmmoType = fp{removeCustomItem, GM.AmmoTypes, "ammo", "onAmmoTypeRemoved", true}
local demoteGroups = {}
function DarkRP.createDemoteGroup(name, tbl)
if DarkRP.DARKRP_LOADING and DarkRP.disabledDefaults["demotegroups"][name] then return end
if not tbl or not tbl[1] then error("No members in the demote group!") end
local set = demoteGroups[tbl[1]] or disjoint.MakeSet(tbl[1])
set.name = name
for i = 2, #tbl do
set = (demoteGroups[tbl*] or disjoint.MakeSet(tbl*)) + set
set.name = name
end
for _, teamNr in pairs(tbl) do
if demoteGroups[teamNr] then
-- Unify the sets if there was already one there
demoteGroups[teamNr] = demoteGroups[teamNr] + set
else
demoteGroups[teamNr] = set
end
end
end
function DarkRP.removeDemoteGroup(name)
local foundSet
for k,v in pairs(demoteGroups) do
local set = disjoint.FindSet(v)
if set.name == name then
foundSet = set
demoteGroups[k] = nil
end
end
hook.Run("onDemoteGroupRemoved", name, foundSet)
end
function DarkRP.getDemoteGroup(teamNr)
demoteGroups[teamNr] = demoteGroups[teamNr] or disjoint.MakeSet(teamNr)
return disjoint.FindSet(demoteGroups[teamNr])
end
DarkRP.getDemoteGroups = fp{fn.Id, demoteGroups}
local categories = {
jobs = {},
entities = {},
shipments = {},
weapons = {},
vehicles = {},
ammo = {},
}
local categoriesMerged = false -- whether categories and custom items are merged.
DarkRP.getCategories = fp{fn.Id, categories}
local categoryOrder = function(a, b)
local aso = a.sortOrder or 100
local bso = b.sortOrder or 100
return aso < bso or aso == bso and a.name < b.name
end
local function insertCategory(destination, tbl)
table.insert(destination, tbl)
local i = #destination
while i > 1 do
if categoryOrder(destination[i - 1], tbl) then break end
destination[i - 1], destination* = destination*, destination[i - 1]
i = i - 1
end
end
function DarkRP.createCategory(tbl)
local valid, err, hints = checkValid(tbl, validCategory)
if not valid then DarkRP.error(string.format("Corrupt category: %s!
%s", tbl.name or "", err), 2, hints) end
tbl.members = {}
local destination = categories[tbl.categorises]
insertCategory(destination, tbl)
-- Too many people made the mistake of not creating a category for weapons as well as shipments
-- when having shipments that can also be sold separately.
if tbl.categorises == "shipments" then
insertCategory(categories.weapons, table.Copy(tbl))
end
end
function DarkRP.addToCategory(item, kind, cat)
cat = cat or "Other"
item.category = cat
-- The merge process will take care of the category:
if not categoriesMerged then return end
-- Post-merge: manual insertion into category
local cats = categories[kind]
for _, c in ipairs(cats) do
if c.name ~= cat then continue end
insertCategory(c.members, item)
return
end
DarkRP.error(string.format([[The category of "%s" ("%s") does not exist!]], item.name, cat), 2, {
"Make sure the category is created with DarkRP.createCategory.",
"The category name is case sensitive!",
"Categories must be created before DarkRP finished loading.",
})
end
function DarkRP.removeFromCategory(item, kind)
local cats = categories[kind]
if not cats then DarkRP.error(string.format("Invalid category kind '%s'.", kind), 2) end
local cat = item.category
if not cat then return end
for _, v in pairs(cats) do
if v.name ~= item.category then continue end
for k, mem in pairs(v.members) do
if mem ~= item then continue end
table.remove(v.members, k)
break
end
break
end
end
-- Assign custom stuff to their categories
local function mergeCategories(customs, catKind, path)
local categories = categories[catKind]
local catByName = {}
for k,v in pairs(categories) do catByName[v.name] = v end
for k,v in pairs(customs) do
-- Override default thing categories:
local catName = v.default and GAMEMODE.Config.CategoryOverride[catKind][v.name] or v.category or "Other"
local cat = catByName[catName]
if not cat then
DarkRP.error(string.format([[The category of "%s" ("%s") does not exist!]], v.name, catName), 1, {
"Make sure the category is created with DarkRP.createCategory.",
"The category name is case sensitive!",
"Categories must be created before DarkRP finished loading."
}, path, -1, path)
end
cat.members = cat.members or {}
table.insert(cat.members, v)
end
-- Sort category members
for k,v in pairs(categories) do table.sort(v.members, categoryOrder) end
end
hook.Add("loadCustomDarkRPItems", "mergeCategories", function()
local shipments = fn.Filter(fc{fn.Not, fp{fn.GetValue, "noship"}}, CustomShipments)
local guns = fn.Filter(fp{fn.GetValue, "seperate"}, CustomShipments)
mergeCategories(RPExtraTeams, "jobs", "your jobs")
mergeCategories(DarkRPEntities, "entities", "your custom entities")
mergeCategories(shipments, "shipments", "your custom shipments")
mergeCategories(guns, "weapons", "your custom weapons")
mergeCategories(CustomVehicles, "vehicles", "your custom vehicles")
mergeCategories(GAMEMODE.AmmoTypes, "ammo", "your custom ammo")
categoriesMerged = true
end)
Thanks, ladies and gentlemen.