Hermit’s Heresy: DQB2 Power Tools
Unauthorized utilities for scheming up scenes. Move mountains, carve canyons, or summon superstructures!
(Power Tools for Dragon Quest Builders 2)
1 Disclaimers
Not authorized by Square Enix or Valve (Steam). Use at your own risk.
Be sure to inspect your results after modifying a stage. In particular, make sure you can sail away from and return to the island you just modified.
This tool and this documentation assumes that you are using Steam. It might work with Switch also, but I have never tested this.
2 Avoiding Catastrophic Data Loss
'B00 – Save Slot 1
'B01 – Save Slot 2
'B02 – Save Slot 3
It is your responsibility to understand how to back up and restore this data. It is your responsibility to make frequent backups of any important work.
Start and then exit DQB2. Leave Steam running.
Using Windows Explorer, copy one of the directories (e.g. B00) to create a backup.
Start DQB2, load that slot, place a single test block, and save your game.
Exit DQB2.
Confirm that the Modified Date has changed on one of your STGDAT files.
Using Windows Explorer, restore the files from the backup you just created.
Start DQB2, load that slot, and confirm that the test block is gone.
Either Google Drive or OneDrive (or maybe both?) has reported issues where an extremely small percentage of files were corrupted or lost. For this reason, I copy my backups to 2 different cloud storage providers.
2.1 Beware Steam Autocloud
3 Getting Started
3.1 Installation
In its current form, Hermit’s Heresy requires you to understand a little bit about programming using Racket. If you’ve never used Racket before, you should work through the Quick Introduction to Racket up to at least section 4 (Definitions). Don’t worry if you don’t understand everything; if you are following a tutorial or an example you won’t need deep programming knowledge. You just need to be familiar with how DrRacket works.
raco pkg install hermits-heresy
3.2 Configuring Writable Slots
An ongoing project is one in which you maintain and evolve your Hermit’s Heresy project (scripts and images) alongside the save file that you play conventionally, possibly for months or even years. You can read more about this workflow here.
A simpler workflow is a one-off script, in which you just run the script once and then play conventionally going forward, allowing you to discard the script as soon as you are satisfied with the results.
3.2.1 For Ongoing Projects
Hermit’s Heresy protects you from accidentally overwriting your hard work. It will never write to a save slot unless you have configured that directory as writable. Personally, I use Save Slot 1 (B00) as my ephemeral save slot, and I always assume that I might intentionally or accidentally delete or overwrite it at any time. Any real, long-term work I am doing belongs in slots 2 or 3.
{"writable": true} |
In general, the file must be named hermits-heresy.config.<<DIR>>.json. Including the directory name in this filename protects against mistakes. For example, if you accidentally copy your B00 config file into the B01 directory, you will not accidentally make B01 writable because the B00 config file will be ignored.
3.2.2 For One-Off Scripts
manually copy your STGDAT file into this directory,
pass its full path into load-stage, and
copy the updated STGDAT file back to a real slot when you are done.
For example, you could create a directory named oneoff with a corresponding config file hermits-heresy.config.oneoff.json. The config file behaves the same way as in For Ongoing Projects.
3.3 Tutorials and Examples
Congratulations on making it this far! Now go to DQB Forever for tutorials/examples that you can copy and modify to suit your needs.
4 Acknowledgments
Thanks to the team who made DQB2 so excellent.
Thanks to all contributors who made Racket so excellent.
Thanks to turtle-insect, who paved the way in save-file-editing.
Thanks to Aura and Sapphire645 for contributions to Hermit’s Heresy.
5 Reference
(require hermits-heresy) | package: hermits-heresy |
5.1 Loading and Saving
parameter
(save-dir) → (or/c #f path-string?)
(save-dir dir) → void? dir : (or/c #f path-string?)
(save-dir "C:/Users/kramer/Documents/My Games/DRAGON QUEST BUILDERS II/Steam/76561198073553084/SD/")
procedure
(load-stage kind slot) → stage?
kind :
(or/c 'IoA 'Furrowfield 'Khrumbul-Dun 'Moonbrooke 'Malhalla 'Anglers-Isle 'Skelkatraz 'BT1 'BT2 'BT3) slot : (or/c 'B00 'B01 'B02 path-string?)
The kinds 'BT1 'BT2 'BT3 refer to Buildertopias 1, 2, and 3.
'B00 – Save Slot 1, relative to save-dir
'B01 – Save Slot 2, relative to save-dir
'B02 – Save Slot 3, relative to save-dir
A path-string? – The full path to any STGDAT file, ignores save-dir
procedure
(save-stage! stage) → any/c
stage : stage?
Will produce an error if the destination directory has not been made writable.
procedure
(copy-all-save-files! #:from from-slot #:to to-slot) → any/c from-slot : (or/c 'B00 'B01 'B02) to-slot : (or/c 'B00 'B01 'B02)
Will produce an error if the destination directory has not been made writable.
5.2 Image Utilities
procedure
(get-template-image id) → pict?
id : symbol?
'IoA-background 'IoA-bedrock-mask 'IoA-chunk-mask 'Furrowfield-background 'Furrowfield-bedrock-mask 'Furrowfield-chunk-mask 'Khrumbul-Dun-background 'Khrumbul-Dun-bedrock-mask 'Khrumbul-Dun-chunk-mask 'Moonbrooke-background 'Moonbrooke-bedrock-mask 'Moonbrooke-chunk-mask 'Malhalla-background 'Malhalla-chunk-mask 'Anglers-Isle-background 'Anglers-Isle-bedrock-mask 'Anglers-Isle-chunk-mask 'Skelkatraz-background 'Skelkatraz-bedrock-mask 'Skelkatraz-chunk-mask
> (require pict hermits-heresy) > (scale (get-template-image 'IoA-background) 0.5)
The masks can be used to guide you while you work in your image editor. The '*-bedrock-mask images black out all pixels which lack "bedrock", the indestructible block that forms the floor of the island. While playing the game normally, it is impossible to place, destroy, or even trowel blocks that are outside of the bedrock area. But using Hermit’s Heresy, it is possible to place blocks outside of the bedrock area. If you do this, you won’t be able to manipulate those blocks while playing normally.
If you want to avoid accidentally placing any blocks outside of the bedrock area, consider passing the bedrock-mask image into bitmap->area and then passing the resulting area into protect-area!.
The '*-chunk-mask images black out all pixels which cannot hold any blocks under any circumstances, because there is no place in the save file that maps to those coordinates. Using these images with protect-area! would be redundant.
> (require pict hermits-heresy)
> (scale (cc-superimpose (get-template-image 'IoA-background) (get-template-image 'IoA-bedrock-mask)) 0.5)
You might also consider using this Minimap Exporter by Sapphire645 instead of these prebuilt '*-background images.
procedure
(save-template-image id) → any/c
id : symbol?
procedure
(bitmap->hill path [ #:adjust-y adjust-y #:semitransparent-handling semitransparent-handling]) → hill? path : path-string? adjust-y : fixnum? = 0 semitransparent-handling : (or/c 'adjust 'ignore) = 'adjust
A totally black pixel (R=G=B=0) indicates max height.
A pixel having R=G=B=16 indicates 16/2=8 blocks short of max height.
A pixel having R=G=B=40 indicates 40/2=20 blocks short of max height.
This version discards the remainder of the division by 2. A future version may decide to respect it via the flat chisel.
The adjust-y can be used to raise or lower the entire hill. Positive values raise; negative values lower.
For the most accurate results, you should avoid semitransparent pixels in your hill. (In other words, alpha should always be either 0 or 255, no in-between values.) But in practice, it’s very easy to accidentally introduce semitransparency. For this reason, semitransparent-handling defaults to 'adjust which reduces the elevation based on how transparent the pixel is. If you really know what you are doing, you might want to disable this behavior by using 'ignore.
procedure
(bitmap->area path) → area?
path : path-string?
5.3 Block Manipulation
An item is a special kind of block, such as a tree or a door or a fountain. A good rule of thumb is that anything capable of facing north/east/south/west is an item. By contrast, simple blocks like Sand or Chert cannot face a cardinal direction; rotating them with your gloves has no effect.
Unfortunately, this version of Hermit’s Heresy is not capable of manipulating items. For example, put-hill! cannot overwrite items, meaning you may end up with items hidden inside your hill unless you manually destroy them first. I hope to solve this problem soon.
Note to Self: Assuming that someday I will learn how to safely overwrite items, what is the best design? Probably a parameter that would apply to all block manipulation procs. Values could be 'no 'yes 'yes-even-indestructible.
> (block 'Chunky-Chert) 153
> (block 'Chert) Choosing (block 'Chert) to mean 149, but other values were possible: (149 165 174)
149
procedure
(find-block-name name) → any/c
name : string?
> (find-block-name "snow")
Exact Matches:
(block 'Seeded-Snow)
(block 'Snow)
(block 'Snow-Cover)
> (find-block-name "posion")
Possible Matches:
(block 'Poisonous-Peat)
(block 'Poison-shallow-block)
(block 'Poison-surface-block)
(block 'Poison-full-block)
If you still can’t find what you want, you can check the complete list in the source code here.
procedure
(protect-area! stage area) → area?
stage : stage? area : area?
This function is supposed to perform a union with any previously protected area, but right now it will throw a "not implemented" error if the previously protected area is not empty. Sorry about that, let me know if you need it. (Consider performing the union using your image editing software if possible.)
This function returns the previously protected area intended for use with a future hypothetical function like revert-protected-area!.
procedure
(build-mottler arg ...) → (-> fixnum?)
arg :
(or/c symbol? fixnum? (list/c (or/c symbol? fixnum?) exact-positive-integer?))
> (define my-grassy-mottler (build-mottler '[Mossy-Earth 3] ; weight 3 'Grassy-Earth ; weight 1 by default '[Earth 1] ; weight 1 '[Stony-Soil 2]))
Choosing (block 'Mossy-Earth) to mean 7, but other values were possible: (7 415)
Choosing (block 'Earth) to mean 2, but other values were possible: (2 414)
; c'mon RNG, please don't embarrass me here:
> (for/list ([i (in-range 25)]) (my-grassy-mottler)) '(7 3 141 2 7 3 141 141 7 7 141 7 2 141 141 7 7 141 141 141 7 7 2 141 3)
procedure
(selection stage area transforms) → selection?
stage : stage? area : area?
transforms :
(listof (or/c (list/c 'translate fixnum? fixnum?) (list/c 'translate-to fixnum? fixnum?) 'mirror-x 'mirror-z (list/c 'rotate integer?) (list/c 'adjust-y fixnum?)))
(list 'translate dx dz) – Shifts the X and Z coordinates by the given dx and dz values.
(list 'translate-to X Z) – Sets the X and Z coordinates of the northwest/top-left corner of the bounding rectangle.
'mirror-x – Reflects such that east and west are swapped. The bounding rectangle remains in the same place.
'mirror-z – Reflects such that north and south are swapped. The bounding rectangle remains in the same place.
(list 'rotate N) – Rotates the selection by N degrees. N must be a multiple of 90. The post-rotation bounding rectangle will be located such that the XZ coordinate of the northwest/top-left corner matches the pre-rotation bounding rectangle. For example, if the rectangle goes from (10,10) to (100,20) a 90-degree rotation would produce a rectangle that goes from (10,10) to (20,100).
(list 'adjust-y dy) – Shifts the Y coordinate up or down by the given dy value. Positive values raise the selection; negative values lower it.
5.4 Traversal
(traverse stage (traversal (cond [(block-matches? 'Snow) (set-block! 'Mossy-Earth)] [(block-matches? 'Chalk 'Chunky-Chalk) (set-block! 'Stony-Soil)] [(block-matches? 'Marble) (set-block! 'Copper-Vein)])))
syntax
(traversal expr ...)
(traversal (when (block-matches? 'Snow) (set-block! 'Ice)))
syntax
(block-matches? block-expr ...)
Returns true if the current block (ignoring its chisel status) matches any of the given block-exprs. Each block-expr should be either a literal symbol (such as 'Chert) or a literal number (such as 414). But if you know what you are doing, it can also be any expression that produces a fixnum?.
> (block 'Earth) Choosing (block 'Earth) to mean 2, but other values were possible: (2 414)
2
syntax
(set-block! block-expr)
Sets the current block to the given value (chisel status is unchanged). When block-expr is a literal symbol, it is equivalent to (block block-expr). Otherwise block-expr must produce a fixnum?.
Simple blocks are overwritten, but items are left intact.
syntax
(with-selection [block-id selection-expr] body ...)
Executes the body when the given selection produces a simple block (including vacancy) at the current coordinate. Within the body, the block-id will contain that block’s value.
In other words, the body will not be executed if the current coordinate lies outside of the selection or if the selection produces an item.
syntax
(in-hill? hill)
Returns true if the current xzy coordinate is inside the given hill, false otherwise. Hills can be created using bitmap->hill.