Overview
This is a collection of GDScript scripts for automatically generating dungeons in the Godot editor/runtime.
Features
-
Configurable dungeon size, number of rooms, and minimum/maximum width/height for rooms and corridors.
-
Layout generation based on a seed.
-
Pre-create exterior/interior layout data and decorate/place objects using basic dungeon tiling patterns.
-
Visually configure minimum/maximum occurrence counts, frequency, and adjacency directions for each pattern.
-
Configure cell occupation settings, pattern coverage, and overwrite permissions per layer/pattern.
By setting up multiple layers, you can create multi-layered decorations.
Usage Instructions
Although there are many settings, you will understand it smoothly if you download the sample project first.
1. Download the Sample Project
-
Download the necessary files from the repository.
https://github.com/ggg-shirokishi/procedural-layout-tools -
Place the
addonsandscriptsdirectories directly under the project root.
2. Add the Layout Generator Node to the Scene
-
Add a
Node2Dto any scene and attach the layout scriptroom_rayout_generatorfrom thescriptsdirectory. -
Set various parameters of
room_rayout_generatorin the Inspector.- Map overall size:
width,height - Number of rooms:
room_count - Room size range:
room_w_min / room_w_max,room_h_min / room_h_max - Corridor width parameters:
corridor_widthcorridor_width_min / maxcorridor_width_randomize_each_corridor- If
corridor_width_randomize_each_corridoris enabled, the corridor width will be randomized betweencorridor_width_minandcorridor_width_max.
- If
- Allow diagonal connections:
connectivity_allow_diagonal- If
connectivity_allow_diagonalis enabled, rooms will also connect diagonally.
- If
- Seed:
seed
- Map overall size:
-
Enabling
editor_auto_generate/editor_live_updateallows changes to various parameters to be reflected immediately in the editor.
3. Configure TileMapLayer and TerrainAtlasPatternPlacer
-
Add a
TileMapLayerand aNode2D(for executing the placement script) to the scene, and attachterrain_atlas_pattern_placerfrom thescriptsdirectory to theNode2D.- The target tilemap layer and the placement
Node2Dhave a 1:1 relationship, and you can add multiple pairs as needed. - There is no lower or upper limit to the number of layers, but in most cases, at least two sets of
TileMapLayer+terrain_atlas_pattern_placerare required: one for exteriors (Terrain tile placement) and one for interiors.
(In the sample project, these areTerrainTileLayer / TerrainTilePlacerandInteriorBaseLayer / InteriorBasePlacer)
- The target tilemap layer and the placement
-
Set a
TileSetfor terrains on theTileMapLayerand register Terrains/Patterns.- This system reads Terrains/patterns from the TileSet of the tilemap layer.
Please set up the TileSet on the tilemap layer side. - Refer to the following for an explanation of Terrains.
https://docs.godotengine.org/en/4.x/tutorials/2d/using_tilesets.html#creating-terrain-sets-autotiling
- This system reads Terrains/patterns from the TileSet of the tilemap layer.
-
Set the following items on the node with the
TerrainAtlasPatternPlacerscript attached.
Main Settings for TerrainAtlasPatternPlacer
layout_node: Path to theroom_rayout_generatornode added in the previous section.target_layer_path: Path to theTileMapLayerthis Placer operates on.target_kind: Cell type handled by this layer (WALLS/FLOORS)
※WALLS: Exterior / impassable terrain (Terrain is usually placed here)
FLOORS: Interiorplacement_type: Whether to paint with Terrain or place with Pattern (TERRAIN/PATTERN).clear_before_place: Whether to clear the layer before each re-placement.register_used_cells_to_layout: Whether to register used cells to the layout’sused_cells_mask.
※Enabling this option prevents overlapping placement by layers/patterns with largerexecution_orderwhenonly_place_on_unoccupiedis also On.only_place_on_unoccupied: Restrict placement to unoccupied cells only.auto_update_on_layout_signal: Whether to automatically re-place upon receiving a layout update signal.pattern_coverage_ratio: Density of pattern placement over the entire area.execution_order: Processing order when there are multipleTerrainAtlasPatternPlacernodes.
Smaller numbers are processed first. It is used in combination withonly_place_on_unoccupiedandregister_used_cells_to_layoutto control overlapping placement.
4. Detailed Pattern Settings (Editor Extension Inspector)
When you link a tilemap layer to TerrainAtlasPatternPlacer, a panel listing patterns from the TileSet is added to the Inspector.
You can set the following items for each pattern.
-
pattern_required_cells: Required adjacent cells
(Cells within the pattern that “must touch the edge”)
Set in combination withpattern_adjacent_dirsdescribed below.
※You can set this by checking the cells below the thumbnail. -
pattern_weights: Appearance frequency. -
pattern_min_counts,pattern_max_counts: Minimum/maximum occurrence counts (unlimited by default). -
pattern_adjacent_dirs: Adjacency directions
(Which of the up, down, left, right directions require adjacent cells)Example:
Ifpattern_required_cellsis set to the top 2 cells andpattern_adjacent_dirsis set to “Up”, it will only be placed on terrains where the top 2 cells are guaranteed to be adjacent. -
pattern_register_used_cells_override: Cell occupation registration override
You can set whether to occupy cells on a per-pattern basis.- Inherit global settings (same as
TerrainAtlasPatternPlacersettings) - Force On
- Force Off
- Inherit global settings (same as
-
pattern_only_place_on_unoccupied_override: Place only on unoccupied cells override- Inherit global settings (same as
TerrainAtlasPatternPlacersettings) - Force On
- Force Off
- Inherit global settings (same as
After setting these items roughly, press run_on_editor_button to generate the layout.
Runtime Regeneration Settings (Optional)
- If you want to regenerate the dungeon via key input during execution, attach a script equivalent to
RuntimeLayoutKeyboardControllerto aNode.
Main Settings
layout_node_path: Layout generator node.input_action_name: InputMap action name used for regeneration (e.g.,regen_dungeon).randomize_layout_size/randomize_room_count/randomize_room_size/randomize_corridor_width: Whether to randomize various parameters for each execution.move_target_node_path: Node to move after regeneration (e.g., player).move_target_tilemap_layer_path:TileMapLayerused for floor collision detection.move_after_generation: Whether to move the target to an empty cell after regeneration.zoom_camera_path: Camera whoselimityou want to adjust according to the layout.
After starting the game, pressing the specified action key will
- Change the seed
- Regenerate the layout
- Automatically move the player and adjust the camera as needed
Multi-Layer Decoration with Multiple Layers
By sharing the same layout node with multiple TerrainAtlasPatternPlacer nodes and controlling the processing order using execution_order, you can achieve multi-layered decorations such as the following:
- Layer 1: Exterior (base tiles for walls/floors)
- Layer 2: Interior (patterns for pillars, windows, beams, etc.)
- Layer 3: Objects (furniture, debris, decorations)
By toggling
register_used_cells_to_layoutonly_place_on_unoccupied
for each layer, or using pattern-side overrides, you can control
- “Stack on top without breaking the base”
- “Overwrite only specific parts”
Tips and Notes
-
Tilemap layers and TileSet sizes must be unified.
-
If you want to place game objects randomly:
Add a Scene Collection to the TileSet, place objects on the tilemap, and then incorporate them into patterns for placement.
(You can check the relevant settings inGameObjectLayerandInteriorBaseLayerwithin the sample project.)
Details of Various Scripts
room_rayout_generator.gd
Usage Overview
This is a layout-specific generator to be attached to a Node2D.
It does not write to any TileMap.
The generation results are held as properties.
grid:int2D array
CELL_WALL = 0,CELL_FLOOR = 1,CELL_DOOR = 2rooms:Array[Rect2i](room rectangles)centers:Array[Vector2i](room centers)room_id_grid: Room ID for each cell (identification per room)corridor_id_grid: Corridor ID for each cellused_cells_mask: Mask of “used cells” registered from outside (Placer)
External API for Layout Generation
generate_now()
Performs synchronous layout generation and emitsgeneration_finished(success: bool)upon completion.generate_async() -> Signal
Performs asynchronous generation. It checks for busy status via_is_generating, executes processing withawait, and finally returns the result via the samegeneration_finishedsignal.
Signals
layout_updated(grid, rooms, centers)
Fires when the grid and room information are updated.generation_finished(success)
Notifies whether the overall generation was successful.
TerrainAtlasPatternPlacer, etc., reference the generated grid and share “empty/used cells on the layout” via
register_used_cells() / get_free_cells().
In the editor, editor_auto_generate / editor_live_update / editor_generate_button allow you to control
- Automatic generation on scene load
- Automatic regeneration when parameters are changed in the Inspector
Main Variables and Properties
Output and State
signal layout_updated(grid: Array, rooms: Array, centers: Array)
A signal that notifies the outside of the current layout information.signal generation_finished(success: bool)
Notifies success/failure when generation is complete.const CELL_WALL: int = 0
Value representing “wall” in the grid.const CELL_FLOOR: int = 1
Value representing “floor” in the grid.const CELL_DOOR: int = 2
Value representing “door candidate cell” in the grid.var rng: RandomNumberGenerator
Instance for random number generation. Seeded fromseedand_seed_internal.var _is_generating: bool
Flag indicating whether asynchronous generation is in progress. Becomestrueduringgenerate_asyncandfalseupon completion.var grid: Array
2D array ofgrid[y][x]. Values are one of theCELL_*constants.var rooms: Array[Rect2i]
Array of room rectangles that were successfully generated.var centers: Array[Vector2i]
Array of center cell coordinates for each room.var room_id_grid: Array
room_id_grid[y][x]holds which room ID the cell belongs to (e.g.,-1if not part of a room).var corridor_id_grid: Array
corridor_id_grid[y][x]holds the corridor ID (e.g.,-1if not a corridor).var _next_corridor_id: int
Internal counter used for assigning corridor IDs.var used_cells_mask: Array
2D array ofused_cells_mask[y][x] = boolrepresenting “whether the cell is a used cell registered from the TilePlacer side.”
Set totrueviaregister_used_cells(cells: Array[Vector2i]).
get_free_cells(kind: int)returns only cells that arefalseas “empty cells.”
Map Size and Room Parameters
@export var width: int = 80
Layout width (number of cells)._set_width()prevents values less than 8, and regeneration occurs on change in the editor.@export var height: int = 60
Layout height (number of cells)._set_height()ensures it does not become less than 8.@export var cell_padding: int = 1
Margin from the outer perimeter. Rooms will not be placed within this number of cells from the outer frame.@export var room_count: int = 18
Target number of rooms to attempt generation. The actual number generated may be lower due to collisions, etc.@export var room_w_min: int = 5/room_w_max: int = 14
Minimum/maximum width (number of cells) for rooms. The setter ensuresroom_w_min <= room_w_max.@export var room_h_min: int = 4/room_h_max: int = 12
Minimum/maximum height (number of cells) for rooms. Consistency is maintained via the setter.
Corridor Parameters
@export var corridor_width: int = 1
Base value for corridor width. This fixed width is used if random width is not used.@export var corridor_width_min: int = 0
Minimum value for random corridor width.@export var corridor_width_max: int = 1
Maximum value for random corridor width.@export var corridor_width_randomize_each_corridor: bool = true
Whentrue, the width is randomly determined individually for each corridor (segment) from the rangecorridor_width_mintocorridor_width_max.@export var corridor_use_diagonal_path: bool = false
Whentrue, diagonal paths are allowed in the digging of corridors connecting room centers (a zigzag path moving diagonally).@export var connectivity_allow_diagonal: bool = false
Whentrue, diagonal (8-directional) connections are considered “connected” during connectivity checks (verifying all rooms are connected).
Randomness, Retry, and Asynchronous
var _seed_internal: int = 123456
Internal seed actually passed to the RNG.@export var seed: int = 123456
Public seed for layout generation. The setter updates_seed_internal, and ifeditor_live_updateistruein the editor, it regenerates immediately.@export var max_retry: int = 25
Maximum number of retries when room generation or corridor connection fails, changing the seed each time.@export var async_yield_rows: int = 6
Represents how many rows to split processing by during asynchronous generation usingawait process_frame, etc.
Larger values dig more at once but block the main thread for longer.
Logging and Editor
@export var log_enabled: bool = true
Whentrue, outputs logs via_log().@export var log_verbosity: int = 1
Log detail level. Higher values produce more detailed logs.@export var editor_auto_generate: bool = true
Flag for automatically generating the layout on scene load, etc.@export var editor_live_update: bool = true
Whether to automatically regenerate on the spot when parameters are changed in the editor.@export var editor_generate_button: bool = false
Flag for the “generate once” trigger in the Inspector.
_generate_editor_safe()is called only at the moment it is set totrue, and it is immediately reset tofalse.
terrain_atlas_pattern_placer.gd (TerrainAtlasPatternPlacer)
Usage Overview
This is a script for Node2D with class_name TerrainAtlasPatternPlacer.
Its role is
“To reference the grid of RoomLayoutGenerator and apply Terrain or Pattern to the specified TileMapLayer.”
Main prerequisites:
- Set
layout_nodeto reference a node that holdsgridand emitslayout_updated/generation_finished, such asroom_rayout_generator.gd. - Set
target_layer_pathto specify theTileMapLayerto apply to.
Target cell types:
- When
target_kind = WALLS, targetCELL_WALLcells. - When
target_kind = FLOORS, targetCELL_FLOOR + CELL_DOORcells.
Application method:
- When
placement_type = TERRAIN,
Paint Terrain usingset_cells_terrain_connect(). - When
placement_type = PATTERN,
Apply usingTileMapPatternfrom theTileSet.
Other behaviors:
- If
auto_update_on_layout_signalistrue, it automatically re-applies upon receivinglayout_updated/generation_finishedfrom thelayout_node. register_used_cells_to_layoutdetermines whether to register cells used during placement tolayout_node.register_used_cells(cells)and reflect them inused_cells_mask.only_place_on_unoccupiedallows restricting placement to “cells that are unoccupied on both the layout and the TileMapLayer.”
You can further override this behavior on a per-pattern basis usingpattern_*_override.- When multiple Placers share the same layout, you can control execution order using
execution_order(executed in ascending order of value).
Signals
signal placement_finished(success: bool)
Emitted when the placement process is complete.
Main Variables and Properties
Basic and Types
signal placement_finished(success: bool)
Notifies whether placement is complete.const CELL_WALL: int = 0/CELL_FLOOR: int = 1/CELL_DOOR: int = 2
Value definitions for the layout grid. Corresponds to theRoomLayoutGeneratorside.enum TargetKind { WALLS, FLOORS }
Specifies which cell type to target.enum PlacementType { TERRAIN, PATTERN }
Mode for Terrain painting or Pattern placement.
Layout Reference and Target Layer
@export var layout_node: NodePath
Path to the layout generation node (e.g.,RoomLayoutGenerator).
The setter_set_layout_nodecaches it internally in_layout_ref.var _layout_ref: Node
Actual reference to the layout node.var _grid: Array
Internal array for caching the currently used layoutgrid.@export var target_layer_path: NodePath
Path to theTileMapLayerto apply to.var _target_layer: TileMapLayer
Reference to the actual tile placement target layer.
Execution Control and Linking
@export var clear_before_place: bool = true
Whentrue, executesTileMapLayer.clear()before starting placement.@export var register_used_cells_to_layout: bool = true
Whentrue, passes the list of cells used during placement tolayout_node.register_used_cells(cells).@export var only_place_on_unoccupied: bool = false
Whentrue, targets only “cells that are unoccupied on the layout and empty on the TileMapLayer.”
Judgment is made by combining the layout’sget_free_cells(kind)with TileMapLayer cell checks.@export var run_on_editor_button: bool = false
Flag for the editor button trigger. The moment it is set totrue,_set_run_on_editor_button()is called, which- Executes
layout.generate_now() - Performs processing equivalent to
place_now()afterward
and resets the flag tofalseupon completion.
- Executes
@export var auto_update_on_layout_signal: bool = true
Whentrue, automatically links with thelayout_updated/generation_finishedsignals oflayout_node.@export var auto_update_in_editor_only: bool = true
Whentrue, updates automatically only during editor execution, not during game execution.@export var auto_update_debounce_frames: int = 1
How many frames to buffer for batch updates during automatic updates.
Even if0, it waits at least 1 frame.@export var log_enabled: bool = true
Whether to output logs.@export var log_verbosity: int = 1
Log detail level.@export var execution_order: int = 0
Specifies execution order among multiple Placers referencing the samelayout_node.
Executed in ascending order of value; if values are the same, sorted by ascendinginstance_id.var _pending_auto_update_local: boolvar _last_used_cells: Array[Vector2i]var _last_pattern_force_register: bool
Internal variables holding the pending state for batch updates and the most recent placement information.
Placement Mode and Terrain
@export var target_kind: TargetKind = TargetKind.WALLS
Specifies which cell type on the layout to target (wall or floor).@export var placement_type: PlacementType = PlacementType.TERRAIN
Selects between Terrain mode or Pattern mode.@export var terrain_set_index: int = 0
Index of the Terrain set to use.@export var terrain_index: int = 0
Index of the Terrain to use within the above set.
Pattern-Related Settings
@export var pattern_indices: PackedInt32Array = PackedInt32Array()
List of indices forTileMapPatternto use. If empty, all available patterns are targeted.@export var pattern_avoid_overlap: bool = true
Whentrue, places patterns so they do not overlap each other.@export var pattern_coverage_ratio: float = 0.1
Target percentage of the total target cells to be covered by patterns (0.0–1.0).@export var pattern_weights: Dictionary = {}
Appearance weight per pattern.- Key:
pattern_index - Value:
float(weight)
- Key:
@export var pattern_adjacent_dirs: Dictionary = {}
Adjacency direction bitmask per pattern.- Key:
pattern_index - Value:
intbitmask- Bit values:
1 = U(Up),2 = R,4 = D,8 = L
- Bit values:
- Key:
@export var pattern_required_cells: Dictionary = {}
“Required adjacent cells” definition per pattern.- Key:
pattern_index - Value:
Array[Vector2i](array of pattern internal cell coordinates)
- Key:
@export var pattern_min_counts: Dictionary = {}
Minimum placement count per pattern.@export var pattern_max_counts: Dictionary = {}
Maximum placement count per pattern.-1or unset means “no limit.”@export var pattern_register_used_cells_override: Dictionary = {}
Per-pattern override forregister_used_cells_to_layout.- Value:
0 = Inherit(inherit global settings) - Value:
1 = Force On(always register as used cell) - Value:
2 = Force Off(always exclude from used cell registration)
- Value:
@export var pattern_only_place_on_unoccupied_override: Dictionary = {}
Per-pattern override foronly_place_on_unoccupied.- Value:
0 = Inherit(inherit global settings) - Value:
1 = Force On(place this pattern only on “unoccupied cells”) - Value:
2 = Force Off(place this pattern even on used cells)
- Value:
terrain_pattern_placer_inspector.gd + plugin.gd / plugin.cfg
Usage Overview
Via plugin.cfg and plugin.gd,
it is registered as the “Terrain Pattern Tools” plugin in the Godot editor.
Contents of plugin.cfg:
name="Terrain Pattern Tools"description="Custom inspector for TerrainAtlasPatternPlacer (pattern weights / adjacency)."script="plugin.gd"
plugin.gd inherits EditorPlugin and in _enter_tree()
- Creates
terrain_pattern_placer_inspector.gdviaScript.new()and - Registers the inspector extension via
add_inspector_plugin(_insp).
In _exit_tree(), it does the opposite via remove_inspector_plugin(_insp).
When you enable this plugin from project settings,
selecting a node with TerrainAtlasPatternPlacer attached will
- Add a per-pattern settings UI in addition to the standard Inspector.
Content Editable in the Added UI
For each TileMapPattern:
- Thumbnail of the raw tile appearance
- Grid thumbnail showing cell placement within the pattern
- Appearance weight (
SpinBox) - Adjacency directions (
U/R/D/Lcheckboxes) - Required adjacent cells (buttons to toggle cells within the pattern by clicking)
- Minimum placement count (
Min) - Maximum placement count (
Max,-1for unlimited) - Per-pattern override for
register_used_cells_to_layout(Reg) - Per-pattern override for
only_place_on_unoccupied(Unocc)
All of these directly rewrite the following properties on TerrainAtlasPatternPlacer:
pattern_weightspattern_adjacent_dirspattern_required_cellspattern_min_countspattern_max_countspattern_register_used_cells_overridepattern_only_place_on_unoccupied_override
class PatternPreviewControl extends Control
A Control class dedicated to pattern preview rendering.
var tileset: TileSet
TileSetused for the preview.var pattern: TileMapPattern
Pattern to preview.var preview_size: Vector2
Display size inside the scroll view.
It generates a TileMapLayer internally, applies the pattern via set_cell(), etc., and displays the appearance as-is in _draw().
Dictionary Access Helpers
- Retrieves each
Dictionaryvia_get_dict_safe(placer, "pattern_weights"), etc., updates it during UI operations,
and writes it back likeplacer.set("pattern_weights", d).
runtime_rayout_controller.gd (RuntimeLayoutKeyboardController)
Usage Overview
A runtime controller to be attached to a Node.
Role:
- When the specified input action is pressed,
- Randomly update the layout node’s
seed, - (Optionally) randomize layout parameters (width/height/room count/room size/corridor width),
- Trigger
generate_now()on thelayout_nodeto regenerate the layout.
- Randomly update the layout node’s
- After regeneration, you can perform the following:
- Move the node specified by
move_target_node_pathto one of the “empty cells.” - Automatically adjust the
limit_*of the camera specified byzoom_camera_pathto match the layout’s outer frame.
- Move the node specified by
Prerequisites
- Set
layout_node_pathto theNode2Dwithroom_rayout_generator.gdattached.
Input Monitoring
- Monitors
Input.is_action_just_pressed(input_action_name)within_process(),
and calls_regenerate_with_random_seed()when the action is pressed.
Movement and Camera
- If
move_after_generationistrue, movement is performed via_move_target_to_free_cell()after layout updates. - If
zoom_camera_pathis set,limit_left/right/top/bottomare recalculated via_update_camera_limits().
Main Variables and Properties
Basic Settings
const CELL_WALL: int = 0/const CELL_FLOOR: int = 1
Value definitions for the layout grid (same asRoomLayoutGenerator).enum MoveTargetKind { MOVE_ON_FLOORS, MOVE_NEAR_WALLS }
Enumeration representing how to select the destination cell.MOVE_ON_FLOORS: Move onto floor cells.MOVE_NEAR_WALLS: Select candidates using logic that prioritizes floor cells near walls.
@export var layout_node_path: NodePath
Path to the layout node (RoomLayoutGenerator).@export var input_action_name: String = "dungeon_regen"
InputMapaction name used as the trigger for layout regeneration.@export var log_enabled: bool = true
Whentrue, outputs internal processing logs viaprint()/push_warning(), etc.var layout_node: Node = null
Actual reference to the layout node. Retrieved fromlayout_node_pathin_ready().var _rng: RandomNumberGenerator = RandomNumberGenerator.new()
RNG used for randomization during each layout regeneration.
Randomization of Layout Parameters
@export var randomize_layout_size: bool = false
Whentrue, randomly changes the layout width and height for each regeneration.@export var layout_width_range: Vector2i = Vector2i(80, 80)
Random range forwidth. (x = min,y = max)@export var layout_height_range: Vector2i = Vector2i(60, 60)
Random range forheight.@export var randomize_room_count: bool = false
Whentrue, randomly changesroom_countfor each regeneration.@export var room_count_range: Vector2i = Vector2i(18, 18)
Random range forroom_count.@export var randomize_room_size: bool = false
Whentrue, randomly changes the room size range for each regeneration.@export var room_width_range: Vector2i = Vector2i(5, 14)
Original random range for room width. Internally, it draws two values to determinemin/max.@export var room_height_range: Vector2i = Vector2i(4, 12)
Original random range for room height.@export var randomize_corridor_width: bool = false
Whentrue, randomly changes the corridor width for each regeneration.@export var corridor_width_range: Vector2i = Vector2i(0, 3)
Original random range for corridor width. Two values are drawn from here to setmin/max, or to select a fixed width.
Movement Target and Camera Related
@export var move_target_node_path: NodePath
Path to the target node to move after layout updates (e.g., player character).@export var move_target_tilemap_layer_path: NodePath
Path to theTileMapLayercorresponding to the layout.
From here, it determines which cells are floors or walls to obtain candidate destination cells.@export var move_target_kind: MoveTargetKind = MoveTargetKind.MOVE_ON_FLOORS
Specifies how to select the destination cell using the above enumeration.@export var move_after_generation: bool = true
Whentrue, executes_move_target_to_free_cell()after layout regeneration.@export var cell_size: Vector2 = Vector2(16.0, 16.0)
Pixel size of one cell in the layout grid.
Used to calculate world coordinates from coordinates on thegrid.@export var zoom_camera_path: NodePath
Path to theCamera2D/ZoomCamera2Dwhoselimit_left/right/top/bottomare automatically adjusted after regeneration.




