Skip to content

Commit ec8475d

Browse files
authored
Adjacency groups (#7233)
1 parent 45bfd7e commit ec8475d

File tree

7 files changed

+195
-7
lines changed

7 files changed

+195
-7
lines changed

citadel.dme

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@
309309
#include "code\__DEFINES\mobs\wounds.dm"
310310
#include "code\__DEFINES\objects\objects.dm"
311311
#include "code\__DEFINES\objects\type_generation.dm"
312+
#include "code\__DEFINES\objects\object_systems\adjacency_group.dm"
312313
#include "code\__DEFINES\overmaps\misc.dm"
313314
#include "code\__DEFINES\overmaps\overmap.dm"
314315
#include "code\__DEFINES\power\balancing.dm"
@@ -378,6 +379,7 @@
378379
#include "code\__DEFINES\traits\items.dm"
379380
#include "code\__DEFINES\traits\mob.dm"
380381
#include "code\__DEFINES\traits\movables.dm"
382+
#include "code\__DEFINES\traits\objects.dm"
381383
#include "code\__DEFINES\traits\sources.dm"
382384
#include "code\__DEFINES\traits\unsorted.dm"
383385
#include "code\__DEFINES\turfs\change_turf.dm"
@@ -2125,6 +2127,7 @@
21252127
#include "code\game\objects\structures\tables\table.dm"
21262128
#include "code\game\objects\structures\tables\update_triggers.dm"
21272129
#include "code\game\objects\systems\_system.dm"
2130+
#include "code\game\objects\systems\adjacency_group.dm"
21282131
#include "code\game\objects\systems\cell_slot.dm"
21292132
#include "code\game\objects\systems\storage\storage-filters.dm"
21302133
#include "code\game\objects\systems\storage\storage-indirection.dm"

code/__DEFINES/directional.dm

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,25 @@
5757
pixel_x = -offset; \
5858
}
5959

60+
//* Individual bits for storing common directions. Unlike BYOND bits, this can store northeast apart from north. *//
61+
//* These should **not** be considered safe for serialization; these can change at any time as they sync to BYOND's. *//
62+
6063
// each dir plus diagonals in its own bit
6164
#define NORTH_BIT NORTH
6265
#define SOUTH_BIT SOUTH
6366
#define EAST_BIT EAST
6467
#define WEST_BIT WEST
65-
#define NORTHEAST_BIT (1<<4)
66-
#define NORTHWEST_BIT (1<<5)
67-
#define SOUTHEAST_BIT (1<<6)
68-
#define SOUTHWEST_BIT (1<<7)
69-
#define ALL_DIRECTION_BITS (NORTH_BIT | SOUTH_BIT | NORTHEAST_BIT | NORTHWEST_BIT | SOUTHEAST_BIT | SOUTHWEST_BIT | WEST_BIT | EAST_BIT)
68+
/// Not included in cardinal/diagonal direction bits.
69+
#define UP_BIT UP
70+
/// Not included in cardinal/diagonal direction bits.
71+
#define DOWN_BIT DOWN
72+
#define NORTHEAST_BIT (1<<6)
73+
#define NORTHWEST_BIT (1<<7)
74+
#define SOUTHEAST_BIT (1<<8)
75+
#define SOUTHWEST_BIT (1<<9)
76+
/// Not included in cardinal/diagonal direction bits.
77+
#define ONTOP_BIT (1<<10)
78+
#define CARDINAL_DIAGONAL_DIRECTION_BITS (NORTH_BIT | SOUTH_BIT | NORTHEAST_BIT | NORTHWEST_BIT | SOUTHEAST_BIT | SOUTHWEST_BIT | WEST_BIT | EAST_BIT)
7079
#define CARDINAL_DIRECTION_BITS (NORTH_BIT | SOUTH_BIT | EAST_BIT | WEST_BIT)
7180
#define DIAGONAL_DIRECTION_BITS (NORTHEAST_BIT | NORTHWEST_BIT | SOUTHEAST_BIT | SOUTHWEST_BIT)
7281

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
//* This file is explicitly licensed under the MIT license. *//
3+
//* Copyright (c) 2025 Citadel Station developers. *//
4+
5+
#define OBJ_BIND_ADJACENCY_GROUP(PATH, VARNAME, KEY) \
6+
##PATH { \
7+
var/datum/object_system/adjacency_group/##VARNAME; \
8+
}; \
9+
##PATH/Initialize(mapload, ...) { \
10+
. = ..(); \
11+
if(. == INITIALIZE_HINT_QDEL) { \
12+
return; \
13+
}; \
14+
VARNAME = new(src, KEY); \
15+
}; \
16+
##PATH/Moved(...) { \
17+
VARNAME.parent_moved(); \
18+
return ..(); \
19+
}

