Skip to content

Commit ee549aa

Browse files
freeroam_newmodels
3.3.0 compatible
1 parent c40b809 commit ee549aa

File tree

5 files changed

+363
-12
lines changed

5 files changed

+363
-12
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Newmodels specific
2+
#
3+
# These files will be modified by the newmodels freeroam tool (new models added)
4+
# and we don't want these changes to be tracked by git.
5+
#
6+
skins.xml
7+
vehicles.xml

[gameplay]/freeroam/fr_client.lua

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1487,15 +1487,13 @@ function createVehicleCommand(cmd, ...)
14871487
return errMsg("Invalid vehicle model!")
14881488
end
14891489

1490+
--[[ -- We won't be using these verifications anymore, since we can now spawn vehicles with custom new models
14901491
if string.len(table.concat(args, " ")) > 25 or tonumber(vehID) and string.len(vehID) > 3 then
14911492
return errMsg("Invalid vehicle model!")
14921493
end
1494+
--]]
14931495

1494-
if vehID and vehID >= 400 and vehID <= 611 then
1495-
server.giveMeVehicles(vehID)
1496-
else
1497-
errMsg("Invalid vehicle model!")
1498-
end
1496+
server.giveMeVehicles(vehID)
14991497
end
15001498
addCommandHandler('createvehicle', createVehicleCommand)
15011499
addCommandHandler('cv', createVehicleCommand)

