Skip to content

Instantly share code, notes, and snippets.

@PG85
Last active July 27, 2020 21:02
Show Gist options
  • Save PG85/fc9e18f190cc3f078a5ea4298903dd65 to your computer and use it in GitHub Desktop.
Save PG85/fc9e18f190cc3f078a5ea4298903dd65 to your computer and use it in GitHub Desktop.
BO4 branching mechanics
BO4 branching mechanics
BO4 branching structures generate from a single origin BO4 and expand outwards in pulses, each pulse extending branches by one segment, until the maximum branch depth is reached, there is no more space, or there are no more branches to spawn.
There are 2 types of branches; required and optional branches.
- Required branches are used to chain together BO4's that must all be spawned at once, or none at all, for instance multiple 16x16 BO4's that form one room or building. Required branches are spawned in chains, so the entire room spawns, or none of it.
- Optional branches are used to create parts of a structure that may spawn if there is enough space, but won't cause the entire structure to fail to spawn if they can't be placed, for instance a tunnel of varying length with randomised corners and rooms. A maximum branch depth can be defined in any branch to limit the maximum number of optional branches it can have as children, so as to limit the size of the structure.
To create variations and randomised layouts, multiple branches can be grouped together into a branch group. OTG tries to spawn the branches in the order listed. When an optional branch fails to spawn, the next branch in the group is tried, until a branch in the group spawns succesfully, there are no more branches in the group, or a required branch in the group fails to spawn *1. When a required branch fails to spawn (whether in or outside of a branch group), its parent BO4 cancels spawning and is rolled back (there was not enough space for its required branches). If the rolled back parent BO4 was itself an optional branch and part of a branch group, the next branch in that group tries to spawn. If the parent was a required branch, another rollback is done. These rollbacks can go all the way back to the origin BO4, which cancels the structure entirely (there was not enough space to spawn all required branches).
Branches are added in pulses, starting from the origin BO4 and expanding outwards. Every pulse adds a new segment of branches at the ends of the currently spawned structure. Pulses alternate between spawning required and optional branches. Required branches are spawned first, while optional branches (as well as branch groups containing optional branches) are ignored. Since required branches always spawn in a chain, one pulse is enough to spawn all required branches at the ends of all branches in the structure, and leave only the optional branches. The second pulse spawns only optional branches, these are not chained so only spawn 1 layer deep. The next pulse spawns all required branches again, rinse and repeat until there are no more branches to spawn (success), the maximum branch depth is reached (success) or the structure is rolled back entirely (fail).
Branch generation is split up into 2 phases, foundations and interiors. Phase 1 is used to spawn the layout or foundations of a branching structure, phase 2 is used to spawn randomised interiors and adapter pieces on top of the layout/foundations. Since the major difference between the two phases is collision detection, canOverride:true optional branches spawn in the second phase (*will create a new branch type for this in the future, instead of using canoverride:true+optional).
Phase 1 layout/foundations uses collision detection for all branches except canOverride:true+optional, combined with rollbacks and branch groups to generate a valid branching pattern within an available space, while making sure none of the branches overlap. This ensures that a structure will never (unintentionally) have unreachable rooms or blocked passages, and that there is always a path back to the origin BO4.
Phase 2 interiors/adapter pieces (canOverride: true+optional branches) does not use collision detection for all branches, since its branches are always placed on top of the existing foundation/layout BO4's. The CannotBeInside and MustBeInside settings can be used to make interior / adapter piece / marker BO4's that spawn only on top of specific other BO4's. The ReplaceBO4 setting can be used to override targetted BO4's blocks entirely.
During branch generation, phase 1 (foundations/layout) handles all branches except canOverride:true+optional branches, while all canOverride:true+optional branches are ignored. When phase 1 is finished, phase 2 (interiors/adapter pieces) traverses the structure and starts spawning all canOverride:true+optional branches. Unlike the alternating required/optional branch pulses, phases currently cannot be repeated.
* Tip: When creating a branch group with optional branches and required branches, you only really need 1 required branch, make sure to list it last.
* Tip: Use Block("BLANK") to create marker/detector BO4's to mark/detect connection points using CannotBeInside/MustBeInside. When using marker BO4's, make them required branches to ensure they are spawned before any branches that try to detect them.
* Tip: MustBeInside supports AND/OR via space/comma, fe "MustBeInside: A1 A2, A3"
* Tip: Phase 2 will never cause a rollback of the entire structure, since interiors and adapter pieces are always optional canoverride:true branches that are guaranteed to be able to spawn.
* Caution: Don't put optional canOverride:true (phase 2) branches in a branch group with other types (phase 1) of branches.
* Caution: Don't use canOverride:false (phase 1) BO4's as child branches of optional canOverride:true (phase 2) branches, they should never spawn.
* CanOverride:true+optional branches should be redesigned in the future and turned into a new "skip a phase" branch type. CanOverride should only affect collision detection, not phases. Required and optional branches should be turned into optional/required branch groups. Things are quite hard to use and understand atm.
*1: There is currently a bug where all optional branches in a branch group try to spawn, even when one spawns successfully. Use mustbeinside or canoverrride:false as a workaround to prevent this.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment