Skip to content

Commit

Permalink
feat(port): mutable specials and unhardcored labs and anthills (#3520)
Browse files Browse the repository at this point in the history
* Support for mutable overmap specials and unhardcode anthills

Co-authored-by: John Bytheway <[email protected]>

* Better error for missing json mapgen object

Previously this would fail with an error which didn't explain where the
problem was.

* Support JSON-defined linear terrain

Linear terrain (roads, ant tunnels, sewers, etc.) was not previously
possible to define in JSON.  This was because the rotation wasn't
correctly calculated for linear terrain.  Make it be.

* Unhardcode anthill mapgen

Co-authored-by: John Bytheway <[email protected]>

* Define std::hash specialization for cube_direction

* Explicit return value construction

* Better debugging for mutable special placement (#51504)

When placement of mutable overmap specials fails, it's hard to figure
out why.  The small amount of context previously given in the debugmsg
wasn't enough.  I did have some further debug printfs in non-compiled
code behind a #if, but that's no use to people who can't compile the
game themselves, nor does it help debug failures in CI.

Reformulate the code so that these more detailed messages are always
saved (rather than printed) and when a placement error occurs, it
includes a full history of the placement to allow for debugging.

* Putative fix for mutable special placement (#51574)

I think I figured out why mutable special placement was sometimes
failing.  Some joins could be postponed but others could appear
pointing at the same tile and be satisfied in the absence of the
postponed joins.  Then, when the postponed joins are restored they
cannot be satisfied.

Avoid this by keeping track of the tiles with postponed joins and
ensuring that they new joins pointing at the same tiles are added
directly to the postponed list rather than the unresolved list.

* Actually fix rare anthill placement errors (#51611)

The previous attempt to fix the rare anthill placement errors did not
work.  I now know this was due to a simple typo, but tracking down the
issue took a lot of refactoring.  That refactoring improved the code, so
I'm including it here.

Changes include:
* The previous fix was that new unresolved joins that might conflict
  with postponed joins would themselves be automatically postponed.  We
  have now reversed this logic so the postponed joins are restored.
  This allows another chance for the current phase to satisfy the joins,
  now that might be possible.
* Postponed joins now need to have their positions indexed for efficient
  access, so I factored out that indexing capability into a new struct
  indexed_joins.
* Removed the list of orphaned joins.  They should no longer ever occur.
* Added a bunch of calls to a consistency_check function that verifies
  the class invariants (except it's disabled for now, with the code left
  in case it's useful for future debugging).
* The order of unresolved joins used to depend on their addresses in
  memory, which made the tests non-reproducible.  Change that to be
  deterministic.
* Add unit test which places ~100k anthills to more easily observe
  placement errors.
* Improve the debugging output for failed placements to make it easier
  to see what happened (add phase boundary markers and capitalize
  FAILED).

* Allow random number of overmaps in mutable special

Rather than always having a fixed number of overmaps for a particular
rule during placement, allow a random number.  Currently this can only
be a poisson-distributed number (or a fixed value, as was already
supported).

* Document distribution feature for mutable specials

Explain how to specify that the max number of instances should be drawn
from a Poisson distribution.

* Optional joins for mutable specials

To add more flexibility to the layout of mutable specials it is
convenient to allow some joins for particular overmaps to be optional.
This helps in particular with the final phase when you want to clean up
all the leftover unresolved joins.

Generalize the definition of joins to allow this in the JSON, and handle
appropriately in the code.

Convert craters to use this feature.

Extend the unit tests to spawn a test crater to exercise this feature.

* Document the optional joins feature

* Document join prioritization (#51661)

* Support asymmetric joins for mutable specials

Sometimes it's important for the two sides of a join to be different,
for example a hallway_to_room join should not join two hallways.  It is
also be important if the set of locations is different for the two
sides.

Add support for this type of asymmetry, and use it to allow anthills to
have multiple entrances.

Update test anthill also.

* Document asymmetric joins

* Avoid large stack object

The Windows stack size limit is smaller than Linux, so need to tweak
this test to avoid a stack overflow.

* Move cube_direction, pos_dir to header

To be able to use these types in regular mapgen they need to be promoted
to headers.

* Record joins from mutable special placement

When a mutable special is placed, keep a record of every join used and
store that in the overmap.  Display this information in the overmap
editor to aid with debugging.

* Allow mapgen values to check joins

Similarly to neighbourhood checks in chunk placement, add join checks to
chunk placement.

* Document join testing for chunk placement

* Acid Anthills (#60095)

* Acid Anthills

* Update ants.json

* Update ants.json

* Debug note on overmap when mutable placement fails (#51878)

When placement fails for a mutable overmap special, add a note to the
overmap with some details to assist debugging.

* displace and displace_XY for direction

There used to be a function called direction_XY that converted a
direction to a point.  This didn't match the naming scheme used for
analogous functions for om_direction and cube_direction.

Rename it to displace_XY and also create a tripoint version displace.

* Set neighbours according to oter rotation

Previously the mapgen feature of placing chunks based on neighbours used
the absolute cardinal directions on the map.  This is unhelpful when the
map is rotated.

Rotate the neighbours to match the rotation of the map so that these
checks make sense for the mapgen.

* Add + overload for cube_direction and int

For symmetry with the - overload.

* Fix join direction checks

Previously these checks were based on absolute map directions.  After
this change they are instead rotated with the map, so that they are
easier to use logically within the map.

* Add tile rotation to debug overmap ui

It can be useful to know the rotation of a particular tile for debugging
purposes.  This may not be obvious for linear terrain.  Add it to the
debugging output in the overmap editor.

* Add support for mutable overmap chunks

It will help a lot with mutable overmap special design if a rule can
place multiple overmaps together in a chunk.

This attempts to implement that.  It entails a significant rewrite of
the guts of overmap special placement.

Add a test_microlab special to test this new feature.

* Document mutable overmap chunks

* Fix some debug / error messages

A couple of these messages were unhelpful or invalid (e.g. passing the
wrong number of args for a format string).  Fix that.

* Support alternative joins in mutable specials

Alternative joins allow an overmap in a mutable special to match an
existing join that they would not otherwise, without affecting the
requirement for onwards matching on other unresolved edges.  This is
particularly useful when spawning a collections of 'clumpy' overmaps
around some existing ones.  For example, the mutable microlab has a
hallway and then microlab terrains.  The microlab terrains should match
the hallway, but their unresolved edges should only match more
microlabs.

* Document alternative joins

* Remove unused field

* Fix road mapgen to check the correct neighbours

The changes in #51913 altered how neighbours were entered into the
mapgendata context.  For JSON mapgen this fixed issues, but for
hardcoded mapgen it could break things.

In particular, for road mapgen it caused problems.

Add a new flag to allow specific oter_ts to opt out of this new
behaviour, and apply it to roads.

* Support mismatched available joins

There was a bug in the mutable special placement code whereby it would
refuse to place two mismatched available joins pointing at each other,
even though that is perfectly reasonable.

Attempt a fix for this.

* Document trick for testing mutable specials

Explain how to tweak the placement test to test placement for your
special when you're designing a new one.

* Area check

Co-authored-by:  George Karfakis <[email protected]>

* Restored interaction between anthills, and labs

* Restored blob crater

* Restored anthills IDs, and obsolete dedicated ants lab

* Mutables expansion, added mutable lab

* Added escape cells to mutable lab

* Connections support for mutable specials

* Mutable central lab

* No more hardcoded labs

* Restored hidden labs, merged z-4 subways

* Mutables can be generated as start location

* Fixed styling and warnings

* Nested specials

* Faster pos check in mutables

* Less hacky lab stairs connections

* Option to configure spacing between specials

* Specials density adjustment for high spacing

* Spread specials between different cities

* Polishing

* Documented specials palcement

* Clean up

* Fixed neighbor check type

* Changed match type

* More polishing

* Better check for neighbour connection

* Documented connection check

* Fixed old issue where no-city connection fallback could build a tangled mess of multiple roads

* Revert type check fix in mapgen - it breaks central labs

* p2p connections for subways

---------

Co-authored-by: John Bytheway <[email protected]>
Co-authored-by: eltank <[email protected]>
Co-authored-by: Maleclypse <[email protected]>
Co-authored-by: George Karfakis <[email protected]>
  • Loading branch information
5 people authored Nov 12, 2023
1 parent f0ae6bc commit 07735ca
Show file tree
Hide file tree
Showing 54 changed files with 5,758 additions and 1,674 deletions.
1,207 changes: 1,207 additions & 0 deletions data/json/mapgen/bugs/ants.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions data/json/mapgen/lab/lab_trains.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@
"p": "t_sewage_pipe"
},
"place_nested": [
{ "chunks": [ "lab_train_subway_east" ], "x": 0, "y": 0, "neighbors": { "east": [ "subway" ] } },
{ "chunks": [ "lab_train_subway_west" ], "x": 0, "y": 0, "neighbors": { "west": [ "subway" ] } },
{ "chunks": [ "lab_train_subway_north" ], "x": 0, "y": 0, "neighbors": { "north": [ "subway" ] } },
{ "chunks": [ "lab_train_subway_south" ], "x": 0, "y": 0, "neighbors": { "south": [ "subway" ] } },
{ "chunks": [ "lab_train_subway_east" ], "x": 0, "y": 0, "connections": { "east": [ "subway_tunnel" ] } },
{ "chunks": [ "lab_train_subway_west" ], "x": 0, "y": 0, "connections": { "west": [ "subway_tunnel" ] } },
{ "chunks": [ "lab_train_subway_north" ], "x": 0, "y": 0, "connections": { "north": [ "subway_tunnel" ] } },
{ "chunks": [ "lab_train_subway_south" ], "x": 0, "y": 0, "connections": { "south": [ "subway_tunnel" ] } },
{ "chunks": [ "lab_train_entrance_north" ], "x": 0, "y": 0, "neighbors": { "north": [ "lab" ] } },
{ "chunks": [ "lab_train_entrance_south" ], "x": 0, "y": 0, "neighbors": { "south": [ "lab" ] } },
{ "chunks": [ "lab_train_entrance_east" ], "x": 0, "y": 0, "neighbors": { "east": [ "lab" ] } },
Expand Down
9 changes: 8 additions & 1 deletion data/json/obsoletion/terrains.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@
"spiral",
"spiral_hub",
"underground_sub_station",
"sewer_sub_station"
"sewer_sub_station",
"anthill",
"acid_anthill",
"ants_food",
"ants_larvae",
"ants_larvae_acid",
"ants_queen",
"ants_queen_acid"
]
}
]
4 changes: 2 additions & 2 deletions data/json/overmap/multitile_city_buildings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2407,13 +2407,13 @@
{ "point": [ 6, 1, 0 ], "overmap": "mall_a_75_south" },
{ "point": [ 5, 1, 0 ], "overmap": "mall_a_76_south" },
{ "point": [ 4, 1, 0 ], "overmap": "mall_a_77_south" },
{ "point": [ 4, 1, -2 ], "overmap": "microlab_sub_connector_north" },
{ "point": [ 3, 1, 0 ], "overmap": "mall_a_78_south" },
{ "point": [ 2, 1, 0 ], "overmap": "mall_a_79_south" },
{ "point": [ 1, 1, 0 ], "overmap": "mall_a_80_south" },
{ "point": [ 0, 1, 0 ], "overmap": "mall_a_81_south" },
{ "point": [ 4, 0, 0 ], "overmap": "road_end_north" }
]
],
"connections": [ { "point": [ 4, 1, -2 ], "connection": "subway_tunnel", "from": [ 4, 2, -2 ] } ]
},
{
"type": "city_building",
Expand Down
3 changes: 2 additions & 1 deletion data/json/overmap/overmap_connections.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@
{
"type": "overmap_connection",
"id": "subway_tunnel",
"layout": "p2p",
"default_terrain": "subway",
"subtypes": [
{ "terrain": "underground_sub_station", "locations": [ "underground_sub_station" ], "flags": [ "ORTHOGONAL" ] },
{ "terrain": "underground_sub_station", "locations": [ ], "flags": [ "ORTHOGONAL" ] },
{ "terrain": "subway", "locations": [ "subterranean_subway" ], "flags": [ "ORTHOGONAL" ] }
]
},
Expand Down
244 changes: 244 additions & 0 deletions data/json/overmap/overmap_mutable/anthill.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
[
{
"type": "overmap_special",
"id": "Anthill",
"subtype": "mutable",
"locations": [ "subterranean_empty" ],
"city_distance": [ 10, -1 ],
"city_sizes": [ 0, 20 ],
"occurrences": [ 40, 100 ],
"flags": [ "ANT", "UNIQUE", "CLASSIC", "WILDERNESS" ],
"spawns": { "group": "GROUP_ANT", "population": [ 1000, 2000 ], "radius": [ 10, 30 ] },
"check_for_locations": [
[ [ 0, 0, 0 ], [ "land" ] ],
[ [ 0, 0, -1 ], [ "subterranean_empty" ] ],
[ [ 1, 0, -1 ], [ "subterranean_empty" ] ],
[ [ 0, 1, -1 ], [ "subterranean_empty" ] ],
[ [ -1, 0, -1 ], [ "subterranean_empty" ] ],
[ [ 0, -1, -1 ], [ "subterranean_empty" ] ]
],
"joins": [
{ "id": "surface_to_tunnel", "opposite": "tunnel_to_surface" },
{ "id": "tunnel_to_surface", "opposite": "surface_to_tunnel", "into_locations": [ "land" ] },
"tunnel_to_tunnel"
],
"overmaps": {
"surface": { "overmap": "anthill_north", "below": "surface_to_tunnel", "locations": [ "land" ] },
"below_entrance": {
"overmap": "ants_nesw",
"above": "tunnel_to_surface",
"north": "tunnel_to_tunnel",
"east": "tunnel_to_tunnel",
"south": "tunnel_to_tunnel",
"west": "tunnel_to_tunnel"
},
"crossroads": {
"overmap": "ants_nesw",
"north": "tunnel_to_tunnel",
"east": "tunnel_to_tunnel",
"south": "tunnel_to_tunnel",
"west": "tunnel_to_tunnel"
},
"tee": { "overmap": "ants_nes", "north": "tunnel_to_tunnel", "east": "tunnel_to_tunnel", "south": "tunnel_to_tunnel" },
"straight_tunnel": { "overmap": "ants_ns", "north": "tunnel_to_tunnel", "south": "tunnel_to_tunnel" },
"corner": { "overmap": "ants_ne", "north": "tunnel_to_tunnel", "east": "tunnel_to_tunnel" },
"dead_end": { "overmap": "ants_end_south", "north": "tunnel_to_tunnel" },
"queen": { "overmap": "ants_queen_north", "north": "tunnel_to_tunnel" },
"larvae": { "overmap": "ants_larvae_north", "north": "tunnel_to_tunnel" },
"food": { "overmap": "ants_food_north", "north": "tunnel_to_tunnel" }
},
"root": "surface",
"shared": { "size": [ 1, 5 ] },
"phases": [
[ { "overmap": "below_entrance", "max": 1 } ],
[
{ "overmap": "straight_tunnel", "scale": "size", "max": { "poisson": 10 } },
{ "overmap": "corner", "scale": "size", "max": { "poisson": 2.5 } },
{ "overmap": "tee", "scale": "size", "max": { "poisson": 5 } },
{ "overmap": "below_entrance", "scale": "size", "max": { "poisson": 0.35 } }
],
[ { "overmap": "queen", "max": 1 } ],
[
{ "overmap": "food", "scale": "size", "max": { "poisson": 2.5 } },
{ "overmap": "larvae", "scale": "size", "max": { "poisson": 2.5 } }
],
[
{ "overmap": "dead_end", "weight": 2000 },
{ "overmap": "straight_tunnel", "weight": 100 },
{ "overmap": "corner", "weight": 100 },
{ "overmap": "tee", "weight": 10 },
{ "overmap": "crossroads", "weight": 1 },
{ "overmap": "surface", "weight": 1 }
]
]
},
{
"type": "overmap_special",
"id": "Acid Anthill",
"subtype": "mutable",
"locations": [ "subterranean_empty" ],
"city_distance": [ 10, -1 ],
"occurrences": [ 40, 100 ],
"flags": [ "ANT", "UNIQUE", "WILDERNESS" ],
"spawns": { "group": "GROUP_ANT_ACID", "population": [ 1000, 2000 ], "radius": [ 10, 30 ] },
"check_for_locations": [
[ [ 0, 0, 0 ], [ "land" ] ],
[ [ 0, 0, -1 ], [ "subterranean_empty" ] ],
[ [ 1, 0, -1 ], [ "subterranean_empty" ] ],
[ [ 0, 1, -1 ], [ "subterranean_empty" ] ],
[ [ -1, 0, -1 ], [ "subterranean_empty" ] ],
[ [ 0, -1, -1 ], [ "subterranean_empty" ] ]
],
"joins": [
{ "id": "surface_to_tunnel", "opposite": "tunnel_to_surface" },
{ "id": "tunnel_to_surface", "opposite": "surface_to_tunnel", "into_locations": [ "land" ] },
"tunnel_to_tunnel"
],
"overmaps": {
"surface": { "overmap": "anthill_north", "below": "surface_to_tunnel", "locations": [ "land" ] },
"below_entrance": {
"overmap": "acid_ants_nesw",
"above": "tunnel_to_surface",
"north": "tunnel_to_tunnel",
"east": "tunnel_to_tunnel",
"south": "tunnel_to_tunnel",
"west": "tunnel_to_tunnel"
},
"crossroads": {
"overmap": "acid_ants_nesw",
"north": "tunnel_to_tunnel",
"east": "tunnel_to_tunnel",
"south": "tunnel_to_tunnel",
"west": "tunnel_to_tunnel"
},
"tee": { "overmap": "acid_ants_nes", "north": "tunnel_to_tunnel", "east": "tunnel_to_tunnel", "south": "tunnel_to_tunnel" },
"straight_tunnel": { "overmap": "acid_ants_ns", "north": "tunnel_to_tunnel", "south": "tunnel_to_tunnel" },
"corner": { "overmap": "acid_ants_ne", "north": "tunnel_to_tunnel", "east": "tunnel_to_tunnel" },
"dead_end": { "overmap": "acid_ants_end_south", "north": "tunnel_to_tunnel" },
"queen": { "overmap": "acid_ants_queen_north", "north": "tunnel_to_tunnel" },
"larvae": { "overmap": "acid_ants_larvae_north", "north": "tunnel_to_tunnel" },
"food": { "overmap": "acid_ants_food_north", "north": "tunnel_to_tunnel" }
},
"root": "surface",
"shared": { "size": [ 1, 5 ] },
"phases": [
[ { "overmap": "below_entrance", "max": 1 } ],
[
{ "overmap": "straight_tunnel", "scale": "size", "max": { "poisson": 10 } },
{ "overmap": "corner", "scale": "size", "max": { "poisson": 2.5 } },
{ "overmap": "tee", "scale": "size", "max": { "poisson": 5 } },
{ "overmap": "below_entrance", "scale": "size", "max": { "poisson": 0.35 } }
],
[ { "overmap": "queen", "max": 1 } ],
[
{ "overmap": "food", "scale": "size", "max": { "poisson": 2.5 } },
{ "overmap": "larvae", "scale": "size", "max": { "poisson": 2.5 } }
],
[
{ "overmap": "dead_end", "weight": 2000 },
{ "overmap": "straight_tunnel", "weight": 100 },
{ "overmap": "corner", "weight": 100 },
{ "overmap": "tee", "weight": 10 },
{ "overmap": "crossroads", "weight": 1 },
{ "overmap": "surface", "weight": 1 }
]
]
},
{
"type": "overmap_special",
"id": "Lab with Anthill",
"subtype": "mutable",
"locations": [ "subterranean_empty" ],
"city_distance": [ 4, -1 ],
"city_sizes": [ 0, 20 ],
"occurrences": [ 30, 100 ],
"flags": [ "ANT", "UNIQUE" ],
"connections": [ { "point": [ 0, -1, 0 ], "connection": "local_road" } ],
"place_nested": [ { "point": [ 0, 0, -1 ], "special": "lab_basement" } ],
"spawns": { "group": "GROUP_ANT", "population": [ 1000, 2000 ], "radius": [ 10, 30 ] },
"check_for_locations": [
[ [ 3, 1, 0 ], [ "land" ] ],
[ [ 3, 1, -1 ], [ "subterranean_empty" ] ],
[ [ 4, 1, -1 ], [ "subterranean_empty" ] ],
[ [ 3, 2, -1 ], [ "subterranean_empty" ] ],
[ [ 2, 1, -1 ], [ "subterranean_empty" ] ],
[ [ 3, 0, -1 ], [ "subterranean_empty" ] ],
[ [ 0, 0, 0 ], [ "land" ] ],
[ [ 0, 0, -1 ], [ "subterranean_empty" ] ]
],
"joins": [
{ "id": "surface_to_tunnel", "opposite": "tunnel_to_surface" },
{ "id": "tunnel_to_surface", "opposite": "surface_to_tunnel", "into_locations": [ "land" ] },
"tunnel_to_tunnel",
"lab"
],
"overmaps": {
"lab_stairs": { "overmap": "lab_stairs", "below": "lab", "locations": [ "land" ] },
"below_stairs": {
"overmap": "empty_rock",
"above": "lab",
"north": { "id": "lab", "type": "available" },
"east": { "id": "lab", "type": "available" },
"south": { "id": "lab", "type": "available" },
"west": { "id": "lab", "type": "available" }
},
"surface": { "overmap": "anthill_north", "below": "surface_to_tunnel", "locations": [ "land" ] },
"below_entrance": {
"overmap": "ants_nesw",
"above": "tunnel_to_surface",
"north": "tunnel_to_tunnel",
"east": "tunnel_to_tunnel",
"south": "tunnel_to_tunnel",
"west": "tunnel_to_tunnel"
},
"crossroads": {
"overmap": "ants_nesw",
"north": "tunnel_to_tunnel",
"east": "tunnel_to_tunnel",
"south": "tunnel_to_tunnel",
"west": "tunnel_to_tunnel"
},
"tee": { "overmap": "ants_nes", "north": "tunnel_to_tunnel", "east": "tunnel_to_tunnel", "south": "tunnel_to_tunnel" },
"straight_tunnel": { "overmap": "ants_ns", "north": "tunnel_to_tunnel", "south": "tunnel_to_tunnel" },
"corner": { "overmap": "ants_ne", "north": "tunnel_to_tunnel", "east": "tunnel_to_tunnel" },
"dead_end": { "overmap": "ants_end_south", "north": "tunnel_to_tunnel" },
"queen": { "overmap": "ants_queen_north", "north": "tunnel_to_tunnel" },
"larvae": { "overmap": "ants_larvae_north", "north": "tunnel_to_tunnel" },
"food": { "overmap": "ants_food_north", "north": "tunnel_to_tunnel" }
},
"root": "lab_stairs",
"shared": { "size": [ 1, 5 ] },
"phases": [
[
{
"name": "ants_lab",
"chunk": [
{ "overmap": "below_stairs", "pos": [ 0, 0, 0 ] },
{ "overmap": "surface", "pos": [ 3, 1, 1 ] },
{ "overmap": "below_entrance", "pos": [ 3, 1, 0 ] }
],
"max": 1
}
],
[
{ "overmap": "straight_tunnel", "scale": "size", "max": { "poisson": 10 } },
{ "overmap": "corner", "scale": "size", "max": { "poisson": 2.5 } },
{ "overmap": "tee", "scale": "size", "max": { "poisson": 5 } },
{ "overmap": "below_entrance", "scale": "size", "max": { "poisson": 0.35 } }
],
[ { "overmap": "queen", "max": 1 } ],
[
{ "overmap": "food", "scale": "size", "max": { "poisson": 2.5 } },
{ "overmap": "larvae", "scale": "size", "max": { "poisson": 2.5 } }
],
[
{ "overmap": "dead_end", "weight": 2000 },
{ "overmap": "straight_tunnel", "weight": 100 },
{ "overmap": "corner", "weight": 100 },
{ "overmap": "tee", "weight": 10 },
{ "overmap": "crossroads", "weight": 1 },
{ "overmap": "surface", "weight": 1 }
]
]
}
]
Loading

0 comments on commit 07735ca

Please sign in to comment.