Modding Guide


This guide will help you with creating your own mods based on my mods.
It will give you examples how you can use my provided APIs to create your own mods.
I will also explain how you can add your own items for mods like spawn select or retexture items.
Entries with a script will always tell you the minimum script module required to use this script. If the module is for example 3_Game, you can call it in 3_Game, 4_World or 5_Mission, but if it is 5_Mission, you need to call it in 5_Mission as it is not defined in 4_World or 3_Game.

folderAchievements

folderTrigger an achievement trigger
Copy

Triggering an achievement trigger is very simple. There are 3 different types of triggers.
  • A simple Trigger will increase the trigger counter for a player by one
  • Trigger Once will only set the trigger to 1 if not already set to 1 or higher
  • Set Trigger will set the minimum of the trigger. So if the trigger already is at 20 and you set it to 10, it will stay at 20. If it was 5, it will change to 10
Here is how to use the trigger system in the scripts now:
#ifdef LBmaster_Core
// Choose from one of the 3 methods depending on what trigger type you work with
AchievementManager.Get().Trigger(LBPlayer(player), "trigger:name");
AchievementManager.Get().TriggerOnce(LBPlayer(player), "trigger:name");
AchievementManager.Get().SetTrigger(LBPlayer(player), "trigger:name", value);
#endif
The LBPlayer(player) can take a steamid string, PlayerIdentity or PlayerBase object as an input for the player variable. I would recommend using the PlayerBase or PlayerIdentity if you have them available and avoid using the steamid

folderAdmin Tools

folderAdd new Admin Pages
Copy

To add a new admin menu page, you need to edit a few files: create a LBAdminMenuFrame_MyFrame and name it however it fits in 5_Mission: The LBAdminMenuFrame extends the ScriptedWidgetEventHandler class, which has all the OnClick, OnItemSelected callbacks for interacting with the layout. You can also overwrite these in the frame
class LBAdminMenuFrame_MyFrame : LBAdminMenuFrame {

    override string GetTitle() {
        return "My Frame"; // Displayed in the admin menu button
    }

    override string GetButtonIcon() {
        return "LBmaster_AdminTools/gui/icons/playerinfo.paa"; // Path to the icon of the button
    }

    override void GetMinSize(out int width, out int height) {
        width = 300;
        height = 60;
    }
    // The minimum size of the frame (Frames can be resized by the admins)

    override void GetInitialSize(out int width, out int height) {
        width = 750;
        height = 700;
    }
    // The initial size of the frame when it's opened the first time

    override TStringArray GetRequiredPermissionsOne() {
        return {"admin.view.playerinfo"}; // Return a list of permissions here. Having one permission of the list is enough to open the frame. If you return null or an empty list, no extra permission is needed to open the menu
    }

    override string GetInfoString() {
        return ""; // If you return a string here, this is displayed when you hover over the Info icon at the top left of the panel. It can be used to show a tooltip explaining how this panel works. It's recommended to give a short explanation of the panel here
    }

    // Put all your Widget variables down here. All variables are automatically filled with the correct references when the menu is created. The variables should be named the same like you named them in your .layout file to automatically link


    override void OnInit() {
        // Do anything here when the layout was just created
    }

    override void UpdateSlow() {
        // Called once each second. This is also called when the admin menu is closed and the panel is pinned, so it's still visible
    }

    override void OnRPC(Object target, int rpc_type, ParamsReadContext ctx) {
        // Called when the client received a RPC and the panel is visible
    }

    override void Show(bool show) {
        super.Show(show); // Call super!!
        if (show) {
            // The panel was opened / got visible either after clicking the button or opening the admin menu
        } else {
            // The panel was hidden / admin menu was closed
        }
    }

    override void OnShow() {
        super.OnShow(); // Call super!
        // Called when the admin menu is shown (this does not mean the panel was shown!)
    }

    override void OnHide() {
        super.OnHide(); // Call super!
        // Called when the admin menu is hidden (this does not mean the panel was hidden!)
    }

