- Dependencies
- Copy Paste (Required)
- Works With
Portable Fort allows players to deploy instant structures by throwing specially configured grenades. When a grenade with a matching skin ID and custom name is thrown, it spawns a complete base from your Copy Paste files instead of exploding.
You can set up multiple grenade types, each with its own deploy timer, building grade, and selection of base designs. The plugin handles everything from validating placement to authorizing players on tool cupboards. If deployment fails due to water, terrain, or proximity issues, the grenade is refunded with an explanation.
This opens up gameplay possibilities like starter base kits for new players, tactical raid camps, mobile furnace arrays, or mystery grenades that randomly choose between different designs. The plugin integrates with Copy Paste for structure spawning and optionally with Better No Escape for combat/raid restrictions.
Features
- Instant base deployment - Throw a grenade and watch a complete structure materialize
- Multiple grenade configurations - Set up different grenade types with unique skins and behaviors
- Random base selection - Configure multiple paste files per grenade for variety
- Smart placement validation - Prevents deployment in water, on roads, in monuments, or near other entities
- Combat/raid blocking - Optional integration with Better No Escape to prevent deployment during combat
- Customizable building grades - Force specific grades or preserve original design
- Animated construction - Choose between instant deployment or progressive twig-to-final-grade animation
- Ownership system - Automatically authorize throwers on tool cupboards
- Grenade refund system - Returns grenades when deployment fails with helpful error messages
- Height adjustment - Fine-tune vertical positioning for each paste file
Dependencies
Required
- Copy Paste -- Required for all base spawning functionality. The plugin cannot function without it.
Optional
- Better No Escape -- Enables combat and raid blocking features. Without this, combat/raid blocking settings are ignored.
Commands
Admin Commands
pf.give <playerNameOrId> <grenadeName> [amount]-- Gives a configured fort grenade to a playerplayerNameOrId-- Target player's name or Steam ID (partial names supported)grenadeName-- Name of the grenade configuration (supports partial matching)amount-- Number of grenades to give (optional, defaults to 1)- Partial name matching works for both players and grenade names
- If multiple grenades match the search term, you'll be prompted to be more specific
- Grenades are given with the correct skin and custom name
- Examples:
pf.give Awesome Port-A-Fortpf.give 76561198012345678 Port-A-Fort 5pf.give player1 Fort 3
pf.list-- Lists all configured fort grenades with their details- Shows grenade name, skin ID, and number of paste files
- Helps admins see what grenade configurations are available
Configuration
JSON:
{
"Version": "1.8.0",
"Minimum Water Depth To Block Placement": 1.0,
"Refund Grenade If Combat Blocked (Requires Better No Escape)": false,
"Refund Grenade If Raid Blocked (Requires Better No Escape)": false,
"Fort Grenades": [
{
"Grenade Skin ID (0 For Default Skin)": 1163186435,
"Custom Grenade Name": "Port-A-Fort",
"Seconds Until Fort Deploys After Throw": 10.0,
"Paste Files (One Is Randomly Chosen)": [
{
"Paste File Name (Without Extension)": "StarterBase",
"Height Offset (Negative Goes Underground)": 2.3,
"Set Building Owner To Thrower (Authorizes On Tool Cupboard)": true,
"Build Directly In Final Grade (Skip Twig Animation)": false,
"Force Building Grade (None Keeps Original)": "None",
"Copy Paste Plugin Options": {
"stability": "false",
"deployables": "true"
}
}
]
}
]
}
Global Settings
Minimum Water Depth To Block Placement-- Controls how deep water must be before blocking fort deployment. If the grenade lands in water deeper than this value, deployment is prevented and the grenade is refunded.0.5-- Blocks deployment in shallow puddles1.0-- Default; prevents deployment in typical rivers/lakes2.0-- Only blocks deployment in deep water0.0-- Allows deployment even in water (not recommended)
Refund Grenade If Combat Blocked-- When enabled, prevents players in combat from deploying forts and refunds their grenade with an explanatory message. Requires Better No Escape plugin.- Use this to prevent combat disengagement by deploying instant cover
- Refund Grenade If Raid Blocked -- When enabled, prevents players engaged in raiding from deploying forts and refunds their grenade with an explanatory message. Requires Better No Escape plugin.
- Use this to prevent raiders from instantly deploying defensive positions during raids
Fort Grenade Configuration
Each entry in theFort Grenades array defines a grenade type that will spawn structures.Grenade Skin ID-- The Workshop skin ID that identifies this grenade type. Players must use grenades with this exact skin ID for the configuration to activate. Set to `0`to match grenades with no skin (default appearance).Custom Grenade Name-- The exact display name the grenade must have. This is checked in addition to the skin ID. Both skin ID AND name must match for the configuration to activate. Leave empty if you only want to match by skin ID.- Give the grenade a custom name in-game or use another plugin to rename it
Seconds Until Fort Deploys After Throw-- Time delay between throwing the grenade and the fort spawning. This replaces the grenade's normal explosion timer.- Shorter times create urgency but less predictability
- Longer times allow for tactical positioning
Paste File Configuration
Each grenade can have multiple paste files; one is randomly selected when the grenade deploys.Paste File Name-- The name of the Copy Paste file to spawn (without the `.json`extension). File must exist inoxide/data/copypaste/[YourFileName].jsonHeight Offset-- Vertical adjustment applied to the spawned structure relative to the ground position. Positive values raise the structure above ground, negative values lower it (potentially underground).2.0to3.0-- Typical offset to prevent foundation clipping into terrain1.0to1.5-- Minimal offset for flat terrain builds0.0-- No offset (may clip into uneven terrain)-1.0-- Partially buried bunker entrance
Set Building Owner To Thrower-- When enabled, all spawned entities (including building blocks and tool cupboards) will have their `OwnerID`set to the player who threw the grenade. Player is automatically authorized on spawned tool cupboards and owns all building blocks and deployables.Build Directly In Final Grade-- Controls the visual construction processfalse(default) -- All blocks spawn as Twig, progressive upgrade animation plays, blocks sequentially upgrade to final grade, construction effects play for each upgrade, takes several seconds to completetrue-- Blocks spawn directly in final grade, no twig phase or animation, instant deployment, still plays placement effect for each block
Force Building Grade-- Overrides the building grade saved in the paste file. Applies ONLY to building blocks (foundations, walls, etc.), not deployables.None-- Use the grade saved in the paste fileTwigs-- Force all blocks to TwigWood-- Force all blocks to WoodStone-- Force all blocks to StoneMetal-- Force all blocks to Sheet MetalTopTier-- Force all blocks to Armored
Copy Paste Plugin Options-- Advanced options passed directly to the Copy Paste plugin during pasting. See the Copy Paste plugin documentation for all available options and their descriptions.- Common options:
stability,deployables,auth,inventories,blockcollision - Note: The plugin automatically forces
autoheightto"false"andblockcollisionto"0"regardless of your config for reliable deployment
- Common options:
Localization
JSON:
{
"Error.NoPermission": "You do not have permission to use this command.",
"Error.NoMatch": "No grenade found matching '{0}'. Use pf.list to see available grenades.",
"Error.Ambiguous": "Multiple grenades match '{0}': {1}. Be more specific.",
"Error.NoPlayerFound": "No active player found matching '{0}'.",
"Error.FailedCreate": "Failed to create grenade.f1 item!",
"Error.InvalidAmount": "Invalid amount '{0}'. It must be a positive number.",
"Give.Usage": "Usage: pf.give <playerNameOrId> <grenadeName> [amount]",
"Give.Success": "Gave {1}x '{0}' grenade to {2}.",
"List.Header": "Available Fort Grenades:",
"List.Entry": " - {0} (skin: {1}, {2})",
"List.Empty": "No fort grenades configured.",
"Info.GrenadeRefund": "Could not place the fort: {0}\nYour grenade has been returned!"
}
Developer Hooks
OnFortDeploy
Called before a fort begins deploying, allowing other plugins to prevent or modify deployment.
C#:
object OnFortDeploy(BasePlayer player, TimedExplosive grenade, string pasteFile)
player-- The player who threw the grenadegrenade-- The TimedExplosive entity (the grenade itself)pasteFile-- The name of the paste file about to be deployed
Allow deployment (return null):
C#:
return null;
C#:
return "You cannot deploy forts in this area!";
C#:
return false;
C#:
return new Dictionary<string, string> { ["PasteFile"] = "AlternateFort" };
C#:
// Block deployment in safe zones
object OnFortDeploy(BasePlayer player, TimedExplosive grenade, string pasteFile)
{
if (IsInSafeZone(player.transform.position))
{
return "Cannot deploy forts in safe zones!";
}
return null;
}
// Replace paste file based on player permission
object OnFortDeploy(BasePlayer player, TimedExplosive grenade, string pasteFile)
{
if (permission.UserHasPermission(player.UserIDString, "portablefort.vip"))
{
return new Dictionary<string, string>
{
["PasteFile"] = "VIPFort"
};
}
return null;
}
// Count deployments
object OnFortDeploy(BasePlayer player, TimedExplosive grenade, string pasteFile)
{
IncrementPlayerFortCount(player.userID);
if (GetPlayerFortCount(player.userID) > 5)
{
return "You've reached your fort deployment limit!";
}
return null;
}
OnFortFinishedPasting
Called after a fort has completely finished spawning and all construction/upgrade animations are complete.
C#:
void OnFortFinishedPasting(BasePlayer player, List<BaseEntity> entities)
player-- The player who threw the grenadeentities-- All entities that were spawned (building blocks, deployables, etc.)
Example Usage:
C#:
// Announce fort deployment
void OnFortFinishedPasting(BasePlayer player, List<BaseEntity> entities)
{
PrintToChat($"{player.displayName} deployed a fort with {entities.Count} entities!");
}
// Track spawned entities
void OnFortFinishedPasting(BasePlayer player, List<BaseEntity> entities)
{
foreach (var entity in entities)
{
RegisterEntity(player.userID, entity.net.ID);
}
}
// Auto-lock doors
void OnFortFinishedPasting(BasePlayer player, List<BaseEntity> entities)
{
foreach (var entity in entities)
{
Door door = entity as Door;
if (door != null)
{
door.SetFlag(BaseEntity.Flags.Locked, true);
CodeLock codeLock = GameManager.server.CreateEntity("assets/prefabs/locks/keypad/lock.code.prefab") as CodeLock;
if (codeLock != null)
{
codeLock.Spawn();
codeLock.SetParent(door);
// Additional codelock setup...
}
}
}
}
// Trigger server event
void OnFortFinishedPasting(BasePlayer player, List<BaseEntity> entities)
{
Interface.CallHook("OnPlayerDeployedFort", player, entities);
}
