Skip to content

Commit

Permalink
Extract transfrom from a DB model only if is selected by the user (#56)
Browse files Browse the repository at this point in the history
Fixed: Model transform points not being initialized
Added: Rearranged the think hook for models validation
Added: Comment classifications about luapad and how to close its panel
Added: Populate origin/angle transform when spawning ghosts and snapping pieces
Added: Selection search point slot being taken with priority
Updated: Spawn margin sends message to the user
  • Loading branch information
dvdvideo1234 authored Mar 30, 2024
1 parent f42bdcf commit ad93c4c
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 86 deletions.
14 changes: 7 additions & 7 deletions lua/autorun/trackassembly_init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ local asmlib = trackasmlib; if(not asmlib) then -- Module present
------------ CONFIGURE ASMLIB ------------

asmlib.InitBase("track","assembly")
asmlib.SetOpVar("TOOL_VERSION","8.741")
asmlib.SetOpVar("TOOL_VERSION","8.749")
asmlib.SetIndexes("V" ,1,2,3)
asmlib.SetIndexes("A" ,1,2,3)
asmlib.SetIndexes("WV",1,2,3)
Expand Down Expand Up @@ -945,27 +945,27 @@ if(CLIENT) then
if(luapad.Frame) then luapad.Frame:SetVisible(true)
else asmlib.SetAsmConvar(oPly, "*luapad", gsToolNameL) end
luapad.AddTab("["..defTab.Nick.."]"..pnSelf:GetText(), fileRead(sFile, "DATA"), sDsv);
if(defTab.Nick == "PIECES") then local sCat = fDSV:format(sPref, "CATEGORY")
if(fileExists(sCat,"DATA")) then
if(defTab.Nick == "PIECES") then -- Load the categoty provider for this DSV
local sCat = fDSV:format(sPref, "CATEGORY"); if(fileExists(sCat,"DATA")) then
luapad.AddTab("[CATEGORY]"..pnSelf:GetText(), fileRead(sCat, "DATA"), sDsv);
end
end
end -- This is done so we can distinguish between luapad and other panels
end -- Luapad is designed not to be closed so we need to make it invisible
luapad.Frame:SetVisible(true); luapad.Frame:Center()
luapad.Frame:MakePopup(); conElements:Push({luapad.Frame})
end
end,
function() fileDelete(sFile)
asmlib.LogInstance("Delete "..asmlib.GetReport1(sFile), sLog..".Button")
if(defTab.Nick == "PIECES") then local sCat = fDSV:format(sPref, "CATEGORY")
if(fileExists(sCat,"DATA")) then fileDelete(sCat)
if(fileExists(sCat,"DATA")) then fileDelete(sCat) -- Delete category when present
asmlib.LogInstance("Deleted "..asmlib.GetReport1(sCat), sLog..".Button") end
end; pnManage:Remove()
end
}
while(tOptions[iO]) do local sO = tostring(iO)
local sDescr = languageGetPhrase("tool."..gsToolNameL..".pn_externdb_bt"..sO)
pnMenu:AddOption(sDescr, tOptions[iO]):SetIcon(asmlib.ToIcon("pn_externdb_bt"..sO))
iO = iO + 1 -- Loop trough the functions list and add to the menu
iO = iO + 1 -- Loop trough the functions list and add them to the menu
end; pnMenu:Open()
end
else asmlib.LogInstance("File missing ["..tostring(iP).."]",sLog..".Button") end
Expand Down
167 changes: 106 additions & 61 deletions lua/trackassembly/trackasmlib.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2146,23 +2146,6 @@ function MakeEntityNone(sModel, vPos, aAng) local eNone
LogInstance("Create "..GetReport2(eNone:EntIndex(),sModel)); return eNone
end

--[[
* Locates an active point on the piece offset record.
* This function is used to check the correct offset and return it.
* It also returns the normalized active point ID if needed
* oRec > Record structure of a track piece
* ivPoID > The POA offset ID to check and locate
]]--
function LocatePOA(oRec, ivPoID)
if(not oRec) then LogInstance("Missing record"); return nil end
if(not oRec.Offs) then LogInstance("Missing offsets for <"..tostring(oRec.Slot)..">"); return nil end
local iPoID = tonumber(ivPoID); if(iPoID) then iPoID = mathFloor(iPoID)
else LogInstance("ID mismatch "..GetReport(ivPoID)); return nil end
local stPOA = oRec.Offs[iPoID]; if(not IsHere(stPOA)) then
LogInstance("Missing ID #"..tostring(iPoID).." for <"..tostring(oRec.Slot)..">"); return nil end
return stPOA, iPoID
end