    void Update(float timeslice, bool otherMenu, bool leftClicked) {
        super.Update(timeslice, otherMenu, leftClicked); // Call super!
        // called each frame with the timeslice (frame time). This is also called when the admin menu is closed and the panel is pinned, so it's still visible
        // To check if the Frame is visible, you can use IsVisible()
    }

}
Register the Admin Menu Frame LBAdminMenuMain in 5_Mission:
modded class LBAdminMenuMain {

    override void RegisterFrames() {
        super.RegisterFrames();
        RegisterFrame("LBAdminMenuFrame_MyFrame");
    }

}
Register the Layout of the Frame LBLayoutManager in 3_Game:
modded class LBLayoutManager {

    override string GetLayoutPath(string name) {
        if (name == "LBAdminMenuFrame_MyFrame") return "Path/to/gui/layouts/myFrame.layout";

        return super.GetLayoutPath(name);
    }

}
The important thing here is to use the classname of your frame for the check if name == "LBAdminMenuFrame_MyFrame". Also make sure the path to your own .layout file is correct

folderFrame Scripting
Copy

Here are a few things you want to know when creating your own Admin Menu Frame: Interacting with other Frames:
// Get another Frame:
LBAdminMenuFrame_PlayerList playerList = LBAdminMenuFrame_PlayerList.Cast(parent.GetFrame("LBAdminMenuFrame_PlayerList"));

// With the playerList Object you can do several things: playerList.selectedPlayer is the PlayerIdentity of the currently selected player in the player list. Note, that this can be null

// the playerList.Event_Players_Selected can be used to add a callback when the playerlist is used to select multiple players. if will call the method with the parameters(array<PlayerIdentity> players)

// the playerList.Event_Player_Selected calls your inserted callback when the playerlist is used to select a single player. It will call the method with the PlayerIdentity as the parameter. It's recommended to register the method in the OnInit of your Frame
Adding Markers to the Map Frame:
modded class LBAdminMenuFrame_PlayerMap {

    override void UpdateMarkers() {
        super.UpdateMarkers();
        // Add whatever markers you like. You can again get your own frame with the method shown above and access your frames variables
    }

}

folderAdvanced Groups

folderAdd / remove a Group Marker
Copy

#ifdef LBmaster_Groups
// Group Markers require you to have a LBGroup object
LBGroup group;
LBMarker marker = group.AddGroupMarker(name, position, icon, ARGB(a,r,g,b));

// Remove the Marker again by using one of these functions:
group.RemoveGroupMarker(markerObject);
// OR
group.RemoveGroupMarker(markerUID);
#endif

folderAdd Server Markers for events like Airdrops
Copy

#ifdef LBmaster_Groups
// Save a reference to the Marker to delete it later. You can also only save the marker UID, which can be retrieved using `int markerUID = marker.uid`
LBServerMarker marker = LBStaticMarkerManager.Get.AddTempServerMarker(name, position, icon, ARGB(a, r, g, b), toSurface, display3D, displayMap, displayGPS);

// Changing Color, Position, name or icon can be easily done if the corresponding function and will be update for all client immediately. They need to be called on the Server to be synched for all Clients. Calling the functions on the Client, will only change them for this client !

marker.SetRadius(radius, a,r,g,b, striked); // Add a radius to the Marker. Striked set's the radius to have lines through the circle to color it in
marker.SetRadius(radius, color, striked); // Same as above, but a ARGB(a,r,g,b) color
marker.SetColorARGB(a,r,g,b); // Change Color
marker.SetPosition(position); // Set new Position
marker.SetName(name); // Change Name
marker.SetIcon(icon); // Change Icon
#endif

folderAdd a Player to a Group
Copy

#ifdef LBmaster_Groups
// You need to have a PlayerBase object of the Player you want to add and also a LBGroup Object from the Group you want to add the Player to. How to get a LBGroup object is explained above.
LBGroup group;
PlayerBase player;
// returns false if Player already in a Group. Do not try to add Players to multiple Groups ! How to remove the Player from the Group: See below
bool success = group.AddMember(player);
if (!success) {
  // You can force the Player out of the Existing Group or handle it differently
  LBGroup oldGroup = player.GetLBGroup();
  oldGroup.RemoveMember(player);
  success = group.AddMember(player);
} else {
  Print("Player Added to the New Group");
}
#endif

folderEdit an Attribute of a Marker by it's UID
Copy

#ifdef LBmaster_Groups
// To get a Marker Object by UID, you need to do the following:
LBServerMarker marker = LBStaticMarkerManager.Get.FindTempMarker(markerUID);

// You should check if the returned marker is not null. If it's null, a marker with this UID does not exist
#endif

folderFind the Group Object by tag or name
Copy

#ifdef LBmaster_Groups
// To find a Group by Group Tag, you need to use the Group Manager Class.
// Find by Group Tag
LBGroup group = LBGroupManager.Get().GetGroupByShortName(groupTag);
// Or by Group Name
LBGroup group = LBGroupManager.Get().GetGroupByName(groupName);
#endif

