mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-11 16:14:08 +01:00
f241399f0c
## About The Pull Request The `(x, y, size)` arguments for `CHORDS_TO_1D` were in the wrong position when a map grid layout is procedurally generated. This results in z-levels that will likely be clustered together near the edge instead of spreading out evenly from the center. ## Why It's Good For The Game Code works as intended now. ## Changelog 🆑 fix: Fix map z-level generation starting position. Z-levels used to cluster together near the edges and will now instead be spread out evenly from the center. /🆑
207 lines
8.0 KiB
Plaintext
207 lines
8.0 KiB
Plaintext
/datum/space_level/proc/set_linkage(new_linkage)
|
|
linkage = new_linkage
|
|
if(linkage == SELFLOOPING)
|
|
neigbours = list(TEXT_NORTH,TEXT_SOUTH,TEXT_EAST,TEXT_WEST)
|
|
for(var/A in neigbours)
|
|
neigbours[A] = src
|
|
|
|
/datum/space_level/proc/set_neigbours(list/L)
|
|
for(var/datum/space_transition_point/P in L)
|
|
if(P.x == xi)
|
|
if(P.y == yi+1)
|
|
neigbours[TEXT_NORTH] = P.spl
|
|
P.spl.neigbours[TEXT_SOUTH] = src
|
|
else if(P.y == yi-1)
|
|
neigbours[TEXT_SOUTH] = P.spl
|
|
P.spl.neigbours[TEXT_NORTH] = src
|
|
else if(P.y == yi)
|
|
if(P.x == xi+1)
|
|
neigbours[TEXT_EAST] = P.spl
|
|
P.spl.neigbours[TEXT_WEST] = src
|
|
else if(P.x == xi-1)
|
|
neigbours[TEXT_WEST] = P.spl
|
|
P.spl.neigbours[TEXT_EAST] = src
|
|
|
|
#define CHORDS_TO_1D(x, y, grid_diameter) ((x) + ((y) - 1) * (grid_diameter))
|
|
/datum/space_transition_point //this is explicitly utilitarian datum type made specially for the space map generation and are absolutely unusable for anything else
|
|
var/list/neigbours = list()
|
|
var/x
|
|
var/y
|
|
var/datum/space_level/spl
|
|
|
|
/datum/space_transition_point/New(nx, ny, list/grid)
|
|
if(!grid)
|
|
qdel(src)
|
|
return
|
|
var/grid_diameter = sqrt(length(grid))
|
|
if(nx > grid_diameter || ny > grid_diameter)
|
|
stack_trace("Attempted to set a position outside the size of [grid_diameter]")
|
|
qdel(src)
|
|
return
|
|
x = nx
|
|
y = ny
|
|
var/position = CHORDS_TO_1D(x, y, grid_diameter)
|
|
if(grid[position])
|
|
return
|
|
grid[position] = src
|
|
|
|
/datum/space_transition_point/proc/set_neigbours(list/grid, size)
|
|
neigbours.Cut()
|
|
|
|
if(x+1 <= size)
|
|
neigbours |= grid[CHORDS_TO_1D(x+1, y, size)]
|
|
if(x-1 >= 1)
|
|
neigbours |= grid[CHORDS_TO_1D(x-1, y, size)]
|
|
if(y+1 <= size)
|
|
neigbours |= grid[CHORDS_TO_1D(x, y + 1, size)]
|
|
if(y-1 >= 1)
|
|
neigbours |= grid[CHORDS_TO_1D(x, y - 1, size)]
|
|
|
|
/datum/controller/subsystem/mapping/proc/setup_map_transitions() //listamania
|
|
var/list/transition_levels = list()
|
|
var/list/cached_z_list = z_list
|
|
var/linkage
|
|
for(var/datum/space_level/level as anything in cached_z_list)
|
|
if(level.linkage != CROSSLINKED && level.linkage != GRIDLINKED)
|
|
continue
|
|
|
|
transition_levels.Add(level)
|
|
|
|
if(!isnull(linkage) && level.linkage != linkage)
|
|
// Either you be gridlinked or crosslinked, both is uhhh... a headache
|
|
stack_trace("Mixed linkages detected in z-level neighbour transitions!")
|
|
continue
|
|
linkage = level.linkage
|
|
|
|
if(linkage == CROSSLINKED)
|
|
set_cross_linkages(transition_levels)
|
|
else if(linkage == GRIDLINKED)
|
|
set_grid_linkages(transition_levels)
|
|
|
|
// Now that we've handed out neighbors, we're gonna handle an edge case
|
|
// Need to check if all our levels have neighbors in all directions
|
|
// If they don't, we'll make them wrap all the way around to the other side of the grid
|
|
for(var/direction in GLOB.cardinals)
|
|
var/dir = "[direction]"
|
|
var/inverse = "[REVERSE_DIR(direction)]"
|
|
for(var/datum/space_level/level as anything in transition_levels)
|
|
// If we have something in this dir that isn't just us, continue on
|
|
if(level.neigbours[dir] && level.neigbours[dir] != level)
|
|
continue
|
|
var/datum/space_level/head = level
|
|
while(head.neigbours[inverse] && head.neigbours[inverse] != head)
|
|
head = head.neigbours[inverse]
|
|
|
|
// Alllright we've landed on someone who we can wrap around onto safely, let's make that connection yeah?
|
|
head.neigbours[inverse] = level
|
|
level.neigbours[dir] = head
|
|
|
|
//Lists below are pre-calculated values arranged in the list in such a way to be easily accessable in the loop by the counter
|
|
//Its either this or madness with lotsa math
|
|
var/inner_max_x = world.maxx - TRANSITIONEDGE
|
|
var/inner_max_y = world.maxy - TRANSITIONEDGE
|
|
var/list/x_pos_beginning = list(1, 1, inner_max_x, 1) //x values of the lowest-leftest turfs of the respective 4 blocks on each side of zlevel
|
|
var/list/y_pos_beginning = list(inner_max_y, 1, 1 + TRANSITIONEDGE, 1 + TRANSITIONEDGE) //y values respectively
|
|
var/list/x_pos_ending = list(world.maxx, world.maxx, world.maxx, 1 + TRANSITIONEDGE) //x values of the highest-rightest turfs of the respective 4 blocks on each side of zlevel
|
|
var/list/y_pos_ending = list(world.maxy, 1 + TRANSITIONEDGE, inner_max_y, inner_max_y) //y values respectively
|
|
var/list/x_pos_transition = list(1, 1, TRANSITIONEDGE + 2, inner_max_x - 1) //values of x for the transition from respective blocks on the side of zlevel, 1 is being translated into turfs respective x value later in the code
|
|
var/list/y_pos_transition = list(TRANSITIONEDGE + 2, inner_max_y - 1, 1, 1) //values of y for the transition from respective blocks on the side of zlevel, 1 is being translated into turfs respective y value later in the code
|
|
|
|
// Cache the range passed to the mirage border element, to reduce world var access in the thousands
|
|
var/range_cached = world.view
|
|
|
|
for(var/datum/space_level/level as anything in cached_z_list)
|
|
if(!level.neigbours.len)
|
|
continue
|
|
var/zlevelnumber = level.z_value
|
|
for(var/side in 1 to 4)
|
|
var/list/turfblock = block(
|
|
x_pos_beginning[side], y_pos_beginning[side], zlevelnumber,
|
|
x_pos_ending[side], y_pos_ending[side], zlevelnumber
|
|
)
|
|
var/dirside = 2**(side-1)
|
|
var/x_target = x_pos_transition[side] == 1 ? 0 : x_pos_transition[side]
|
|
var/y_target = y_pos_transition[side] == 1 ? 0 : y_pos_transition[side]
|
|
var/datum/space_level/neighbor = level.neigbours["[dirside]"]
|
|
var/zdestination = neighbor.z_value
|
|
|
|
for(var/turf/open/S in turfblock)
|
|
S.destination_x = x_target || S.x
|
|
S.destination_y = y_target || S.y
|
|
S.destination_z = zdestination
|
|
|
|
// Mirage border code
|
|
var/mirage_dir
|
|
if(S.x == 1 + TRANSITIONEDGE)
|
|
mirage_dir |= WEST
|
|
else if(S.x == inner_max_x)
|
|
mirage_dir |= EAST
|
|
if(S.y == 1 + TRANSITIONEDGE)
|
|
mirage_dir |= SOUTH
|
|
else if(S.y == inner_max_y)
|
|
mirage_dir |= NORTH
|
|
if(!mirage_dir)
|
|
continue
|
|
|
|
var/turf/place = locate(S.destination_x, S.destination_y, zdestination)
|
|
S.AddElement(/datum/element/mirage_border, place, mirage_dir, range_cached)
|
|
|
|
/// Construct linkages randomly to get maze-like space transitions
|
|
/// We do this by constructing a very large grid, and placing the levels randomly inside, and then filling out the empty spaces
|
|
/datum/controller/subsystem/mapping/proc/set_cross_linkages(list/transition_levels)
|
|
var/grid_diameter = (length(transition_levels) * 2) + 1
|
|
var/list/grid = new /list(grid_diameter ** 2)
|
|
|
|
var/datum/space_transition_point/point
|
|
for(var/x in 1 to grid_diameter)
|
|
for(var/y in 1 to grid_diameter)
|
|
point = new /datum/space_transition_point(x, y, grid)
|
|
grid[CHORDS_TO_1D(x, y, grid_diameter)] = point
|
|
for(point as anything in grid)
|
|
point.set_neigbours(grid, grid_diameter)
|
|
|
|
var/center = round(grid_diameter / 2)
|
|
if(transition_levels.len)
|
|
point = grid[CHORDS_TO_1D(center, center, grid_diameter)]
|
|
grid.Cut()
|
|
|
|
var/list/transition_pick = transition_levels.Copy()
|
|
var/list/possible_points = list()
|
|
var/list/used_points = list()
|
|
|
|
while(transition_pick.len)
|
|
var/datum/space_level/level = pick_n_take(transition_pick)
|
|
level.xi = point.x
|
|
level.yi = point.y
|
|
point.spl = level
|
|
possible_points |= point.neigbours
|
|
used_points |= point
|
|
possible_points.Remove(used_points)
|
|
level.set_neigbours(used_points)
|
|
point = pick(possible_points)
|
|
CHECK_TICK
|
|
|
|
/// Connect the z-levels in a non-randomized grid
|
|
/datum/controller/subsystem/mapping/proc/set_grid_linkages(list/transition_levels)
|
|
var/grid_diameter = ceil(sqrt(length(transition_levels)))
|
|
var/list/grid = new /list(grid_diameter ** 2)
|
|
|
|
// Construct an imaginary grid with the right neighbours etc for our grid
|
|
var/datum/space_transition_point/point
|
|
for(var/x in 1 to grid_diameter)
|
|
for(var/y in 1 to grid_diameter)
|
|
point = new /datum/space_transition_point(x, y, grid)
|
|
grid[CHORDS_TO_1D(x, y, grid_diameter)] = point
|
|
|
|
// Translate the grid we made to the z-levels
|
|
var/list/used_points = list()
|
|
for(var/i in 1 to transition_levels.len)
|
|
var/datum/space_level/level = transition_levels[i]
|
|
point = grid[i]
|
|
level.xi = point.x
|
|
level.yi = point.y
|
|
point.spl = level
|
|
used_points += point //this used_points list is kinda lame, you can remove it if you can find out what the slice function in byond is
|
|
level.set_neigbours(used_points)
|
|
#undef CHORDS_TO_1D
|