function ReloadPOA(nXP,nYY,nZR)
local arPOA = GetOpVar("ARRAY_DECODEPOA")
arPOA[1] = (tonumber(nXP) or 0)
Expand Down Expand Up @@ -2211,7 +2194,7 @@ function TransferPOA(tData,sMode)
LogInstance("Mode mismatch "..GetReport(sMode)); return nil end
local arPOA = GetOpVar("ARRAY_DECODEPOA")
local ctA, ctB, ctC = GetIndexes(sMode); if(not (ctA and ctB and ctC)) then
LogInstance("Missed offset mode <"..sMode..">"); return nil end
LogInstance("Missed offset mode "..GetReport(sMode)); return nil end
tData[ctA], tData[ctB], tData[ctC] = arPOA[1], arPOA[2], arPOA[3]; return arPOA
end

Expand All @@ -2224,7 +2207,7 @@ function DecodePOA(sStr)
for iD = 1, arPOA.Size do -- Apply on all components
local nCom = tonumber(atPOA[iD]) -- Is the data really a number
if(not IsHere(nCom)) then nCom = 0 -- If not write zero and report it
LogInstance("Mismatch <"..sStr..">") end; arPOA[iD] = nCom
LogInstance("Mismatch "..GetReport(sStr)) end; arPOA[iD] = nCom
end; return arPOA -- Return the converted string to POA
end

Expand All @@ -2250,6 +2233,67 @@ function GetTransformOA(sModel, sKey)
return mTOA.Pos, mTOA.Ang -- The function must return transform origin and angle
end

--[[
* Locates an active point on the piece offset cache record.
* This function is used to check the correct offset and return it.
* It also returns the normalized active point ID if needed
* Updates current record origin and angle when they use attachments
* oRec > Record structure of a track piece stored in the cache
* ivPoID > The POA offset ID to be checked and located
* Returns a cache record and the converted to number offset ID
]]--
function LocatePOA(oRec, ivPoID)
if(not oRec) then LogInstance("Missing record"); return nil end
if(not oRec.Offs) then LogInstance("Missing offsets for "..GetReport(oRec.Slot)); return nil end
local iPoID = tonumber(ivPoID); if(iPoID) then iPoID = mathFloor(iPoID)
else LogInstance("ID mismatch "..GetReport(ivPoID)); return nil end
local stPOA = oRec.Offs[iPoID]; if(not IsHere(stPOA)) then
LogInstance("Missing ID "..GetReport2(iPoID, oRec.Slot)); return nil end
if(oRec.Tran) then oRec.Tran = nil -- Transforming has started
local sE = GetOpVar("OPSYM_ENTPOSANG") -- Extract transform from model
local sD = GetOpVar("OPSYM_DISABLE") -- Use for searched hit point disabled
for ID = 1, oRec.Size do local tOA = oRec.Offs[ID] -- Index current offset
local sP, sO, sA = tOA.P.Slot, tOA.O.Slot, tOA.A.Slot -- Localize transform index
---------- Origin ----------
if(sO and sO:sub(1,1) == sE) then -- POA origin must extracted from the model
local sO = sO:sub(2, -1) -- Read origin transform ID and try to index
local vO, aA = GetTransformOA(oRec.Slot, sO) -- Read transform position/angle
if(IsHere(vO)) then ReloadPOA(vO[cvX], vO[cvY], vO[cvZ]) -- Load origin into POA
else -- Try decoding the transform origin when not applicable
if(IsNull(sO) or IsBlank(sO)) then ReloadPOA() else
if(not DecodePOA(sO)) then LogInstance("Origin mismatch "..GetReport2(ID, oRec.Slot)) end
end end -- Decode the transformation when is not null or empty string
if(not IsHere(TransferPOA(tOA.O, "V"))) then
LogInstance("Origin transfer fail "..GetReport(ID, oRec.Slot)) end
LogInstance("Origin transform from model "..GetReport3(ID, sO, StringPOA(tOA.O, "V")))
end -- Transform origin is decoded from the model and stored in the cache
---------- Angle ----------
if(sA and sA:sub(1,1) == sE) then -- POA angle must extracted from the model
local sA = sA:sub(2, -1) -- Read angle transform ID and try to index
local vO, aA = GetTransformOA(oRec.Slot, sA) -- Read transform position/angle
if(IsHere(aA)) then ReloadPOA(aA[caP], aA[caY], aA[caR]) -- Load angle into POA
else -- Try decoding the transform angle when not applicable
if(IsNull(sA) or IsBlank(sA)) then ReloadPOA() else
if(not DecodePOA(sA)) then LogInstance("Angle mismatch "..GetReport2(ID, oRec.Slot)) end
end end -- Decode the transformation when is not null or empty string
if(not IsHere(TransferPOA(tOA.A, "A"))) then
LogInstance("Angle mismatch fail "..GetReport2(ID, oRec.Slot)) end
LogInstance("Angle transform from model "..GetReport3(ID, sA, StringPOA(tOA.A, "A")))
end -- Transform angle is decoded from the model and stored in the cache
---------- Point ----------
if(sP:sub(1,1) == sD) then -- Check whenever point is disabled
ReloadPOA(tOA.O[cvX], tOA.O[cvY], tOA.O[cvZ]) -- Override with the origin
else -- When the point is disabled take the origin otherwise try to process it
if(IsNull(sP) or IsBlank(sP)) then -- In case of empty value or null use the origin
ReloadPOA(tOA.O[cvX], tOA.O[cvY], tOA.O[cvZ]) -- Override with the origin
else -- When the point is empty use the origin otherwise decode the value
if(not DecodePOA(sP)) then LogInstance("Point mismatch "..GetReport2(ID, oRec.Slot)) end
end -- The point is already decoded and ready to be populated in the cache
end; if(not IsHere(TransferPOA(tOA.P, "V"))) then LogInstance("Point transfer fail"); return nil end
end -- Loop and transform all the POA configuration at once. Game model slot will be taken
end; return stPOA, iPoID
end