folderGet the Group of a Player
Copy

#ifdef LBmaster_Groups
// Requires the PlayerBase Object for the Player you want to get the Group from
PlayerBase player;

LBGroup group = player.GetLBGroup();
if (group == null) {
  // Player has no Group
  return;
}
string groupTag = group.shortname;
string groupName = group.name;
int groupLevel = group.level
// All Group Attributes can be found in LBmaster_Groups/scripts/4_World/Groups/configs/LBGroup.c
#endif

folderGet the Players group by his Steamid
Copy

#ifdef LBmaster_Groups
// To get the Group of an offline Player by SteamID, you can use the Group Manager.
LBGroup group = LBGroupManager.Get().GetPlayersGroup(steamid);
#endif

folderRemove Server Markers for events when they despawned
Copy

#ifdef LBmaster_Groups
// You need to have a reference to the Marker Object or at least the Markers UID (see above)

bool success = LBStaticMarkerManager.Get.RemoveServerMarker(markerUID);
// OR
bool success = LBStaticMarkerManager.Get.RemoveServerMarker(markerObject);

// returns true if marker was found and deleted. If no marker was found, it will return false
#endif

folderAdvanced Loot Chests

folderAdding new Chests
Copy

If you want to add new chests to the mod, you need to know how to create a simple PBO with a config.cpp and your models p3d and texture files. I'm not going to explain how to create them here. I'm explaining what you need to have in your model and in your config.cpp to make it work with my mod. The config.cpp shown here is the minimal config.cpp you will need to make it work. You might need to add hiddenSelections or hiddenSelectionsTextures depending on your model.
class CfgPatches {
    class LBmaster_CustomLootChestExample {
        requiredAddons[]= {
            "LBmaster_LootChests"
        };
    };
};
class CfgVehicles {
    class LB_LC_Base;
    class LB_LC_Example_Chest : LB_LC_Base {
        scope = 2;
        model = "\LBmaster_CustomLootChestExample\items\ExampleItem\Chest.p3d";
        class AnimationSources
        {
            class top
            {
                source="user";
                initPhase=0;
                animPeriod=1.0;
            };
        };
    };
};
it's important to:
  • have the LBmaster_LootChests in your requiredAddons, otherwise the mod will not properly load my base class.
  • in the cfgVehicles, make sure you import the base class once at the start with class LB_LC_Base;.
  • make sure all your own classes / items inherit from this LB_LC_Base class. You can make intermediate classes like Custom_LB_LC_Base and inherit from that class to organize it a bit more.
  • set the scope to 2 for all items, which should spawn at the end. Also make sure the model path is set properly.
Optional:
  • If you want to add animations when the chest is opened, you need to add the AnimationSources class. In there define classes with the same name you used in the model.cfg. When the mod is opening the chest if will start all animations from this list.
If you want to see a fully working example PBO, you can download the Zip from the Download Page and check out the PBO in the @Serverpack/Examples folder

folderCore / General

folderCheck for Admin Permissions
Copy

To check if a player has certain permissions set in the Admins.json, you can do the following to achieve that:
#ifdef LBmaster_Core
// To check for permissions on the server, you always need to pass the steamid, PlayerIdentity or the PlayerBase object. On the client, you do not pass this in the Parameters or just pass ""
// 1. Check if he has permissions by using the steamid
if (LBAdmins.Get().HasPermission("my.test.permission", steamid)) {
    // Player has permissions
}
// 2. Check if he has permissions by using the PlayerIdentity
if (LBAdmins.Get().HasPermission("my.test.permission", identity)) {
    // Player has permissions
}
// 3. Check if he has permissions by using the PlayerBase
if (LBAdmins.Get().HasPermission("my.test.permission", player)) {
    // Player has permissions
}

// Check for permissions on the client
if (LBAdmins.Get().HasPermission("my.test.permission")) {
    // Player has permissions
}

// There is one special case you might need at some point: The HasPermissions function takes 3 Parameters: The Permission, The steamid and a bool cannotUseGrantAllPermissions. You can set this to true, so checking permissions will ignore if the player got the permission through grant All Permissions. He need to explicitly have this permission enabled. This can be useful and is used to check for chat prefixes, where the owners most likely have grant all permissions enabled, but when the VIP prefix is the first one in the list, they would always have the VIP prefix and not their Owner prefix

#endif
Minimum Script Module: 3_Game

folderCheck if Admin Mode is enabled
Copy