[gameplay]/freeroam/fr_server.lua

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -283,9 +283,8 @@ addEventHandler('onPlayerGravInit', root,
283283

284284
function setMySkin(skinid)
285285
if not isElement(source) then return end
286-
if getElementModel(source) == skinid then return end
287286
if isPedDead(source) then
288-
local x, y, z = getElementPosition(source)
287+
local x, y, z = getElementPosition(source)
289288

290289
if isPedTerminated(source) then
291290
x = 0
@@ -295,12 +294,11 @@ function setMySkin(skinid)
295294

296295
local r = getPedRotation(source)
297296
local interior = getElementInterior(source)
298-
spawnPlayer(source, x, y, z, r, skinid)
297+
spawnPlayer(source, x, y, z, r, 0)
299298
setElementInterior(source, interior)
300299
setCameraInterior(source, interior)
301-
else
302-
setElementModel(source, skinid)
303300
end
301+
exports["newmodels-engine"]:setElementModel(source, skinid)
304302
setCameraTarget(source, source)
305303
setCameraInterior(source, getElementInterior(source))
306304
end
@@ -390,14 +388,14 @@ function giveMeVehicles(vehID)
390388
if not table.find(getOption('vehicles.disallowed'), vehID) then
391389
if #vehicleList >= getOption('vehicles.maxperplayer') then unloadVehicle(vehicleList[1]) end
392390
local vehPos = posVector+vehMatrix.right*3
393-
local vehicle = Vehicle(vehID, vehPos, rotVector) or false
391+
local vehicle = exports["newmodels-engine"]:createVehicle(vehID, vehPos, rotVector) or false
394392
if vehicle then
395393
vehicle.interior = source.interior
396394
vehicle.dimension = source.dimension
397395
if vehicle.vehicleType == "Bike" then vehicle.velocity = Vector3(0,0,-0.01) end
398396
table.insert(vehicleList, vehicle)
399397
g_VehicleData[vehicle] = { creator = source, timers = {} }
400-
if g_Trailers[vehID] then
398+
if g_Trailers[getElementModel(vehicle)] then
401399
if getOption('vehicles.maxidletime') >= 0 then
402400
if getOption('vehicles.idleexplode') then
403401
g_VehicleData[vehicle].timers.fire = setTimer(commitArsonOnVehicle, getOption('vehicles.maxidletime'), 1, vehicle)
Lines changed: 321 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
--[[
2+
core_s.lua
3+
4+
Main server script for the New Models Freeroam system.
5+
6+
Author: https://github.com/Fernando-A-Rocha
7+
]]
8+
9+
----------- GENERAL SCRIPT CONFIGURATION -----------
10+
11+
COMMAND_NAMES = { "newmodelsfreeroam", "newmodels_freeroam", "freeroam_newmodels", "freeroamnewmodels" }
12+
13+
function canUseTool(player)
14+
return hasObjectPermissionTo(player, "command.start", false) and hasObjectPermissionTo(player, "command.stop", false)
15+
end
16+
17+
local FREEROAM_GUI_XML_GROUP_NAMES = {
18+
["vehicles"] = "New Models",
19+
["skins"] = "New Models",
20+
}
21+
22+
----------- FUNCTIONALITIES -----------
23+
24+
local waitingFreeroamStop = nil
25+
26+
local function getModelGroupNames(rootChildrenNodes, theType)
27+
local groupNames = {} -- [id] = name
28+
local function getGroupNames(node)
29+
if xmlNodeGetName(node) == "group" then
30+
local children = xmlNodeGetChildren(node)
31+
if children then
32+
local parentName = xmlNodeGetAttribute(node, "name")
33+
if parentName then
34+
for i, child in ipairs(children) do
35+
local id = tonumber(xmlNodeGetAttribute(child, "id"))
36+
if id then
37+
if groupNames[id] then
38+
-- outputDebugString("Model "..id.." is already in group "..groupNames[id]..", skipping.", 2)
39+
return
40+
end
41+
groupNames[id] = parentName
42+
end
43+
if xmlNodeGetName(child) == "group" then
44+
getGroupNames(child)
45+
end
46+
end
47+
end
48+
end
49+
end
50+
end
51+
for i, node in ipairs(rootChildrenNodes) do
52+
getGroupNames(node)
53+
end
54+
return groupNames
55+
end
56+
57+
--[[
58+
Updates the freeroam vehicle & skins XML list with thew new models.
59+
This cannot be done live when freeroam is running because the XML files are
60+
loaded as config files in meta.xml and it would be a mess to reload them.
61+
]]
62+
local function updateFreeroamGUIFiles()
63+
local VEHICLES_PATH = ":freeroam/data/vehicles.xml"
64+
local SKINS_PATH = ":freeroam/data/skins.xml"
65+
66+
local groupNames = {}
67+
if not FREEROAM_GUI_XML_GROUP_NAMES then
68+
return false, "The freeroam GUI group names are not defined."
69+
end
70+
if not FREEROAM_GUI_XML_GROUP_NAMES["vehicles"] then
71+
return false, "The freeroam GUI vehicles group name is not defined."
72+
end
73+
if not FREEROAM_GUI_XML_GROUP_NAMES["skins"] then
74+
return false, "The freeroam GUI skins group name is not defined."
75+
end
76+
groupNames["vehicles"] = FREEROAM_GUI_XML_GROUP_NAMES["vehicles"]
77+
groupNames["skins"] = FREEROAM_GUI_XML_GROUP_NAMES["skins"]
78+
79+
if not (fileExists(VEHICLES_PATH) and fileExists(SKINS_PATH)) then
80+
return false, "The freeroam GUI files could not be found."
81+
end
82+
83+
local allMods = exports.newmodels:getModList()
84+
if not allMods then
85+
return false, "Failed to get the newmodels mod list."
86+
end
87+
88+
local modsList = {
89+
["vehicles"] = {},
90+
["skins"] = {}
91+
}
92+
93+
for elementType, mods in pairs(allMods) do
94+
if type(elementType) ~= "string" or type(mods) ~= "table" then
95+
return false, "The newmodels mod list is not valid."
96+
end
97+
for i, mod in ipairs(mods) do
98+
if type(mod) ~= "table" then
99+
return false, "Mod #"..i.." is not a table."
100+
end
101+
local modID = mod.id
102+
local modBaseID = mod.base_id
103+
local modName = mod.name
104+
105+
if type(modID) ~= "number" or type(modBaseID) ~= "number" or type(modName) ~= "string" then
106+
return false, "Mod #"..i.." is not valid."
107+
end
108+
109+
if elementType == "vehicle" then
110+
table.insert(modsList["vehicles"], {id = modID, base_id = modBaseID, name = modName})
111+
elseif elementType == "ped" then
112+
table.insert(modsList["skins"], {id = modID, base_id = modBaseID, name = modName})
113+
end
114+
end
115+
end
116+
117+
local function addNewModels(theType)
118+
119+
local path = VEHICLES_PATH
120+
if theType == "skins" then
121+
path = SKINS_PATH
122+
end
123+
124+
local xmlRoot = xmlLoadFile(path)
125+
if not xmlRoot then
126+
return false, "Failed to load file: "..path
127+
end
128+
local groupNodes = xmlNodeGetChildren(xmlRoot)
129+
if not groupNodes then
130+
return false, "Failed to get the group nodes from file: "..path
131+
end
132+
for i, groupNode in ipairs(groupNodes) do
133+
local groupName = xmlNodeGetAttribute(groupNode, "name")
134+
if groupName and groupName == groupNames[theType] then
135+
xmlDestroyNode(groupNode)
136+
break
137+
end
138+
end
139+
groupNodes = xmlNodeGetChildren(xmlRoot)
140+
if not groupNodes then
141+
return false, "Failed to get the group nodes from file: "..path
142+
end
143+
local defaultGroupNames = getModelGroupNames(groupNodes, xmlNodeGetAttribute(xmlRoot, "type"))
144+
145+
local parentGroupNode = xmlCreateChild(xmlRoot, "group")
146+
xmlNodeSetAttribute(parentGroupNode, "name", groupNames[theType])
147+
local usedGroupNames = {}
148+
local usedModelGroupNames = {}
149+
for i, mod in ipairs(modsList[theType]) do
150+
local modelBaseID = mod.base_id
151+
local groupName = defaultGroupNames[modelBaseID]
152+
if not groupName then
153+
groupName = "Other"
154+
end
155+
if not usedGroupNames[groupName] then
156+
usedGroupNames[groupName] = true
157+
end
158+
usedModelGroupNames[modelBaseID] = groupName
159+
end
160+
local usedGroupNodes = {}
161+
for groupName, _ in pairs(usedGroupNames) do
162+
local groupNode = xmlCreateChild(parentGroupNode, "group")
163+
xmlNodeSetAttribute(groupNode, "name", groupName)
164+
usedGroupNodes[groupName] = groupNode
165+
end
166+
167+
local count = 0
168+
for i, mod in ipairs(modsList[theType]) do
169+
local modelID = mod.id
170+
local modelBaseID = mod.base_id
171+
local modelName = mod.name
172+
local groupName = usedModelGroupNames[modelBaseID]
173+
local groupNode = usedGroupNodes[groupName]
174+
local tagName = string.sub(theType, 1, -2)
175+
local modelNode = xmlCreateChild(groupNode, tagName)
176+
xmlNodeSetAttribute(modelNode, "id", modelID)
177+
xmlNodeSetAttribute(modelNode, "base_model", modelBaseID)
178+
xmlNodeSetAttribute(modelNode, "name", modelName)
179+
count = count + 1
180+
end
181+
182+
xmlSaveFile(xmlRoot)
183+
xmlUnloadFile(xmlRoot)
184+
185+
return count
186+
end
187+
188+
local theTypeCounts = {}
189+
for theType, _ in pairs(modsList) do
190+
local count, errorMessage = addNewModels(theType)
191+
if not count then
192+
return false, errorMessage
193+
end
194+
195+
theTypeCounts[theType] = count
196+
end
197+
198+
return theTypeCounts
199+
end
200+
201+
function freeroamStopped()
202+
if not waitingFreeroamStop then return end
203+
204+
setTimer(function()
205+
206+
local thePlayer, cmd = waitingFreeroamStop[1], waitingFreeroamStop[2]
207+
waitingFreeroamStop = nil
208+
209+
if thePlayer~="SYSTEM" and isElement(thePlayer) then
210+
updateFreeroamNewModels(thePlayer, cmd)
211+
else
212+
updateFreeroamNewModels()
213+
end
214+
215+
end, 50, 1)
216+
end
217+
218+
--[[
219+
Runs the tool
220+
]]
221+
function updateFreeroamNewModels(thePlayer, cmd)
222+
223+
if (waitingFreeroamStop ~= nil) then
224+
if isElement(thePlayer) then
225+
outputChatBox("Please wait for the freeroam resource to stop.", thePlayer, 255, 22, 22)
226+
end
227+
return
228+
end
229+
230+
local freeroam = getResourceFromName("freeroam")
231+
if not freeroam then
232+
if isElement(thePlayer) then
233+
outputChatBox("The 'freeroam' resource could not be found.", thePlayer, 255, 22, 22)
234+
end
235+
return
236+
end
237+
238+
if getResourceState(freeroam) == "running" then
239+
local freeroamRoot = getResourceRootElement(freeroam)
240+
addEventHandler("onResourceStop", freeroamRoot, freeroamStopped)
241+
242+
if isElement(thePlayer) then
243+
waitingFreeroamStop = {thePlayer, cmd}
244+
else
245+
waitingFreeroamStop = {"SYSTEM"}
246+
end
247+
248+
if not stopResource(freeroam) then
249+
250+
if isElement(thePlayer) then
251+
outputChatBox("Failed to stop the 'freeroam' resource.", thePlayer, 255, 22, 22)
252+
outputChatBox(" Try to stop the resource manually (/stop freeroam).", thePlayer, 255, 22, 22)
253+
else
254+
outputDebugString("Failed to stop the 'freeroam' resource.", 1)
255+
end
256+
257+
removeEventHandler("onResourceStop", freeroamRoot, freeroamStopped)
258+
waitingFreeroamStop = nil
259+
return
260+
end
261+
for _, player in ipairs(getElementsByType("player")) do
262+
playSoundFrontEnd(player, 40)
263+
outputChatBox("[New-Models Freeroam] The freeroam resource is now restarting to apply changes...", player, 255, 255, 22)
264+
end
265+
return
266+
end
267+
268+
local result, reason = updateFreeroamGUIFiles()
269+
if not result then
270+
if isElement(thePlayer) then
271+
outputChatBox("Failed to update the freeroam GUI files: "..reason, thePlayer, 255, 22, 22)
272+
else
273+
outputDebugString("Failed to update the freeroam GUI files: "..reason, 1)
274+
end
275+
return
276+
end
277+
278+
if isElement(thePlayer) then
279+
outputChatBox("The freeroam GUI files have been updated.", thePlayer, 22, 255, 22)
280+
281+
local added = {}
282+
for theType, count in pairs(result) do
283+
added[#added+1] = count.." new "..theType
284+
end
285+
outputChatBox(" Added: "..(table.concat(added, ", ")), thePlayer, 222, 222, 222)
286+
else
287+
outputDebugString("Updated the freeroam GUI files:", 3)
288+
outputDebugString(inspect(result), 3)
289+
end
290+
291+
local freeroamState = getResourceState(freeroam)
292+
if freeroamState == "loaded" then
293+
if not startResource(freeroam, true) then
294+
if isElement(thePlayer) then
295+
outputChatBox("Failed to start the resource 'freeroam'.", thePlayer, 255, 22, 22)
296+
else
297+
outputDebugString("Failed to start the resource 'freeroam'.", 1)
298+
end
299+
return
300+
end
301+
else
302+
if isElement(thePlayer) then
303+
outputChatBox("The 'freeroam' resource is currently "..freeroamState..".", thePlayer, 255, 255, 22)
304+
outputChatBox(" Try to start the resource manually (/start freeroam).", thePlayer, 255, 255, 22)
305+
else
306+
outputDebugString("The 'freeroam' resource is currently "..freeroamState..".", 2)
307+
end
308+
end
309+
end
310+
addEventHandler("onResourceStart", resourceRoot, updateFreeroamNewModels)
311+
312+
function newModelsFreeroamCmd(thePlayer, cmd)
313+
if not canUseTool(thePlayer) then
314+
return outputChatBox("You don't have permission to use /"..cmd..".", thePlayer, 255, 22, 22)
315+
end
316+
317+
updateFreeroamNewModels(thePlayer, cmd)
318+
end
319+
for i, cmd in ipairs(COMMAND_NAMES) do
320+
addCommandHandler(cmd, newModelsFreeroamCmd, false, false)
321+
end

0 commit comments

Comments
 (0)