function RegisterPOA(stData, ivID, sP, sO, sA)
local sNull = GetOpVar("MISS_NOSQL"); if(not stData) then
LogInstance("Cache record invalid"); return nil end
Expand All @@ -2274,40 +2318,34 @@ function RegisterPOA(stData, ivID, sP, sO, sA)
end; local sE, sD = GetOpVar("OPSYM_ENTPOSANG"), GetOpVar("OPSYM_DISABLE")
---------- Origin ----------
if(sO:sub(1,1) == sD) then ReloadPOA() else
if(sO:sub(1,1) == sE) then tOffs.O.Slot = sO; sO = sO:sub(2,-1)
local vO, aA = GetTransformOA(stData.Slot, sO)
if(IsHere(vO)) then ReloadPOA(vO[cvX], vO[cvY], vO[cvZ])
else -- Decode the transform origin and angle when not applicable
if(IsNull(sO) or IsBlank(sO)) then ReloadPOA() else
if(not DecodePOA(sO)) then LogInstance("Origin mismatch ["..iID.."]"..stData.Slot) end
end end -- Decode the transformation when is not null or empty string
if(sO:sub(1,1) == sE) then -- To be decoded on spawn via locating
stData.Tran = true; ReloadPOA(); tOffs.O.Slot = sO -- Store transform
LogInstance("Origin transform "..GetReport2(sO, stData.Slot))
elseif(IsNull(sO) or IsBlank(sO)) then ReloadPOA() else
if(not DecodePOA(sO)) then LogInstance("Origin mismatch ["..iID.."]"..stData.Slot) end
if(not DecodePOA(sO)) then LogInstance("Origin mismatch "..GetReport2(iID, stData.Slot)) end
end
end; if(not IsHere(TransferPOA(tOffs.O, "V"))) then LogInstance("Origin mismatch"); return nil end
end; if(not IsHere(TransferPOA(tOffs.O, "V"))) then LogInstance("Origin transfer fail"); return nil end
---------- Angle ----------
if(sA:sub(1,1) == sD) then ReloadPOA() else
if(sA:sub(1,1) == sE) then tOffs.A.Slot = sA; sA = sA:sub(2,-1)
local vO, aA = GetTransformOA(stData.Slot, sA)
if(IsHere(aA)) then ReloadPOA(aA[caP], aA[caY], aA[caR])
else -- Decode the transform origin and angle when not applicable
if(IsNull(sA) or IsBlank(sA)) then ReloadPOA() else
if(not DecodePOA(sA)) then LogInstance("Angle mismatch ["..iID.."]"..stData.Slot) end
end end -- Decode the transformation when is not null or empty string
if(sA:sub(1,1) == sE) then -- To be decoded on spawn via locating
stData.Tran = true; ReloadPOA(); tOffs.A.Slot = sA -- Store transform
LogInstance("Angle transform "..GetReport2(sA, stData.Slot))
elseif(IsNull(sA) or IsBlank(sA)) then ReloadPOA() else
if(not DecodePOA(sA)) then LogInstance("Angle mismatch ["..iID.."]"..stData.Slot) end
if(not DecodePOA(sA)) then LogInstance("Angle mismatch "..GetReport2(iID, stData.Slot)) end
end
end; if(not IsHere(TransferPOA(tOffs.A, "A"))) then LogInstance("Angle mismatch"); return nil end
end; if(not IsHere(TransferPOA(tOffs.A, "A"))) then LogInstance("Angle transfer fail"); return nil end
---------- Point ----------
if(sP:sub(1,1) == sD) then
if(tOffs.O.Slot) then -- Origin transform point trigger
stData.Tran = true; ReloadPOA(); tOffs.P.Slot = sP
elseif(sP:sub(1,1) == sD) then -- Point is disabled
ReloadPOA(tOffs.O[cvX], tOffs.O[cvY], tOffs.O[cvZ])
else -- when the point is disabled take the origin
if(IsNull(sP) or IsBlank(sP)) then
ReloadPOA(tOffs.O[cvX], tOffs.O[cvY], tOffs.O[cvZ])
else -- When the point is empty use the origin otherwise decode the value
if(not DecodePOA(sP)) then LogInstance("Point mismatch ["..iID.."]"..stData.Slot) end
if(not DecodePOA(sP)) then LogInstance("Point mismatch "..GetReport2(iID, stData.Slot)) end
end
end; if(not IsHere(TransferPOA(tOffs.P, "V"))) then LogInstance("Point mismatch"); return nil end
end; if(not IsHere(TransferPOA(tOffs.P, "V"))) then LogInstance("Point transfer fail"); return nil end
return tOffs
end