To check if a player has admin mode enabled, you can do this:
#ifdef LBmaster_Core
// You can check this on the client and the server. The client sends this information to the server automatically
if (LBAdmins.Get().IsActive(steamid)) { // You can pass the steamid, PlayerIdentity or PlayerBase object here
    // Admin Mode enabled
}

// To check if Admin Mode is enabled on the client, you can call
if (LBAdmins.Get().IsActive()) { // Just don't pass anything here on the client
    // Admin Mode enabled
}
#endif
Minimum Script Module: 3_Game

folderOverwrite layout files
Copy

If you want to replace a .layout file from any of my mods with your own version, because you want to change the look of the menus, you can simply do this:
#ifdef LBmaster_Core
modded class LBLayoutManager {

    override string GetLayoutPath(string name) {
        if (name == "Map Page 0 0") return "MyMod/gui/layouts/overwritten.layout";
        if (name == "PlayerList_Normal") return "MyMod/gui/layouts/overwritten.layout";

        return super.GetLayoutPath(name); // Always call super again
    }

}
#endif
You find the names of the layouts in the LBLayoutManager class of the mod you want to overwrite the layout of. They are always located in the LBmaster_MODNAME/scripts/3_Game/LBmaster_MODNAME/LBLayoutManager.c file Minimum Script Module: 3_Game

folderRegister and use my own Permissions
Copy

To Register your own permissions properly, you need to do something similar to this (you don't have to call it in the MissionServer constructor, but that's where I register my permissions):
#ifdef LBmaster_Core
modded class MissionServer {

    void MissionServer() {
        LBAdmins.Get().RegisterPermission("my.first.permission", "Anyone with this permission can to ..."); // Register a permission with "Owner", "Admin", "Moderator" and "Support" groups having this permission granted by default
        LBAdmins.Get().RegisterPermission("my.second.permission", false, "Anyone with this permission can to ..."); // Register a permission with "Owner", "Admin" and "Moderator" groups having this permission granted by default
        LBAdmins.Get().RegisterPermission("my.third.permission", false, false, false, "Anyone with this permission can to ..."); // Register a permission with only the "Owner" group having this permission granted by default
        LBAdmins.Get().RegisterPermissionImportant("my.important.permission", false, false, false, "This permission is very important and should be granted with care"); // This does the same as RegisterPermission, but in the ingame admin menu, this permission will be colored red to show this is an important permission

        LBAdmins.Get().OnRegisterFinished(); // Always call this at the end when you finished registering permissions to write any changed to the admins.json file if new permissions were found
    }

}
#endif
To check if a player has this permission, you can now use LBAdmins.Get().HasPermissions("my.first.permission", steamid). There are more ways you can check permissions. Please check the Check for Admin Permission section for that Minimum Script Module: 3_Game

folderSpawn Loot Presets
Copy

My Loot Preset System configured in the LootPresets.json can be used by anyone to spawn items in their own mods. Using the system to spawn items is done like this:
#ifdef LBmaster_Core
// Get a preset by name:
LB_PresetBase preset = LB_PresetLoader.Get.GetPreset("Test Preset"); // Would try to get the preset named "Test Preset" from the config. If not found, this is null
// You can use the preset object to spawn it like this now:
PlayerBase player; // Used to set quickbar entries and spawn items in hands. This can also be null if you do not spawn items on a player. If you spawn items on a player, this should be set
EntityAI target; // Item you want to spawn the preset in. This can also be the same as the player to spawn items on the player

preset.SpawnPreset(player, target); // You can also pass an alternate vector to spawn item at the ground, which could not be spawned on the target. Also you can pass a radius the items should be spawned in


// To spawn multiple presets at once, you can also simply do this:
TStringArray loadouts = {"Preset 1", "Preset 2", "Preset 3"};
LB_PresetBase.SpawnPresets(player, LB_PresetLoader.Get.FindPresets(loadouts), target); // This will spawn all presets at once. Be aware, that this also checks the chances of the presets now. You might end up with some presets not spawning when they are not all set to individual chance 1 and have a chance of 100% to spawn

#endif
Minimum Script Module: 4_World

folderEnhanced Banking

folderAdd Money to the ATM
Copy

To add money to a player ATM account, you can do this on the Server:
#ifdef LBmaster_Core
PlayerBase player; // Get the playerbase object of the player
LB_ATM_Playerbase atmPlayer = new LB_ATM_Playerbase(player);
int added = atmPlayer.AddATMMoney(amount); // This method returns how much money was actually added to the ATM. This can be different from the amount when the account is full
#endif
Minimum Script Module: 4_World

