mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-25 22:19:30 +01:00
* Smart pipes now actually go over smart pipes
This was ostensibly the main feature of smart pipes, so should be
fairly important
Parentheses are technically not required around the |, but I think
it's important to emphasise the importance of parentheses around
bitwise operators as this was apparently broken since 3e8407c471
Also purge var/connections and var/connection_num, since it's
literally only used for a `machine.connection_num < 3` check
which is completely redundant, and we already build a bitfield
tracking active directions.
* Smart pipes bridge over straight pipe types
This improves bridging to now go over most devices where possible,
letting you, for example, bridge over a perpindicular layer adapter.
* Pipe construction: Better documentation
Cleans up smart pipe documentation to match what's actually happening.
No functional changes.
* Skip pipes on different layers earlier
This prevents a pipe being made into a bridge perpindicular to a pipe
on a completely different layer, then failing to turn into a bridge
perpindicular to a relevant pipe.
* Pop superfluous parentheses
* Prevent creation of stub pipes
Setting the RPD to create a smart pipe that can only connect in one
direction would cause it to create invisible pipes that would still
block the placement of other pipes.
* The RPD can now reprogram smart pipes
The RPD can only affect smart pipes in directions that they are not
currently connected to.
This makes it easier to adjust designs after the fact, including
prevening round-start pipes from eagerly linking to a grey layer
adapter.
* Even smarter pipes
No more turning into bridge pipes. We now only try to be smart if
placement would fail, and we do our best to find a solution
whenever there is at least one smart pipe involved, regardless
of whether we're currently placing a smart pipe or not.
We never reconfigure any directions that a smart pipe is currently
connected, and we never reconfigure a smart pipe to have one or less
usable directions.
* Smartly go across perpindicular layer adapters
Also works when we're placing a layer adapter perpindicular to a
promiscuous smart pipe
* Pipes: Factor out loops and some bitfiddling
Create and use helpers with documented purposes over inline bitfiddling,
when it makes sense.
Many loops and switch statements were recreating information that was
already there.
Some relationships between pipe bitfield states were already assumed.
This centralises the functionality that relies upon these assumptions,
places them where the bits are defined, and documents them.
Rewrite some bitwise operations to be more idiomatic.
* Smart pipes: Debugging output
I normally clean history before pushing changes to any project,
but I feel like this should be saved.
* Revert "Smart pipes: Debugging output"
This reverts commit bb3aa76cf6d08e4d0951113a26fc9d48b6bc1735.
* Add trailing comma
The lack of this was making the linter sad
348 lines
12 KiB
Plaintext
348 lines
12 KiB
Plaintext
/*CONTENTS
|
|
Buildable pipes
|
|
Buildable meters
|
|
*/
|
|
|
|
//construction defines are in __defines/pipe_construction.dm
|
|
//update those defines ANY TIME an atmos path is changed...
|
|
//...otherwise construction will stop working
|
|
|
|
/obj/item/pipe
|
|
name = "pipe"
|
|
desc = "A pipe."
|
|
var/pipe_type
|
|
var/pipename
|
|
force = 7
|
|
throwforce = 7
|
|
icon = 'icons/obj/atmospherics/pipes/pipe_item.dmi'
|
|
icon_state = "simple"
|
|
inhand_icon_state = "buildpipe"
|
|
w_class = WEIGHT_CLASS_NORMAL
|
|
///Piping layer that we are going to be on
|
|
var/piping_layer = PIPING_LAYER_DEFAULT
|
|
///Type of pipe-object made, selected from the RPD
|
|
var/RPD_type
|
|
///Whether it can be painted
|
|
var/paintable = FALSE
|
|
///Color of the pipe is going to be made from this pipe-object
|
|
var/pipe_color
|
|
///Initial direction of the created pipe (either made from the RPD or after unwrenching the pipe)
|
|
var/p_init_dir = SOUTH
|
|
|
|
/obj/item/pipe/directional
|
|
RPD_type = PIPE_UNARY
|
|
/obj/item/pipe/binary
|
|
RPD_type = PIPE_STRAIGHT
|
|
/obj/item/pipe/binary/bendable
|
|
RPD_type = PIPE_BENDABLE
|
|
/obj/item/pipe/trinary
|
|
RPD_type = PIPE_TRINARY
|
|
/obj/item/pipe/trinary/flippable
|
|
RPD_type = PIPE_TRIN_M
|
|
var/flipped = FALSE
|
|
/obj/item/pipe/quaternary
|
|
RPD_type = PIPE_ONEDIR
|
|
|
|
/obj/item/pipe/ComponentInitialize()
|
|
//Flipping handled manually due to custom handling for trinary pipes
|
|
AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE)
|
|
|
|
/obj/item/pipe/Initialize(mapload, _pipe_type, _dir, obj/machinery/atmospherics/make_from, device_color, device_init_dir = SOUTH)
|
|
if(make_from)
|
|
make_from_existing(make_from)
|
|
else
|
|
p_init_dir = device_init_dir
|
|
pipe_type = _pipe_type
|
|
pipe_color = device_color
|
|
setDir(_dir)
|
|
|
|
update()
|
|
pixel_x += rand(-5, 5)
|
|
pixel_y += rand(-5, 5)
|
|
return ..()
|
|
|
|
/obj/item/pipe/proc/make_from_existing(obj/machinery/atmospherics/make_from)
|
|
p_init_dir = make_from.initialize_directions
|
|
setDir(make_from.dir)
|
|
pipename = make_from.name
|
|
add_atom_colour(make_from.color, FIXED_COLOUR_PRIORITY)
|
|
pipe_type = make_from.type
|
|
paintable = make_from.paintable
|
|
pipe_color = make_from.pipe_color
|
|
|
|
/obj/item/pipe/trinary/flippable/make_from_existing(obj/machinery/atmospherics/components/trinary/make_from)
|
|
..()
|
|
if(make_from.flipped)
|
|
do_a_flip()
|
|
|
|
/obj/item/pipe/dropped()
|
|
if(loc)
|
|
setPipingLayer(piping_layer)
|
|
return ..()
|
|
|
|
/obj/item/pipe/proc/setPipingLayer(new_layer = PIPING_LAYER_DEFAULT)
|
|
var/obj/machinery/atmospherics/fakeA = pipe_type
|
|
|
|
if(initial(fakeA.pipe_flags) & PIPING_ALL_LAYER)
|
|
new_layer = PIPING_LAYER_DEFAULT
|
|
piping_layer = new_layer
|
|
|
|
PIPING_LAYER_SHIFT(src, piping_layer)
|
|
layer = initial(layer) + ((piping_layer - PIPING_LAYER_DEFAULT) * PIPING_LAYER_LCHANGE)
|
|
|
|
/obj/item/pipe/proc/update()
|
|
var/obj/machinery/atmospherics/fakeA = pipe_type
|
|
name = "[initial(fakeA.name)] fitting"
|
|
icon_state = initial(fakeA.pipe_state)
|
|
if(ispath(pipe_type,/obj/machinery/atmospherics/pipe/heat_exchanging))
|
|
resistance_flags |= FIRE_PROOF | LAVA_PROOF
|
|
|
|
/obj/item/pipe/verb/flip()
|
|
set category = "Object"
|
|
set name = "Flip Pipe"
|
|
set src in view(1)
|
|
|
|
if ( usr.incapacitated() )
|
|
return
|
|
|
|
do_a_flip()
|
|
|
|
/obj/item/pipe/proc/do_a_flip()
|
|
setDir(turn(dir, -180))
|
|
|
|
/obj/item/pipe/trinary/flippable/do_a_flip()
|
|
setDir(turn(dir, flipped ? 45 : -45))
|
|
flipped = !flipped
|
|
|
|
/obj/item/pipe/Move()
|
|
var/old_dir = dir
|
|
..()
|
|
setDir(old_dir) //pipes changing direction when moved is just annoying and buggy
|
|
|
|
// Convert dir of fitting into dir of built component
|
|
/obj/item/pipe/proc/fixed_dir()
|
|
return dir
|
|
|
|
/obj/item/pipe/binary/fixed_dir()
|
|
. = dir
|
|
if(dir == SOUTH)
|
|
. = NORTH
|
|
else if(dir == WEST)
|
|
. = EAST
|
|
|
|
/obj/item/pipe/trinary/flippable/fixed_dir()
|
|
. = dir
|
|
if(ISDIAGONALDIR(dir))
|
|
. = turn(dir, 45)
|
|
|
|
/obj/item/pipe/attack_self(mob/user)
|
|
setDir(turn(dir,-90))
|
|
|
|
///Check if the pipe on the turf and our to be placed binary pipe are perpendicular to each other
|
|
/obj/item/pipe/proc/check_ninety_degree_dir(obj/machinery/atmospherics/machine)
|
|
if(ISDIAGONALDIR(machine.dir))
|
|
return FALSE
|
|
if(EWCOMPONENT(machine.dir) && EWCOMPONENT(dir))
|
|
return FALSE
|
|
if(NSCOMPONENT(machine.dir) && NSCOMPONENT(dir))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/obj/item/pipe/wrench_act(mob/living/user, obj/item/wrench/wrench)
|
|
. = ..()
|
|
if(!isturf(loc))
|
|
return TRUE
|
|
|
|
add_fingerprint(user)
|
|
|
|
var/obj/machinery/atmospherics/fakeA = pipe_type
|
|
var/flags = initial(fakeA.pipe_flags)
|
|
var/list/potentially_conflicting_machines = list()
|
|
// Work out which machines we would potentially conflict with
|
|
for(var/obj/machinery/atmospherics/machine in loc)
|
|
// skip checks if we don't overlap layers, either by being on the same layer or by something being on all layers
|
|
if(machine.piping_layer != piping_layer && !((machine.pipe_flags | flags) & PIPING_ALL_LAYER))
|
|
continue
|
|
// Only one dense/requires density object per tile, eg connectors/cryo/heater/coolers.
|
|
if((machine.pipe_flags & flags & PIPING_ONE_PER_TURF))
|
|
to_chat(user, span_warning("Something is hogging the tile!"))
|
|
return TRUE
|
|
potentially_conflicting_machines += machine
|
|
|
|
// See if we would conflict with any of the potentially interacting machines
|
|
for(var/obj/machinery/atmospherics/machine as anything in potentially_conflicting_machines)
|
|
// if the pipes have any directions in common, we can't place it that way.
|
|
var/our_init_dirs = SSair.get_init_dirs(pipe_type, fixed_dir(), p_init_dir)
|
|
if(machine.GetInitDirections() & our_init_dirs)
|
|
// We have a conflict!
|
|
if (length(potentially_conflicting_machines) != 1 || !try_smart_reconfiguration(machine, our_init_dirs, user))
|
|
// No solutions found
|
|
to_chat(user, span_warning("There is already a pipe at that location!"))
|
|
return TRUE
|
|
// no conflicts found
|
|
|
|
var/obj/machinery/atmospherics/built_machine = new pipe_type(loc, , , p_init_dir)
|
|
build_pipe(built_machine)
|
|
built_machine.on_construction(pipe_color, piping_layer)
|
|
transfer_fingerprints_to(built_machine)
|
|
|
|
wrench.play_tool_sound(src)
|
|
user.visible_message( \
|
|
"[user] fastens \the [src].", \
|
|
span_notice("You fasten \the [src]."), \
|
|
span_hear("You hear ratcheting."))
|
|
|
|
qdel(src)
|
|
|
|
/**
|
|
* Attempt to automatically resolve a pipe conflict by reconfiguring any smart pipes involved.
|
|
*
|
|
* Constraints:
|
|
* - A smart pipe cannot have current connections reconfigured.
|
|
* - A smart pipe cannot have fewer than two directions in which it will connect.
|
|
* - A smart pipe, existing or new, will not automatically reconfigure itself to permit directions it was not previously permitting.
|
|
*/
|
|
/obj/item/pipe/proc/try_smart_reconfiguration(obj/machinery/atmospherics/machine, our_init_dirs, mob/living/user)
|
|
// If we're a smart pipe, we might be able to solve this by placing down a more constrained version of ourselves.
|
|
var/obj/machinery/atmospherics/pipe/smart/other_smart_pipe = machine
|
|
if(ispath(pipe_type, /obj/machinery/atmospherics/pipe/smart/))
|
|
// If we're conflicting with another smart pipe, see if we can negotiate.
|
|
if(istype(other_smart_pipe))
|
|
// Two smart pipes. This is going to get complicated.
|
|
// Check to see whether the already placed pipe is bent or not.
|
|
if (ISDIAGONALDIR(other_smart_pipe.dir))
|
|
// The other pipe is bent, with at least two current connections. See if we can bounce off it as a bent pipe in the other direction.
|
|
var/opposing_dir = our_init_dirs & ~other_smart_pipe.connections
|
|
if (ISNOTSTUB(opposing_dir))
|
|
// We only get here if both smart pipes have two directions.
|
|
p_init_dir = opposing_dir
|
|
other_smart_pipe.SetInitDirections(other_smart_pipe.connections)
|
|
other_smart_pipe.update_pipe_icon()
|
|
return TRUE
|
|
// We're left with one or no available directions if we look at the complement of the other smart pipe's live connections.
|
|
// There's nothing further we can do.
|
|
return FALSE
|
|
else
|
|
// The other pipe is straight. See if we can go over it in a perpindicular direction.
|
|
// Note that the other pipe cannot be unconnected, since we have a conflict.
|
|
if(EWCOMPONENT(other_smart_pipe.dir))
|
|
if ((NORTH|SOUTH) & ~p_init_dir)
|
|
// Not allowed to connect this way
|
|
return FALSE
|
|
if (~other_smart_pipe.GetInitDirections() & (EAST|WEST))
|
|
// Not allowed to reconfigure the other pipe this way
|
|
return FALSE
|
|
p_init_dir = NORTH|SOUTH
|
|
other_smart_pipe.SetInitDirections(EAST|WEST)
|
|
other_smart_pipe.update_pipe_icon()
|
|
return TRUE
|
|
if (NSCOMPONENT(other_smart_pipe.dir))
|
|
if ((EAST|WEST) & ~p_init_dir)
|
|
// Not allowed to connect this way
|
|
return FALSE
|
|
if (~other_smart_pipe.GetInitDirections() & (NORTH|SOUTH))
|
|
// Not allowed to reconfigure the other pipe this way
|
|
return FALSE
|
|
p_init_dir = EAST|WEST
|
|
other_smart_pipe.SetInitDirections(NORTH|SOUTH)
|
|
other_smart_pipe.update_pipe_icon()
|
|
return TRUE
|
|
return FALSE
|
|
// We're not dealing with another smart pipe. See if we can become the complement of the conflicting machine.
|
|
var/opposing_dir = our_init_dirs & ~machine.GetInitDirections()
|
|
if (ISNOTSTUB(opposing_dir))
|
|
// We have at least two permitted directions in the complement. Use them.
|
|
p_init_dir = opposing_dir
|
|
return TRUE
|
|
return FALSE
|
|
|
|
else if(istype(other_smart_pipe))
|
|
// We're not a smart pipe ourselves, but we are conflicting with a smart pipe. We might be able to solve this by constraining the smart pipe.
|
|
if (our_init_dirs & other_smart_pipe.connections)
|
|
// We needed to go where a smart pipe already had connections, nothing further we can do
|
|
return FALSE
|
|
var/opposing_dir = other_smart_pipe.GetInitDirections() & ~our_init_dirs
|
|
if (ISNOTSTUB(opposing_dir))
|
|
// At least two directions remain for that smart pipe, reconfigure it
|
|
other_smart_pipe.SetInitDirections(opposing_dir)
|
|
other_smart_pipe.update_pipe_icon()
|
|
return TRUE
|
|
return FALSE
|
|
// No smart pipes involved, the conflict can't be solved this way.
|
|
return FALSE
|
|
|
|
/obj/item/pipe/proc/build_pipe(obj/machinery/atmospherics/A)
|
|
A.setDir(fixed_dir())
|
|
A.SetInitDirections(p_init_dir)
|
|
|
|
if(pipename)
|
|
A.name = pipename
|
|
if(A.on)
|
|
// Certain pre-mapped subtypes are on by default, we want to preserve
|
|
// every other aspect of these subtypes (name, pre-set filters, etc.)
|
|
// but they shouldn't turn on automatically when wrenched.
|
|
A.on = FALSE
|
|
|
|
/obj/item/pipe/trinary/flippable/build_pipe(obj/machinery/atmospherics/components/trinary/T)
|
|
..()
|
|
T.flipped = flipped
|
|
|
|
/obj/item/pipe/directional/suicide_act(mob/user)
|
|
user.visible_message(span_suicide("[user] shoves [src] in [user.p_their()] mouth and turns it on! It looks like [user.p_theyre()] trying to commit suicide!"))
|
|
if(iscarbon(user))
|
|
var/mob/living/carbon/C = user
|
|
for(var/i=1 to 20)
|
|
C.vomit(0, TRUE, FALSE, 4, FALSE)
|
|
if(prob(20))
|
|
C.spew_organ()
|
|
sleep(5)
|
|
C.blood_volume = 0
|
|
return(OXYLOSS|BRUTELOSS)
|
|
|
|
/obj/item/pipe_meter
|
|
name = "meter"
|
|
desc = "A meter that can be wrenched on pipes, or attached to the floor with screws."
|
|
icon = 'icons/obj/atmospherics/pipes/pipe_item.dmi'
|
|
icon_state = "meter"
|
|
inhand_icon_state = "buildpipe"
|
|
w_class = WEIGHT_CLASS_BULKY
|
|
var/piping_layer = PIPING_LAYER_DEFAULT
|
|
|
|
/obj/item/pipe_meter/wrench_act(mob/living/user, obj/item/wrench/W)
|
|
. = ..()
|
|
var/obj/machinery/atmospherics/pipe/pipe
|
|
for(var/obj/machinery/atmospherics/pipe/P in loc)
|
|
if(P.piping_layer == piping_layer)
|
|
pipe = P
|
|
break
|
|
if(!pipe)
|
|
to_chat(user, span_warning("You need to fasten it to a pipe!"))
|
|
return TRUE
|
|
new /obj/machinery/meter(loc, piping_layer)
|
|
W.play_tool_sound(src)
|
|
to_chat(user, span_notice("You fasten the meter to the pipe."))
|
|
qdel(src)
|
|
|
|
/obj/item/pipe_meter/screwdriver_act(mob/living/user, obj/item/S)
|
|
. = ..()
|
|
if(.)
|
|
return TRUE
|
|
|
|
if(!isturf(loc))
|
|
to_chat(user, span_warning("You need to fasten it to the floor!"))
|
|
return TRUE
|
|
|
|
new /obj/machinery/meter/turf(loc, piping_layer)
|
|
S.play_tool_sound(src)
|
|
to_chat(user, span_notice("You fasten the meter to the [loc.name]."))
|
|
qdel(src)
|
|
|
|
/obj/item/pipe_meter/dropped()
|
|
. = ..()
|
|
if(loc)
|
|
setAttachLayer(piping_layer)
|
|
|
|
/obj/item/pipe_meter/proc/setAttachLayer(new_layer = PIPING_LAYER_DEFAULT)
|
|
piping_layer = new_layer
|
|
PIPING_LAYER_DOUBLE_SHIFT(src, piping_layer)
|