Expand Down Expand Up @@ -2647,7 +2685,7 @@ function CreateTable(sTable,defTab,bDelete,bReload)
local qtCol = qtDef[iD]; if(qtCol) then return qtCol[1] end
LogInstance("Mismatch "..GetReport(vD), tabDef.Nick); return nil
end
-- Returns the colomn information by the given ID > 0
-- Returns the column information by the given ID > 0
function self:GetColumnInfo(vD, vI)
local iD = (tonumber(vD) or 0)
local qtDef = self:GetDefinition()
Expand Down Expand Up @@ -2857,7 +2895,7 @@ function CreateTable(sTable,defTab,bDelete,bReload)
local qtCmd = self:GetCommand()
qtCmd.Commit = "COMMIT;"; return self
end
-- Build create/drop/delete statement table of statemenrts
-- Build create/drop/delete statement table of statements
function self:Create()
local qtDef = self:GetDefinition()
local qtCmd, iInd = self:GetCommand(), 1; qtCmd.STMT = "Create"
Expand Down Expand Up @@ -3215,7 +3253,7 @@ function ExportPanelDB(stPanel, bExp, makTab, sFunc)
if(not cT or cT ~= sT) then -- Category has been changed
F:Write("# Categorize [ "..sMoDB.." ]("..sT.."): "..tostring(WorkshopID(sT) or sMiss))
F:Write("\n"); cT = sT -- Cache category name
end -- Otherwise just wite down the piece active point
end -- Otherwise just write down the piece active point
F:Write("\""..sM.."\""..symSep.."\""..sT.."\""..symSep.."\""..sN.."\"")
F:Write("\n"); iCnt = iCnt + 1
end; F:Flush(); F:Close()
Expand Down Expand Up @@ -3360,7 +3398,8 @@ end