folderAdd new ATM Models
Copy

To add custom ATM models ingame, you need to make sure the ATM inherits the LB_ATM_Base class in the config.cpp of the item. If you don't have control over the config.cpp of the item, because it is included in another mod in the workshop, you need to create your own config.cpp with the same path to the same model of the mod. This config.cpp must be packed into a pbo, signed and uploaded to your serverpack. Here is an example what you would need to write into the config.cpp. Make sure the model="path/to/the/model.p3d" is correct and points to your custom model. You also might need to add hiddenSelectionTextures.
class CfgPatches {
    class CustomATMModels {
        units[]={};
        weapons[]={};
        requiredVersion=0.1;
        requiredAddons[]={
            "LBmaster_Groups_ATM"
        };
    };
};
class CfgVehicles {
    class LB_ATM_Base;
    class Custom_ATM_Name : LB_ATM_Base{
        scope=1;
        model= "LBmaster_Groups_ATM\items\ATM\atm.p3d";
        hiddenSelections[] = {"texture"};
        hiddenSelectionsTextures[] = {"LBmaster_Groups_ATM\items\ATM\atm_co_green.paa"};
        isLBAtm = 1;
    };
};

folderGet Money on ATM
Copy

To get the money a player has on his ATM account, you can do this on the Server. This info is not available on the client without extra RPCs
#ifdef LBmaster_Core
PlayerBase player; // Get the playerbase object of the player
LB_ATM_Playerbase atmPlayer = new LB_ATM_Playerbase(player);
int moneyOnATM = atmPlayer.GetATMMoney(); // returns the money on the player ATM account
#endif
Minimum Script Module: 4_World

folderRemove Money from the ATM
Copy

To remove money from a player ATM account, you can do this on the Server:
#ifdef LBmaster_Core
PlayerBase player; // Get the playerbase object of the player
LB_ATM_Playerbase atmPlayer = new LB_ATM_Playerbase(player);
int removed = atmPlayer.RemoveATMMoney(amount); // returns the money removed from the account. If the player has less money on the account than amount, a lower value is returned here
#endif
Minimum Script Module: 4_World

folderQuiz

folderCheck if Quiz was finished
Copy

To check if a player passed a quiz at least once, you can do the following:
#ifdef LBmaster_Quiz
// Optionally get the quiz ID first, if it's unknown
int quizId = LBQuizHandler.Get().GetQuizId("Quiz Name"); // Get the quizId from the Quiz Name
LBQuizHandler.Get().HasPassedQuizCached(steamid, quizId);
#endif
Minimum Script Module: 3_Game

folderGet notified when a player finished a quiz
Copy

If you want to have a method called when a player finished a quiz or closed the menu, you can register it like this:
#ifdef LBmaster_Quiz
void OnQuizFinished(PlayerIdentity player, int quizId, bool passed, int wrongAnswers) {
    // You can do whatever you want here with the information you get
}

// Call this once to register the OnuizFinished function. Make sure this is not added multiple times or the method will be called multiple times
LBQuizHandler.Get().AddFinishQuizListener(ScriptCaller.Create(OnQuizFinished));
#endif
Minimum Script Module: 3_Game

folderOpen a Quiz on the client
Copy

To open the quiz menu on the client with a certain quiz, you can do this:
#ifdef LBmaster_Quiz
// Optionally get the quiz ID first, if it's unknown
int quizId = LBQuizHandler.Get().GetQuizId("Quiz Name"); // Get the quizId from the Quiz Name
PlayerIdentity player; // The PlayerIdentity of the targetPlayer
bool opened = LBQuizHandler.Get().StartQuiz(player, quizId, false); // False will not ignore any timeouts. When set to true, the menu will always open. The method will return if it opened the menu on the client or not
#endif
Minimum Script Module: 3_Game

folderRaid Alert

folderTrigger towers nearby
Copy

To trigger all raid alert towers around a certain position to manually trigger the raid alert, you can do this:
#ifdef LBmaster_RaidAlert
PlayerIdentity player; // You can pass a PlayerIdentity here to log who triggered the alert if available. Otherwise just pass null
LBRaidAlertManager.Get().OnRaidAlert(position, LBRaidAlertTriggerType.CUSTOM, player);
#endif
Minimum Script Module: 4_World

folderSkin System

folderGet / Set a skin for an item
Copy

