diff --git a/[gameplay]/freeroam/data/.gitignore b/[gameplay]/freeroam/data/.gitignore
new file mode 100644
index 000000000..c9dfa34de
--- /dev/null
+++ b/[gameplay]/freeroam/data/.gitignore
@@ -0,0 +1,7 @@
+# Newmodels specific
+#
+# These files will be modified by the newmodels freeroam tool (new models added)
+# and we don't want these changes to be tracked by git.
+#
+skins.xml
+vehicles.xml
diff --git a/[gameplay]/freeroam/fr_client.lua b/[gameplay]/freeroam/fr_client.lua
index 38cd74318..48178b5ae 100644
--- a/[gameplay]/freeroam/fr_client.lua
+++ b/[gameplay]/freeroam/fr_client.lua
@@ -1521,15 +1521,13 @@ function createVehicleCommand(cmd, ...)
return errMsg("Invalid vehicle model!")
end
+ --[[ -- We won't be using these verifications anymore, since we can now spawn vehicles with custom new models
if string.len(table.concat(args, " ")) > 25 or tonumber(vehID) and string.len(vehID) > 3 then
return errMsg("Invalid vehicle model!")
end
+ --]]
- if vehID and vehID >= 400 and vehID <= 611 then
- server.giveMeVehicles(vehID)
- else
- errMsg("Invalid vehicle model!")
- end
+ server.giveMeVehicles(vehID)
end
addCommandHandler('createvehicle', createVehicleCommand)
addCommandHandler('cv', createVehicleCommand)
diff --git a/[gameplay]/freeroam/fr_server.lua b/[gameplay]/freeroam/fr_server.lua
index ed1ca06af..5f85937a8 100644
--- a/[gameplay]/freeroam/fr_server.lua
+++ b/[gameplay]/freeroam/fr_server.lua
@@ -287,7 +287,6 @@ addEventHandler('onPlayerGravInit', root,
function setMySkin(skinid)
if not isElement(client) then return end
- if getElementModel(client) == skinid then return end
if isPedDead(client) then
local x, y, z = getElementPosition(client)
@@ -296,15 +295,14 @@ function setMySkin(skinid)
y = 0
z = 3
end
-
+
local r = getPedRotation(client)
local interior = getElementInterior(client)
- spawnPlayer(client, x, y, z, r, skinid)
+ spawnPlayer(client, x, y, z, r, 0)
setElementInterior(client, interior)
setCameraInterior(client, interior)
- else
- setElementModel(client, skinid)
end
+ exports["newmodels-engine"]:setElementModel(client, skinid)
setCameraTarget(client, client)
setCameraInterior(client, getElementInterior(client))
end
@@ -395,14 +393,14 @@ function giveMeVehicles(vehID)
if not table.find(getOption('vehicles.disallowed'), vehID) then
if #vehicleList >= getOption('vehicles.maxperplayer') then unloadVehicle(vehicleList[1]) end
local vehPos = posVector+vehMatrix.right*3
- local vehicle = Vehicle(vehID, vehPos, rotVector) or false
+ local vehicle = exports["newmodels-engine"]:createVehicle(vehID, vehPos, rotVector) or false
if vehicle then
vehicle.interior = player.interior
vehicle.dimension = player.dimension
if vehicle.vehicleType == "Bike" then vehicle.velocity = Vector3(0,0,-0.01) end
table.insert(vehicleList, vehicle)
g_VehicleData[vehicle] = { creator = player, timers = {} }
- if g_Trailers[vehID] then
+ if g_Trailers[getElementModel(vehicle)] then
if getOption('vehicles.maxidletime') >= 0 then
if getOption('vehicles.idleexplode') then
g_VehicleData[vehicle].timers.fire = setTimer(commitArsonOnVehicle, getOption('vehicles.maxidletime'), 1, vehicle)
diff --git a/[gameplay]/freeroam_newmodels/core_s.lua b/[gameplay]/freeroam_newmodels/core_s.lua
new file mode 100644
index 000000000..dcc3fd0da
--- /dev/null
+++ b/[gameplay]/freeroam_newmodels/core_s.lua
@@ -0,0 +1,321 @@
+--[[
+ core_s.lua
+
+ Main server script for the New Models Freeroam system.
+
+ Author: https://github.com/Fernando-A-Rocha
+]]
+
+----------- GENERAL SCRIPT CONFIGURATION -----------
+
+COMMAND_NAMES = { "newmodelsfreeroam", "newmodels_freeroam", "freeroam_newmodels", "freeroamnewmodels" }
+
+function canUseTool(player)
+ return hasObjectPermissionTo(player, "command.start", false) and hasObjectPermissionTo(player, "command.stop", false)
+end
+
+local FREEROAM_GUI_XML_GROUP_NAMES = {
+ ["vehicles"] = "New Models",
+ ["skins"] = "New Models",
+}
+
+----------- FUNCTIONALITIES -----------
+
+local waitingFreeroamStop = nil
+
+local function getModelGroupNames(rootChildrenNodes, theType)
+ local groupNames = {} -- [id] = name
+ local function getGroupNames(node)
+ if xmlNodeGetName(node) == "group" then
+ local children = xmlNodeGetChildren(node)
+ if children then
+ local parentName = xmlNodeGetAttribute(node, "name")
+ if parentName then
+ for i, child in ipairs(children) do
+ local id = tonumber(xmlNodeGetAttribute(child, "id"))
+ if id then
+ if groupNames[id] then
+ -- outputDebugString("Model "..id.." is already in group "..groupNames[id]..", skipping.", 2)
+ return
+ end
+ groupNames[id] = parentName
+ end
+ if xmlNodeGetName(child) == "group" then
+ getGroupNames(child)
+ end
+ end
+ end
+ end
+ end
+ end
+ for i, node in ipairs(rootChildrenNodes) do
+ getGroupNames(node)
+ end
+ return groupNames
+end
+
+--[[
+ Updates the freeroam vehicle & skins XML list with thew new models.
+ This cannot be done live when freeroam is running because the XML files are
+ loaded as config files in meta.xml and it would be a mess to reload them.
+]]
+local function updateFreeroamGUIFiles()
+ local VEHICLES_PATH = ":freeroam/data/vehicles.xml"
+ local SKINS_PATH = ":freeroam/data/skins.xml"
+
+ local groupNames = {}
+ if not FREEROAM_GUI_XML_GROUP_NAMES then
+ return false, "The freeroam GUI group names are not defined."
+ end
+ if not FREEROAM_GUI_XML_GROUP_NAMES["vehicles"] then
+ return false, "The freeroam GUI vehicles group name is not defined."
+ end
+ if not FREEROAM_GUI_XML_GROUP_NAMES["skins"] then
+ return false, "The freeroam GUI skins group name is not defined."
+ end
+ groupNames["vehicles"] = FREEROAM_GUI_XML_GROUP_NAMES["vehicles"]
+ groupNames["skins"] = FREEROAM_GUI_XML_GROUP_NAMES["skins"]
+
+ if not (fileExists(VEHICLES_PATH) and fileExists(SKINS_PATH)) then
+ return false, "The freeroam GUI files could not be found."
+ end
+
+ local allMods = exports.newmodels:getModList()
+ if not allMods then
+ return false, "Failed to get the newmodels mod list."
+ end
+
+ local modsList = {
+ ["vehicles"] = {},
+ ["skins"] = {}
+ }
+
+ for elementType, mods in pairs(allMods) do
+ if type(elementType) ~= "string" or type(mods) ~= "table" then
+ return false, "The newmodels mod list is not valid."
+ end
+ for i, mod in ipairs(mods) do
+ if type(mod) ~= "table" then
+ return false, "Mod #"..i.." is not a table."
+ end
+ local modID = mod.id
+ local modBaseID = mod.base_id
+ local modName = mod.name
+
+ if type(modID) ~= "number" or type(modBaseID) ~= "number" or type(modName) ~= "string" then
+ return false, "Mod #"..i.." is not valid."
+ end
+
+ if elementType == "vehicle" then
+ table.insert(modsList["vehicles"], {id = modID, base_id = modBaseID, name = modName})
+ elseif elementType == "ped" then
+ table.insert(modsList["skins"], {id = modID, base_id = modBaseID, name = modName})
+ end
+ end
+ end
+
+ local function addNewModels(theType)
+
+ local path = VEHICLES_PATH
+ if theType == "skins" then
+ path = SKINS_PATH
+ end
+
+ local xmlRoot = xmlLoadFile(path)
+ if not xmlRoot then
+ return false, "Failed to load file: "..path
+ end
+ local groupNodes = xmlNodeGetChildren(xmlRoot)
+ if not groupNodes then
+ return false, "Failed to get the group nodes from file: "..path
+ end
+ for i, groupNode in ipairs(groupNodes) do
+ local groupName = xmlNodeGetAttribute(groupNode, "name")
+ if groupName and groupName == groupNames[theType] then
+ xmlDestroyNode(groupNode)
+ break
+ end
+ end
+ groupNodes = xmlNodeGetChildren(xmlRoot)
+ if not groupNodes then
+ return false, "Failed to get the group nodes from file: "..path
+ end
+ local defaultGroupNames = getModelGroupNames(groupNodes, xmlNodeGetAttribute(xmlRoot, "type"))
+
+ local parentGroupNode = xmlCreateChild(xmlRoot, "group")
+ xmlNodeSetAttribute(parentGroupNode, "name", groupNames[theType])
+ local usedGroupNames = {}
+ local usedModelGroupNames = {}
+ for i, mod in ipairs(modsList[theType]) do
+ local modelBaseID = mod.base_id
+ local groupName = defaultGroupNames[modelBaseID]
+ if not groupName then
+ groupName = "Other"
+ end
+ if not usedGroupNames[groupName] then
+ usedGroupNames[groupName] = true
+ end
+ usedModelGroupNames[modelBaseID] = groupName
+ end
+ local usedGroupNodes = {}
+ for groupName, _ in pairs(usedGroupNames) do
+ local groupNode = xmlCreateChild(parentGroupNode, "group")
+ xmlNodeSetAttribute(groupNode, "name", groupName)
+ usedGroupNodes[groupName] = groupNode
+ end
+
+ local count = 0
+ for i, mod in ipairs(modsList[theType]) do
+ local modelID = mod.id
+ local modelBaseID = mod.base_id
+ local modelName = mod.name
+ local groupName = usedModelGroupNames[modelBaseID]
+ local groupNode = usedGroupNodes[groupName]
+ local tagName = string.sub(theType, 1, -2)
+ local modelNode = xmlCreateChild(groupNode, tagName)
+ xmlNodeSetAttribute(modelNode, "id", modelID)
+ xmlNodeSetAttribute(modelNode, "base_model", modelBaseID)
+ xmlNodeSetAttribute(modelNode, "name", modelName)
+ count = count + 1
+ end
+
+ xmlSaveFile(xmlRoot)
+ xmlUnloadFile(xmlRoot)
+
+ return count
+ end
+
+ local theTypeCounts = {}
+ for theType, _ in pairs(modsList) do
+ local count, errorMessage = addNewModels(theType)
+ if not count then
+ return false, errorMessage
+ end
+
+ theTypeCounts[theType] = count
+ end
+
+ return theTypeCounts
+end
+
+function freeroamStopped()
+ if not waitingFreeroamStop then return end
+
+ setTimer(function()
+
+ local thePlayer, cmd = waitingFreeroamStop[1], waitingFreeroamStop[2]
+ waitingFreeroamStop = nil
+
+ if thePlayer~="SYSTEM" and isElement(thePlayer) then
+ updateFreeroamNewModels(thePlayer, cmd)
+ else
+ updateFreeroamNewModels()
+ end
+
+ end, 50, 1)
+end
+
+--[[
+ Runs the tool
+]]
+function updateFreeroamNewModels(thePlayer, cmd)
+
+ if (waitingFreeroamStop ~= nil) then
+ if isElement(thePlayer) then
+ outputChatBox("Please wait for the freeroam resource to stop.", thePlayer, 255, 22, 22)
+ end
+ return
+ end
+
+ local freeroam = getResourceFromName("freeroam")
+ if not freeroam then
+ if isElement(thePlayer) then
+ outputChatBox("The 'freeroam' resource could not be found.", thePlayer, 255, 22, 22)
+ end
+ return
+ end
+
+ if getResourceState(freeroam) == "running" then
+ local freeroamRoot = getResourceRootElement(freeroam)
+ addEventHandler("onResourceStop", freeroamRoot, freeroamStopped)
+
+ if isElement(thePlayer) then
+ waitingFreeroamStop = {thePlayer, cmd}
+ else
+ waitingFreeroamStop = {"SYSTEM"}
+ end
+
+ if not stopResource(freeroam) then
+
+ if isElement(thePlayer) then
+ outputChatBox("Failed to stop the 'freeroam' resource.", thePlayer, 255, 22, 22)
+ outputChatBox(" Try to stop the resource manually (/stop freeroam).", thePlayer, 255, 22, 22)
+ else
+ outputDebugString("Failed to stop the 'freeroam' resource.", 1)
+ end
+
+ removeEventHandler("onResourceStop", freeroamRoot, freeroamStopped)
+ waitingFreeroamStop = nil
+ return
+ end
+ for _, player in ipairs(getElementsByType("player")) do
+ playSoundFrontEnd(player, 40)
+ outputChatBox("[New-Models Freeroam] The freeroam resource is now restarting to apply changes...", player, 255, 255, 22)
+ end
+ return
+ end
+
+ local result, reason = updateFreeroamGUIFiles()
+ if not result then
+ if isElement(thePlayer) then
+ outputChatBox("Failed to update the freeroam GUI files: "..reason, thePlayer, 255, 22, 22)
+ else
+ outputDebugString("Failed to update the freeroam GUI files: "..reason, 1)
+ end
+ return
+ end
+
+ if isElement(thePlayer) then
+ outputChatBox("The freeroam GUI files have been updated.", thePlayer, 22, 255, 22)
+
+ local added = {}
+ for theType, count in pairs(result) do
+ added[#added+1] = count.." new "..theType
+ end
+ outputChatBox(" Added: "..(table.concat(added, ", ")), thePlayer, 222, 222, 222)
+ else
+ outputDebugString("Updated the freeroam GUI files:", 3)
+ outputDebugString(inspect(result), 3)
+ end
+
+ local freeroamState = getResourceState(freeroam)
+ if freeroamState == "loaded" then
+ if not startResource(freeroam, true) then
+ if isElement(thePlayer) then
+ outputChatBox("Failed to start the resource 'freeroam'.", thePlayer, 255, 22, 22)
+ else
+ outputDebugString("Failed to start the resource 'freeroam'.", 1)
+ end
+ return
+ end
+ else
+ if isElement(thePlayer) then
+ outputChatBox("The 'freeroam' resource is currently "..freeroamState..".", thePlayer, 255, 255, 22)
+ outputChatBox(" Try to start the resource manually (/start freeroam).", thePlayer, 255, 255, 22)
+ else
+ outputDebugString("The 'freeroam' resource is currently "..freeroamState..".", 2)
+ end
+ end
+end
+addEventHandler("onResourceStart", resourceRoot, updateFreeroamNewModels)
+
+function newModelsFreeroamCmd(thePlayer, cmd)
+ if not canUseTool(thePlayer) then
+ return outputChatBox("You don't have permission to use /"..cmd..".", thePlayer, 255, 22, 22)
+ end
+
+ updateFreeroamNewModels(thePlayer, cmd)
+end
+for i, cmd in ipairs(COMMAND_NAMES) do
+ addCommandHandler(cmd, newModelsFreeroamCmd, false, false)
+end
diff --git a/[gameplay]/freeroam_newmodels/meta.xml b/[gameplay]/freeroam_newmodels/meta.xml
new file mode 100644
index 000000000..b3920506b
--- /dev/null
+++ b/[gameplay]/freeroam_newmodels/meta.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+