A code library for working with Portal 2 Puzzle (P2C) files.
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.


Portal 2 Puzzle Library

A library for working with Portal 2's P2C (puzzle) files in C#. It supports reading, writing, creating and modifying puzzle files. The APIs are designed to be simple and intuitive to use - e.g. instead of specifying a rotation angle for an item, you'll specify what wall it is placed on and what direction it faces.

Getting Started

  1. Make sure you have opened the puzzle editor in Portal 2 at least once (it helps to have saved a puzzle).
  2. Open TestPuzzle.sln in Visual Studio 2010 (Visual C# 2010 Express should work too) and hit F5 to compile and run the project.
  3. Open Portal 2, open "Community Test Chambers", click "Create Test Chambers" and you should see a puzzle titled "Test Puzzle". This is the level generated by the code you just ran!

The included source files in the TestPuzzle project demonstrate most of the functionality available in the library, from editing voxels to adding turrets. You can either modify the test project or just link to the puzzle library project from your own project.


There are three major components in a puzzle - the voxels that define the map, the items in the map, and the connections between those items. For information on the P2C file format, visit https://developer.valvesoftware.com/wiki/P2C.


You can edit the voxels in a level using the Voxels object. Voxel positions are in (X, Y, Z) order, with X being left/right, Y being forward/back, and Z being up/down. Voxels are edited in a bitwise manner, using a mask (defining which bits to edit) and a value (defining the values to set). You can edit either single voxels using SetVoxelData or blocks of voxels using SetRange. If you would like the level size to automatically grow as you edit voxels, set Voxels.AutoGrow to true.

puzzle.Voxels.AutoGrow = true;
puzzle.Voxels.SetVoxelData(new Point3(1, 7, 4), VoxelData.IsSolid, VoxelData.NotSolid);
puzzle.Voxels.SetRange(new Point3(4, 5, 1), new Point3(3, 4, 3), VoxelData.IsSolid | VoxelData.IsPortalableNegZ, VoxelData.NotSolid | VoxelData.NotPortalable);


Items are added to a puzzle by creating an instance of the item's class and then adding it to the puzzle's Items collection. Items that require an associated item (e.g. Cubes require a CubeDropper) will create that item automatically (which can then be accessed through a property on the creating item, e.g. cube.Dropper).

Cube companion = new Cube(puzzle)
	VoxelPosition = new Point3(5, 10, 0),
	CubeType = CubeType.Companion,
	AutoDrop = true,
	AutoRespawn = true,
	DropperVisible = true


Items can be connected through their connection points. For example, a button includes a ConnectionSender property, which can be connected to a FlipPanel's ConnectionReceiver property.

FlipPanel panel = new FlipPanel(puzzle)
	VoxelPosition = new Point3(2, 0, 0),
	FlipAxis = Direction.Z,
	Wall = Direction.NegY

Button button = new Button(puzzle)
	VoxelPosition = roomOffset


puzzle.Items.AddRange(new Item[] { panel, button });


  1. There is one major piece of functionality needed to consider this library complete - deleting complex objects is currently difficult. Specifically, anything with extents. You must manually find and remove the extents. I intend to correct this shortly.
  2. There is currently no validation of item placement (aside from a few exceptions within door orientation properties). I intend to add a validation step, I just need to determine an intuitive way to do it (since you may not always want it to just throw an exception if the level is invalid, and you may want real-time validation results).
  3. I'm not incredibly happy with the connection API. It still feels a bit awkward to me, and I may revise it a bit. There are some edge cases, but for the most part, a connection sending item should be able to connect directly to a connection receiving item, without having to use the connection point APIs.
  4. Faith plates include a "speed" parameter that is used in the puzzle editor to display the launch arc, but isn't used when compiling the level. I've spent some time trying to determine the formula used to calculate speed, but have thus far only managed to generate an equation for when the launcher and target are at the same vertical position. I've placed a TODO note in the code explaining this, and will try to update the code in the future with a correct calculation (or you're welcome to!).


While this library will produce fully functional levels, there will be slight differences in the puzzle files generated from this library as opposed to those generated by the puzzle editor. This is due primarily to the fact that there are many equivalent rotation angles, and the library often will pick one different from what the puzzle editor would generate. This does not impact the function of the levels at all.