To get or set a Texture of an Item manually, you can do this:
#ifdef LBmaster_SkinSystem
ItemBase item; // You need the ItemBase of the item you want to retexture
string texture = item.GetTextureLB(); // This will return the name of the texture applied. will be an empty string if no texture is applied

item.SetTextureLB("Texture name"); // This will apply the texture to this item. The player holding this item still needs the permission to have this skin
#endif
Minimum Script Module: 4_World

folderSpawn Select

folderAdd new Bags with a new 3D Model
Copy

To add a new Bag with a new .p3d model, you need to add the following into your config.cpp of the mod
class CfgPatches {
    class LBmaster_SpawnsystemBagExample {
        units[] = {};
        weapons[] = {};
        requiredVersion = 0.1;
        requiredAddons[] = {
            "LBmaster_Spawnsystem"
        };
    };
};
class CfgVehicles {
    class LBS_SleepingBag_Base;
    class LBS_SleepingPacked_Base;

    class LBS_Custom_Placed: LBS_SleepingBag_Base {
        scope = 2;
        displayName = "My Custom Placed Sleeping Bag";
        model = "DZ\structures\furniture\Beds\matress_white.p3d";
        packedItemname = "LBS_Custom_Packed";
    };
    class LBS_Custom_Packed: LBS_SleepingPacked_Base {
        scope = 2;
        displayName = "My Custom Packed Sleeping Bag";
        model = "DZ\structures\furniture\Decoration\box_c\box_c.p3d";
        placedItemname = "LBS_Custom_Placed";
        hologramItemname = "LBS_Custom_Placed";
    };
};
What is important here to do exactly like in the example:
  • Make sure you have LBmaster_Spawnsystem in the requiredAddons list
  • Make sure you got the LBS_SleepingBag_Base and LBS_SleepingPacked_Base classes imported at the top of your CfgVehicles class
  • Make sure you have the packedItemname set to the itemname of your packed sleeping bag
  • Make sure you have the placedItemname and hologramItemname set properly. You can use the same itemnames, if you don't have a seperate hologram item
What can / should you change:
  • You can name your CfgPatches class however you want. You don't need to name it LBmaster_SpawnsystemBagExample
  • You can name your Placed and Packed bag items however you want, just make sure to use the correct itemnames in the packedItemname, placedItemname and hologramItemname variables
  • You can change the displayName of the items to whatever you like
  • You can / should change the model paths to the correct models

folderRetexture existing Bags
Copy

To retexture my sleeping bags, you need to add this in your config.cpp
class CfgPatches {
    class LBmaster_SpawnsystemBagExample {
        units[] = {};
        weapons[] = {};
        requiredVersion = 0.1;
        requiredAddons[] = {
            "LBmaster_Spawnsystem"
        };
    };
};
class CfgVehicles {
    class LBS_SleepingBag_New_Base;
    class LBS_SleepingPacked_New_Base;

    class LBS_SleepingBag_New_CUSTOM : LBS_SleepingBag_New_Base {
        scope = 2;
        displayName = "Sleeping Bag (CUSTOM)";
        packedItemname = "LBS_SleepingPacked_New_CUSTOM";
        hiddenSelectionsTextures[] = {
            "LBmaster_SleepingBags\items\sleepingbag_1\textures\sleepingbag_camo_co.paa"
        };
    };
    class LBS_SleepingPacked_New_CUSTOM : LBS_SleepingPacked_New_Base {
        scope = 2;
        displayName = "Sleeping Bag (CUSTOM)";
        placedItemname = "LBS_SleepingBag_New_CUSTOM";
        hiddenSelectionsTextures[] = {
            "LBmaster_SleepingBags\items\sleepingbag_1_bag\textures\packed_camo_co.paa",
            "LBmaster_SleepingBags\items\sleepingbag_1_bag\hologram_co.paa"
        };
    };
};
What is important here to do exactly like in the example:
  • Make sure you have LBmaster_Spawnsystem in the requiredAddons list
  • Make sure you got the LBS_SleepingBag_Base and LBS_SleepingPacked_Base classes imported at the top of your CfgVehicles class and you also inherit from these classes
  • Do not use the LBS_SleepingBag_Base or LBS_SleepingPacked_Base classes! Otherwise you would have to add the sleeping bag items to your BedSpawnConfig.json manually
  • Make sure you have the packedItemname set to the itemname of your packed sleeping bag
  • Make sure you have the placedItemname set properly
