Factorize external automatic DSV track pack generators (#43)

Added: Conditional user spawn multy-tasking
Added: Method `QUEUE:Next` including `Retain` and `Remove`
Added: Wire module self tester
Added: Custom character support to file reader
Fixed: Ensure `\n` at the end of the generated autorun file
Fixed: Export autorun type doubling `\n` in the output
Removed: Function `asmlib.GetAngleXY`. Use `ATAN2` instead
Renamed: Method `QUEUE:Execute` to `Work`
Updated: Base file for autorun exports
Updated: External automatic DSV track pack generators
Updated: Error handling and status in separate functions
Updated: TABLEMAKER:Match using local variable
This commit is contained in:
Deyan Dobromirov 2022-10-24 22:31:42 +03:00 committed by GitHub
parent 4ab6010f5d
commit 21b9c5d907
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 751 additions and 672 deletions

View File

@ -0,0 +1,79 @@
@name Track assembly wire extension
@inputs UCS:wirelink Oth:wirelink
@outputs T:string A:array
@persist
@trigger
@model models/props_phx/trains/tracks/track_1x.mdl
if(first() || dupefinished())
{
E = entity()
O = Oth:entity()
M = E:model()
D = O:model()
print(E:trackasmlibGenActivePointINS(UCS:entity(),"Test","",1,"AAA"))
print(E:trackasmlibGenActivePointDSV(UCS:entity(),"Test","",1,"AAA"))
if(trackasmlibIsPiece(M) && E:trackasmlibIsPiece())
{
print("Exists OK <"+trackasmlibIsPiece(M)+">")
}else{ error("Exists fail "+M) }
if(!trackasmlibIsPiece(D) && !O:trackasmlibIsPiece())
{
print("Other OK <"+D+">")
}else{ error("Other fail "+D) }
if(trackasmlibGetType(M) == E:trackasmlibGetType())
{
print("Type OK <"+trackasmlibGetType(M)+">")
}else{ error("Type fail "+M) }
if(trackasmlibGetName(M) == E:trackasmlibGetName())
{
print("Name OK <"+trackasmlibGetName(M)+">")
}else{ error("Name fail "+M) }
if(trackasmlibGetPointsCount(M) == E:trackasmlibGetPointsCount())
{
print("Points OK <"+trackasmlibGetPointsCount(M)+">")
}else{ error("Points fail "+M) }
if((trackasmlibHasAdditions(M) == E:trackasmlibHasAdditions()))
{
print("Has Additions OK<"+trackasmlibHasAdditions(M)+">")
}else{ error("Additions fail "+M) }
if((trackasmlibGetAdditionsCount(M) == E:trackasmlibGetAdditionsCount()))
{
print("Additions count OK<"+trackasmlibGetAdditionsCount(M)+">")
}else{ error("Additions count fail "+M) }
A1 = trackasmlibGetAdditionsLine(M,1)
A2 = E:trackasmlibGetAdditionsLine(1)
if(A1[1, string] == A2[1, string])
{
print("Additions line OK <"+A1[1,string]+">")
}else{ error("Additions line fail "+M) }
P1 = trackasmlibGetProperty()
if(P1[3,string] == "Metal")
{
P2 = trackasmlibGetProperty(P1[3,string])
if(P2[5, string] == "crowbar")
{
print("Property type OK <"+P2[5, string]+">")
}else{ error("Property type fail <"+P2[5, string]+">")}
}else{ error("Property fail <"+P1[3,string]+">")}
B = E:trackasmlibGetBodyGroups()
if(B == "0"){
print("Bodygroup OK<"+B+">")
}else{ error("Bodygroup fail "+B) }
S = E:trackasmlibGetBodyGroups()
if(S == "0"){
print("Skin OK<"+B+">")
}else{ error("Skin fail "+B) }
}

View File

@ -11,7 +11,12 @@
-- Local reference to the module.
local asmlib = trackasmlib
-- Your addon name goes here. Change this to your addon name!
--[[
* This is your addon name. It is mandatory and it must be string.
* It is used by TA in order to classify the content you are creating
* It must NOT be an empty string nil or any other type regarding
* The value will be automatically pattern converted to a index prefix
]]
local myAddon = "Test's track pack"
--[[
@ -40,6 +45,7 @@ local myPrefix = myAddon:gsub("[^%w]","_") -- Addon prefix
-- Do not touch this also, it is used for debugging
local myScript = tostring(debug.getinfo(1).source or "N/A")
myScript = "@"..myScript:gsub("^%W+", ""):gsub("\\","/")
--[[
* This function defines what happens when there is an error present
* Usually you can tell Gmod that you want it to generate an error
@ -48,275 +54,262 @@ local myScript = tostring(debug.getinfo(1).source or "N/A")
* when you need it to do something else.
--]]
local function myThrowError(vMesg)
local sMesg = tostring(vMesg) -- Make sure the message is string
if(asmlib) then asmlib.LogInstance(sMesg) end -- Output the message into the logs
myError(myScript.." > ("..myAddon.."): "..sMesg) -- Generate an error in the console ( optional )
local sMesg = (myScript.." > ("..myAddon.."): "..tostring(vMesg)) -- Make sure the message is string
if(asmlib) then asmlib.LogInstance(sMesg) end; myError(sMesg) -- Output the message into the logs
end
if(asmlib) then
-- Store a reference to disable symbol
local gsMissDB = asmlib.GetOpVar("MISS_NOSQL")
local gsToolPF = asmlib.GetOpVar("TOOLNAME_PU")
local gsSymOff = asmlib.GetOpVar("OPSYM_DISABLE")
local gsModeDB = asmlib.GetOpVar("MODE_DATABASE")
local gsFormPF = asmlib.GetOpVar("FORM_PREFIXDSV")
-- There is something to error about stop the execution and report it
if(not asmlib) then myThrowError("Failed loading the required module!"); return end
-- This is the path to your DSV
local myDsv = asmlib.GetOpVar("DIRPATH_BAS")..
asmlib.GetOpVar("DIRPATH_DSV")..
gsFormPF:format(myPrefix, gsToolPF.."PIECES")
--[[
* This logic statement is needed for reporting the error
* in the console if the process fails.
*
@ bSuccess = SynchronizeDSV(sTable, tData, bRepl, sPref, sDelim)
* sTable > The table you want to sync
* tData > A data table like the one described above
* bRepl > If set to /true/, makes the API replace the repeating models with
these of your addon. This is nice when you are constantly updating your track packs
If set to /false/ keeps the current model in the
database and ignores yours if they are the same file.
* sPref > An export file custom prefix. For synchronizing it must be related to your addon
* sDelim > The delimiter used by the server/client ( default is a tab symbol )
*
@ bSuccess = TranslateDSV(sTable, sPref, sDelim)
* sTable > The table you want to translate to Lua script
* sPref > An export file custom prefix. For synchronizing it must be related to your addon
* sDelim > The delimiter used by the server/client ( default is a tab symbol )
]]--
local function mySyncTable(sName, tData, bRepl)
if(not asmlib.IsEmpty(tData)) then -- Somrting to be processed. Do stuff when the table is not empty
asmlib.LogInstance("SynchronizeDSV START <"..myPrefix..">") -- Signal start synchronization
if(not asmlib.SynchronizeDSV(sName, tData, bRepl, myPrefix)) then -- Attempt to synchronize
myThrowError("Failed to synchronize: "..sName) -- Raise error when fails to sync tracks data
else -- Successful. You are saving me from all the work for manually generating these
asmlib.LogInstance("TranslateDSV START <"..myPrefix..">") -- Signal start translation
if(not asmlib.TranslateDSV(sName, myPrefix)) then -- Attempt to translate the DSV to Lua source
myThrowError("Failed to translate DSV: "..sName) end -- Raise error when fails
asmlib.LogInstance("TranslateDSV OK <"..myPrefix..">") -- Translation is successful
end -- Now we have Lua inserts and DSV. Otherwise sent empty table and print status in logs
else asmlib.LogInstance("SynchronizeDSV EMPTY <"..myPrefix..">") end -- Nothing to be done
end
--[[
* This flag is used when the track pieces list needs to be processed.
* It generally represents the locking file persistence flag. It is
* bound to finding a "PIECES" DSV external database for the prefix
* of your addon. You can use it for boolean value deciding whenever
* or not to run certain events. For example you can stop exporting
* your local database every time Gmod loads, but then the user will
* skip the available updates of your addon until he/she deletes the DSVs.
]]--
local myFlag = file.Exists(myDsv, "DATA")
-- Tell TA what custom script we just called don't touch it
asmlib.LogInstance(">>> "..myScript.." ("..tostring(myFlag).."): {"..myAddon..", "..myPrefix.."}")
--[[
* Register the addon to the workshop ID list. Apply the ID of your addon here!
* This is needed so TA user can open GUI to your addon webpage.
* Open your workshop addon with any browser then copy the visited URL
* https://steamcommunity.com/sharedfiles/filedetails/?id=287012681
* This URL is used as an example obtained from the TA workshop link
* You only need the number at the end. Write it down as a string below.
* You need to change the `WorkshopID` function second argument to your ID.
]]
asmlib.WorkshopID(myAddon, "287012681")
--[[
* Register the addon to the auto-load prefix list when the
* PIECES file is missing. The auto-load list is located in
* (/garrysmod/data/trackassembly/trackasmlib_dsv.txt)
* a.k.a the DATA folder of Garry's mod.
*
* @bSuccess = RegisterDSV(sProg, sPref, sDelim)
* sProg > The program which registered the DSV
* sPref > The external data prefix to be added ( default instance prefix )
* sDelim > The delimiter to be used for processing ( default tab )
* bSkip > Skip addition for the DSV prefix if exists ( default `false` )
]]--
asmlib.LogInstance("RegisterDSV start <"..myPrefix..">")
if(myFlag) then -- Your DSV must be registered only once when loading for the first time
asmlib.LogInstance("RegisterDSV skip <"..myPrefix..">")
--[[
* Register the addon to the auto-load prefix list when the
* PIECES file is missing. The auto-load list is located in
* (/garrysmod/data/trackassembly/set/trackasmlib_dsv.txt)
* a.k.a the DATA folder of Garry's mod.
*
* @bSuccess = RegisterDSV(sProg, sPref, sDelim)
* sProg > The program which registered the DSV
* sPref > The external data prefix to be added ( default instance prefix )
* sDelim > The delimiter to be used for processing ( default tab )
* bSkip > Skip addition for the DSV prefix if exists ( default `false` )
]]--
local function myRegisterDSV(bSkip)
asmlib.LogInstance("RegisterDSV START <"..myPrefix..">")
if(bSkip) then -- Your DSV must be registered only once when loading for the first time
asmlib.LogInstance("RegisterDSV SKIP <"..myPrefix..">")
else -- If the locking file is not located that means this is the first run of your script
if(not asmlib.RegisterDSV(myScript, myPrefix)) then -- Register the DSV prefix and check for error
myThrowError("Failed to register DSV") -- Throw the error if fails
end -- Third argument is the delimiter. The default tab is used
asmlib.LogInstance("RegisterDSV done <"..myPrefix..">")
asmlib.LogInstance("RegisterDSV OK <"..myPrefix..">")
end
--[[
* This is used if you want to make internal categories for your addon
* You must make a function as a string under the hash of your addon
* The function must take only one argument and that is the model
* For every sub-category of your track pieces, you must return a table
* with that much elements or return a /nil/ value to add the piece to
* the root of your branch. You can also return a second value if you
* want to override the track piece name. If you need to use categories
* for multiple track types, just put their hashes in the table below
* and make every track point to its dedicated category handler.
]]--
local myCategory = {
[myType] = {Txt = [[
function(m)
local r = m:gsub("models/props_phx/construct/",""):gsub("_","/")
local s = r:find("/"); r = s and r:sub(1,s-1) or nil
local n = nil
if(r) then
if(r == "metal" ) then n = "My metal plate" end
if(r == "windows") then n = "My glass plate" end
end
r = r and r:gsub("^%l", string.upper) or nil
p = r and {r} or nil
return p, n
end
]]}
}
--[[
* This logic statement is needed for reporting the error in the console if the
* process fails.
*
@ bSuccess = ExportCategory(nInd, tData, sPref)
* nInd > The index equal indent format to be stored with ( generally = 3 )
* tData > The category functional definition you want to use to divide your stuff with
* sPref > An export file custom prefix. For synchronizing
* it must be related to your addon ( default is instance prefix )
]]--
asmlib.LogInstance("ExportCategory start <"..myPrefix..">")
if(CLIENT) then -- Category handling is client side only
if(not asmlib.IsEmpty(myCategory)) then
if(not asmlib.ExportCategory(3, myCategory, myPrefix)) then
myThrowError("Failed to synchronize category")
end; asmlib.LogInstance("ExportCategory done <"..myPrefix..">")
else asmlib.LogInstance("ExportCategory skip <"..myPrefix..">") end
else asmlib.LogInstance("ExportCategory server <"..myPrefix..">") end
--[[
* Create a table and populate it as shown below
* In the square brackets goes your MODEL,
* and then for every active point, you must have one array of
* strings, where the elements match the following data settings.
* You can use the disable event /#/ to make TA auto-fill
* the value provided and you can also add multiple track types myType[1-n].
* If you need to use piece origin/angle with model attachment, you must use
* the attachment extraction event /!/. The model attachment format is
* /!<attachment_name>/ and it depends what attachment name you gave it when you
* created the model. If you need TA to extract the origin/angle from an attachment named
* /test/ for example, you just need to put the string /!test/ in the origin/angle column for that model.
* {MODEL, TYPE, NAME, LINEID, POINT, ORIGIN, ANGLE, CLASS}
* MODEL > This string contains the path to your /*.mdl/ file. It is mandatory and
* taken in pairs with LINEID, it forms the unique identifier of every record.
* When used in /DSV/ mode ( like seen below ) is is used as a hash index.
* TYPE > This string is the name of the type your stuff will reside in the panel.
* Disabling this, makes it use the value of the /DEFAULT_TYPE/ variable.
* If it is empty uses the string /TYPE/, so make sure you fill this.
* NAME > This is the name of your track piece. Put /#/ here to be auto-generated from
* the model ( from the last slash to the file extension ).
* LINEID > This is the ID of the point that can be selected for building. They must be
* sequential and mandatory. If provided, the ID must the same as the row index under
* a given model key. Disabling this, makes it use the the index of the current line.
* Use that to swap the active points around by only moving the desired row up or down.
* For the example table definition below, the line ID in the database will be the same.
* POINT > This is the local position vector that TA searches and selects the related
* ORIGIN for. An empty or disabled string is treated as taking the ORIGIN.
* Disabling this using the disable event makes it hidden when the active point is searched for
* ORIGIN > This is the origin relative to which the next track piece position is calculated
* An empty string is treated as {0,0,0}. Disabling this also makes it use {0,0,0}
* You can also fill it with attachment event /!/ followed by your attachment name. It's mandatory
* ANGLE > This is the angle relative to which the forward and up vectors are calculated.
* An empty string is treated as {0,0,0}. Disabling this also makes it use {0,0,0}
* You can also fill it with attachment event /!/ followed by your attachment name. It's mandatory
* CLASS > This string is populated up when your entity class is not /prop_physics/ but something else
* used by ents.Create of the gmod ents API library. Keep this empty if your stuff is a normal prop.
]]--
local myPieces = {
["models/props_phx/construct/metal_plate1x2.mdl"] = { -- Here goes the model of your pack
{myType , gsSymOff, 1, "","0,-47.455105,1.482965","0,-90,0",""}, -- The first point parameter
{myType , gsSymOff, 2, "","0, 47.455105,1.482965","0, 90,0",""} -- The second point parameter
},
["models/props_phx/construct/windows/window1x2.mdl"] = {
{myType , gsSymOff, gsSymOff, "","0,-23.73248,1.482965","0,-90,0",""},
{myType , gsSymOff, gsSymOff, "","0, 71.17773,1.482965","0, 90,0",""}
}
}
--[[
* This logic statement is needed for reporting the error in the console if the
* process fails.
*
@ bSuccess = SynchronizeDSV(sTable, tData, bRepl, sPref, sDelim)
* sTable > The table you want to sync
* tData > A data table like the one described above
* bRepl > If set to /true/, makes the API replace the repeating models with
these of your addon. This is nice when you are constantly updating your track packs
If set to /false/ keeps the current model in the
database and ignores yours if they are the same file.
* sPref > An export file custom prefix. For synchronizing it must be related to your addon
* sDelim > The delimiter used by the server/client ( default is a tab symbol )
*
@ bSuccess = TranslateDSV(sTable, sPref, sDelim)
* sTable > The table you want to translate to Lua script
* sPref > An export file custom prefix. For synchronizing it must be related to your addon
* sDelim > The delimiter used by the server/client ( default is a tab symbol )
]]--
if(not asmlib.IsEmpty(myPieces)) then
asmlib.LogInstance("SynchronizeDSV start <"..myPrefix..">")
if(not asmlib.SynchronizeDSV("PIECES", myPieces, true, myPrefix)) then
myThrowError("Failed to synchronize track pieces")
else -- You are saving me from all the work for manually generating these
asmlib.LogInstance("TranslateDSV start <"..myPrefix..">")
if(not asmlib.TranslateDSV("PIECES", myPrefix)) then
myThrowError("Failed to translate DSV into Lua") end
asmlib.LogInstance("TranslateDSV done <"..myPrefix..">")
end -- Now we have Lua inserts and DSV
end
--[[
* Create a table and populate it as shown below
* In the square brackets goes your MODELBASE,
* and then for every active point, you must have one array of
* strings and numbers, where the elements match the following data settings.
* {MODELBASE, MODELADD, ENTCLASS, LINEID, POSOFF, ANGOFF, MOVETYPE, PHYSINIT, DRSHADOW, PHMOTION, PHYSLEEP, SETSOLID}
* MODELBASE > This string contains the path to your base /*.mdl/ file the additions are gonna be attached to.
* It is mandatory and taken in pairs with LINEID, it forms the unique identifier of every record.
* When used in /DSV/ mode ( like seen below ) is is used as a hash index.
* MODELADD > This is the /*.mdl/ path of the addition entity. It is mandatory and cannot be disabled.
* ENTCLASS > This is the class of the addition entity. It is mandatory and cannot be disabled.
* LINEID > This is the ID of the point that can be selected for building. They must be
* sequential and mandatory. If provided, the ID must the same as the row index under
* a given model key. Disabling this, makes it use the the index of the current line.
* Use that to swap the active points around by only moving the desired row up or down.
* For the example table definition below, the line ID in the database will be the same.
* POSOFF > This is the local position vector offset that TA uses to place the addition relative to MODELBASE.
* A NULL, empty, disabled or not available string is treated as taking {0,0,0}.
* ANGOFF > This is the local angle offset that TA uses to place the addition.
* A NULL, empty, disabled or not available string is treated as taking {0,0,0}.
* MOVETYPE > This internally calls /Entity:SetMoveType/ if the database parameter is zero or greater.
* PHYSINIT > This internally calls /Entity:PhysicsInit/ if the database parameter is zero or greater.
* DRSHADOW > This internally calls /Entity:DrawShadow/ if the database parameter is not zero.
* The call evaluates to /true/ for positive numbers and /false/ for negative.
* When the parameter is equal to zero skips the call of /Entity:DrawShadow/
* PHMOTION > This internally calls /PhysObj:EnableMotion/ if the database parameter is not zero on the validated physics object.
* The call evaluates to /true/ for positive numbers and /false/ for negative.
* When the parameter is equal to zero skips the call of /Entity:EnableMotion/
* PHYSLEEP > This internally calls /PhysObj:Sleep/ if the database parameter is grater than zero on the validated physics object.
* When the parameter is equal or less than zero skips the call of /Entity:Sleep/
* SETSOLID > This internally calls /Entity:SetSolid/ if the database parameter is zero or greater.
]]--
local myAdditions = {}
if(not asmlib.IsEmpty(myAdditions)) then
asmlib.LogInstance("SynchronizeDSV start <"..myPrefix..">")
if(not asmlib.SynchronizeDSV("ADDITIONS", myAdditions, true, myPrefix)) then
myThrowError("Failed to synchronize track additions")
else -- You are saving me from all the work for manually generating these
asmlib.LogInstance("TranslateDSV start <"..myPrefix..">")
if(not asmlib.TranslateDSV("ADDITIONS", myPrefix)) then
myThrowError("Failed to translate DSV into Lua") end
asmlib.LogInstance("TranslateDSV done <"..myPrefix..">")
end -- Now we have Lua inserts and DSV
end
--[[
* Create a table and populate it as shown below
* In the square brackets goes your TYPE,
* and then for every active point, you must have one array of
* strings and numbers, where the elements match the following data settings.
* {TYPE, LINEID, NAME}
* TYPE > This is the category under your physical properties are stored internally.
* It is mandatory and taken in pairs with LINEID, it forms the unique identifier of every record.
* When used in /DSV/ mode ( like seen below ) is is used as a hash index.
* LINEID > This is the ID of the point that can be selected for building. They must be
* sequential and mandatory. If provided, the ID must the same as the row index under
* a given model key. Disabling this, makes it use the the index of the current line.
* Use that to swap the active points around by only moving the desired row up or down.
* For the example table definition below, the line ID in the database will be the same.
* NAME > This stores the name of the physical property. It must an actual physical property.
]]--
local myPhysproperties = {}
if(not asmlib.IsEmpty(myPhysproperties)) then
asmlib.LogInstance("SynchronizeDSV start <"..myPrefix..">")
if(not asmlib.SynchronizeDSV("PHYSPROPERTIES", myPhysproperties, true, myPrefix)) then
myThrowError("Failed to synchronize track additions")
else -- You are saving me from all the work for manually generating these
asmlib.LogInstance("TranslateDSV start <"..myPrefix..">")
if(not asmlib.TranslateDSV("PHYSPROPERTIES", myPrefix)) then
myThrowError("Failed to translate DSV into Lua") end
asmlib.LogInstance("TranslateDSV done <"..myPrefix..">")
end -- Now we have Lua inserts and DSV
end
asmlib.LogInstance("<<< "..myScript)
else
myThrowError("Failed loading the required module")
end
--[[
* This logic statement is needed for reporting the error in the console if the
* process fails.
*
@ bSuccess = ExportCategory(nInd, tData, sPref)
* nInd > The index equal indent format to be stored with ( generally = 3 )
* tData > The category functional definition you want to use to divide your stuff with
* sPref > An export file custom prefix. For synchronizing
* it must be related to your addon ( default is instance prefix )
]]--
local function myExportCategory(tCatg)
asmlib.LogInstance("ExportCategory START <"..myPrefix..">")
if(CLIENT) then -- Category handling is client side only
if(not asmlib.IsEmpty(tCatg)) then
if(not asmlib.ExportCategory(3, tCatg, myPrefix)) then
myThrowError("Failed to synchronize category")
end; asmlib.LogInstance("ExportCategory OK <"..myPrefix..">")
else asmlib.LogInstance("ExportCategory SKIP <"..myPrefix..">") end
else asmlib.LogInstance("ExportCategory SERVER <"..myPrefix..">") end
end
-- Store a reference to disable symbol
local gsMissDB = asmlib.GetOpVar("MISS_NOSQL")
local gsToolPF = asmlib.GetOpVar("TOOLNAME_PU")
local gsSymOff = asmlib.GetOpVar("OPSYM_DISABLE")
local gsModeDB = asmlib.GetOpVar("MODE_DATABASE")
local gsFormPF = asmlib.GetOpVar("FORM_PREFIXDSV")
-- This is the path to your DSV
local myDsv = asmlib.GetOpVar("DIRPATH_BAS")..
asmlib.GetOpVar("DIRPATH_DSV")..
gsFormPF:format(myPrefix, gsToolPF.."PIECES")
--[[
* This flag is used when the track pieces list needs to be processed.
* It generally represents the locking file persistence flag. It is
* bound to finding a "PIECES" DSV external database for the prefix
* of your addon. You can use it for boolean value deciding whenever
* or not to run certain events. For example you can stop exporting
* your local database every time Gmod loads, but then the user will
* skip the available updates of your addon until he/she deletes the DSVs.
]]--
local myFlag = file.Exists(myDsv, "DATA")
-- Tell TA what custom script we just called don't touch it
asmlib.LogInstance(">>> "..myScript.." ("..tostring(myFlag).."): {"..myAddon..", "..myPrefix.."}")
-- Register the addon to the workshop ID list
asmlib.WorkshopID(myAddon, "287012681")
-- Register the addon to the pluggable DSV list
myRegisterDSV(myFlag)
--[[
* This is used if you want to make internal categories for your addon
* You must make a function as a string under the hash of your addon
* The function must take only one argument and that is the model
* For every sub-category of your track pieces, you must return a table
* with that much elements or return a /nil/ value to add the piece to
* the root of your branch. You can also return a second value if you
* want to override the track piece name. If you need to use categories
* for multiple track types, just put their hashes in the table below
* and make every track point to its dedicated category handler.
]]--
local myCategory = {
[myType] = {Txt = [[
function(m)
local r = m:gsub("models/props_phx/construct/",""):gsub("_","/")
local s = r:find("/"); r = s and r:sub(1,s-1) or nil
local n = nil
if(r) then
if(r == "metal" ) then n = "My metal plate" end
if(r == "windows") then n = "My glass plate" end
end
r = r and r:gsub("^%l", string.upper) or nil
p = r and {r} or nil
return p, n
end
]]}
}
-- Register the addon category to the pluggable DSV list
myExportCategory(myCategory)
--[[
* Create a table and populate it as shown below
* In the square brackets goes your MODEL,
* and then for every active point, you must have one array of
* strings, where the elements match the following data settings.
* You can use the disable event /#/ to make TA auto-fill
* the value provided and you can also add multiple track types myType[1-n].
* If you need to use piece origin/angle with model attachment, you must use
* the attachment extraction event /!/. The model attachment format is
* /!<attachment_name>/ and it depends what attachment name you gave it when you
* created the model. If you need TA to extract the origin/angle from an attachment named
* /test/ for example, you just need to put the string /!test/ in the origin/angle column for that model.
* {MODEL, TYPE, NAME, LINEID, POINT, ORIGIN, ANGLE, CLASS}
* MODEL > This string contains the path to your /*.mdl/ file. It is mandatory and
* taken in pairs with LINEID, it forms the unique identifier of every record.
* When used in /DSV/ mode ( like seen below ) is is used as a hash index.
* TYPE > This string is the name of the type your stuff will reside in the panel.
* Disabling this, makes it use the value of the /DEFAULT_TYPE/ variable.
* If it is empty uses the string /TYPE/, so make sure you fill this.
* NAME > This is the name of your track piece. Put /#/ here to be auto-generated from
* the model ( from the last slash to the file extension ).
* LINEID > This is the ID of the point that can be selected for building. They must be
* sequential and mandatory. If provided, the ID must the same as the row index under
* a given model key. Disabling this, makes it use the the index of the current line.
* Use that to swap the active points around by only moving the desired row up or down.
* For the example table definition below, the line ID in the database will be the same.
* POINT > This is the local position vector that TA searches and selects the related
* ORIGIN for. An empty or disabled string is treated as taking the ORIGIN.
* Disabling this using the disable event makes it hidden when the active point is searched for
* ORIGIN > This is the origin relative to which the next track piece position is calculated
* An empty string is treated as {0,0,0}. Disabling this also makes it use {0,0,0}
* You can also fill it with attachment event /!/ followed by your attachment name. It's mandatory
* ANGLE > This is the angle relative to which the forward and up vectors are calculated.
* An empty string is treated as {0,0,0}. Disabling this also makes it use {0,0,0}
* You can also fill it with attachment event /!/ followed by your attachment name. It's mandatory
* CLASS > This string is populated up when your entity class is not /prop_physics/ but something else
* used by ents.Create of the gmod ents API library. Keep this empty if your stuff is a normal prop.
]]--
local myPieces = {
["models/props_phx/construct/metal_plate1x2.mdl"] = { -- Here goes the model of your pack
{myType , gsSymOff, 1, "","0,-47.455105,1.482965","0,-90,0",""}, -- The first point parameter
{myType , gsSymOff, 2, "","0, 47.455105,1.482965","0, 90,0",""} -- The second point parameter
},
["models/props_phx/construct/windows/window1x2.mdl"] = {
{myType , gsSymOff, gsSymOff, "","0,-23.73248,1.482965","0,-90,0",""},
{myType , gsSymOff, gsSymOff, "","0, 71.17773,1.482965","0, 90,0",""}
}
}
-- Register the addon PIECES to the pluggable DSV list
mySyncTable("PIECES", myPieces, true)
--[[
* Create a table and populate it as shown below
* In the square brackets goes your MODELBASE,
* and then for every active point, you must have one array of
* strings and numbers, where the elements match the following data settings.
* {MODELBASE, MODELADD, ENTCLASS, LINEID, POSOFF, ANGOFF, MOVETYPE, PHYSINIT, DRSHADOW, PHMOTION, PHYSLEEP, SETSOLID}
* MODELBASE > This string contains the path to your base /*.mdl/ file the additions are gonna be attached to.
* It is mandatory and taken in pairs with LINEID, it forms the unique identifier of every record.
* When used in /DSV/ mode ( like seen below ) is is used as a hash index.
* MODELADD > This is the /*.mdl/ path of the addition entity. It is mandatory and cannot be disabled.
* ENTCLASS > This is the class of the addition entity. It is mandatory and cannot be disabled.
* LINEID > This is the ID of the point that can be selected for building. They must be
* sequential and mandatory. If provided, the ID must the same as the row index under
* a given model key. Disabling this, makes it use the the index of the current line.
* Use that to swap the active points around by only moving the desired row up or down.
* For the example table definition below, the line ID in the database will be the same.
* POSOFF > This is the local position vector offset that TA uses to place the addition relative to MODELBASE.
* A NULL, empty, disabled or not available string is treated as taking {0,0,0}.
* ANGOFF > This is the local angle offset that TA uses to place the addition.
* A NULL, empty, disabled or not available string is treated as taking {0,0,0}.
* MOVETYPE > This internally calls /Entity:SetMoveType/ if the database parameter is zero or greater.
* PHYSINIT > This internally calls /Entity:PhysicsInit/ if the database parameter is zero or greater.
* DRSHADOW > This internally calls /Entity:DrawShadow/ if the database parameter is not zero.
* The call evaluates to /true/ for positive numbers and /false/ for negative.
* When the parameter is equal to zero skips the call of /Entity:DrawShadow/
* PHMOTION > This internally calls /PhysObj:EnableMotion/ if the database parameter is not zero on the validated physics object.
* The call evaluates to /true/ for positive numbers and /false/ for negative.
* When the parameter is equal to zero skips the call of /Entity:EnableMotion/
* PHYSLEEP > This internally calls /PhysObj:Sleep/ if the database parameter is grater than zero on the validated physics object.
* When the parameter is equal or less than zero skips the call of /Entity:Sleep/
* SETSOLID > This internally calls /Entity:SetSolid/ if the database parameter is zero or greater.
]]--
local myAdditions = {}
-- Register the addon ADDITIONS to the pluggable DSV list
mySyncTable("ADDITIONS", myAdditions, true)
--[[
* Create a table and populate it as shown below
* In the square brackets goes your TYPE,
* and then for every active point, you must have one array of
* strings and numbers, where the elements match the following data settings.
* {TYPE, LINEID, NAME}
* TYPE > This is the category under your physical properties are stored internally.
* It is mandatory and taken in pairs with LINEID, it forms the unique identifier of every record.
* When used in /DSV/ mode ( like seen below ) is is used as a hash index.
* LINEID > This is the ID of the point that can be selected for building. They must be
* sequential and mandatory. If provided, the ID must the same as the row index under
* a given model key. Disabling this, makes it use the the index of the current line.
* Use that to swap the active points around by only moving the desired row up or down.
* For the example table definition below, the line ID in the database will be the same.
* NAME > This stores the name of the physical property. It must an actual physical property.
]]--
local myPhysproperties = {}
-- Register the addon PHYSPROPERTIES to the pluggable DSV list
mySyncTable("PHYSPROPERTIES", myPhysproperties, true)
asmlib.LogInstance("<<< "..myScript)

View File

@ -32,6 +32,7 @@ local guiMouseY = gui and gui.MouseY
local guiOpenURL = gui and gui.OpenURL
local guiEnableScreenClicker = gui and gui.EnableScreenClicker
local entsGetByIndex = ents and ents.GetByIndex
local mathAtan2 = math and math.atan2
local mathAbs = math and math.abs
local mathCeil = math and math.ceil
local mathFloor = math and math.floor
@ -92,7 +93,7 @@ local asmlib = trackasmlib; if(not asmlib) then -- Module present
------------ CONFIGURE ASMLIB ------------
asmlib.InitBase("track","assembly")
asmlib.SetOpVar("TOOL_VERSION","8.693")
asmlib.SetOpVar("TOOL_VERSION","8.694")
asmlib.SetIndexes("V" ,1,2,3)
asmlib.SetIndexes("A" ,1,2,3)
asmlib.SetIndexes("WV",1,2,3)
@ -640,7 +641,7 @@ if(CLIENT) then
asmlib.RotateXY(vN, nBr); asmlib.RotateXY(vF, nBr) -- Near and far base rotation
asmlib.NegY(asmlib.SubXY(vA, mXY, vCn)) -- Origin [0;0] is located at top left
asmlib.RotateXY(vA, -nBr) -- Correctly read the wiper vector to identify working mode
local aW = asmlib.GetAngleXY(vA) -- Read wiper angle and normalize the value
local aW = mathAtan2(vA.y, vA.x) -- Read wiper angle and normalize the value
aW = ((aW < 0) and (aW + nMr) or aW) -- Convert [0;+pi;-pi;0] to [0;2pi]
local iW = mathFloor(((aW / nMr) * nN) + 1) -- Calculate fraction ID for working mode
local dA = (nMr / (nK * nN)) -- Two times smaller step to hangle centers as well

View File

@ -11,7 +11,12 @@
-- Local reference to the module.
local asmlib = trackasmlib
-- Change this to your addon name.
--[[
* This is your addon name. It is mandatory and it must be string.
* It is used by TA in order to classify the content you are creating
* It must NOT be an empty string nil or any other type regarding
* The value will be automatically pattern converted to a index prefix
]]
local myAddon = "Shinji85's Rails" -- Your addon name goes here
--[[
@ -40,6 +45,7 @@ local myPrefix = myAddon:gsub("[^%w]","_") -- Addon prefix
-- Do not touch this also, it is used for debugging
local myScript = tostring(debug.getinfo(1).source or "N/A")
myScript = "@"..myScript:gsub("^%W+", ""):gsub("\\","/")
--[[
* This function defines what happens when there is an error present
* Usually you can tell Gmod that you want it to generate an error
@ -48,368 +54,363 @@ local myScript = tostring(debug.getinfo(1).source or "N/A")
* when you need it to do something else.
--]]
local function myThrowError(vMesg)
local sMesg = tostring(vMesg) -- Make sure the message is string
if(asmlib) then asmlib.LogInstance(sMesg) end -- Output the message into the logs
myError(myScript.." > ("..myAddon.."): "..sMesg) -- Generate an error in the console ( optional )
local sMesg = (myScript.." > ("..myAddon.."): "..tostring(vMesg)) -- Make sure the message is string
if(asmlib) then asmlib.LogInstance(sMesg) end; myError(sMesg) -- Output the message into the logs
end
if(asmlib) then
-- Store a reference to disable symbol
local gsMissDB = asmlib.GetOpVar("MISS_NOSQL")
local gsToolPF = asmlib.GetOpVar("TOOLNAME_PU")
local gsSymOff = asmlib.GetOpVar("OPSYM_DISABLE")
local gsFormPF = asmlib.GetOpVar("FORM_PREFIXDSV")
-- There is something to error about stop the execution and report it
if(not asmlib) then myThrowError("Failed loading the required module!"); return end
-- This is the path to your DSV
local myDsv = asmlib.GetOpVar("DIRPATH_BAS")..
asmlib.GetOpVar("DIRPATH_DSV")..
gsFormPF:format(myPrefix, gsToolPF.."PIECES")
--[[
* This logic statement is needed for reporting the error
* in the console if the process fails.
*
@ bSuccess = SynchronizeDSV(sTable, tData, bRepl, sPref, sDelim)
* sTable > The table you want to sync
* tData > A data table like the one described above
* bRepl > If set to /true/, makes the API replace the repeating models with
these of your addon. This is nice when you are constantly updating your track packs
If set to /false/ keeps the current model in the
database and ignores yours if they are the same file.
* sPref > An export file custom prefix. For synchronizing it must be related to your addon
* sDelim > The delimiter used by the server/client ( default is a tab symbol )
*
@ bSuccess = TranslateDSV(sTable, sPref, sDelim)
* sTable > The table you want to translate to Lua script
* sPref > An export file custom prefix. For synchronizing it must be related to your addon
* sDelim > The delimiter used by the server/client ( default is a tab symbol )
]]--
local function mySyncTable(sName, tData, bRepl)
if(not asmlib.IsEmpty(tData)) then -- Somrting to be processed. Do stuff when the table is not empty
asmlib.LogInstance("SynchronizeDSV START <"..myPrefix..">") -- Signal start synchronization
if(not asmlib.SynchronizeDSV(sName, tData, bRepl, myPrefix)) then -- Attempt to synchronize
myThrowError("Failed to synchronize: "..sName) -- Raise error when fails to sync tracks data
else -- Successful. You are saving me from all the work for manually generating these
asmlib.LogInstance("TranslateDSV START <"..myPrefix..">") -- Signal start translation
if(not asmlib.TranslateDSV(sName, myPrefix)) then -- Attempt to translate the DSV to Lua source
myThrowError("Failed to translate DSV: "..sName) end -- Raise error when fails
asmlib.LogInstance("TranslateDSV OK <"..myPrefix..">") -- Translation is successful
end -- Now we have Lua inserts and DSV. Otherwise sent empty table and print status in logs
else asmlib.LogInstance("SynchronizeDSV EMPTY <"..myPrefix..">") end -- Nothing to be done
end
--[[
* This flag is used when the track pieces list needs to be processed.
* It generally represents the locking file persistence flag. It is
* bound to finding a "PIECES" DSV external database for the prefix
* of your addon. You can use it for boolean value deciding whenever
* or not to run certain events. For example you can stop exporting
* your local database every time Gmod loads, but then the user will
* skip the available updates of your addon until he/she deletes the DSVs.
]]--
local myFlag = file.Exists(myDsv, "DATA")
-- Tell TA what custom script we just called don't touch it
asmlib.LogInstance(">>> "..myScript.." ("..tostring(myFlag).."): {"..myAddon..", "..myPrefix.."}")
-- Register the addon to the workshop ID list
asmlib.WorkshopID(myAddon, "326640186")
--[[
* Register the addon to the auto-load prefix list when the
* PIECES file is missing. The auto-load list is located in
* (/garrysmod/data/trackassembly/set/trackasmlib_dsv.txt)
* a.k.a the DATA folder of Garry's mod.
*
* @bSuccess = RegisterDSV(sProg, sPref, sDelim)
* sProg > The program which registered the DSV
* sPref > The external data prefix to be added ( default instance prefix )
* sDelim > The delimiter to be used for processing ( default tab )
* bSkip > Skip addition for the DSV prefix if exists ( default `false` )
]]--
asmlib.LogInstance("RegisterDSV start <"..myPrefix..">")
if(myFlag) then -- Your DSV must be registered only once when loading for the first time
asmlib.LogInstance("RegisterDSV skip <"..myPrefix..">")
--[[
* Register the addon to the auto-load prefix list when the
* PIECES file is missing. The auto-load list is located in
* (/garrysmod/data/trackassembly/set/trackasmlib_dsv.txt)
* a.k.a the DATA folder of Garry's mod.
*
* @bSuccess = RegisterDSV(sProg, sPref, sDelim)
* sProg > The program which registered the DSV
* sPref > The external data prefix to be added ( default instance prefix )
* sDelim > The delimiter to be used for processing ( default tab )
* bSkip > Skip addition for the DSV prefix if exists ( default `false` )
]]--
local function myRegisterDSV(bSkip)
asmlib.LogInstance("RegisterDSV START <"..myPrefix..">")
if(bSkip) then -- Your DSV must be registered only once when loading for the first time
asmlib.LogInstance("RegisterDSV SKIP <"..myPrefix..">")
else -- If the locking file is not located that means this is the first run of your script
if(not asmlib.RegisterDSV(myScript, myPrefix)) then -- Register the DSV prefix and check for error
myThrowError("Failed to register DSV") -- Throw the error if fails
end -- Third argument is the delimiter. The default tab is used
asmlib.LogInstance("RegisterDSV done <"..myPrefix..">")
asmlib.LogInstance("RegisterDSV OK <"..myPrefix..">")
end
--[[
* This is used if you want to make internal categories for your addon
* You must make a function as a string under the hash of your addon
* The function must take only one argument and that is the model
* For every sub-category of your track pieces, you must return a table
* with that much elements or return a /nil/ value to add the piece to
* the root of your branch. You can also return a second value if you
* want to override the track piece name. If you need to use categories
* for multiple track types, just put their hashes in the table below
* and make every track point to its dedicated category handler.
]]--
local myCategory = {
[myType] = {Txt = [[
function(m) local c
local r = m:gsub("models/shinji85/train/rail_", "")
if(r:find("cross")) then c = "crossing"
elseif(r:find("switch")) then c = "switch"
elseif(r:find("curve")) then c = "curve"
elseif(r:find("bumper")) then c = "bumper"
elseif(r:find("junction")) then c = "junction"
elseif(r:find("%dx")) then c = "straight" end; return c end
]]}
}
--[[
* This logic statement is needed for reporting the error in the console if the
* process fails.
*
@ bSuccess = ExportCategory(nInd, tData, sPref)
* nInd > The index equal indent format to be stored with ( generally = 3 )
* tData > The category functional definition you want to use to divide your stuff with
* sPref > An export file custom prefix. For synchronizing
* it must be related to your addon ( default is instance prefix )
]]--
asmlib.LogInstance("ExportCategory start <"..myPrefix..">")
if(CLIENT) then -- Category handling is client side only
if(not asmlib.IsEmpty(myCategory)) then
if(not asmlib.ExportCategory(3, myCategory, myPrefix)) then
myThrowError("Failed to synchronize category")
end; asmlib.LogInstance("ExportCategory done <"..myPrefix..">")
else asmlib.LogInstance("ExportCategory skip <"..myPrefix..">") end
else asmlib.LogInstance("ExportCategory server <"..myPrefix..">") end
--[[
* Create a table and populate it as shown below
* In the square brackets goes your MODEL,
* and then for every active point, you must have one array of
* strings, where the elements match the following data settings.
* You can use the disable event /#/ to make TA auto-fill
* the value provided and you can also add multiple track types myType[1-n].
* If you need to use piece origin/angle with model attachment, you must use
* the attachment extraction event /!/. The model attachment format is
* /!<attachment_name>/ and it depends what attachment name you gave it when you
* created the model. If you need TA to extract the origin/angle from an attachment named
* /test/ for example, you just need to put the string /!test/ in the origin/angle column for that model.
* {MODEL, TYPE, NAME, LINEID, POINT, ORIGIN, ANGLE, CLASS}
* MODEL > This string contains the path to your /*.mdl/ file. It is mandatory and
* taken in pairs with LINEID, it forms the unique identifier of every record.
* When used in /DSV/ mode ( like seen below ) is is used as a hash index.
* TYPE > This string is the name of the type your stuff will reside in the panel.
* Disabling this, makes it use the value of the /DEFAULT_TYPE/ variable.
* If it is empty uses the string /TYPE/, so make sure you fill this.
* NAME > This is the name of your track piece. Put /#/ here to be auto-generated from
* the model ( from the last slash to the file extension ).
* LINEID > This is the ID of the point that can be selected for building. They must be
* sequential and mandatory. If provided, the ID must the same as the row index under
* a given model key. Disabling this, makes it use the the index of the current line.
* Use that to swap the active points around by only moving the desired row up or down.
* For the example table definition below, the line ID in the database will be the same.
* POINT > This is the local position vector that TA searches and selects the related
* ORIGIN for. An empty string is treated as taking the ORIGIN.
* Disabling this using the disable event makes it hidden when the active point is searched for
* ORIGIN > This is the origin relative to which the next track piece position is calculated
* An empty string is treated as {0,0,0}. Disabling this also makes it use {0,0,0}
* You can also fill it with attachment event /!/ followed by your attachment name. It's mandatory
* ANGLE > This is the angle relative to which the forward and up vectors are calculated.
* An empty string is treated as {0,0,0}. Disabling this also makes it use {0,0,0}
* You can also fill it with attachment event /!/ followed by your attachment name. It's mandatory
* CLASS > This string is populated up when your entity class is not /prop_physics/ but something else
* used by ents.Create of the gmod ents API library. Keep this empty if your stuff is a normal prop.
]]--
local myPieces = {
["models/shinji85/train/rail_16x.mdl"] = {
{myType, "Straight 16x", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Straight 16x", gsSymOff, gsMissDB, "-2048,0,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_1x.mdl"] = {
{myType, "Straight 1x", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Straight 1x", gsSymOff, gsMissDB, "-128,0,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_2x.mdl"] = {
{myType, "Straight 2x", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Straight 2x", gsSymOff, gsMissDB, "-256,0,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_4x.mdl"] = {
{myType, "Straight 4x", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Straight 4x", gsSymOff, gsMissDB, "-512,0,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_4x_crossing.mdl"] = {
{myType, "Crossing 4x", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Crossing 4x", gsSymOff, gsMissDB, "-512,0,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_8x.mdl"] = {
{myType, "Straight 8x", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Straight 8x", gsSymOff, gsMissDB, "-1024,0,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_bumper.mdl"] = {
{myType, "Bumper", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB}
},
["models/shinji85/train/rail_cross_4x.mdl"] = {
{myType, "Cross 4x", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Cross 4x", gsSymOff, gsMissDB, "-512,0,7.346", "0,180,0", gsMissDB},
{myType, "Cross 4x", gsSymOff, gsMissDB, "-256,-256,7.346", "0,270,0", gsMissDB},
{myType, "Cross 4x", gsSymOff, gsMissDB, "-256,256,7.346", "0,90,0", gsMissDB}
},
["models/shinji85/train/rail_cs.mdl"] = {
{myType, "Counter Switch", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Counter Switch", gsSymOff, gsMissDB, "-908.81165,0,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_csfix.mdl"] = {
{myType, "Counter Switch Fix", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Counter Switch Fix", gsSymOff, gsMissDB, "-115.18847,0,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_curve_cc.mdl"] = {
{myType, "Curve Cc", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Curve Cc", gsSymOff, gsMissDB, "-966.40515,128,7.346", "0,165,0", gsMissDB}
},
["models/shinji85/train/rail_curve_r1.mdl"] = {
{myType, "Curve R1", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Curve R1", gsSymOff, gsMissDB, "-1060.12341,139.56763,7.346", "0,165,0", gsMissDB}
},
["models/shinji85/train/rail_curve_r11.mdl"] = {
{myType, "Curve R11", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Curve R11", gsSymOff, gsMissDB, "-1086.11584,449.88458,7.346", "0,135,0", gsMissDB}
},
["models/shinji85/train/rail_curve_r12.mdl"] = {
{myType, "Curve R12", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Curve R12", gsSymOff, gsMissDB, "-905.09656,374.90414,7.346", "0,135,0", gsMissDB}
},
["models/shinji85/train/rail_curve_r13.mdl"] = {
{myType, "Curve R13", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Curve R13", gsSymOff, gsMissDB, "-724.07727,299.92276,7.346", "0,135,0", gsMissDB}
},
["models/shinji85/train/rail_curve_r2.mdl"] = {
{myType, "Curve R2", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Curve R2", gsSymOff, gsMissDB, "-993.86572,130.84471,7.346", "0,165,0", gsMissDB}
},
["models/shinji85/train/rail_curve_r3.mdl"] = {
{myType, "Curve R3", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Curve R3", gsSymOff, gsMissDB, "-927.60797,122.1218,7.346", "0,165,0", gsMissDB}
},
["models/shinji85/train/rail_cx.mdl"] = {
{myType, "Counter X", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Counter X", gsSymOff, gsMissDB, "-362.51361,0,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_cxfix.mdl"] = {
{myType, "Counter X Fix", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Counter X Fix", gsSymOff, gsMissDB, "-149.48648,0,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_double_4x_crossing.mdl"] = {
{myType, "Crossing Double 4x", gsSymOff, gsMissDB, "0,128,7.346", gsMissDB, gsMissDB},
{myType, "Crossing Double 4x", gsSymOff, gsMissDB, "-512,128,7.346", "0,180,0", gsMissDB},
{myType, "Crossing Double 4x", gsSymOff, gsMissDB, "0,-128,7.346", gsMissDB, gsMissDB},
{myType, "Crossing Double 4x", gsSymOff, gsMissDB, "-512,-128,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_double_bumper.mdl"] = {
{myType, "Bumper Double", gsSymOff, gsMissDB, "0,128,7.346", gsMissDB, gsMissDB},
{myType, "Bumper Double", gsSymOff, gsMissDB, "0,-128,7.346", gsMissDB, gsMissDB}
},
["models/shinji85/train/rail_l_switch.mdl"] = {
{myType, "Left Switch", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Left Switch", gsSymOff, gsMissDB, "-1024,0,7.346", "0,180,0", gsMissDB},
{myType, "Left Switch", gsSymOff, gsMissDB, "-966.40515,-128,7.346", "0,195,0", gsMissDB}
},
["models/shinji85/train/rail_r_switch.mdl"] = {
{myType, "Right Switch", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Right Switch", gsSymOff, gsMissDB, "-1024,0,7.346", "0,180,0", gsMissDB},
{myType, "Right Switch", gsSymOff, gsMissDB, "-966.40515,128,7.346", "0,165,0", gsMissDB}
},
["models/shinji85/train/rail_x_junction.mdl"] = {
{myType, "X Junction", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "X Junction", gsSymOff, gsMissDB, "-494.55,0,7.346", "0,180,0", gsMissDB},
{myType, "X Junction", gsSymOff, gsMissDB, "-33.129,-123.63866,7.346", "0,-30,0", gsMissDB},
{myType, "X Junction", gsSymOff, gsMissDB, "-461.42175,123.63649,7.346", "0,150,0", gsMissDB}
}
}
--[[
* This logic statement is needed for reporting the error in the console if the
* process fails.
*
@ bSuccess = SynchronizeDSV(sTable, tData, bRepl, sPref, sDelim)
* sTable > The table you want to sync
* tData > A data table like the one described above
* bRepl > If set to /true/, makes the API replace the repeating models with
these of your addon. This is nice when you are constantly updating your track packs
If set to /false/ keeps the current model in the
database and ignores yours if they are the same file.
* sPref > An export file custom prefix. For synchronizing it must be related to your addon
* sDelim > The delimiter used by the server/client ( default is a tab symbol )
*
@ bSuccess = TranslateDSV(sTable, sPref, sDelim)
* sTable > The table you want to translate to Lua script
* sPref > An export file custom prefix. For synchronizing it must be related to your addon
* sDelim > The delimiter used by the server/client ( default is a tab symbol )
]]--
if(not asmlib.IsEmpty(myPieces)) then
asmlib.LogInstance("SynchronizeDSV start <"..myPrefix..">")
if(not asmlib.SynchronizeDSV("PIECES", myPieces, true, myPrefix)) then
myThrowError("Failed to synchronize track pieces")
else -- You are saving me from all the work for manually generating these
asmlib.LogInstance("TranslateDSV start <"..myPrefix..">")
if(not asmlib.TranslateDSV("PIECES", myPrefix)) then
myThrowError("Failed to translate DSV into Lua") end
asmlib.LogInstance("TranslateDSV done <"..myPrefix..">")
end -- Now we have Lua inserts and DSV
end
--[[
* Create a table and populate it as shown below
* In the square brackets goes your MODELBASE,
* and then for every active point, you must have one array of
* strings and numbers, where the elements match the following data settings.
* {MODELBASE, MODELADD, ENTCLASS, LINEID, POSOFF, ANGOFF, MOVETYPE, PHYSINIT, DRSHADOW, PHMOTION, PHYSLEEP, SETSOLID}
* MODELBASE > This string contains the path to your base /*.mdl/ file the additions are gonna be attached to.
* It is mandatory and taken in pairs with LINEID, it forms the unique identifier of every record.
* When used in /DSV/ mode ( like seen below ) is is used as a hash index.
* MODELADD > This is the /*.mdl/ path of the addition entity. It is mandatory and cannot be disabled.
* ENTCLASS > This is the class of the addition entity. It is mandatory and cannot be disabled.
* LINEID > This is the ID of the point that can be selected for building. They must be
* sequential and mandatory. If provided, the ID must the same as the row index under
* a given model key. Disabling this, makes it use the the index of the current line.
* Use that to swap the active points around by only moving the desired row up or down.
* For the example table definition below, the line ID in the database will be the same.
* POSOFF > This is the local position vector offset that TA uses to place the addition relative to MODELBASE.
* A NULL, empty, disabled or not available string is treated as taking {0,0,0}.
* ANGOFF > This is the local angle offset that TA uses to place the addition.
* A NULL, empty, disabled or not available string is treated as taking {0,0,0}.
* MOVETYPE > This internally calls /Entity:SetMoveType/ if the database parameter is zero or greater.
* PHYSINIT > This internally calls /Entity:PhysicsInit/ if the database parameter is zero or greater.
* DRSHADOW > This internally calls /Entity:DrawShadow/ if the database parameter is not zero.
* The call evaluates to /true/ for positive numbers and /false/ for negative.
* When the parameter is equal to zero skips the call of /Entity:DrawShadow/
* PHMOTION > This internally calls /PhysObj:EnableMotion/ if the database parameter is not zero on the validated physics object.
* The call evaluates to /true/ for positive numbers and /false/ for negative.
* When the parameter is equal to zero skips the call of /Entity:EnableMotion/
* PHYSLEEP > This internally calls /PhysObj:Sleep/ if the database parameter is grater than zero on the validated physics object.
* When the parameter is equal or less than zero skips the call of /Entity:Sleep/
* SETSOLID > This internally calls /Entity:SetSolid/ if the database parameter is zero or greater.
]]--
local myAdditions = {
["models/shinji85/train/rail_l_switch.mdl"] = {
{"models/shinji85/train/sw_lever.mdl", "buttonswitch", gsSymOff, "-100,-125,0", "0,180,0", -1, -1, -1, 0, -1, -1},
{"models/shinji85/train/rail_l_switcher1.mdl", "prop_dynamic", gsSymOff, gsMissDB, gsMissDB, MOVETYPE_VPHYSICS, SOLID_VPHYSICS, -1, -1, 1, SOLID_VPHYSICS},
{"models/shinji85/train/rail_l_switcher2.mdl", "prop_dynamic", gsSymOff, gsMissDB, gsMissDB, MOVETYPE_VPHYSICS, SOLID_VPHYSICS, -1, 0, -1, SOLID_NONE}
},
["models/shinji85/train/rail_r_switch.mdl"] = {
{"models/shinji85/train/sw_lever.mdl", "buttonswitch", gsSymOff, "-100,125,0", gsMissDB, -1, -1, -1, 0, -1, -1},
{"models/shinji85/train/rail_r_switcher1.mdl", "prop_dynamic", gsSymOff, gsMissDB, gsMissDB, MOVETYPE_VPHYSICS, SOLID_VPHYSICS, -1, -1, 1, SOLID_VPHYSICS},
{"models/shinji85/train/rail_r_switcher2.mdl", "prop_dynamic", gsSymOff, gsMissDB, gsMissDB, MOVETYPE_VPHYSICS, SOLID_VPHYSICS, -1, 0, -1, SOLID_NONE}
}
}
if(not asmlib.IsEmpty(myAdditions)) then
asmlib.LogInstance("SynchronizeDSV start <"..myPrefix..">")
if(not asmlib.SynchronizeDSV("ADDITIONS", myAdditions, true, myPrefix)) then
myThrowError("Failed to synchronize track additions")
else -- You are saving me from all the work for manually generating these
asmlib.LogInstance("TranslateDSV start <"..myPrefix..">")
if(not asmlib.TranslateDSV("ADDITIONS", myPrefix)) then
myThrowError("Failed to translate DSV into Lua") end
asmlib.LogInstance("TranslateDSV done <"..myPrefix..">")
end -- Now we have Lua inserts and DSV
end
--[[
* Create a table and populate it as shown below
* In the square brackets goes your TYPE,
* and then for every active point, you must have one array of
* strings and numbers, where the elements match the following data settings.
* {TYPE, LINEID, NAME}
* TYPE > This is the category under your physical properties are stored internally.
* It is mandatory and taken in pairs with LINEID, it forms the unique identifier of every record.
* When used in /DSV/ mode ( like seen below ) is is used as a hash index.
* LINEID > This is the ID of the point that can be selected for building. They must be
* sequential and mandatory. If provided, the ID must the same as the row index under
* a given model key. Disabling this, makes it use the the index of the current line.
* Use that to swap the active points around by only moving the desired row up or down.
* For the example table definition below, the line ID in the database will be the same.
* NAME > This stores the name of the physical property. It must an actual physical property.
]]--
local myPhysproperties = {}
if(not asmlib.IsEmpty(myPhysproperties)) then
asmlib.LogInstance("SynchronizeDSV start <"..myPrefix..">")
if(not asmlib.SynchronizeDSV("PHYSPROPERTIES", myPhysproperties, true, myPrefix)) then
myThrowError("Failed to synchronize track additions")
else -- You are saving me from all the work for manually generating these
asmlib.LogInstance("TranslateDSV start <"..myPrefix..">")
if(not asmlib.TranslateDSV("PHYSPROPERTIES", myPrefix)) then
myThrowError("Failed to translate DSV into Lua") end
asmlib.LogInstance("TranslateDSV done <"..myPrefix..">")
end -- Now we have Lua inserts and DSV
end
asmlib.LogInstance("<<< "..myScript)
else
myThrowError("Failed loading the required module")
end
--[[
* This logic statement is needed for reporting the error in the console if the
* process fails.
*
@ bSuccess = ExportCategory(nInd, tData, sPref)
* nInd > The index equal indent format to be stored with ( generally = 3 )
* tData > The category functional definition you want to use to divide your stuff with
* sPref > An export file custom prefix. For synchronizing
* it must be related to your addon ( default is instance prefix )
]]--
local function myExportCategory(tCatg)
asmlib.LogInstance("ExportCategory START <"..myPrefix..">")
if(CLIENT) then -- Category handling is client side only
if(not asmlib.IsEmpty(tCatg)) then
if(not asmlib.ExportCategory(3, tCatg, myPrefix)) then
myThrowError("Failed to synchronize category")
end; asmlib.LogInstance("ExportCategory OK <"..myPrefix..">")
else asmlib.LogInstance("ExportCategory SKIP <"..myPrefix..">") end
else asmlib.LogInstance("ExportCategory SERVER <"..myPrefix..">") end
end
-- Store a reference to disable symbol
local gsMissDB = asmlib.GetOpVar("MISS_NOSQL")
local gsToolPF = asmlib.GetOpVar("TOOLNAME_PU")
local gsSymOff = asmlib.GetOpVar("OPSYM_DISABLE")
local gsFormPF = asmlib.GetOpVar("FORM_PREFIXDSV")
-- This is the path to your DSV
local myDsv = asmlib.GetOpVar("DIRPATH_BAS")..
asmlib.GetOpVar("DIRPATH_DSV")..
gsFormPF:format(myPrefix, gsToolPF.."PIECES")
--[[
* This flag is used when the track pieces list needs to be processed.
* It generally represents the locking file persistence flag. It is
* bound to finding a "PIECES" DSV external database for the prefix
* of your addon. You can use it for boolean value deciding whenever
* or not to run certain events. For example you can stop exporting
* your local database every time Gmod loads, but then the user will
* skip the available updates of your addon until he/she deletes the DSVs.
]]--
local myFlag = file.Exists(myDsv, "DATA")
-- Tell TA what custom script we just called don't touch it
asmlib.LogInstance(">>> "..myScript.." ("..tostring(myFlag).."): {"..myAddon..", "..myPrefix.."}")
-- Register the addon to the workshop ID list
asmlib.WorkshopID(myAddon, "326640186")
-- Register the addon to the pluggable DSV list
myRegisterDSV(myFlag)
--[[
* This is used if you want to make internal categories for your addon
* You must make a function as a string under the hash of your addon
* The function must take only one argument and that is the model
* For every sub-category of your track pieces, you must return a table
* with that much elements or return a /nil/ value to add the piece to
* the root of your branch. You can also return a second value if you
* want to override the track piece name. If you need to use categories
* for multiple track types, just put their hashes in the table below
* and make every track point to its dedicated category handler.
]]--
local myCategory = {
[myType] = {Txt = [[
function(m) local c
local r = m:gsub("models/shinji85/train/rail_", "")
if(r:find("cross")) then c = "crossing"
elseif(r:find("switch")) then c = "switch"
elseif(r:find("curve")) then c = "curve"
elseif(r:find("bumper")) then c = "bumper"
elseif(r:find("junction")) then c = "junction"
elseif(r:find("%dx")) then c = "straight" end; return c end
]]}
}
-- Register the addon category to the pluggable DSV list
myExportCategory(myCategory)
--[[
* Create a table and populate it as shown below
* In the square brackets goes your MODEL,
* and then for every active point, you must have one array of
* strings, where the elements match the following data settings.
* You can use the disable event /#/ to make TA auto-fill
* the value provided and you can also add multiple track types myType[1-n].
* If you need to use piece origin/angle with model attachment, you must use
* the attachment extraction event /!/. The model attachment format is
* /!<attachment_name>/ and it depends what attachment name you gave it when you
* created the model. If you need TA to extract the origin/angle from an attachment named
* /test/ for example, you just need to put the string /!test/ in the origin/angle column for that model.
* {MODEL, TYPE, NAME, LINEID, POINT, ORIGIN, ANGLE, CLASS}
* MODEL > This string contains the path to your /*.mdl/ file. It is mandatory and
* taken in pairs with LINEID, it forms the unique identifier of every record.
* When used in /DSV/ mode ( like seen below ) is is used as a hash index.
* TYPE > This string is the name of the type your stuff will reside in the panel.
* Disabling this, makes it use the value of the /DEFAULT_TYPE/ variable.
* If it is empty uses the string /TYPE/, so make sure you fill this.
* NAME > This is the name of your track piece. Put /#/ here to be auto-generated from
* the model ( from the last slash to the file extension ).
* LINEID > This is the ID of the point that can be selected for building. They must be
* sequential and mandatory. If provided, the ID must the same as the row index under
* a given model key. Disabling this, makes it use the the index of the current line.
* Use that to swap the active points around by only moving the desired row up or down.
* For the example table definition below, the line ID in the database will be the same.
* POINT > This is the local position vector that TA searches and selects the related
* ORIGIN for. An empty string is treated as taking the ORIGIN.
* Disabling this using the disable event makes it hidden when the active point is searched for
* ORIGIN > This is the origin relative to which the next track piece position is calculated
* An empty string is treated as {0,0,0}. Disabling this also makes it use {0,0,0}
* You can also fill it with attachment event /!/ followed by your attachment name. It's mandatory
* ANGLE > This is the angle relative to which the forward and up vectors are calculated.
* An empty string is treated as {0,0,0}. Disabling this also makes it use {0,0,0}
* You can also fill it with attachment event /!/ followed by your attachment name. It's mandatory
* CLASS > This string is populated up when your entity class is not /prop_physics/ but something else
* used by ents.Create of the gmod ents API library. Keep this empty if your stuff is a normal prop.
]]--
local myPieces = {
["models/shinji85/train/rail_16x.mdl"] = {
{myType, "Straight 16x", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Straight 16x", gsSymOff, gsMissDB, "-2048,0,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_1x.mdl"] = {
{myType, "Straight 1x", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Straight 1x", gsSymOff, gsMissDB, "-128,0,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_2x.mdl"] = {
{myType, "Straight 2x", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Straight 2x", gsSymOff, gsMissDB, "-256,0,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_4x.mdl"] = {
{myType, "Straight 4x", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Straight 4x", gsSymOff, gsMissDB, "-512,0,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_4x_crossing.mdl"] = {
{myType, "Crossing 4x", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Crossing 4x", gsSymOff, gsMissDB, "-512,0,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_8x.mdl"] = {
{myType, "Straight 8x", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Straight 8x", gsSymOff, gsMissDB, "-1024,0,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_bumper.mdl"] = {
{myType, "Bumper", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB}
},
["models/shinji85/train/rail_cross_4x.mdl"] = {
{myType, "Cross 4x", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Cross 4x", gsSymOff, gsMissDB, "-512,0,7.346", "0,180,0", gsMissDB},
{myType, "Cross 4x", gsSymOff, gsMissDB, "-256,-256,7.346", "0,270,0", gsMissDB},
{myType, "Cross 4x", gsSymOff, gsMissDB, "-256,256,7.346", "0,90,0", gsMissDB}
},
["models/shinji85/train/rail_cs.mdl"] = {
{myType, "Counter Switch", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Counter Switch", gsSymOff, gsMissDB, "-908.81165,0,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_csfix.mdl"] = {
{myType, "Counter Switch Fix", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Counter Switch Fix", gsSymOff, gsMissDB, "-115.18847,0,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_curve_cc.mdl"] = {
{myType, "Curve Cc", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Curve Cc", gsSymOff, gsMissDB, "-966.40515,128,7.346", "0,165,0", gsMissDB}
},
["models/shinji85/train/rail_curve_r1.mdl"] = {
{myType, "Curve R1", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Curve R1", gsSymOff, gsMissDB, "-1060.12341,139.56763,7.346", "0,165,0", gsMissDB}
},
["models/shinji85/train/rail_curve_r11.mdl"] = {
{myType, "Curve R11", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Curve R11", gsSymOff, gsMissDB, "-1086.11584,449.88458,7.346", "0,135,0", gsMissDB}
},
["models/shinji85/train/rail_curve_r12.mdl"] = {
{myType, "Curve R12", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Curve R12", gsSymOff, gsMissDB, "-905.09656,374.90414,7.346", "0,135,0", gsMissDB}
},
["models/shinji85/train/rail_curve_r13.mdl"] = {
{myType, "Curve R13", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Curve R13", gsSymOff, gsMissDB, "-724.07727,299.92276,7.346", "0,135,0", gsMissDB}
},
["models/shinji85/train/rail_curve_r2.mdl"] = {
{myType, "Curve R2", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Curve R2", gsSymOff, gsMissDB, "-993.86572,130.84471,7.346", "0,165,0", gsMissDB}
},
["models/shinji85/train/rail_curve_r3.mdl"] = {
{myType, "Curve R3", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Curve R3", gsSymOff, gsMissDB, "-927.60797,122.1218,7.346", "0,165,0", gsMissDB}
},
["models/shinji85/train/rail_cx.mdl"] = {
{myType, "Counter X", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Counter X", gsSymOff, gsMissDB, "-362.51361,0,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_cxfix.mdl"] = {
{myType, "Counter X Fix", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Counter X Fix", gsSymOff, gsMissDB, "-149.48648,0,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_double_4x_crossing.mdl"] = {
{myType, "Crossing Double 4x", gsSymOff, gsMissDB, "0,128,7.346", gsMissDB, gsMissDB},
{myType, "Crossing Double 4x", gsSymOff, gsMissDB, "-512,128,7.346", "0,180,0", gsMissDB},
{myType, "Crossing Double 4x", gsSymOff, gsMissDB, "0,-128,7.346", gsMissDB, gsMissDB},
{myType, "Crossing Double 4x", gsSymOff, gsMissDB, "-512,-128,7.346", "0,180,0", gsMissDB}
},
["models/shinji85/train/rail_double_bumper.mdl"] = {
{myType, "Bumper Double", gsSymOff, gsMissDB, "0,128,7.346", gsMissDB, gsMissDB},
{myType, "Bumper Double", gsSymOff, gsMissDB, "0,-128,7.346", gsMissDB, gsMissDB}
},
["models/shinji85/train/rail_l_switch.mdl"] = {
{myType, "Left Switch", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Left Switch", gsSymOff, gsMissDB, "-1024,0,7.346", "0,180,0", gsMissDB},
{myType, "Left Switch", gsSymOff, gsMissDB, "-966.40515,-128,7.346", "0,195,0", gsMissDB}
},
["models/shinji85/train/rail_r_switch.mdl"] = {
{myType, "Right Switch", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "Right Switch", gsSymOff, gsMissDB, "-1024,0,7.346", "0,180,0", gsMissDB},
{myType, "Right Switch", gsSymOff, gsMissDB, "-966.40515,128,7.346", "0,165,0", gsMissDB}
},
["models/shinji85/train/rail_x_junction.mdl"] = {
{myType, "X Junction", gsSymOff, gsMissDB, "0,0,7.346", gsMissDB, gsMissDB},
{myType, "X Junction", gsSymOff, gsMissDB, "-494.55,0,7.346", "0,180,0", gsMissDB},
{myType, "X Junction", gsSymOff, gsMissDB, "-33.129,-123.63866,7.346", "0,-30,0", gsMissDB},
{myType, "X Junction", gsSymOff, gsMissDB, "-461.42175,123.63649,7.346", "0,150,0", gsMissDB}
}
}
-- Register the addon PIECES to the pluggable DSV list
mySyncTable("PIECES", myPieces, true)
--[[
* Create a table and populate it as shown below
* In the square brackets goes your MODELBASE,
* and then for every active point, you must have one array of
* strings and numbers, where the elements match the following data settings.
* {MODELBASE, MODELADD, ENTCLASS, LINEID, POSOFF, ANGOFF, MOVETYPE, PHYSINIT, DRSHADOW, PHMOTION, PHYSLEEP, SETSOLID}
* MODELBASE > This string contains the path to your base /*.mdl/ file the additions are gonna be attached to.
* It is mandatory and taken in pairs with LINEID, it forms the unique identifier of every record.
* When used in /DSV/ mode ( like seen below ) is is used as a hash index.
* MODELADD > This is the /*.mdl/ path of the addition entity. It is mandatory and cannot be disabled.
* ENTCLASS > This is the class of the addition entity. It is mandatory and cannot be disabled.
* LINEID > This is the ID of the point that can be selected for building. They must be
* sequential and mandatory. If provided, the ID must the same as the row index under
* a given model key. Disabling this, makes it use the the index of the current line.
* Use that to swap the active points around by only moving the desired row up or down.
* For the example table definition below, the line ID in the database will be the same.
* POSOFF > This is the local position vector offset that TA uses to place the addition relative to MODELBASE.
* A NULL, empty, disabled or not available string is treated as taking {0,0,0}.
* ANGOFF > This is the local angle offset that TA uses to place the addition.
* A NULL, empty, disabled or not available string is treated as taking {0,0,0}.
* MOVETYPE > This internally calls /Entity:SetMoveType/ if the database parameter is zero or greater.
* PHYSINIT > This internally calls /Entity:PhysicsInit/ if the database parameter is zero or greater.
* DRSHADOW > This internally calls /Entity:DrawShadow/ if the database parameter is not zero.
* The call evaluates to /true/ for positive numbers and /false/ for negative.
* When the parameter is equal to zero skips the call of /Entity:DrawShadow/
* PHMOTION > This internally calls /PhysObj:EnableMotion/ if the database parameter is not zero on the validated physics object.
* The call evaluates to /true/ for positive numbers and /false/ for negative.
* When the parameter is equal to zero skips the call of /Entity:EnableMotion/
* PHYSLEEP > This internally calls /PhysObj:Sleep/ if the database parameter is grater than zero on the validated physics object.
* When the parameter is equal or less than zero skips the call of /Entity:Sleep/
* SETSOLID > This internally calls /Entity:SetSolid/ if the database parameter is zero or greater.
]]--
local myAdditions = {
["models/shinji85/train/rail_l_switch.mdl"] = {
{"models/shinji85/train/sw_lever.mdl", "buttonswitch", gsSymOff, "-100,-125,0", "0,180,0", -1, -1, -1, 0, -1, -1},
{"models/shinji85/train/rail_l_switcher1.mdl", "prop_dynamic", gsSymOff, gsMissDB, gsMissDB, MOVETYPE_VPHYSICS, SOLID_VPHYSICS, -1, -1, 1, SOLID_VPHYSICS},
{"models/shinji85/train/rail_l_switcher2.mdl", "prop_dynamic", gsSymOff, gsMissDB, gsMissDB, MOVETYPE_VPHYSICS, SOLID_VPHYSICS, -1, 0, -1, SOLID_NONE}
},
["models/shinji85/train/rail_r_switch.mdl"] = {
{"models/shinji85/train/sw_lever.mdl", "buttonswitch", gsSymOff, "-100,125,0", gsMissDB, -1, -1, -1, 0, -1, -1},
{"models/shinji85/train/rail_r_switcher1.mdl", "prop_dynamic", gsSymOff, gsMissDB, gsMissDB, MOVETYPE_VPHYSICS, SOLID_VPHYSICS, -1, -1, 1, SOLID_VPHYSICS},
{"models/shinji85/train/rail_r_switcher2.mdl", "prop_dynamic", gsSymOff, gsMissDB, gsMissDB, MOVETYPE_VPHYSICS, SOLID_VPHYSICS, -1, 0, -1, SOLID_NONE}
}
}
-- Register the addon ADDITIONS to the pluggable DSV list
mySyncTable("ADDITIONS", myAdditions, true)
--[[
* Create a table and populate it as shown below
* In the square brackets goes your TYPE,
* and then for every active point, you must have one array of
* strings and numbers, where the elements match the following data settings.
* {TYPE, LINEID, NAME}
* TYPE > This is the category under your physical properties are stored internally.
* It is mandatory and taken in pairs with LINEID, it forms the unique identifier of every record.
* When used in /DSV/ mode ( like seen below ) is is used as a hash index.
* LINEID > This is the ID of the point that can be selected for building. They must be
* sequential and mandatory. If provided, the ID must the same as the row index under
* a given model key. Disabling this, makes it use the the index of the current line.
* Use that to swap the active points around by only moving the desired row up or down.
* For the example table definition below, the line ID in the database will be the same.
* NAME > This stores the name of the physical property. It must an actual physical property.
]]--
local myPhysproperties = {}
-- Register the addon PHYSPROPERTIES to the pluggable DSV list
mySyncTable("PHYSPROPERTIES", myPhysproperties, true)
asmlib.LogInstance("<<< "..myScript)

View File

@ -569,15 +569,16 @@ end
* But seriously returns the sting line and EOF flag
* pFile > The file to read the line of characters from
* bCons > Keeps data consistency. Enable to skip trim
* sChar > Custom pattern to be used when trimming
* Reurns line contents and reaching EOF flag
* sLine > The line being read from the file
* isEOF > Flag indicating pointer reached EOF
]]
function GetStringFile(pFile, bCons)
function GetStringFile(pFile, bCons, sChar)
if(not pFile) then LogInstance("No file"); return "", true end
local sLine = (pFile:ReadLine() or "") -- Read one line at once
local isEOF = pFile:EndOfFile() -- Check for file EOF status
if(not bCons) then sLine = sLine:Trim() end
if(not bCons) then sLine = sLine:Trim(sChar) end
return sLine, isEOF
end
@ -1026,11 +1027,6 @@ function RotateXY(xyR, nR)
xyR.y = (nX * nS + nY * nC); return xyR
end
function GetAngleXY(xyR)
if(not xyR) then LogInstance("Base invalid"); return nil end
return mathAtan2(xyR.y, xyR.x)
end
----------------- OOP ------------------
-- https://github.com/GitSparTV/cavefight/blob/master/gamemodes/cavefight/gamemode/init.lua#L115
@ -1065,10 +1061,10 @@ function GetQueue(sKey)
P = oP, -- Current task player ( mandatory )
A = oA, -- Current task arguments ( mandatory )
M = oM, -- Current task main routine ( mandatory )
S = oS, -- Current task start routine ( if any )
E = oE, -- Current task end routine ( if any )
D = tostring(oD), -- Current task start description ( if any )
N = oN -- Current task sequential pointer ( if any )
S = oS, -- Current task start routine ( optional )
E = oE, -- Current task end routine ( optional )
D = tostring(oD), -- Current task start description ( optional )
N = oN -- Current task sequential pointer ( optional )
}
end
-- Checks whenever the queue is busy for that player
@ -1076,17 +1072,22 @@ function GetQueue(sKey)
if(not IsHere(oPly)) then return true end
local bB = mBusy[oPly]; return (bB and IsBool(bB))
end
-- Removes the finished task for the queue
function self:Remove()
-- Switch to the next tasks in the list
function self:Next()
if(self:IsEmpty()) then return self end
if(self:IsBusy(mS.P)) then return self end
LogInstance("Start "..GetReport2(mS.D, mS.P:Nick()), mKey)
if(mS.E) then -- Post-processing. Return value is ignored
local bOK, bErr = pcall(mS.E, mS.P, mS.A); if(not bOK) then
LogInstance("Error "..GetReport2(mS.D, mS.P:Nick()).." "..bErr, mKey)
else LogInstance("Finish "..GetReport2(mS.D, mS.P:Nick()), mKey) end
end -- Wipe all the columns in the item and go to the next item
local tD = mS.N; tableEmpty(mS); mS = tD; return self -- Wipe entry
if(self:IsBusy(mS.P)) then -- Task runnning
if(mS == mE) then return self end -- Only one element
mE.N = mS; mE = mS; mS = mE.N; mE.N = nil --Move entry
return self -- Moves only busy tasks with work remaining
else -- Task has been done. Run post processing and clear
if(mS.E) then -- Post-processing. Return value is ignored
local bOK, bErr = pcall(mS.E, mS.P, mS.A); if(not bOK) then
LogInstance("Error "..GetReport2(mS.D, mS.P:Nick()).." "..bErr, mKey)
else LogInstance("Finish "..GetReport2(mS.D, mS.P:Nick()), mKey) end
end -- Wipe all the columns in the item and go to the next item
LogInstance("Clear "..GetReport2(mS.D, mS.P:Nick()), mKey)
local tD = mS.N; tableEmpty(mS); mS = tD; return self -- Wipe entry
end
end
-- Setups a task to be called in the queue
function self:Attach(oPly, tArg, fFoo, aDsc)
@ -1113,7 +1114,7 @@ function GetQueue(sKey)
mE.E = fFoo; return self
end
-- Execute the current task at the queue beginning
function self:Execute()
function self:Work()
if(self:IsEmpty()) then return self end
if(mS.S) then -- Pre-processing. Return value is ignored
local bOK, bErr = pcall(mS.S, mS.P, mS.A); if(not bOK) then
@ -2710,15 +2711,16 @@ function CreateTable(sTable,defTab,bDelete,bReload)
LogInstance("Column ID mismatch "..GetReport(ivID),tabDef.Nick); return nil end
local defCol = qtDef[nvInd]; if(not IsHere(defCol)) then
LogInstance("Invalid col #"..tostring(nvInd),tabDef.Nick); return nil end
local tipCol, sMoDB, snOut = tostring(defCol[2]), GetOpVar("MODE_DATABASE")
if(tipCol == "TEXT") then snOut = tostring(snValue or "")
local sMoDB, snOut = GetOpVar("MODE_DATABASE")
local tyCol, opCol = tostring(defCol[2]), defCol[3]
if(tyCol == "TEXT") then snOut = tostring(snValue or "")
if(not bNoNull and IsBlank(snOut)) then
if (sMoDB == "SQL") then snOut = sNull
elseif(sMoDB == "LUA") then snOut = sNull
else LogInstance("Wrong database empty mode <"..sMoDB..">",tabDef.Nick); return nil end
end
if (defCol[3] == "LOW") then snOut = snOut:lower()
elseif(defCol[3] == "CAP") then snOut = snOut:upper() end
if (opCol == "LOW") then snOut = snOut:lower()
elseif(opCol == "CAP") then snOut = snOut:upper() end
if(not bNoRev and sMoDB == "SQL" and defCol[4] == "QMK") then
snOut = snOut:gsub("'","''") end
if(bQuoted) then local sqChar
@ -2730,15 +2732,15 @@ function CreateTable(sTable,defTab,bDelete,bReload)
else LogInstance("Wrong database quote mode <"..sMoDB..">",tabDef.Nick); return nil end
end; snOut = sqChar..snOut..sqChar
end
elseif(tipCol == "REAL" or tipCol == "INTEGER") then
elseif(tyCol == "REAL" or tyCol == "INTEGER") then
snOut = tonumber(snValue)
if(IsHere(snOut)) then
if(tipCol == "INTEGER") then
if (defCol[3] == "FLR") then snOut = mathFloor(snOut)
elseif(defCol[3] == "CEL") then snOut = mathCeil (snOut) end
if(tyCol == "INTEGER") then
if (opCol == "FLR") then snOut = mathFloor(snOut)
elseif(opCol == "CEL") then snOut = mathCeil (snOut) end
end
else LogInstance("Failed converting "..GetReport(snValue).." to NUMBER col #"..nvInd,tabDef.Nick); return nil end
else LogInstance("Invalid col type <"..tipCol..">",tabDef.Nick); return nil
else LogInstance("Failed converting "..GetReport(snValue).." to NUMBER column #"..nvInd,tabDef.Nick); return nil end
else LogInstance("Invalid column type <"..tyCol..">",tabDef.Nick); return nil
end; return snOut
end
-- Build drop statment
@ -3427,7 +3429,7 @@ end
* Import table data from DSV database created earlier
* sTable > Definition KEY to import
* bComm > Calls TABLE:Record(arLine) when set to true
* sPref > Prefix used on importing ( if any )
* sPref > Prefix used on importing ( optional )
* sDelim > Delimiter separating the values
]]--
function ImportDSV(sTable, bComm, sPref, sDelim)
@ -3798,7 +3800,7 @@ local function ExportPiecesAR(fF,qData,sName,sInd,qList)
end
end
if(IsTable(qData) and IsHere(qData[1])) then
fF:Write(sInd:rep(1).."local "..sName.." = {\n")
fF:Write("local "..sName.." = {\n")
local pkID, sInd, fRow = 1, " ", true
local idxID = makTab:GetColumnID("LINEID")
for iD = 1, #qData do local qRow = qData[iD]
@ -3810,22 +3812,22 @@ local function ExportPiecesAR(fF,qData,sName,sInd,qList)
if(vA == dbNull) then aRow[iA] = "gsMissDB" end
end
if(fRow) then fRow = false
fF:Write(sInd:rep(2).."["..aRow[pkID].."] = {\n")
fF:Write(sInd:rep(1).."["..aRow[pkID].."] = {\n")
SetAdditionsAR(mMod, makAdd, qList)
else
if(aRow[idxID] == 1) then fF:Seek(fF:Tell() - 2)
fF:Write("\n"..sInd:rep(2).."},\n"..sInd:rep(2).."["..aRow[pkID].."] = {\n")
fF:Write("\n"..sInd:rep(1).."},\n"..sInd:rep(1).."["..aRow[pkID].."] = {\n")
SetAdditionsAR(mMod, makAdd, qList)
end
end
mgrTab.ExportAR(aRow); tableRemove(aRow, 1)
fF:Write(sInd:rep(3).."{"..tableConcat(aRow, ", ").."},\n")
fF:Write(sInd:rep(2).."{"..tableConcat(aRow, ", ").."},\n")
end
fF:Seek(fF:Tell() - 2)
fF:Write("\n"..sInd:rep(2).."}\n")
fF:Write(sInd:rep(1).."}\n")
fF:Write("\n"..sInd:rep(1).."}\n")
fF:Write("}\n")
else
fF:Write(sInd:rep(1).."local "..sName.." = {}\n")
fF:Write("local "..sName.." = {}\n")
end
end
@ -3921,26 +3923,28 @@ function ExportTypeAR(sType)
local patAddon = GetOpVar("PATTEX_VARADDON")
local keyBuild = GetOpVar("KEYQ_BUILDER"); qPieces[keyBuild] = makP
local sLine, isEOF, isSkip, sInd, qAdditions = "", false, false, " ", {}
while(not isEOF) do sLine, isEOF = GetStringFile(fS, true)
while(not isEOF) do
sLine, isEOF = GetStringFile(fS, true)
sLine = sLine:gsub("%s*$", "")
if(sLine:find(patAddon)) then isSkip = true
fE:Write("local myAddon = \""..sType.."\"\n")
elseif(sLine:find(patCateg)) then isSkip = true
local tCat = GetOpVar("TABLE_CATEGORIES")[sType]
if(IsTable(tCat) and tCat.Txt) then
fE:Write(sInd:rep(1).."local myCategory = {\n")
fE:Write(sInd:rep(2).."[myType] = {Txt = [[\n")
fE:Write(sInd:rep(3)..tCat.Txt:gsub("\n","\n"..sInd:rep(3)).."\n")
fE:Write(sInd:rep(2).."]]}\n")
fE:Write(sInd:rep(1).."}\n")
fE:Write("local myCategory = {\n")
fE:Write(sInd:rep(1).."[myType] = {Txt = [[\n")
fE:Write(sInd:rep(2)..tCat.Txt:gsub("\n","\n"..sInd:rep(2)).."\n")
fE:Write(sInd:rep(1).."]]}\n")
fE:Write("}\n")
else
fE:Write(sInd:rep(1).."local myCategory = {}\n")
fE:Write("local myCategory = {}\n")
end
elseif(sLine:find(patWorks)) then isSkip = true
local sID = WorkshopID(sType)
if(sID and sID:len() > 0) then
fE:Write(sInd:rep(1).."asmlib.WorkshopID(myAddon, \""..sID.."\")\n")
fE:Write("asmlib.WorkshopID(myAddon, \""..sID.."\")\n")
else
fE:Write(sInd:rep(1).."asmlib.WorkshopID(myAddon)\n")
fE:Write("asmlib.WorkshopID(myAddon)\n")
end
elseif(sLine:find(patPiece)) then isSkip = true
ExportPiecesAR(fE, qPieces, "myPieces", sInd, qAdditions)
@ -3953,7 +3957,8 @@ function ExportTypeAR(sType)
if(isEOF) then fE:Write(sLine) else fE:Write(sLine.."\n") end
end
end
fE:Flush(); fE:Close(); fS:Close()
fE:Write("\n"); fE:Flush()
fE:Close(); fS:Close()
end
end
end

View File

@ -194,7 +194,7 @@ if(CLIENT) then
end
if(SERVER) then local poThQueue = asmlib.GetQueue("THINK")
hookAdd("Think", gsToolPrefL.."think_task", function() poThQueue:Execute():Remove() end)
hookAdd("Think", gsToolPrefL.."think_task", function() poThQueue:Work():Next() end)
hookAdd("PlayerDisconnected", gsToolPrefL.."player_quit", asmlib.GetActionCode("PLAYER_QUIT"))
hookAdd("PhysgunDrop", gsToolPrefL.."physgun_drop_snap", asmlib.GetActionCode("PHYSGUN_DROP"))
duplicatorRegisterEntityModifier(gsToolPrefL.."dupe_phys_set",asmlib.GetActionCode("DUPE_PHYS_SETTINGS"))