Contributing to c20

The Reclaimers Library is an open source wiki created by modders for modders. It has a primary team of volunteer editors who incorporate new information from their own research or from community tips. This page details how you can contribute, and how to write pages as an editor.

How to contribute

Pages are not publicly editable, but anyone can:

Editors will review and incorporate information into the wiki. Don't worry if writing isn't your strong suit. We'll ensure your tips are properly included and you're credited for them.

Becoming an editor

Interested in curating pages? Go to #role-selection in our Discord and select the "Wiki Contributor" role. We expect that you'll be able to:

  • Run the wiki in development mode locally and work with git.
  • Verify new information (unless it's something trivial). There's a lot of misconceptions in the community and people often relate solving their modding issues to coincidental changes that aren't actually related. Editors can generally trust the word of community experts, but their contributions can still benefit from elaboration. Ask for more details if needed.
  • Research topics on your own by doing experiments in the editing kits, pulling clues from old forums, Discord history, YouTube, etc.
  • Explain things in a way that's easy to understand by beginners.
  • Follow the guidelines on this page.

Although you can suggest edits on GitHub, it's recommended for writers and developers that you run c20 in development mode on your own sytem for the best experience. You'll be able to see instant previews of how your pages will look and verify everything is valid and building successfully. Instructions for this can be found on our GitHub page.

How the wiki works

This wiki is a "static website" once hosted, meaning it's just a bunch of HTML files which are served to your browser as-is rather than generated by a server from a database. Editors update Markdown and YAML files in the c20 codebase on GitHub when content changes are needed and it's automatically rebuilt into new HTML files and uploaded to our hosting. This makes the wiki very cheap to host.

Page content

All pages are written as readme.md files under the src/content folder, where the folder structure reflects the eventual URLs of the pages. The parent folder is the parent page, and the subfolders are child pages.

The folder for a page can include certain other files related to that topic, like images, that you can refer to from readme.md. Do not place images in a subfolder for organization; subdirectories should be used for child pages only. Supported file types are: .dot, .jpg, .png, .gif, .ms, .mp4, .blend, and .zip.

Tips for organization:

  • All H1-specific content should exist under src/content/h1, whereas topics which could apply to multiple games should live under src/content/general.
  • If a topic is only really related to another topic, ensure it is either a child page (a subfolder) or under heading of that parent topic instead.
  • If a topic is growing too large, consider splitting it up and taking its large sections to a child page (a subfolder).
  • Renaming page folders means breaking bookmarks or links to c20 pages from outside. Don't do it lightly.

Any src/content/*/todo.md file or src/content/*/todo folder is git-ignored and can be used to mock out page structures and keep notes for later writing.

Structured data

Some wiki content is better described as structured data in YAML, such as descriptions of Halo's tag data layouts, lists of script functions, and workflows between tools and file formats. These files can be found under src/data and are used by the various wiki features documented on this page:

  • hsc: contains definitions for script functions and globals by game
  • structs: contains type definitions for data structure tables
  • tags: lists tags and their purpose for each game
  • workflows: describes modding tools and resources and how they interact

How to write a page

Firstly, a page requires a readme.md file for its body content and metadata. This file must exist somewhere under src/content. The folder names within must follow the URL naming convention, which is lowercase only without any special characters and only separating words with - or _. For example:

  • src/content/h1/guides/my-cool-tutorial/readme.md
  • src/content/h1/tags/shader/shader_transparent_chicago/readme.md

You can also create a readme_es.md if you want to translate a page to Spanish, for example.

These files contain the main body content of each page and are where most content will be written. The following sections will describe how to write it.

Page metadata

At the top of every readme.md page file is a metadata section enclosed by two ---, for example:

---
title: Blender
about: 'tool:Blender'
img: blender.jpg
caption: 'Blender being used to model a [BSP](~h1/tags/scenario_structure_bsp)'
---
**Blender** is a free and open-source 3D modeling, animating, and rendering toolset...

This metadata section is in YAML format and needs a title at bare minimum. See below for a full description of available metadata parameters:

# If the title contains a ": ", put it within quotes, e.g. "Halo: CE".
# Please use sentence case ("My biped tutorial", NOT "My Biped Tutorial").
title: My biped tutorial

# Identifies this page as being about a particular type of *thing*. This can
# take a few forms with different effects:
#
# tag:<game>/<tag_name>
# Visually styles the metabox and adds tag dependency and workflow info if
# documented in src/data YAML files. Example: tag:h1/scenario
#
# tool:<tool_name>
# Styles the metabox and adds any workflow info from src/data/workflows.
# Example: tool:Chimera
#
# resource:<resource_name>
# Styles the metabox and adds any workflow info from src/data/workflows.
# Example: resource:JMS
about: tag:h1/scenario

# All page content is automatically searchable, but you may want to add
# additional "boosted" keyword search terms. These help your page show up
# higher in search results.
keywords:
  - skeleton
  - character

# Related pages show up in the page's sidebar. You must provide the related
# pages as their full folder path starting with a "/". A related page link
# will NOT automatically be added in the opposite direction.
related:
  - /h1/tags/object/unit/biped
  - /h1/tags/actor_variant

# This image will be shown in the page's info box (aka "metabox") and in
# previews when pasting the page into Discord or social media. The image file
# should be in the same folder as the readme.md file and should represents
# the page's topic as a whole.
img: blind-wolf.jpg

# The caption adds text to the metabox below the image. It should add context
# to the image or describe anything notable about it. If an image is provided
# then you should also add a caption.
caption: A screenshot of the biped we'll create.

# Adds a section for arbitrary text to the metabox below the caption, such as
# for download or source code links.
info: |
  You can write **markdown** with:
  * Multiple
  * Lines

# Marking a page as a "stub" adds an alert to its header that it's a work in
# progress and also prevents the page from being added to the sitemap (used by
# search bots). Once a page has reached a useful state, remove this flag.
stub: true

# Please credit anyone who helped create this page or was a source for its
# information. Thanked individuals are listed at the bottom of a page and in
# the summary on this page. Use quotes if the person's name contains spaces.
thanks:
  PersonName: Explaining how actors work
  "Person Name": Writing this guide

# If provided, child pages of this one will be ordered as follows in the
# navigation tree. Otherwise child pages are ordered alphabetically by their
# slug (last part of their URL). Manually-ordered pages always appear first.
childOrder:
  - general
  - h1
  - h2

# Excludes the page from the search index. Use this for pages which are
# incomplete or people won't need to search for.
noSearch: true

# An optional list of page IDs that should be redirected to this page instead.
# Use this when content is moved between pages or pages are moved to new URLs.
# We're limited to 50 total redirects across the site, so use sparingly.
redirects:
  - /h1/old/page
  - /h1/another/old/page

# You probably don't need to use this. Some pages contain headings which are
# directly referenced by code in this project. This config allows other page
# languages page to provide a stable ID alias for headings that's translated
# during autolink resolution.
headingRefs:
  group-ids: id-de-motor

Markdown basics

After the metadata is the main body of the page, which we write in Markdown format. You may be familiar with basic forms of it in Discord or Reddit, but c20 uses a more powerful version with custom extensions tailored to our needs. You can read more about it here.

Paragraphs should be written as single lines of text (enable word wrap in your text editor), and separate each paragraph with an empty line. You can make text italic by surrounding it with * or bold with **, for example: **this is important!**. We do not support inline HTML in our Markdown files.

Introduction

Always write an introduction at the start of each page. It should clearly explain what the page is about and give a high level summary so someone can quickly know if the page describes what they're looking for. The first 100 characters of the introduction are also used for social media previews. There is no need to include the page title, which is added automatically from the metadata.

If the introduction mentions common names for the topic, please make them bold like Wikipedia does. For example:

The **scenario structure BSP** tag, commonly just called the **BSP**, contains level geometry...

Writing style

  • Use a "textbook" or Wikipedia style of writing. When writing guide/tutorial pages you can be a little more conversational, using phrases like "you will see a..." or "next we need to...".
  • Make points plainly with simple English and avoid expressions unfamiliar to those whose first language is not English.
  • There is no strong preference at this time for UK/Canada vs. US spelling of words like colour, unless you are referring to a tag's field name which should match exactly.
  • Assume your reader is a novice. Include links to prerequisite information and other pages to fill out concepts. Use examples that the reader might be familiar with to help get your points across.
  • Don't refer to tags by their group ID (e.g. shader_environment not senv).
  • Italicize tag fields when they are not links.
  • Avoid using words like "below" or "above" to refer to other content on the page unless it is fairly certain the content will stay together. We may wish to reorder sections and paragraphs in the future. Consider using an anchor link to a relevant heading on the same page.
  • Capitalize in-universe or map names, like Warthog or Sidewinder.

Headings

Use headings to separate articles into easy-to-read sections and aid navigation. Start at H1 for each major section of the page and never skip levels for sub-headings (e.g. never put an H3 directly under an H1). There is generally little need to go deeper than an H3.

# Heading (H1)
...
## Subheading (H2)
...
### Subheading (H3)
...
## Subheading (H2)
...
# Heading level (H1)
...
## Subheading level (H2)
...

Headings automatically generate a table of contents shown in the page sidebar and have a clickable "#" anchor for easy sharing of links to specific sections of a page.

Always use sentence case, with the first word capitalized only. The exception is if the title contains a proper name like a tool. To maintain consistency across pages, try to use some of these common heading names if relevant:

  • Overview
  • Installation
  • Usage
  • Troubleshooting
  • Limits
  • Related HaloScript
  • Compatibility
  • Known issues
  • Changelog
  • Appendix
  • External tutorials

Standard Markdown links can be written in a few ways:

Go visit [the best website](http://halomaps.org/).
See [the about heading](#about).
Download [the tags][short].

[short]: https://www.example.com/very/long/url/tags.zip

However, linking within c20 is cumbersome and fragile with standard markdown. Therefore we have a custom smart links feature using the ~ symbol. This feature automatically resolves links using a trailing fragment of the destination page's path, preferring pages in the same tree as the current page. The build verifies that the page actually exists. For example:

[Sapien](~h1-sapien)
[Chimera](~) is equivalent to [Chimera](~chimera)
[H1 tags](~tags) if current page is in the h1 section
[H1 tags](~h1/tags)
[H2 tags](~h2/tags)
[the scenario _bipeds_ block](~scenario#tag-field-bipeds)

When multiple target pages match, c20 will choose the one most related to the origin page in the content tree. When this is not possible, the ambiguity must be resolved by specifying more of the path tail in the link.

Another special type of link we support is global links to URLs defined in external-urls.ts:

[Joint issue tracker](JIF_ISSUE_URL)
[Reclaimers Discord server](DISCORD_URL)
[The c20 GitHub repo](REPO_URL)

Tips for good links:

  • Add links where it is helpful for the reader, but avoid over-linking especially when there is already the same link visible within a short scrolling distance on the same page.
  • Avoid orphaned content. Every new page should have at least one link to it from other relevant locations.
  • Links from one page to another usually need one in reverse too.
  • If a tag page has a dedicated section describing a certain concept, prefer linking to that over linking to the relevant fields in the tag structure.
  • Note: we do not support "raw" URL links. Use the syntax above.

Lists

Lists are created by putting each item on a line prefixed by either *, -, or 1., 2., etc. List items should not have empty lines between them or they will not be considered a list. Don't overuse lists; a well-written paragraph is better than a list of notes.

To create a bulleted list (preferred style):

* Item 1
* Item 2
* Item 3

Also works for bulleted lists:

- Item 1
- Item 2

Ordered list:

1. Item 1
2. Item 2

If you have a large ordered list that's prone to edits you can also do:

1. a
1. b
1. c
1. d
1. e

Alert boxes

An alert box is a highlighted container which draws attention to some content, usually text. It can be added with the following syntax:

{% alert %}
Markdown content within
{% /alert %}

or:

{% alert type="danger" %}
Markdown content within
{% /alert %}

The possible alert types are info (default), danger, and success. Rendered, an alert looks like:

Don't overuse alerts. If something's important, consider prioritizing that information earlier in an article or making something bold.

Images and videos

Images (.png and .jpg) and videos (.mp4) can be placed in a page folder and used from its readme.md. We prefer .jpg with about 80-90% quality, but .png can be used if lossless images are needed. Any standard markdown image will be rendered as a clickable figure:

![343 Guilty Spark floating in The Library](343.jpg "We must continue. This way please.")

343 Guilty Spark floating in The Library

We must continue. This way please.

The alt text is provided between the [] and aids the reader in the case that images don't load or is using assistive technologies. You can omit it when failure to see the image does not detract from the reader's ability to understand the article, such as when the information is repeated in the image caption or a paragraph. Alt text must be plain text (not Markdown)

The image caption is provided after the filename and must be enclosed in quotes. If the caption itself contains a quote character you can escape it using \". The caption should supplement or add context to the image but can be omitted. Technically, in standard Markdown this sets the title attribute but for C20 we use it for figure captions because information should not be hidden behind a hover title.

If the filename ends with .mp4, e.g. ![](video.mp4), the figure will appear as a video player instead. The wiki's build automatically generates poster images from the first video frame (requires ffmpeg to be on your PATH, or set the environment variable C20_NO_THUMBNAILS=true to bypass).

For more control, you can use the figure extension:

{% figure src="img.jpg" alt="Alt text" inline=true %}
Caption with:

* Multiple lines...
* and *formatting*
{% /figure %}

Diagrams

If a page folder contains a file with .dot extension it will be rendered into a diagram SVG image using Graphviz. You must use the .svg extension from within Markdown, like: ![](diagram.svg).

Hotkeys

Write hotkeys like Alt + N with:

Press {% key "Alt+N" /%} to open the normals menu in Blender.

HaloScript and code

Short inline code excerpts, data values, math, or tool commands can be stylized using single backticks:

Avoid dying by using the `cheat_deathless_player 1` console command.

Use code blocks/fences for larger excerpts.

A code block containing python

```python
scenario_tag = scnr_def.build(filepath=scenario_path)
```

A code block containing HaloScript:

```hsc
; comment
(script static "unit" player0
  (unit (list_get (players) 0))
)
```

The above would be rendered as:

; comment
(script static "unit" player0
  (unit (list_get (players) 0))
)

C20 supports syntax highlighting for a variety of common languages encountered in Halo modding, including special support for:

  • hsc for HaloScript as it would be written for scenarios
  • inittxt for Halo console commands embedded in an init.txt
  • console, consoleh1a, consoleh2a, consoleh3 for Halo console commands entered into the in-game console
  • vrml for highlighting VRML 1.0 as used in Tool's WRL files

If you want to have support for new highlighting languages, please contact a maintainer.

Basic tables

We have multiple ways of writing tables depending on their size. Small tables can use standard Markdown tables syntax:

| Symbol | Usage
|--------|---------------------------------
| `%`    | **Two-sided property**. Etc...
| `#`    | **Transparent property**. Etc...
| `!`    | **Render-only property**. Etc...

Medium sized tables or those that require multiple lines per cell can use Markdoc's table extension:

{% table %}
* Heading 1
* Heading 2

---

* Row 1 Cell 1
* Row 1 Cell 2

---

* Row 2 Cell 1
*
  Row 2 cell 2 line 1
  Row 2 cell 2 line 2
{% /table %}

Data tables

For large and data-driven tables we support generating tables from YAML files. All data under src/data is exposed to this feature, as well as any data in .yml files adjacent to your readme.md. Data tables allow you to iterate over any object or array within structured data and extract arbitrary columns from keys within, formatting them in a few common ways.

The H1 tags list reads src/data/tags/h1.yml and looks like this:

{% dataTable
  dataPath="tags/h1"
  id="tags"
  rowSortKey="key"
  linkSlugKey="key"
  rowFilterKey="value"
  rowFilterExpr="NOT unused"
  columns=[
    {name: "Tag name", key: "key", format: "pageLinkRaw"},
    {name: "Group ID", key: "value/id", format: "code"},
    {name: "Parent", key: "value/parentName", format: "pageLinkRaw"},
    {name: "Purpose", key: "value/description/en"},
  ]
/%}

Supported options:

  • dataPath: String or array of strings denoting the path under src/data where rows come from. For example, tags/h1 will load src/data/tags/h1.yml. You can extend the data path with additional keys to select from nested objects within a YAML file. If the selected data is an object, its entries will become rows as {key, value, dataPathIndex}. If multiple data paths are provided the data from each will be merged into a single row array. The index of the data path will be included in each row as dataPathIndex, which can be useful in combination with indexedValue columns.
  • id: Determines the prefix for HTML ID attributes given to each row. Choose something unique.
  • rowSortKey: A slash-separated path into each row which selects a value to sort rows by.
  • rowSortReverse: If true, reverses the sort order.
  • rowFilterKey: A slash-separated path into each row which selects values to filter by. If the path is to an object, truthy keys will be selected. If an array, truthy values will be selected as strings. Otherwise the value is stringified.
  • rowFilterExpr: The seleted row filter values will be filtered by this logical expression. See voll.
  • linkCol: If true, adds a column with linkable anchors for easy sharing. If this is a numeric index then then cells of the indexed column will become the clickable links.
  • linkSlugKey: A slash-separated path into each row which selects a value to append to id for anchor links. Defaults to the value of linkCol if numeric.
  • noClear: If true, allows the table to fit beside figures or the metabox without having to clear floating elements. Not suitable for long or wide tables.
  • wrapPre: If true, forces cells with code content to wrap rather than stretch the table.
  • columns: An array of objects describing each column:
    • name: Name shown in table header.
    • key: A slash-separated path into each row which selects the column's value.
    • format: How the cell value should be rendered. Can be text (the default), indexedValue (see below), code, codeblock-<lang>, anchor (smart link using destination page title), or pageLinkRaw (smart link using the raw value as the title).
    • values: An array or object of plaintext strings used with the indexedValue format. The value at key will be used to index into this array/object to provide the actual cell value.
    • style: CSS style to inject into the header cell.

Struct tables

Another type of data-driven table is structTable. It is intended for documenting data structures like savegame files and tags and supports byte offset counting, nested data structures, and field comments.

{% structTable
  entryModule="h1/files/savegame.bin"
  entryType="Savegame"
  showOffsets=true
  id="savegame"
/%}

Available options:

  • entryModule: The structs module which contains the type you want to display. The above example would resolve to the module src/data/structs/h1/files/savegame.bin.yml.
  • entryType: The root type from the entry module to display.
  • id: A prefix to use for HTML ID attributes given to all rows in the table for easy sharing.
  • noEmbed: An array of type names which should be skipped rather than embedded in the resulting table.
  • showOffsets: If true, adds a column showing the byte offsets of fields.
  • noRootExtend: If true, does include fields from any parent types that the entry type extends.
  • skipPadding: If true, any padding (empty) fields are hidden.
  • simpleTypes: If true, simplifies complex type names.

For tag struct tables you can use the shorthand {% tagStruct "h1/sky" /%} which configures all the options for you based on information in src/data/tags/<game>.yml.

See the appendix for help creating new struct definitions.

You can easily add a table of related script functions and globals like this:

{% relatedHsc game="h1" tagFilter="lightmaps" /%}

This takes data from src/data/hsc/<game>/*.yml and filters by each row's tags list if tagFilter is present. You can optionally provide only="globals" or only="functions" to limit the selection.

Misc. features

These features are only used for a few specific cases:

  • Create a block of color with a hex code using {% color "#FF56B9" /%}. Used here.
  • The scale page uses {% unitConverter /%}.
  • This page uses {% thanksIndex /%} to list all contributors from all pages.
  • You can comment out some markdown by surrounding it with {% comment %}..{% /comment %}.
  • A list of child pages can be created with {% childList /%}.

Appendix

Struct definitions

Struct definitions used by structTable live under module files in src/data/structs. These YAML files have the following structure:

# If types in this module rely on types in another, they must be imported
imports:
  <module>:
    - <Type>
# A map of all types by name to their definition
typeDefs:
  # example bitfield
  ShaderEnvironmentFlags:
    class: bitfield
    size: 2 # size in bytes
    comments:
      en: A markdown description of this type.
    bits:
      - name: alpha_tested
      - name: decal
      - name: two_sided
      - name: bump_map_is_specular_mask
  # example enum
  DifficultyOpts:
    class: enum
    size: 1
    comments:
      en: A markdown description of this type.
    options:
      - name: easy
        value: 0x00 # optional, defaults to index in option list
      - name: normal
      - name: hard
      - name: legendary
        comments:
          en: Also called impossible.
  # example struct
  Savegame:
    class: struct
    endianness: little # optional
    assertSize: 0x2000 # checks that all field sizes add up to this
    comments:
      en: A markdown description of this type.
    fields:
      # an intrinsic resizable padding type
      - type: pad
        size: 0x1E2
      - name: last difficulty
        type: DifficultyOpts
      - type: pad
        size: 5
      - name: last played scenario
        type: char
        count: 32 # indicates char[32]
        comments:
          en: >
            An ASCII-encoded [scenario][] tag path, null-terminated and 32
            characters max. An example value is `levels\b30\b30` for The Silent
            Cartographer.

Intrinsic types do not need to be defined:

  • Fixed size types: byte, bool, char, uint8, int8, uint16, int16, int32, uint32, int64, uint64, float, double.
  • Pointer types (require type argument): ptr32, ptr64.
  • Variable-size types (require size argument): pad, UTF-8, UTF-16.

Contributor list

The primary maintainer of c20 is Conscars, with technical and content contributions from num0005, General-101, Krevil, gbMichelle, Mimickal (Donut), and more.

We extend a thanks to the developers involved in Halo over the years, including Bungie, Gearbox Software, Hired Gun, Certain Affinity, Saber Interactive, and 343 Industries.

Most of the community's knowledge comes from the hard work of modders who have researched, reverse engineered, experimented, and documented over the years. Our current understanding is built on a mountain of documents, forum posts, tutorials, and conversations. While we can't include everyone, we would at least like to thank the following people for their roles either writing or being a source for c20:

  • 1SDAN
  • Abstract Ingenuity
  • AKlinth
  • aLTis
  • Chris Butcher
  • Connor Dawn
  • Conscars
  • Crisp
  • crow
  • Dennis
  • Elefant
  • EmmanuelCD
  • FaberTheCatgirl
  • FD
  • Firescythe
  • Fubih
  • GAIGHER
  • Galap
  • gbMichelle
  • General_101
  • Giraffe
  • gruntfromhalo
  • Halo PC End User Editing Kit Development Team
  • Hari
  • Hasuku
  • hellux
  • Ifafudafi
  • InfernoPlus
  • J-S
  • Jakey
  • Jason Zimmer
  • Jesse
  • justinpyne
  • Kashiiera
  • Kavawuvi
  • King Feraligatr
  • kirby_422
  • Knight Artorias
  • Kolshgamer
  • Kornman
  • kornman00
  • Krevil
  • lag supreme
  • Lavadeeto
  • Ludus
  • Masterz1337
  • MattDratt
  • MercyMoon
  • Mimickal
  • miriem
  • Miris
  • Modders of Halo Xfire Clan
  • Mortis
  • MosesOfEgypt
  • MrChromed
  • Neo
  • nToss
  • num0005
  • ODX
  • PepperMan
  • Pyrosocial
  • R93_Sniper
  • Rataz
  • Real Fake Doors
  • Reindeer
  • Rosy
  • s3anyboy
  • Satania
  • Siliconmaster
  • Sparky
  • Spiral
  • stunt_man
  • t3h lag
  • t3h m00kz
  • The Cereal Killer
  • TheChunkierBean
  • TheGhost
  • TheGrimDealer
  • Vaporeon
  • Vennobennu
  • Vuthakral
  • WaeV
  • XLNC
  • xScruffyDaSasquatchx
  • zatarita
  • Zeddikins