What can / should you change:
  • You can name your CfgPatches class however you want. You don't need to name it LBmaster_SpawnsystemBagExample
  • You can name your Placed and Packed bag items however you want, just make sure to use the correct itemnames in the packedItemname and placedItemname variables. I would recommend sticking with a similar naming scheme LBS_SleepingPacked_New_COLOR
  • You can change the displayName of the items to whatever you like. The default Name is Sleeping Bag (COLOR)
  • You can add a hologramItemname, but the LBS_SleepingPacked_New_Base class has this already setup

folderVirtual Garage

folderGet the Garage content of a player / group or base
Copy

To get the Garage contents of a player/group or base sign, you need to know the garage config group you want to load the vehicles from and either the steamid, the group shortname or have the sign object
#ifdef LBmaster_Garage
PlayerBase player; // The player object you want to get the garage content of
// You can either load the garage by passing the garage config name (Here "Personal Vehicles") and the steamid of the player
VirtualGaragePlayer_ personal = LBGarageAPI.Get().GetGarage("Personal Vehicles", player.GetIdentity().GetSteamid());
// or you pass the Garage_Sign object and the PlayerBase object (both must not be null!)
VirtualGaragePlayer_ personal2 = LBGarageAPI.Get().GetGarage(sign, player);

// To get the content of a group garage, you need to know the garage config name and the shortname of the group
VirtualGaragePlayer_ group = LBGarageAPI.Get().GetGarage("Personal Vehicles", grp.shortname);

// To get the content of a garage sign placed by a player at their base, you just need a reference to the garage player sign object. You can use the `Garage_Sign_Player.allSigns` array on the server to search through all garage player signs, of you get it on a different way.
Garage_Sign_Player sign = Garage_Sign_Player.GetGarageByUID(uid); // Example how you could get a sign by the UID. To get the UID of a sign, you can use `int uid = sign.GetGarageUID()`
VirtualGaragePlayer_ base = LBGarageAPI.Get().GetSignGarage(sign);

#endif
The class VirtualGaragePlayer_ is in the LBmaster_Garage/scripts/4_World/LBmaster_Garage/Config/VirtualGaragePlayer.c file, which you can open with any text editor. The structure of the files is also explained here Minimum Script Module: 5_Mission

folderPark a vehicle into the base garage
Copy

To park in a vehicle into a garage of a Garage_Sign_Player, you need to have the Garage_Sign_Player object and the vehicle you want to park in
#ifdef LBmaster_Garage
Transport vehicle; // The vehicle object required to park in this vehicle
Garage_Sign_Player sign; // The sign you want to park the vehicle in at 
PlayerBase player; // If you pass a player object to the function below, it will try to remove the vehicle key from the player inventory if that's enabled in the config group. It's valid to pass null for the player.
VirtualGarageVehicle storedVehicle; // Will be passed back from the mod with the info about the stored vehicle

StoreVehicleErrorCode status = LBGarageAPI.Get().ParkInSignGarage(vehicle, sign, player, storedVehicle, StoreVehicleChecks.ALL);

// The garage will return a status code depending on the StoreVehicleChecks you passed it. By default it will do all checks, you can skip some checks if you want.
// If you only want to check if the vehicle is on the whitelist and the garage is full, you would pass StoreVehicleChecks.WHITELIST | StoreVehicleChecks.GARAGE_FULL
#endif
List of vehicle store Checks:
  • StoreVehicleChecks.NONE: No Checks are performed. Only passing invalid identity or vehicle parameters can cause it to fail
  • StoreVehicleChecks.VEHICLE_KEY: Check if the player object passed to the function has a vehicle key. If the player is null, it will skip this check
  • StoreVehicleChecks.GARAGE_FULL: Check if the target garage has enough space for the vehicle
  • StoreVehicleChecks.WHITELIST: Check if the vehicle type is on the whitelist of the garage group
  • StoreVehicleChecks.BLACKLISTED_ITEMS: Check if the vehicle contains blacklisted items
  • StoreVehicleChecks.ALL: Combines all checks and will check for everything is ok or will return the appropriate StoreVehicleErrorCode. To combine your own flags, you need to bitwise or (|) the flags. For example StoreVehicleChecks.BLACKLISTED_ITEMS | StoreVehicleChecks.GARAGE_FULL