code/__DEFINES/objects/objects.dm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//* This file is explicitly licensed under the MIT license. *//
22
//* Copyright (c) 2024 Citadel Station Developers *//
33

4-
//* /obj/var/hides_underfloor
4+
//* /obj/var/hides_underfloor *//
55

66
/// do not change these willy-nilly, these are strings for the map editor.
77

code/__DEFINES/traits/objects.dm

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/// A special trait key used for adjacency group registration.
2+
/// This is used because status_traits already exists, and a full COMSIG system is too high in overhead.
3+
#define TRAIT_OBJ_ADJACENCY_GROUP(KEY) "obj-adj-group-[KEY]"
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
//* This file is explicitly licensed under the MIT license. *//
2+
//* Copyright (c) 2025 Citadel Station developers. *//
3+
4+
/**
5+
* Connects transitively to cardinals.
6+
*
7+
* Does something very, very evil to status_traits.
8+
*/
9+
/datum/object_system/adjacency_group
10+
/// string key
11+
var/key
12+
/// current group
13+
var/datum/adjacency_group/group
14+
/// rebuild queued?
15+
var/rebuild_queued = FALSE
16+
/// connected directions
17+
var/connected_directions = NONE
18+
19+
/datum/object_system/adjacency_group/New(obj/parent, key)
20+
src.key = key
21+
ASSERT(key)
22+
LAZYSET(parent.status_traits, TRAIT_OBJ_ADJACENCY_GROUP(src.key), src)
23+
queue_rebuild()
24+
return ..()
25+
26+
/datum/object_system/adjacency_group/Destroy()
27+
LAZYREMOVE(parent.status_traits, TRAIT_OBJ_ADJACENCY_GROUP(key))
28+
QDEL_NULL(group)
29+
rebuild_queued = FALSE
30+
return ..()
31+
32+
/datum/object_system/adjacency_group/proc/parent_moved()
33+
QDEL_NULL(group)
34+
queue_rebuild()
35+
36+
/datum/object_system/adjacency_group/proc/create_initial_data()
37+
if(rebuild_queued)
38+
return
39+
return parent.object_adjacency_group_create_initial_data(src)
40+
41+
/datum/object_system/adjacency_group/proc/queue_rebuild()
42+
addtimer(CALLBACK(src, PROC_REF(rebuild_if_needed)), 0)
43+
rebuild_queued = TRUE
44+
45+
/datum/object_system/adjacency_group/proc/rebuild_if_needed()
46+
if(!rebuild_queued)
47+
return
48+
rebuild_queued = FALSE
49+
if(group)
50+
return
51+
rebuild()
52+
53+
/datum/object_system/adjacency_group/proc/rebuild()
54+
QDEL_NULL(group)
55+
var/datum/adjacency_group/new_group = new(key, create_initial_data())
56+
new_group.build(src)
57+
58+
/datum/adjacency_group
59+
/// all systems in group
60+
var/list/datum/object_system/adjacency_group/in_group
61+
/// string key
62+
var/key
63+
/// arbitrary data variable you can set to whatever you want
64+
var/data
65+
66+
/datum/adjacency_group/New(key)
67+
src.key = key
68+
src.data = data
69+
in_group = list()
70+
71+
/datum/adjacency_group/Destroy()
72+
teardown()
73+
return ..()
74+
75+
/datum/adjacency_group/proc/build(datum/object_system/adjacency_group/from_node)
76+
// very destructive build process; when encountering 'enemy', immediately destroy it and trample over
77+
var/list/datum/object_system/adjacency_group/expanding = list(from_node)
78+
var/trait = TRAIT_OBJ_ADJACENCY_GROUP(key)
79+
while(length(expanding))
80+
var/datum/object_system/adjacency_group/expand_this = expanding[length(expanding)]
81+
--expanding.len
82+
var/dirs_connecting = NONE
83+
84+
var/atom/expand_loc = expand_this.parent.loc
85+
if(isturf(expand_loc))
86+
for(var/dir in GLOB.cardinal)
87+
for(var/obj/in_cardinal_tile in get_step(expand_loc, dir))
88+
var/datum/object_system/adjacency_group/their_cardinal_group = in_cardinal_tile.status_traits?[trait]
89+
if(their_cardinal_group)
90+
dirs_connecting |= dir
91+
if(their_cardinal_group.group == src)
92+
break
93+
expanding += in_cardinal_tile.status_traits[trait]
94+
break
95+
96+
for(var/obj/in_tile in expand_loc)
97+
var/datum/object_system/adjacency_group/their_group = in_tile.status_traits?[trait]
98+
if(!their_group || their_group == expand_this)
99+
continue
100+
dirs_connecting |= ONTOP_BIT
101+
if(their_group.group != src)
102+
if(their_group.group)
103+
qdel(their_group.group)
104+
their_group.group = src
105+
their_group.connected_directions = dirs_connecting
106+
in_group += their_group
107+
108+
if(expand_this.group != src)
109+
if(expand_this.group)
110+
qdel(expand_this.group)
111+
expand_this.group = src
112+
expand_this.connected_directions = dirs_connecting
113+
in_group += expand_this
114+
115+
for(var/datum/object_system/adjacency_group/member as anything in in_group)
116+
member.parent.object_adjacency_group_join(member, src, member.connected_directions)
117+
118+
/datum/adjacency_group/proc/teardown()
119+
for(var/datum/object_system/adjacency_group/member as anything in in_group)
120+
member.parent.object_adjacency_group_leave(member, src, member.connected_directions)
121+
member.group = null
122+
member.connected_directions = NONE
123+
member.queue_rebuild()
124+
in_group = null
125+
126+
//* /obj hooks *//
127+
128+
/**
129+
* Called when an adjacency group is being built from ourselves and initial data is needed
130+
*/
131+
/obj/proc/object_adjacency_group_create_initial_data(datum/object_system/adjacency_group/group_holder)
132+
return null
133+
134+
/**
135+
* Called on adjacency group join
136+
*
137+
* @params
138+
* * group_holder - object system handling the group
139+
* * group - the group in question
140+
* * directions - directions we're connecting in, as X_BIT (e.g. NORTHEAST_BIT). This will only have cardinals and ONTOP_BIT if something is atop us.
141+
*/
142+
/obj/proc/object_adjacency_group_join(datum/object_system/adjacency_group/group_holder, datum/object_system/adjacency_group/group, directions)
143+
return
144+
145+
/**
146+
* Called on adjacency group leave
147+
*
148+
* @params
149+
* * group_holder - object system handling the group
150+
* * group - the group in question
151+
* * directions - directions we **were** connected in, as X_BIT (e.g. NORTHEAST_BIT). This will only have cardinals and ONTOP_BIT if something was atop us.
152+
*/
153+
/obj/proc/object_adjacency_group_leave(datum/object_system/adjacency_group/group_holder, datum/object_system/adjacency_group/group, directions)
154+
return

code/modules/automata/types/wave.dm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
if(WAVE_SPREAD_SHADOW_LIKE)
3434
// directionals fully allowed
3535
// we use our own dir bits
36-
edges[T] = dir? dir : ALL_DIRECTION_BITS
36+
edges[T] = dir? dir : CARDINAL_DIAGONAL_DIRECTION_BITS
3737
powers[T] = power
3838
if(WAVE_SPREAD_SHOCKWAVE)
3939
// no directionals. we use cardinal bits, though, due to the algorithm.

0 commit comments

Comments
 (0)