AddCSLuaFile() module("mw_utils", package.seeall) local cachedBones = {} function mw_utils.LookupBoneCached(ent, boneName) local model = ent:GetModel() if (model == nil) then return nil end cachedBones[model] = cachedBones[model] || {} cachedBones[model][boneName] = cachedBones[model][boneName] || ent:LookupBone(boneName) return cachedBones[model][boneName] end local cachedAttachments = {} function mw_utils.LookupAttachmentCached(ent, attName) local model = ent:GetModel() if (model == nil) then return nil end cachedAttachments[model] = cachedAttachments[model] || {} cachedAttachments[model][attName] = cachedAttachments[model][attName] || ent:LookupAttachment(attName) return cachedAttachments[model][attName] > 0 && cachedAttachments[model][attName] || nil end local function recmatch(table1, table2) for k, v in pairs(table2) do if (table1[k] == nil) then return false end if (istable(v)) then if (!recmatch(table1[k], v)) then return false end else if (v != table1[k]) then return false end end end for k, v in pairs(table1) do if (table2[k] == nil) then return false end if (istable(v)) then if (!recmatch(table2[k], v)) then return false end else if (v != table2[k]) then return false end end end return true end function mw_utils.CheckMatchingTables(table1, table2) return recmatch(table1, table2) end function mw_utils.CheckMatchingBones(model1, model2) for b = 0, model1:GetBoneCount(), 1 do if (mw_utils.LookupBoneCached(model2, model1:GetBoneName(b))) != nil then return true end end return false end function mw_utils.IsAttachmentBasedOn(current, base) while current != nil do if (current == base) then return true end if (MW_ATTS[current] == nil) then return false end current = MW_ATTS[current].Base end return false end local function requireAttachment(ent, attName) ent.m_AttachmentRequests = ent.m_AttachmentRequests || {} ent.m_AttachmentDeliveries = ent.m_AttachmentDeliveries || {} local attId = mw_utils.LookupAttachmentCached(ent, attName) if (ent.m_AttachmentRequests[attId] == nil) then ent.m_AttachmentRequests[attId] = Matrix() timer.Simple(0, function() if (!IsValid(ent)) then return end local attData = ent:GetAttachment(attId) local computeMatrix = Matrix() computeMatrix:SetTranslation(attData.Pos) computeMatrix:SetAngles(attData.Ang) local worldMatrix = ent:GetBoneMatrix(0) computeMatrix = worldMatrix:GetInverse() * computeMatrix ent.m_AttachmentRequests[attId] = computeMatrix end) ent.m_AttachmentDeliveries[attId] = {Pos = Vector(), Ang = Angle()} end if (!ent.m_bFastAttachment) then if (ent.OnBuildFastAttachments == nil) then ent.OnBuildFastAttachments = function() end --to avoid if end ent:AddCallback("BuildBonePositions", function(ent, numbones) local matrix = ent:GetBoneMatrix(0) if (matrix == nil) then return end for attId, localMat in pairs(ent.m_AttachmentRequests) do local newMatrix = matrix * localMat ent.m_AttachmentDeliveries[attId].Pos = newMatrix:GetTranslation() ent.m_AttachmentDeliveries[attId].Ang = newMatrix:GetAngles() ent:OnBuildFastAttachments() end end) ent.m_bFastAttachment = true end end --this is only really good for static props --it calculates local offset from root bone, so if the attachment isn't parented to that (or not static), it won't return --good values function mw_utils.GetFastAttachment(ent, attName) requireAttachment(ent, attName) return ent.m_AttachmentDeliveries[mw_utils.LookupAttachmentCached(ent, attName)] end local function isInjectorLoadingConversions(inj) if (!istable(inj.Attachment)) then if (MW_ATTS[inj.Attachment] != nil) then return mw_utils.IsAttachmentBasedOn(inj.Attachment, "att_conversion") end else for i, injatt in pairs(inj.Attachment) do if (MW_ATTS[injatt] != nil) then if (mw_utils.IsAttachmentBasedOn(injatt, "att_conversion")) then return true end end end end return false end function mw_utils.LoadInjectors(swep) if (swep.Customization == nil) then mw_utils.ErrorPrint("LoadInjectors: Customization is nil! Are you loading before it?") return end local swepClass = string.GetFileFromFilename(swep.Folder) for className, inj in SortedPairs(MW_ATT_INJECTORS) do if (inj.SWEPs != nil && table.HasValue(inj.SWEPs, swepClass)) then local slot = isInjectorLoadingConversions(inj) && SLOT_CONVERSIONS || math.max(inj.Slot, SLOT_CONVERSIONS + 1) swep.Customization[slot] = swep.Customization[slot] || {} if (!istable(inj.Attachment)) then if (MW_ATTS[inj.Attachment] != nil && !table.HasValue(swep.Customization[slot], inj.Attachment)) then table.insert(swep.Customization[slot], inj.Attachment) end else for i, injatt in pairs(inj.Attachment) do if (MW_ATTS[injatt] != nil && !table.HasValue(swep.Customization[slot], injatt)) then table.insert(swep.Customization[slot], injatt) end end end end end end --https://gist.github.com/jrus/3197011 local random = math.random local function uuid() local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' return string.gsub(template, '[xy]', function (c) local v = (c == 'x') and random(0, 0xf) or random(8, 0xb) return string.format('%x', v) end) end function mw_utils.DevPrint(message) if (GetConVar("developer"):GetInt() > 0 && CLIENT) then MsgC(Color(120, 220, 100, 255), "MW Base: ", Color(255, 255, 255, 255), message.."\n") end end function mw_utils.ErrorPrint(message) if (GetConVar("developer"):GetInt() > 0 && CLIENT) then MsgC(Color(220, 40, 80, 255), "MW Base ERROR: ", Color(255, 235, 235, 255), message.."\n") end end function mw_utils.LoadPreset(weapon, preset) for slot, atts in SortedPairs(weapon.Customization) do local index = 1 for i, att in SortedPairs(atts) do if (table.HasValue(preset.Attachments, att)) then index = i end end if (!weapon:HasAttachment(weapon.Customization[slot][index])) then --just sending what we need if (CLIENT) then mw_utils.SendAttachmentToServer(weapon, slot, index) else weapon:CreateAttachmentEntity(weapon.Customization[slot][index]) end end end end function mw_utils.ReloadAttachment(attachmentClass) if (GetConVar("sv_cheats"):GetInt() > 0 && GetConVar("developer"):GetInt() > 0) then local currentClass = attachmentClass while (currentClass != nil) do local folder = MW_ATTS[currentClass].Folder MW_ATTS[currentClass] = nil LoadAttachment(folder, currentClass..".lua") --mw_loader currentClass = MW_ATTS[currentClass].Base end currentClass = attachmentClass while (currentClass != nil) do DoAttachmentInheritance(MW_ATTS[currentClass]) --mw_loader currentClass = MW_ATTS[currentClass].Base end end end if (CLIENT) then --used to reparent ents after a fullupdate local FullUpdateEntities = {} function mw_utils.DealWithFullUpdate(ent) FullUpdateEntities[ent] = true end hook.Add("PreRender", "mw_utilsFullUpdatePreRender", function() for ent, _ in pairs(FullUpdateEntities) do if (!IsValid(ent)) then FullUpdateEntities[ent] = nil continue end local fullUpdateParent = ent:GetInternalVariable("m_hNetworkMoveParent") if (!IsValid(ent:GetParent()) && IsValid(fullUpdateParent)) then ent:SetParent(fullUpdateParent) end end end) function mw_utils.SendAttachmentToServer(weapon, slot, index) net.Start("mgbase_customize_att") net.WriteInt(slot, 32) net.WriteInt(index, 32) net.WriteEntity(weapon) net.SendToServer() end local function saveFavs() file.CreateDir("mwbase") file.Write("mwbase/favorites.json", util.TableToJSON(MW_FAVORITES)) end function mw_utils.FavoriteAsset(swepClass, assetClass) MW_FAVORITES[swepClass] = MW_FAVORITES[swepClass] || {} if (!table.HasValue(MW_FAVORITES[swepClass], assetClass)) then table.insert(MW_FAVORITES[swepClass], assetClass) saveFavs() end end function mw_utils.UnfavoriteAsset(swepClass, assetClass) if (MW_FAVORITES[swepClass] == nil || !table.HasValue(MW_FAVORITES[swepClass], assetClass)) then return end table.RemoveByValue(MW_FAVORITES[swepClass], assetClass) if (#MW_FAVORITES[swepClass] <= 0) then MW_FAVORITES[swepClass] = nil end saveFavs() end function mw_utils.IsAssetFavorite(swepClass, assetClass) return MW_FAVORITES[swepClass] != nil && table.HasValue(MW_FAVORITES[swepClass], assetClass) end function mw_utils.GetPresetsForSWEP(swepClass) local presets = {} for _, preset in pairs(MW_PRESETS) do if (preset.SWEP == swepClass) then presets[#presets + 1] = table.Copy(preset) end end return presets end function mw_utils.SavePreset(swepClass, name, attachmentList) local fileName = uuid() local preset = { SWEP = swepClass, Name = name, Attachments = attachmentList } file.CreateDir("mwbase/presets") file.Write("mwbase/presets/" .. fileName .. ".json", util.TableToJSON(preset, true)) preset.ClassName = fileName preset._bUserGenerated = true MW_PRESETS[fileName] = table.Copy(preset) end function mw_utils.RemovePreset(className) local pathAndName = "mwbase/presets/"..className..".json" if (file.Exists(pathAndName, "DATA")) then file.Delete(pathAndName) MW_PRESETS[className] = nil end end end function mw_utils.CompileFile(f) local content = file.Read(f, "LUA") if (content != nil) then AddCSLuaFile(f) if (string.find(content, "AddCSLuaFile")) then mw_utils.DevPrint("safeInclude: Don't run AddCSLuaFile! This is already done for you ("..f..")") end local incl = CompileString(content, "MWBaseSafeInclude_"..f, false)--RunString(content, "MWBaseSafeInclude", false) if (isstring(incl)) then mw_utils.ErrorPrint("safeInclude: "..f.." errored! Stack trace below") mw_utils.ErrorPrint("... "..incl) return false, incl end incl() return true, "no error" end mw_utils.ErrorPrint("safeInclude: missing file "..f.."!") return false, "missing file" end