List of store error Codes:
  • StoreVehicleErrorCode.OK: Vehicle was parked in successfully
  • StoreVehicleErrorCode.VEHICLE_NULL: The vehicle object passed to the function was null (vehicle does not exist)
  • StoreVehicleErrorCode.VEHICLE_ALREADY_DELETED: The vehicle is already about to be deleted due to being parked in (prevents vehicles parked in multiple times and duped this way)
  • StoreVehicleErrorCode.NO_VEHICLE_KEY: The player object passed does not have a key for the vehicle in the inventory, but a key is required.
  • StoreVehicleErrorCode.COULD_NOT_LOAD_PLAYER: Could not load the config file of the players/groups garage
  • StoreVehicleErrorCode.GARAGE_FULL: The garage is full and the vehicle cannot be parked in
  • StoreVehicleErrorCode.NOT_WHITELISTED: The vehicle type is not on the whitelist of the garage group
  • StoreVehicleErrorCode.PLAYER_IN_VEHICLE: A player is still in the vehicle and deleting this vehicle would cause the player to crash
  • StoreVehicleErrorCode.BLACKLISTED_ITEM: The vehicle contains blacklisted items
  • StoreVehicleErrorCode.GROUP_NOT_FOUND: Could not find the passed garage config group name
Minimum Script Module: 5_Mission

folderPark a vehicle into the player / group garage
Copy

To park in a vehicle into a player public garage, you would need to do something like this:
#ifdef LBmaster_Garage
Transport vehicle; // The vehicle object required to park in this vehicle
PlayerBase player; // If you pass a player object to the function below, it will try to remove the vehicle key from the player inventory if that's enabled in the config group. It's valid to pass null for the player.
string identity = player.GetIdentity().GetPlainId(); // To park a vehicle into the personal garage, you need to set this to the steamid of the player. To park it into a group garage, this would be the short group tag. (grp.shortname)
VirtualGarageVehicle storedVehicle; // Will be passed back from the mod with the info about the stored vehicle
string configGroup = "Ground Vehicles"; // Set this to your config group you want to park in a vehicle to

StoreVehicleErrorCode status = LBGarageAPI.Get().ParkInVehicle(vehicle, configGroup, identity, player, storedVehicle, StoreVehicleChecks.ALL);

// The garage will return a status code depending on the StoreVehicleChecks you passed it. By default it will do all checks, you can skip some checks if you want.
// If you only want to check if the vehicle is on the whitelist and the garage is full, you would pass StoreVehicleChecks.WHITELIST | StoreVehicleChecks.GARAGE_FULL
#endif
List of vehicle store Checks:
  • StoreVehicleChecks.NONE: No Checks are performed. Only passing invalid identity or vehicle parameters can cause it to fail
  • StoreVehicleChecks.VEHICLE_KEY: Check if the player object passed to the function has a vehicle key. If the player is null, it will skip this check
  • StoreVehicleChecks.GARAGE_FULL: Check if the target garage has enough space for the vehicle
  • StoreVehicleChecks.WHITELIST: Check if the vehicle type is on the whitelist of the garage group
  • StoreVehicleChecks.BLACKLISTED_ITEMS: Check if the vehicle contains blacklisted items
  • StoreVehicleChecks.ALL: Combines all checks and will check for everything is ok or will return the appropriate StoreVehicleErrorCode. To combine your own flags, you need to bitwise or (|) the flags. For example StoreVehicleChecks.BLACKLISTED_ITEMS | StoreVehicleChecks.GARAGE_FULL
List of store error Codes:
  • StoreVehicleErrorCode.OK: Vehicle was parked in successfully
  • StoreVehicleErrorCode.VEHICLE_NULL: The vehicle object passed to the function was null (vehicle does not exist)
  • StoreVehicleErrorCode.VEHICLE_ALREADY_DELETED: The vehicle is already about to be deleted due to being parked in (prevents vehicles parked in multiple times and duped this way)
  • StoreVehicleErrorCode.NO_VEHICLE_KEY: The player object passed does not have a key for the vehicle in the inventory, but a key is required.
  • StoreVehicleErrorCode.COULD_NOT_LOAD_PLAYER: Could not load the config file of the players/groups garage
  • StoreVehicleErrorCode.GARAGE_FULL: The garage is full and the vehicle cannot be parked in
  • StoreVehicleErrorCode.NOT_WHITELISTED: The vehicle type is not on the whitelist of the garage group
  • StoreVehicleErrorCode.PLAYER_IN_VEHICLE: A player is still in the vehicle and deleting this vehicle would cause the player to crash
  • StoreVehicleErrorCode.BLACKLISTED_ITEM: The vehicle contains blacklisted items
  • StoreVehicleErrorCode.GROUP_NOT_FOUND: Could not find the passed garage config group name
Minimum Script Module: 5_Mission