Tile highlight and placement validity for building placement #48
No reviewers
Labels
No labels
assets
bug
duplicate
enhancement
help wanted
invalid
question
testing
ui
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
liamjd/UntitledColonyBuilder!48
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "37-building-placement-validity-and-highlight"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Highlights the tiles under the cursor during building placement and shows whether the
site is buildable, green for valid and red for not.
Closes #37
What's here
PlacementRules.evaluate()(entities/buildings/placement_rules.gd) — a purestatic rule, separated from the drawing so it can be tested headless. Returns a
Reasonrather than aboolso a future "why can't I build here?" tooltip does nothave to re-derive it. Checks run in the order #37 specified: bounds, then water, then
level spread — bounds first because
level_at()on an off-map cell silently returns_datum_indexand would otherwise compute a spread against a fictional tile.Flatness is the spread across the occupied cells, not
TerrainGrid.is_sloped().is_sloped()answers a pathfinding question about a cell's eight neighbours, whichboth fails a flat pad sitting beside a cliff and, for a 1x1, consults only tiles the
building does not occupy.
The highlight — one
ImmediateMeshon a singleMeshInstance3D, one quad perfootprint cell, rebuilt only when the anchor, building type or validity changes. The
mesh instance takes
map.global_transformin_ready(), so vertices are emitted inraw map-local coordinates with no
to_global()anywhere. Colour is carried on thevertices under one unshaded material, which is what makes per-cell tinting free.
Each quad sits at its own
surface_y(level_at(cell)) + 0.02, so the highlightsteps with the terraced ground instead of clipping through it.
The ghost mesh tints to match via its
material_override, never written back ontothe mesh itself —
BuildingMeshes.build()bakes colour intoPrimitiveMesh.materialand #41 will start sharing those meshes.
max_level_spreadis an@exportonPlacementController, default0, so thetolerance can be tuned by eye.
Two things that landed while building it
Cells are inset by a proportion of
tile_size(highlight_inset_ratio, default0.15), leaving a small gap between neighbours so the footprint reads as distinct tiles
rather than one blob. This was tried on a whim and kept because it looks better. It is
a ratio rather than a fixed distance so it scales with the grid and cannot invert the
quads on a small
tile_size.The anchor cell draws inverted when the footprint has more than one cell, which
makes it obvious which tile the cursor actually owns on a large footprint. Marked
temporary in the code — it is a stand-in for real per-cell colouring, and it is ugly
against some tints.
Also adds the
five_thick_cfootprint (a 5-wide block with an indent) and a Warehousethat uses it, which is what the multi-cell and non-rectangular cases were tested
against.
Verification
test/unit/test_placement_rules.gdcovers every case #37 asked for: flat 3x3 valid;one cell raised →
TOO_STEEPat spread 0 andVALIDat spread 1; water beating aspread that would also fail; overhang off each of the four edges; an L-shape whose
bounding box covers a bad cell the shape itself does not (guards against
evaluating
bounds()instead ofcells()); 1x1 always valid on dry land.gl_compatibilityrendersnothing headless, so none of the drawing is covered by the suite and a green run says
nothing about how it looks. Confirmed manually: per-cell stepping on terraced ground,
green/red tinting, red rather than a misplaced highlight when overhanging the map
edge or covering water, and the highlight disappearing with the ghost on right-click
cancel and when the cursor is over UI.
Left undone
quads at higher alpha. Filed as #47 rather than left as a TODO comment. The inset gap
covers most of what the border was for at zoom, and the inset changes what "border"
should mean (per-cell frame vs. one outline around the whole plot), which is a
look-at-it decision rather than one to settle blind.
Reason.MISSING_RESOURCEis in the enum and wired to a tint, butevaluate()can never return it and no test covers it. It is a deliberate placeholder for the
resource rule, which is far-future work.
placed on water; today water is unconditionally invalid.