function ExportPOA(stPOA,sOut)
local sE = tostring(sOut or GetOpVar("MISS_NOSQL"))
local sP = (IsEqualPOA(stPOA.P, stPOA.O) and sE or StringPOA(stPOA.P, "V"))
local sP = (IsZeroPOA(stPOA.P, "V") and sE or StringPOA(stPOA.P, "V"))
sP = (stPOA.P.Slot and stPOA.P.Slot or sP)
local sO = (IsZeroPOA(stPOA.O, "V") and sE or StringPOA(stPOA.O, "V"))
sO = (stPOA.O.Slot and stPOA.O.Slot or sO)
local sA = (IsZeroPOA(stPOA.A, "A") and sE or StringPOA(stPOA.A, "A"))
Expand Down Expand Up @@ -3606,7 +3645,7 @@ function SynchronizeDSV(sTable, tData, bRepl, sPref, sDelim)
vID = tRow[iD-1]; nID, sID = tonumber(vID), tostring(vID)
nID = (nID or (sID:sub(1,1) == symOff and iCnt or 0))
-- Where the line ID must be read from. Skip the key itself and convert the disabled value
if(iCnt ~= nID) then -- Validate the line ID being in proper borders abd sequential
if(iCnt ~= nID) then -- Validate the line ID being in proper borders and sequential
LogInstance("("..fPref.."@"..sTable.."@"..sKey..") Sync point ["
..tostring(iCnt).."] ID scatter "..GetReport3(vID, nID, sID))
return false end; tRow[iD-1] = nID
Expand Down Expand Up @@ -4623,26 +4662,32 @@ end
--[[
* Checks whenever the spawned piece is inside the previous spawn margin
]]
function InSpawnMargin(oRec,vPos,aAng)
function InSpawnMargin(oPly,oRec,vPos,aAng)
if(CLIENT) then return true end
local nMarg = GetOpVar("SPAWN_MARGIN")
if(nMarg == 0) then return false end
if(vPos and aAng) then
if(oRec.Mpos and oRec.Mray) then
local nMpow = (nMarg ^ 2) -- Square root is expensive
local nBpos = oRec.Mpos:DistToSqr(vPos) -- Distance
if(nBpos <= nMpow) then -- Check the margin area
LogInstance("Spawn pos ["..nBpos.."]["..nMpow.."]")
if(nMarg < 0) then return true end -- Negative checks position
local nMray = (1 - (nMpow * GetOpVar("EPSILON_ZERO")))
local nBray = oRec.Mray:Dot(aAng:Forward())
if(nBray >= nMray) then -- Positive checks position and direction
LogInstance("Spawn ray ["..nBray.."]["..nMray.."]"); return true
end -- Piece angles will not align when spawned
end -- Piece will be spawned outside of spawn margin
oRec.Mpos:Set(vPos); oRec.Mray:Set(aAng:Forward())
else -- Store the last location the piece was spawned
local cMarg = mathAbs(nMarg)
local nBpos = oRec.Mpos:Distance(vPos) -- Distance
if(nBpos <= cMarg) then -- Check the margin area
if(nMarg < 0) then -- When negative check position only
Notify(oPly,"Spawn pos ["..nBpos.."]["..nMarg.."]", "ERROR")
LogInstance("Spawn pos ["..nBpos.."]["..nMarg.."]"); return true
else -- Otherwise check the spawn direction ray for being the same
local nBray = oRec.Mray:Dot(aAng:Forward())
local nMray = (1 - (cMarg * GetOpVar("EPSILON_ZERO")))
if(nBray >= nMray) then -- Positive checks position and direction
Notify(oPly,"Spawn ray ["..nBpos.."]["..nMarg.."]["..nBray.."]["..nMray.."]", "ERROR")
LogInstance("Spawn ray ["..nBpos.."]["..nMarg.."]["..nBray.."]["..nMray.."]"); return true
end -- Piece angles will not align when spawned
end -- Negative checks position
end; oRec.Mpos:Set(vPos); oRec.Mray:Set(aAng:Forward())
return false -- Piece will be spawned outside of spawn margin
else -- Otherwise create memory entry and sore the piece location
oRec.Mpos, oRec.Mray = Vector(vPos), aAng:Forward()
end; return false
return false -- Store the last location the piece was spawned
end -- Otherwise wipe the current memoty when not provided
else oRec.Mpos, oRec.Mray = nil, nil end; return false
end

Expand All @@ -4660,7 +4705,7 @@ function MakePiece(pPly,sModel,vPos,aAng,nMass,sBgSkIDs,clColor,sMode)
local stData = CacheQueryPiece(sModel) if(not IsHere(stData)) then
LogInstance("Record missing for <"..sModel..">"); return nil end
local aAng = Angle(aAng or GetOpVar("ANG_ZERO"))
if(InSpawnMargin(stData, vPos, aAng)) then
if(InSpawnMargin(pPly, stData, vPos, aAng)) then
LogInstance("Spawn margin stop <"..sModel..">"); return nil end
local sClass = GetOpVar("ENTITY_DEFCLASS")
local ePiece = entsCreate(GetTerm(stData.Unit, sClass, sClass))
Expand Down
Loading

0 comments on commit ad93c4c

Please sign in to comment.