Merge branch 'master' into AI_langs

This commit is contained in:
Atermonera
2017-09-25 11:40:40 -07:00
committed by GitHub
1349 changed files with 67646 additions and 27065 deletions
+3 -3
View File
@@ -3,9 +3,9 @@ language: c
sudo: false
env:
BYOND_MAJOR="510"
BYOND_MINOR="1346"
MACRO_COUNT=986
BYOND_MAJOR="511"
BYOND_MINOR="1381"
MACRO_COUNT=4
cache:
directories:
+6 -4
View File
@@ -7,16 +7,18 @@ Polaris is a fork of the Baystation12 code branch for the game Spacestation13.
---
### LICENSE
Polaris is licensed under the GNU Affero General Public License version 3, which can be found in full in LICENSE-AGPL3.txt.
The code for Polaris is licensed under the [GNU Affero General Public License](http://www.gnu.org/licenses/agpl.html) version 3, which can be found in full in LICENSE-AGPL3.txt.
Commits with a git authorship date prior to `1420675200 +0000` (2015/01/08 00:00) are licensed under the GNU General Public License version 3, which can be found in full in LICENSE-GPL3.txt.
Code with a git authorship date prior to `1420675200 +0000` (2015/01/08 00:00) are licensed under the GNU General Public License version 3, which can be found in full in LICENSE-GPL3.txt.
All commits whose authorship dates are not prior to `1420675200 +0000` are assumed to be licensed under AGPL v3, if you wish to license under GPL v3 please make this clear in the commit message and any added files.
All code whose authorship dates are not prior to `1420675200 +0000` is assumed to be licensed under AGPL v3, if you wish to license under GPL v3 please make this clear in the commit message and any added files.
If you wish to develop and host this codebase in a closed source manner you may use all commits prior to `1420675200 +0000`, which are licensed under GPL v3. The major change here is that if you host a server using any code licensed under AGPLv3 you are required to provide full source code for your servers users as well including addons and modifications you have made.
If you wish to develop and host this codebase in a closed source manner you may use all code prior to `1420675200 +0000`, which is licensed under GPL v3. The major change here is that if you host a server using any code licensed under AGPLv3 you are required to provide full source code for your servers users as well including addons and modifications you have made.
See [here](https://www.gnu.org/licenses/why-affero-gpl.html) for more information.
All assets including icons and sound are under a [CC BY-SA 3.0](http://creativecommons.org/licenses/by-sa/3.0/) license unless otherwise indicated.
### GETTING THE CODE
The simplest way to obtain the code is using the github .zip feature.
+8 -1
View File
@@ -124,4 +124,11 @@ obj/machinery/atmospherics/proc/check_connect_types(obj/machinery/atmospherics/a
/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference)
/obj/machinery/atmospherics/update_icon()
return null
return null
/obj/machinery/atmospherics/proc/can_unwrench()
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
if((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
return 0
return 1
@@ -1,4 +1,4 @@
obj/machinery/atmospherics/binary
/obj/machinery/atmospherics/binary
dir = SOUTH
initialize_directions = SOUTH|NORTH
use_power = 1
@@ -9,125 +9,131 @@ obj/machinery/atmospherics/binary
var/datum/pipe_network/network1
var/datum/pipe_network/network2
New()
..()
switch(dir)
if(NORTH)
initialize_directions = NORTH|SOUTH
if(SOUTH)
initialize_directions = NORTH|SOUTH
if(EAST)
initialize_directions = EAST|WEST
if(WEST)
initialize_directions = EAST|WEST
air1 = new
air2 = new
/obj/machinery/atmospherics/binary/New()
..()
air1.volume = 200
air2.volume = 200
air1 = new
air2 = new
air1.volume = 200
air2.volume = 200
/obj/machinery/atmospherics/binary/init_dir()
switch(dir)
if(NORTH)
initialize_directions = NORTH|SOUTH
if(SOUTH)
initialize_directions = NORTH|SOUTH
if(EAST)
initialize_directions = EAST|WEST
if(WEST)
initialize_directions = EAST|WEST
// Housekeeping and pipe network stuff below
network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
if(reference == node1)
network1 = new_network
/obj/machinery/atmospherics/binary/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
if(reference == node1)
network1 = new_network
else if(reference == node2)
network2 = new_network
else if(reference == node2)
network2 = new_network
if(new_network.normal_members.Find(src))
return 0
if(new_network.normal_members.Find(src))
return 0
new_network.normal_members += src
new_network.normal_members += src
return null
return null
Destroy()
loc = null
/obj/machinery/atmospherics/binary/Destroy()
. = ..()
if(node1)
node1.disconnect(src)
qdel(network1)
if(node2)
node2.disconnect(src)
qdel(network2)
if(node1)
node1.disconnect(src)
qdel(network1)
if(node2)
node2.disconnect(src)
qdel(network2)
node1 = null
node2 = null
/obj/machinery/atmospherics/binary/initialize()
if(node1 && node2)
return
init_dir()
var/node2_connect = dir
var/node1_connect = turn(dir, 180)
for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node2 = target
break
update_icon()
update_underlays()
/obj/machinery/atmospherics/binary/build_network()
if(!network1 && node1)
network1 = new /datum/pipe_network()
network1.normal_members += src
network1.build_network(node1, src)
if(!network2 && node2)
network2 = new /datum/pipe_network()
network2.normal_members += src
network2.build_network(node2, src)
/obj/machinery/atmospherics/binary/return_network(obj/machinery/atmospherics/reference)
build_network()
if(reference==node1)
return network1
if(reference==node2)
return network2
return null
/obj/machinery/atmospherics/binary/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
if(network1 == old_network)
network1 = new_network
if(network2 == old_network)
network2 = new_network
return 1
/obj/machinery/atmospherics/binary/return_network_air(datum/pipe_network/reference)
var/list/results = list()
if(network1 == reference)
results += air1
if(network2 == reference)
results += air2
return results
/obj/machinery/atmospherics/binary/disconnect(obj/machinery/atmospherics/reference)
if(reference==node1)
qdel(network1)
node1 = null
else if(reference==node2)
qdel(network2)
node2 = null
..()
update_icon()
update_underlays()
initialize()
if(node1 && node2) return
var/node2_connect = dir
var/node1_connect = turn(dir, 180)
for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect))
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect))
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node2 = target
break
update_icon()
update_underlays()
build_network()
if(!network1 && node1)
network1 = new /datum/pipe_network()
network1.normal_members += src
network1.build_network(node1, src)
if(!network2 && node2)
network2 = new /datum/pipe_network()
network2.normal_members += src
network2.build_network(node2, src)
return_network(obj/machinery/atmospherics/reference)
build_network()
if(reference==node1)
return network1
if(reference==node2)
return network2
return null
reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
if(network1 == old_network)
network1 = new_network
if(network2 == old_network)
network2 = new_network
return 1
return_network_air(datum/pipe_network/reference)
var/list/results = list()
if(network1 == reference)
results += air1
if(network2 == reference)
results += air2
return results
disconnect(obj/machinery/atmospherics/reference)
if(reference==node1)
qdel(network1)
node1 = null
else if(reference==node2)
qdel(network2)
node2 = null
update_icon()
update_underlays()
return null
return null
@@ -89,11 +89,11 @@
/obj/machinery/atmospherics/binary/circulator/attackby(obj/item/weapon/W as obj, mob/user as mob)
if(istype(W, /obj/item/weapon/wrench))
playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1)
playsound(src, W.usesound, 75, 1)
anchored = !anchored
user.visible_message("[user.name] [anchored ? "secures" : "unsecures"] the bolts holding [src.name] to the floor.", \
"You [anchored ? "secure" : "unsecure"] the bolts holding [src] to the floor.", \
"You hear a ratchet")
"You hear a ratchet.")
if(anchored)
if(dir & (NORTH|SOUTH))
@@ -11,15 +11,15 @@
desc = "A one-way air valve that can be used to regulate input or output pressure, and flow rate. Does not require power."
use_power = 0
var/unlocked = 0 //If 0, then the valve is locked closed, otherwise it is open(-able, it's a one-way valve so it closes if gas would flow backwards).
var/target_pressure = ONE_ATMOSPHERE
var/max_pressure_setting = 15000 //kPa
var/set_flow_rate = ATMOS_DEFAULT_VOLUME_PUMP * 2.5
var/regulate_mode = REGULATE_OUTPUT
var/flowing = 0 //for icons - becomes zero if the valve closes itself due to regulation mode
var/frequency = 0
var/id = null
var/datum/radio_frequency/radio_connection
@@ -46,9 +46,9 @@
/obj/machinery/atmospherics/binary/passive_gate/process()
..()
last_flow_rate = 0
if(!unlocked)
return 0
@@ -61,35 +61,35 @@
pressure_delta = input_starting_pressure - target_pressure
if (REGULATE_OUTPUT)
pressure_delta = target_pressure - output_starting_pressure
//-1 if pump_gas() did not move any gas, >= 0 otherwise
var/returnval = -1
if((regulate_mode == REGULATE_NONE || pressure_delta > 0.01) && (air1.temperature > 0 || air2.temperature > 0)) //since it's basically a valve, it makes sense to check both temperatures
flowing = 1
//flow rate limit
var/transfer_moles = (set_flow_rate/air1.volume)*air1.total_moles
//Figure out how much gas to transfer to meet the target pressure.
switch (regulate_mode)
if (REGULATE_INPUT)
transfer_moles = min(transfer_moles, calculate_transfer_moles(air2, air1, pressure_delta, (network1)? network1.volume : 0))
if (REGULATE_OUTPUT)
transfer_moles = min(transfer_moles, calculate_transfer_moles(air1, air2, pressure_delta, (network2)? network2.volume : 0))
//pump_gas() will return a negative number if no flow occurred
returnval = pump_gas_passive(src, air1, air2, transfer_moles)
if (returnval >= 0)
if(network1)
network1.update = 1
if(network2)
network2.update = 1
if (last_flow_rate)
flowing = 1
update_icon()
@@ -178,7 +178,7 @@
// this is the data which will be sent to the ui
var/data[0]
data = list(
"on" = unlocked,
"pressure_set" = round(target_pressure*100), //Nano UI can't handle rounded non-integers, apparently.
@@ -203,16 +203,16 @@
/obj/machinery/atmospherics/binary/passive_gate/Topic(href,href_list)
if(..()) return 1
if(href_list["toggle_valve"])
unlocked = !unlocked
if(href_list["regulate_mode"])
switch(href_list["regulate_mode"])
if ("off") regulate_mode = REGULATE_NONE
if ("input") regulate_mode = REGULATE_INPUT
if ("output") regulate_mode = REGULATE_OUTPUT
switch(href_list["set_press"])
if ("min")
target_pressure = 0
@@ -221,7 +221,7 @@
if ("set")
var/new_pressure = input(usr,"Enter new output pressure (0-[max_pressure_setting]kPa)","Pressure Control",src.target_pressure) as num
src.target_pressure = between(0, new_pressure, max_pressure_setting)
switch(href_list["set_flow_rate"])
if ("min")
set_flow_rate = 0
@@ -230,7 +230,7 @@
if ("set")
var/new_flow_rate = input(usr,"Enter new flow rate limit (0-[air1.volume]kPa)","Flow Rate Control",src.set_flow_rate) as num
src.set_flow_rate = between(0, new_flow_rate, air1.volume)
usr.set_machine(src) //Is this even needed with NanoUI?
src.update_icon()
src.add_fingerprint(usr)
@@ -242,15 +242,13 @@
if (unlocked)
user << "<span class='warning'>You cannot unwrench \the [src], turn it off first.</span>"
return 1
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
user << "<span class='warning'>You cannot unwrench \the [src], it too exerted due to internal pressure.</span>"
if(!can_unwrench())
to_chat(user, "<span class='warning'>You cannot unwrench \the [src], it too exerted due to internal pressure.</span>")
add_fingerprint(user)
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
playsound(src, W.usesound, 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -36,7 +36,7 @@
initialize_directions = NORTH|SOUTH
Destroy()
loc = null
. = ..()
if(node1)
node1.disconnect(src)
@@ -48,8 +48,6 @@
node1 = null
node2 = null
..()
process()
..()
if(anchored && !(stat&BROKEN))
@@ -88,6 +86,7 @@
attackby(obj/item/weapon/W as obj, mob/user as mob)
if(istype(W, /obj/item/weapon/wrench))
anchored = !anchored
playsound(src, W.usesound, 50, 1)
user << "<span class='notice'>You [anchored ? "secure" : "unsecure"] the bolts holding \the [src] to the floor.</span>"
if(anchored)
@@ -259,6 +258,7 @@
attackby(obj/item/weapon/W as obj, mob/user as mob)
if(istype(W, /obj/item/weapon/wrench))
anchored = !anchored
playsound(src, W.usesound, 50, 1)
turbine = null
user << "<span class='notice'>You [anchored ? "secure" : "unsecure"] the bolts holding \the [src] to the floor.</span>"
updateConnection()
@@ -221,15 +221,13 @@ Thus, the two variables affect pump operation are set in New():
if (!(stat & NOPOWER) && use_power)
user << "<span class='warning'>You cannot unwrench this [src], turn it off first.</span>"
return 1
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
user << "<span class='warning'>You cannot unwrench this [src], it too exerted due to internal pressure.</span>"
if(!can_unwrench())
to_chat(user, "<span class='warning'>You cannot unwrench this [src], it too exerted due to internal pressure.</span>")
add_fingerprint(user)
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
playsound(src, W.usesound, 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -28,7 +28,7 @@
input = null
output = null
filters.Cut()
..()
return ..()
/obj/machinery/atmospherics/omni/filter/sort_ports()
for(var/datum/omni_port/P in ports)
@@ -82,17 +82,13 @@
if(!istype(W, /obj/item/weapon/wrench))
return ..()
var/int_pressure = 0
for(var/datum/omni_port/P in ports)
int_pressure += P.air.return_pressure()
var/datum/gas_mixture/env_air = loc.return_air()
if ((int_pressure - env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
user << "<span class='warning'>You cannot unwrench \the [src], it is too exerted due to internal pressure.</span>"
if(!can_unwrench())
to_chat(user, "<span class='warning'>You cannot unwrench \the [src], it is too exerted due to internal pressure.</span>")
add_fingerprint(user)
return 1
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
if(do_after(user, 40))
playsound(src, W.usesound, 50, 1)
if(do_after(user, 40 * W.toolspeed))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -100,6 +96,15 @@
new /obj/item/pipe(loc, make_from=src)
qdel(src)
/obj/machinery/atmospherics/omni/can_unwrench()
var/int_pressure = 0
for(var/datum/omni_port/P in ports)
int_pressure += P.air.return_pressure()
var/datum/gas_mixture/env_air = loc.return_air()
if((int_pressure - env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
return 0
return 1
/obj/machinery/atmospherics/omni/attack_hand(user as mob)
if(..())
return
@@ -247,6 +252,7 @@
if(P.node || P.mode == 0)
continue
for(var/obj/machinery/atmospherics/target in get_step(src, P.dir))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
P.node = target
@@ -18,9 +18,11 @@
use_power = 0
level = 1
/obj/machinery/atmospherics/portables_connector/init_dir()
initialize_directions = dir
/obj/machinery/atmospherics/portables_connector/New()
initialize_directions = dir
init_dir()
..()
/obj/machinery/atmospherics/portables_connector/update_icon()
@@ -61,7 +63,7 @@
return null
/obj/machinery/atmospherics/portables_connector/Destroy()
loc = null
. = ..()
if(connected_device)
connected_device.disconnect()
@@ -72,14 +74,16 @@
node = null
..()
/obj/machinery/atmospherics/portables_connector/initialize()
if(node) return
if(node)
return
init_dir()
var/node_connect = dir
for(var/obj/machinery/atmospherics/target in get_step(src,node_connect))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node = target
@@ -138,15 +142,13 @@
return 1
if (locate(/obj/machinery/portable_atmospherics, src.loc))
return 1
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
user << "<span class='warning'>You cannot unwrench \the [src], it too exerted due to internal pressure.</span>"
if(!can_unwrench())
to_chat(user, "<span class='warning'>You cannot unwrench \the [src], it too exerted due to internal pressure.</span>")
add_fingerprint(user)
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
playsound(src, W.usesound, 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -131,15 +131,13 @@
/obj/machinery/atmospherics/trinary/filter/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
if (!istype(W, /obj/item/weapon/wrench))
return ..()
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
user << "<span class='warning'>You cannot unwrench \the [src], it too exerted due to internal pressure.</span>"
if(!can_unwrench())
to_chat(user, "<span class='warning'>You cannot unwrench \the [src], it too exerted due to internal pressure.</span>")
add_fingerprint(user)
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
playsound(src, W.usesound, 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -68,7 +68,7 @@
air1.volume = ATMOS_DEFAULT_VOLUME_MIXER
air2.volume = ATMOS_DEFAULT_VOLUME_MIXER
air3.volume = ATMOS_DEFAULT_VOLUME_MIXER * 1.5
if (!mixing_inputs)
mixing_inputs = list(src.air1 = node1_concentration, src.air2 = node2_concentration)
@@ -106,15 +106,13 @@
/obj/machinery/atmospherics/trinary/mixer/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
if (!istype(W, /obj/item/weapon/wrench))
return ..()
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
user << "<span class='warning'>You cannot unwrench \the [src], it too exerted due to internal pressure.</span>"
if(!can_unwrench())
to_chat(user, "<span class='warning'>You cannot unwrench \the [src], it too exerted due to internal pressure.</span>")
add_fingerprint(user)
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
playsound(src, W.usesound, 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -1,4 +1,4 @@
obj/machinery/atmospherics/trinary
/obj/machinery/atmospherics/trinary
dir = SOUTH
initialize_directions = SOUTH|NORTH|WEST
use_power = 0
@@ -13,155 +13,163 @@ obj/machinery/atmospherics/trinary
var/datum/pipe_network/network2
var/datum/pipe_network/network3
New()
..()
switch(dir)
if(NORTH)
initialize_directions = EAST|NORTH|SOUTH
if(SOUTH)
initialize_directions = SOUTH|WEST|NORTH
if(EAST)
initialize_directions = EAST|WEST|SOUTH
if(WEST)
initialize_directions = WEST|NORTH|EAST
air1 = new
air2 = new
air3 = new
/obj/machinery/atmospherics/trinary/New()
..()
init_dir()
air1.volume = 200
air2.volume = 200
air3.volume = 200
air1 = new
air2 = new
air3 = new
air1.volume = 200
air2.volume = 200
air3.volume = 200
/obj/machinery/atmospherics/trinary/init_dir()
switch(dir)
if(NORTH)
initialize_directions = EAST|NORTH|SOUTH
if(SOUTH)
initialize_directions = SOUTH|WEST|NORTH
if(EAST)
initialize_directions = EAST|WEST|SOUTH
if(WEST)
initialize_directions = WEST|NORTH|EAST
// Housekeeping and pipe network stuff below
network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
if(reference == node1)
network1 = new_network
/obj/machinery/atmospherics/trinary/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
if(reference == node1)
network1 = new_network
else if(reference == node2)
network2 = new_network
else if(reference == node2)
network2 = new_network
else if (reference == node3)
network3 = new_network
else if (reference == node3)
network3 = new_network
if(new_network.normal_members.Find(src))
return 0
if(new_network.normal_members.Find(src))
return 0
new_network.normal_members += src
new_network.normal_members += src
return null
return null
Destroy()
loc = null
/obj/machinery/atmospherics/trinary/Destroy()
. = ..()
if(node1)
node1.disconnect(src)
qdel(network1)
if(node2)
node2.disconnect(src)
qdel(network2)
if(node3)
node3.disconnect(src)
qdel(network3)
if(node1)
node1.disconnect(src)
qdel(network1)
if(node2)
node2.disconnect(src)
qdel(network2)
if(node3)
node3.disconnect(src)
qdel(network3)
node1 = null
node2 = null
node3 = null
/obj/machinery/atmospherics/trinary/initialize()
if(node1 && node2 && node3)
return
init_dir()
var/node1_connect = turn(dir, -180)
var/node2_connect = turn(dir, -90)
var/node3_connect = dir
for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node2 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node3 = target
break
update_icon()
update_underlays()
/obj/machinery/atmospherics/trinary/build_network()
if(!network1 && node1)
network1 = new /datum/pipe_network()
network1.normal_members += src
network1.build_network(node1, src)
if(!network2 && node2)
network2 = new /datum/pipe_network()
network2.normal_members += src
network2.build_network(node2, src)
if(!network3 && node3)
network3 = new /datum/pipe_network()
network3.normal_members += src
network3.build_network(node3, src)
/obj/machinery/atmospherics/trinary/return_network(obj/machinery/atmospherics/reference)
build_network()
if(reference==node1)
return network1
if(reference==node2)
return network2
if(reference==node3)
return network3
return null
/obj/machinery/atmospherics/trinary/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
if(network1 == old_network)
network1 = new_network
if(network2 == old_network)
network2 = new_network
if(network3 == old_network)
network3 = new_network
return 1
/obj/machinery/atmospherics/trinary/return_network_air(datum/pipe_network/reference)
var/list/results = list()
if(network1 == reference)
results += air1
if(network2 == reference)
results += air2
if(network3 == reference)
results += air3
return results
/obj/machinery/atmospherics/trinary/disconnect(obj/machinery/atmospherics/reference)
if(reference==node1)
qdel(network1)
node1 = null
else if(reference==node2)
qdel(network2)
node2 = null
else if(reference==node3)
qdel(network3)
node3 = null
..()
update_underlays()
initialize()
if(node1 && node2 && node3) return
var/node1_connect = turn(dir, -180)
var/node2_connect = turn(dir, -90)
var/node3_connect = dir
for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect))
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect))
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node2 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect))
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node3 = target
break
update_icon()
update_underlays()
build_network()
if(!network1 && node1)
network1 = new /datum/pipe_network()
network1.normal_members += src
network1.build_network(node1, src)
if(!network2 && node2)
network2 = new /datum/pipe_network()
network2.normal_members += src
network2.build_network(node2, src)
if(!network3 && node3)
network3 = new /datum/pipe_network()
network3.normal_members += src
network3.build_network(node3, src)
return_network(obj/machinery/atmospherics/reference)
build_network()
if(reference==node1)
return network1
if(reference==node2)
return network2
if(reference==node3)
return network3
return null
reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
if(network1 == old_network)
network1 = new_network
if(network2 == old_network)
network2 = new_network
if(network3 == old_network)
network3 = new_network
return 1
return_network_air(datum/pipe_network/reference)
var/list/results = list()
if(network1 == reference)
results += air1
if(network2 == reference)
results += air2
if(network3 == reference)
results += air3
return results
disconnect(obj/machinery/atmospherics/reference)
if(reference==node1)
qdel(network1)
node1 = null
else if(reference==node2)
qdel(network2)
node2 = null
else if(reference==node3)
qdel(network3)
node3 = null
update_underlays()
return null
return null
+13 -12
View File
@@ -47,10 +47,10 @@
update_underlays()
/obj/machinery/atmospherics/tvalve/New()
initialize_directions()
init_dir()
..()
/obj/machinery/atmospherics/tvalve/proc/initialize_directions()
/obj/machinery/atmospherics/tvalve/init_dir()
switch(dir)
if(NORTH)
initialize_directions = SOUTH|NORTH|EAST
@@ -100,7 +100,7 @@
return null
/obj/machinery/atmospherics/tvalve/Destroy()
loc = null
. = ..()
if(node1)
node1.disconnect(src)
@@ -116,8 +116,6 @@
node2 = null
node3 = null
..()
/obj/machinery/atmospherics/tvalve/proc/go_to_side()
if(state) return 0
@@ -191,21 +189,26 @@
var/node2_dir
var/node3_dir
init_dir()
node1_dir = turn(dir, 180)
node2_dir = turn(dir, -90)
node3_dir = dir
for(var/obj/machinery/atmospherics/target in get_step(src,node1_dir))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node2_dir))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node2 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node3_dir))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node3 = target
@@ -350,15 +353,13 @@
if (istype(src, /obj/machinery/atmospherics/tvalve/digital))
user << "<span class='warning'>You cannot unwrench \the [src], it's too complicated.</span>"
return 1
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
user << "<span class='warnng'>You cannot unwrench \the [src], it too exerted due to internal pressure.</span>"
if(!can_unwrench())
to_chat(user, "<span class='warnng'>You cannot unwrench \the [src], it too exerted due to internal pressure.</span>")
add_fingerprint(user)
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
playsound(src, W.usesound, 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -373,7 +374,7 @@
icon_state = "map_tvalvem1"
state = 1
/obj/machinery/atmospherics/tvalve/mirrored/initialize_directions()
/obj/machinery/atmospherics/tvalve/mirrored/init_dir()
switch(dir)
if(NORTH)
initialize_directions = SOUTH|NORTH|WEST
@@ -162,7 +162,7 @@
if(istype(P, /obj/item/weapon/stock_parts/matter_bin))
bin_rating += P.rating
power_rating = initial(power_rating) * cap_rating / 2 //more powerful
max_power_rating = initial(max_power_rating) * cap_rating / 2 //more powerful
heatsink_temperature = initial(heatsink_temperature) / ((manip_rating + bin_rating) / 2) //more efficient
air_contents.volume = max(initial(internal_volume) - 200, 0) + 200 * bin_rating
set_power_level(power_setting)
@@ -72,15 +72,13 @@
if (level==1 && isturf(T) && !T.is_plating())
user << "<span class='warning'>You must remove the plating first.</span>"
return 1
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
if (!can_unwrench())
user << "<span class='warning'>You cannot unwrench \the [src], it is too exerted due to internal pressure.</span>"
add_fingerprint(user)
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
playsound(src, W.usesound, 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -11,85 +11,90 @@
var/welded = 0 //defining this here for ventcrawl stuff
New()
..()
initialize_directions = dir
air_contents = new
/obj/machinery/atmospherics/unary/New()
..()
init_dir()
air_contents = new
air_contents.volume = 200
air_contents.volume = 200
/obj/machinery/atmospherics/unary/init_dir()
initialize_directions = dir
// Housekeeping and pipe network stuff below
network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
if(reference == node)
network = new_network
/obj/machinery/atmospherics/unary/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
if(reference == node)
network = new_network
if(new_network.normal_members.Find(src))
return 0
if(new_network.normal_members.Find(src))
return 0
new_network.normal_members += src
new_network.normal_members += src
return null
return null
Destroy()
loc = null
/obj/machinery/atmospherics/unary/Destroy()
. = ..()
if(node)
node.disconnect(src)
qdel(network)
if(node)
node.disconnect(src)
qdel(network)
node = null
/obj/machinery/atmospherics/unary/initialize()
if(node)
return
init_dir()
var/node_connect = dir
for(var/obj/machinery/atmospherics/target in get_step(src,node_connect))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node = target
break
update_icon()
update_underlays()
/obj/machinery/atmospherics/unary/build_network()
if(!network && node)
network = new /datum/pipe_network()
network.normal_members += src
network.build_network(node, src)
/obj/machinery/atmospherics/unary/return_network(obj/machinery/atmospherics/reference)
build_network()
if(reference==node)
return network
return null
/obj/machinery/atmospherics/unary/reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
if(network == old_network)
network = new_network
return 1
/obj/machinery/atmospherics/unary/return_network_air(datum/pipe_network/reference)
var/list/results = list()
if(network == reference)
results += air_contents
return results
/obj/machinery/atmospherics/unary/disconnect(obj/machinery/atmospherics/reference)
if(reference==node)
qdel(network)
node = null
..()
update_icon()
update_underlays()
initialize()
if(node) return
var/node_connect = dir
for(var/obj/machinery/atmospherics/target in get_step(src,node_connect))
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node = target
break
update_icon()
update_underlays()
build_network()
if(!network && node)
network = new /datum/pipe_network()
network.normal_members += src
network.build_network(node, src)
return_network(obj/machinery/atmospherics/reference)
build_network()
if(reference==node)
return network
return null
reassign_network(datum/pipe_network/old_network, datum/pipe_network/new_network)
if(network == old_network)
network = new_network
return 1
return_network_air(datum/pipe_network/reference)
var/list/results = list()
if(network == reference)
results += air_contents
return results
disconnect(obj/machinery/atmospherics/reference)
if(reference==node)
qdel(network)
node = null
update_icon()
update_underlays()
return null
return null
+10 -16
View File
@@ -80,7 +80,10 @@
/obj/machinery/atmospherics/unary/vent_pump/Destroy()
unregister_radio(src, frequency)
..()
if(initial_loc)
initial_loc.air_vent_info -= id_tag
initial_loc.air_vent_names -= id_tag
return ..()
/obj/machinery/atmospherics/unary/vent_pump/high_volume
name = "Large Air Vent"
@@ -355,9 +358,9 @@
var/obj/item/weapon/weldingtool/WT = W
if (WT.remove_fuel(0,user))
user << "<span class='notice'>Now welding the vent.</span>"
if(do_after(user, 20))
if(do_after(user, 20 * WT.toolspeed))
if(!src || !WT.isOn()) return
playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1)
playsound(src.loc, WT.usesound, 50, 1)
if(!welded)
user.visible_message("<span class='notice'>\The [user] welds the vent shut.</span>", "<span class='notice'>You weld the vent shut.</span>", "You hear welding.")
welded = 1
@@ -398,15 +401,13 @@
if (node && node.level==1 && isturf(T) && !T.is_plating())
user << "<span class='warning'>You must remove the plating first.</span>"
return 1
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
user << "<span class='warning'>You cannot unwrench \the [src], it is too exerted due to internal pressure.</span>"
if(!can_unwrench())
to_chat(user, "<span class='warning'>You cannot unwrench \the [src], it is too exerted due to internal pressure.</span>")
add_fingerprint(user)
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
playsound(src, W.usesound, 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -414,13 +415,6 @@
new /obj/item/pipe(loc, make_from=src)
qdel(src)
/obj/machinery/atmospherics/unary/vent_pump/Destroy()
if(initial_loc)
initial_loc.air_vent_info -= id_tag
initial_loc.air_vent_names -= id_tag
..()
return
#undef DEFAULT_PRESSURE_DELTA
#undef EXTERNAL_PRESSURE_BOUND
@@ -44,8 +44,10 @@
/obj/machinery/atmospherics/unary/vent_scrubber/Destroy()
unregister_radio(src, frequency)
..()
if(initial_loc)
initial_loc.air_scrub_info -= id_tag
initial_loc.air_scrub_names -= id_tag
return ..()
/obj/machinery/atmospherics/unary/vent_scrubber/update_icon(var/safety = 0)
if(!check_icon_cache())
@@ -105,6 +107,7 @@
"filter_co2" = ("carbon_dioxide" in scrubbing_gas),
"filter_phoron" = ("phoron" in scrubbing_gas),
"filter_n2o" = ("sleeping_agent" in scrubbing_gas),
"filter_fuel" = ("volatile_fuel" in scrubbing_gas),
"sigtype" = "status"
)
if(!initial_loc.air_scrub_names[id_tag])
@@ -231,6 +234,11 @@
else if(signal.data["toggle_n2o_scrub"])
toggle += "sleeping_agent"
if(!isnull(signal.data["fuel_scrub"]) && text2num(signal.data["fuel_scrub"]) != ("volatile_fuel" in scrubbing_gas))
toggle += "volatile_fuel"
else if(signal.data["toggle_fuel_scrub"])
toggle += "volatile_fuel"
scrubbing_gas ^= toggle
if(signal.data["init"] != null)
@@ -264,15 +272,13 @@
if (node && node.level==1 && isturf(T) && !T.is_plating())
user << "<span class='warning'>You must remove the plating first.</span>"
return 1
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
user << "<span class='warning'>You cannot unwrench \the [src], it is too exerted due to internal pressure.</span>"
if(!can_unwrench())
to_chat(user, "<span class='warning'>You cannot unwrench \the [src], it is too exerted due to internal pressure.</span>")
add_fingerprint(user)
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
playsound(src, W.usesound, 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -285,10 +291,3 @@
user << "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W"
else
user << "You are too far away to read the gauge."
/obj/machinery/atmospherics/unary/vent_scrubber/Destroy()
if(initial_loc)
initial_loc.air_scrub_info -= id_tag
initial_loc.air_scrub_names -= id_tag
..()
return
+9 -11
View File
@@ -38,13 +38,12 @@
/obj/machinery/atmospherics/valve/hide(var/i)
update_underlays()
/obj/machinery/atmospherics/valve/New()
/obj/machinery/atmospherics/valve/init_dir()
switch(dir)
if(NORTH || SOUTH)
initialize_directions = NORTH|SOUTH
if(EAST || WEST)
initialize_directions = EAST|WEST
..()
/obj/machinery/atmospherics/valve/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference)
if(reference == node1)
@@ -72,7 +71,7 @@
return null
/obj/machinery/atmospherics/valve/Destroy()
loc = null
. = ..()
if(node1)
node1.disconnect(src)
@@ -84,8 +83,6 @@
node1 = null
node2 = null
..()
/obj/machinery/atmospherics/valve/proc/open()
if(open) return 0
@@ -144,6 +141,7 @@
return
/obj/machinery/atmospherics/valve/initialize()
init_dir()
normalize_dir()
var/node1_dir
@@ -157,11 +155,13 @@
node2_dir = direction
for(var/obj/machinery/atmospherics/target in get_step(src,node1_dir))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node2_dir))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node2 = target
@@ -294,15 +294,13 @@
if (istype(src, /obj/machinery/atmospherics/valve/digital) && !src.allowed(user))
user << "<span class='warning'>Access denied.</span>"
return 1
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
user << "<span class='warning'>You cannot unwrench \the [src], it is too exerted due to internal pressure.</span>"
if(!can_unwrench())
to_chat(user, "<span class='warning'>You cannot unwrench \the [src], it is too exerted due to internal pressure.</span>")
add_fingerprint(user)
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
playsound(src, W.usesound, 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
+9
View File
@@ -16,6 +16,15 @@ datum/pipe_network
..()
Destroy()
pipe_networks -= src
for(var/datum/pipeline/line_member in line_members)
line_member.network = null
for(var/obj/machinery/atmospherics/normal_member in normal_members)
normal_member.reassign_network(src, null)
gases.Cut() // Do not qdel the gases, we don't own them
return ..()
proc/process()
//Equalize gases amongst pipe if called for
if(update)
+7 -5
View File
@@ -10,14 +10,14 @@ datum/pipeline
var/alert_pressure = 0
Destroy()
if(network)
qdel(network)
qdel_null(network)
if(air && air.volume)
temporarily_store_air()
qdel(air)
for(var/obj/machinery/atmospherics/pipe/P in members)
P.parent = null
..()
. = ..()
proc/process()//This use to be called called from the pipe networks
@@ -116,15 +116,17 @@ datum/pipeline
if(istype(target) && target.zone)
//Have to consider preservation of group statuses
var/datum/gas_mixture/turf_copy = new
var/datum/gas_mixture/turf_original = new
turf_copy.copy_from(target.zone.air)
turf_copy.volume = target.zone.air.volume //Copy a good representation of the turf from parent group
turf_original.copy_from(turf_copy)
equalize_gases(list(air_sample, turf_copy))
air.merge(air_sample)
turf_copy.subtract(target.zone.air)
target.zone.air.remove(turf_original.total_moles)
target.zone.air.merge(turf_copy)
else
+108 -99
View File
@@ -1,5 +1,5 @@
obj/machinery/atmospherics/pipe/simple/heat_exchanging
/obj/machinery/atmospherics/pipe/simple/heat_exchanging
icon = 'icons/atmos/heat.dmi'
icon_state = "intact"
pipe_icon = "hepipe"
@@ -17,93 +17,100 @@ obj/machinery/atmospherics/pipe/simple/heat_exchanging
buckle_lying = 1
// BubbleWrap
New()
..()
initialize_directions_he = initialize_directions // The auto-detection from /pipe is good enough for a simple HE pipe
// BubbleWrap END
color = "#404040" //we don't make use of the fancy overlay system for colours, use this to set the default.
/obj/machinery/atmospherics/pipe/simple/heat_exchanging/New()
..()
init_dir()
// BubbleWrap END
color = "#404040" //we don't make use of the fancy overlay system for colours, use this to set the default.
initialize()
normalize_dir()
var/node1_dir
var/node2_dir
/obj/machinery/atmospherics/pipe/simple/heat_exchanging/init_dir()
..()
initialize_directions_he = initialize_directions // The auto-detection from /pipe is good enough for a simple HE pipe
for(var/direction in cardinal)
if(direction&initialize_directions_he)
if (!node1_dir)
node1_dir = direction
else if (!node2_dir)
node2_dir = direction
/obj/machinery/atmospherics/pipe/simple/heat_exchanging/initialize()
init_dir()
normalize_dir()
var/node1_dir
var/node2_dir
for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,node1_dir))
if(target.initialize_directions_he & get_dir(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,node2_dir))
if(target.initialize_directions_he & get_dir(target,src))
node2 = target
break
if(!node1 && !node2)
qdel(src)
return
for(var/direction in cardinal)
if(direction&initialize_directions_he)
if (!node1_dir)
node1_dir = direction
else if (!node2_dir)
node2_dir = direction
update_icon()
for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,node1_dir))
target.init_dir()
if(target.initialize_directions_he & get_dir(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,node2_dir))
target.init_dir()
if(target.initialize_directions_he & get_dir(target,src))
node2 = target
break
if(!node1 && !node2)
qdel(src)
return
update_icon()
return
process()
if(!parent)
..()
else
var/datum/gas_mixture/pipe_air = return_air()
if(istype(loc, /turf/simulated/))
var/environment_temperature = 0
if(loc:blocks_air)
environment_temperature = loc:temperature
else
var/datum/gas_mixture/environment = loc.return_air()
environment_temperature = environment.temperature
if(abs(environment_temperature-pipe_air.temperature) > minimum_temperature_difference)
parent.temperature_interact(loc, volume, thermal_conductivity)
else if(istype(loc, /turf/space/))
parent.radiate_heat_to_space(surface, 1)
if(buckled_mob)
var/hc = pipe_air.heat_capacity()
var/avg_temp = (pipe_air.temperature * hc + buckled_mob.bodytemperature * 3500) / (hc + 3500)
pipe_air.temperature = avg_temp
buckled_mob.bodytemperature = avg_temp
/obj/machinery/atmospherics/pipe/simple/heat_exchanging/process()
if(!parent)
..()
else
var/datum/gas_mixture/pipe_air = return_air()
if(istype(loc, /turf/simulated/))
var/environment_temperature = 0
if(loc:blocks_air)
environment_temperature = loc:temperature
else
var/datum/gas_mixture/environment = loc.return_air()
environment_temperature = environment.temperature
if(abs(environment_temperature-pipe_air.temperature) > minimum_temperature_difference)
parent.temperature_interact(loc, volume, thermal_conductivity)
else if(istype(loc, /turf/space/))
parent.radiate_heat_to_space(surface, 1)
var/heat_limit = 1000
if(buckled_mob)
var/hc = pipe_air.heat_capacity()
var/avg_temp = (pipe_air.temperature * hc + buckled_mob.bodytemperature * 3500) / (hc + 3500)
pipe_air.temperature = avg_temp
buckled_mob.bodytemperature = avg_temp
var/mob/living/carbon/human/H = buckled_mob
if(istype(H) && H.species)
heat_limit = H.species.heat_level_3
var/heat_limit = 1000
if(pipe_air.temperature > heat_limit + 1)
buckled_mob.apply_damage(4 * log(pipe_air.temperature - heat_limit), BURN, BP_TORSO, used_weapon = "Excessive Heat")
var/mob/living/carbon/human/H = buckled_mob
if(istype(H) && H.species)
heat_limit = H.species.heat_level_3
//fancy radiation glowing
if(pipe_air.temperature && (icon_temperature > 500 || pipe_air.temperature > 500)) //start glowing at 500K
if(abs(pipe_air.temperature - icon_temperature) > 10)
icon_temperature = pipe_air.temperature
if(pipe_air.temperature > heat_limit + 1)
buckled_mob.apply_damage(4 * log(pipe_air.temperature - heat_limit), BURN, BP_TORSO, used_weapon = "Excessive Heat")
var/h_r = heat2color_r(icon_temperature)
var/h_g = heat2color_g(icon_temperature)
var/h_b = heat2color_b(icon_temperature)
//fancy radiation glowing
if(pipe_air.temperature && (icon_temperature > 500 || pipe_air.temperature > 500)) //start glowing at 500K
if(abs(pipe_air.temperature - icon_temperature) > 10)
icon_temperature = pipe_air.temperature
if(icon_temperature < 2000) //scale up overlay until 2000K
var/scale = (icon_temperature - 500) / 1500
h_r = 64 + (h_r - 64)*scale
h_g = 64 + (h_g - 64)*scale
h_b = 64 + (h_b - 64)*scale
var/h_r = heat2color_r(icon_temperature)
var/h_g = heat2color_g(icon_temperature)
var/h_b = heat2color_b(icon_temperature)
animate(src, color = rgb(h_r, h_g, h_b), time = 20, easing = SINE_EASING)
if(icon_temperature < 2000) //scale up overlay until 2000K
var/scale = (icon_temperature - 500) / 1500
h_r = 64 + (h_r - 64)*scale
h_g = 64 + (h_g - 64)*scale
h_b = 64 + (h_b - 64)*scale
animate(src, color = rgb(h_r, h_g, h_b), time = 20, easing = SINE_EASING)
obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction
/obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction
icon = 'icons/atmos/junction.dmi'
icon_state = "intact"
pipe_icon = "hejunction"
@@ -112,37 +119,39 @@ obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction
minimum_temperature_difference = 300
thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT
// BubbleWrap
New()
.. ()
switch ( dir )
if ( SOUTH )
initialize_directions = NORTH
initialize_directions_he = SOUTH
if ( NORTH )
initialize_directions = SOUTH
initialize_directions_he = NORTH
if ( EAST )
initialize_directions = WEST
initialize_directions_he = EAST
if ( WEST )
initialize_directions = EAST
initialize_directions_he = WEST
// BubbleWrap END
/obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction/init_dir()
..()
switch ( dir )
if ( SOUTH )
initialize_directions = NORTH
initialize_directions_he = SOUTH
if ( NORTH )
initialize_directions = SOUTH
initialize_directions_he = NORTH
if ( EAST )
initialize_directions = WEST
initialize_directions_he = EAST
if ( WEST )
initialize_directions = EAST
initialize_directions_he = WEST
initialize()
for(var/obj/machinery/atmospherics/target in get_step(src,initialize_directions))
if(target.initialize_directions & get_dir(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,initialize_directions_he))
if(target.initialize_directions_he & get_dir(target,src))
node2 = target
break
if(!node1&&!node2)
qdel(src)
return
/obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction/initialize()
init_dir()
for(var/obj/machinery/atmospherics/target in get_step(src,initialize_directions))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src,initialize_directions_he))
target.init_dir()
if(target.initialize_directions_he & get_dir(target,src))
node2 = target
break
update_icon()
if(!node1&&!node2)
qdel(src)
return
update_icon()
return
+80 -31
View File
@@ -34,6 +34,10 @@
return 1
// This is used to set up what directions pipes will connect to. Called inside New(), initialize(), and when pipes look at another pipe, incase they didn't get to initialize() yet.
/obj/machinery/atmospherics/proc/init_dir()
return
/obj/machinery/atmospherics/pipe/return_air()
if(!parent)
parent = new /datum/pipeline()
@@ -63,17 +67,15 @@
return parent.return_network(reference)
/obj/machinery/atmospherics/pipe/Destroy()
qdel(parent)
qdel_null(parent)
if(air_temporary)
loc.assume_air(air_temporary)
..()
. = ..()
/obj/machinery/atmospherics/pipe/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
if (istype(src, /obj/machinery/atmospherics/pipe/tank))
return ..()
if (istype(src, /obj/machinery/atmospherics/pipe/vent))
return ..()
if(istype(W,/obj/item/device/pipe_painter))
return 0
@@ -84,15 +86,13 @@
if (level==1 && isturf(T) && !T.is_plating())
user << "<span class='warning'>You must remove the plating first.</span>"
return 1
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
user << "<span class='warning'>You cannot unwrench \the [src], it is too exerted due to internal pressure.</span>"
if(!can_unwrench())
to_chat(user, "<span class='warning'>You cannot unwrench \the [src], it is too exerted due to internal pressure.</span>")
add_fingerprint(user)
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
playsound(src, W.usesound, 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40 * W.toolspeed))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -170,19 +170,9 @@
icon = null
alpha = 255
switch(dir)
if(SOUTH || NORTH)
initialize_directions = SOUTH|NORTH
if(EAST || WEST)
initialize_directions = EAST|WEST
if(NORTHEAST)
initialize_directions = NORTH|EAST
if(NORTHWEST)
initialize_directions = NORTH|WEST
if(SOUTHEAST)
initialize_directions = SOUTH|EAST
if(SOUTHWEST)
initialize_directions = SOUTH|WEST
init_dir()
/obj/machinery/atmospherics/pipe/simple/hide(var/i)
if(istype(loc, /turf/simulated))
@@ -210,6 +200,21 @@
else return 1
/obj/machinery/atmospherics/pipe/simple/init_dir()
switch(dir)
if(SOUTH || NORTH)
initialize_directions = SOUTH|NORTH
if(EAST || WEST)
initialize_directions = EAST|WEST
if(NORTHEAST)
initialize_directions = NORTH|EAST
if(NORTHWEST)
initialize_directions = NORTH|WEST
if(SOUTHEAST)
initialize_directions = SOUTH|EAST
if(SOUTHWEST)
initialize_directions = SOUTH|WEST
/obj/machinery/atmospherics/pipe/simple/proc/burst()
src.visible_message("<span class='danger'>\The [src] bursts!</span>");
playsound(src.loc, 'sound/effects/bang.ogg', 25, 1)
@@ -227,10 +232,12 @@
/obj/machinery/atmospherics/pipe/simple/Destroy()
if(node1)
node1.disconnect(src)
node1 = null
if(node2)
node2.disconnect(src)
node1 = null
..()
. = ..()
/obj/machinery/atmospherics/pipe/simple/pipeline_expansion()
return list(node1, node2)
@@ -268,6 +275,7 @@
return
/obj/machinery/atmospherics/pipe/simple/initialize()
init_dir()
normalize_dir()
var/node1_dir
var/node2_dir
@@ -280,11 +288,13 @@
node2_dir = direction
for(var/obj/machinery/atmospherics/target in get_step(src,node1_dir))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,node2_dir))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node2 = target
@@ -434,6 +444,9 @@
alpha = 255
icon = null
init_dir()
/obj/machinery/atmospherics/pipe/manifold/init_dir()
switch(dir)
if(NORTH)
initialize_directions = EAST|SOUTH|WEST
@@ -461,12 +474,15 @@
/obj/machinery/atmospherics/pipe/manifold/Destroy()
if(node1)
node1.disconnect(src)
node1 = null
if(node2)
node2.disconnect(src)
node2 = null
if(node3)
node3.disconnect(src)
node3 = null
..()
. = ..()
/obj/machinery/atmospherics/pipe/manifold/disconnect(obj/machinery/atmospherics/reference)
if(reference == node1)
@@ -539,11 +555,13 @@
update_icon()
/obj/machinery/atmospherics/pipe/manifold/initialize()
init_dir()
var/connect_directions = (NORTH|SOUTH|EAST|WEST)&(~dir)
for(var/direction in cardinal)
if(direction&connect_directions)
for(var/obj/machinery/atmospherics/target in get_step(src,direction))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
@@ -556,6 +574,7 @@
for(var/direction in cardinal)
if(direction&connect_directions)
for(var/obj/machinery/atmospherics/target in get_step(src,direction))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node2 = target
@@ -568,6 +587,7 @@
for(var/direction in cardinal)
if(direction&connect_directions)
for(var/obj/machinery/atmospherics/target in get_step(src,direction))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node3 = target
@@ -705,14 +725,18 @@
/obj/machinery/atmospherics/pipe/manifold4w/Destroy()
if(node1)
node1.disconnect(src)
node1 = null
if(node2)
node2.disconnect(src)
node2 = null
if(node3)
node3.disconnect(src)
node3 = null
if(node4)
node4.disconnect(src)
node4 = null
..()
. = ..()
/obj/machinery/atmospherics/pipe/manifold4w/disconnect(obj/machinery/atmospherics/reference)
if(reference == node1)
@@ -814,24 +838,28 @@
/obj/machinery/atmospherics/pipe/manifold4w/initialize()
for(var/obj/machinery/atmospherics/target in get_step(src,1))
target.init_dir()
if(target.initialize_directions & 2)
if (check_connect_types(target,src))
node1 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,2))
target.init_dir()
if(target.initialize_directions & 1)
if (check_connect_types(target,src))
node2 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,4))
target.init_dir()
if(target.initialize_directions & 8)
if (check_connect_types(target,src))
node3 = target
break
for(var/obj/machinery/atmospherics/target in get_step(src,8))
target.init_dir()
if(target.initialize_directions & 4)
if (check_connect_types(target,src))
node4 = target
@@ -949,6 +977,9 @@
/obj/machinery/atmospherics/pipe/cap/New()
..()
init_dir()
/obj/machinery/atmospherics/pipe/cap/init_dir()
initialize_directions = dir
/obj/machinery/atmospherics/pipe/cap/hide(var/i)
@@ -967,8 +998,9 @@
/obj/machinery/atmospherics/pipe/cap/Destroy()
if(node)
node.disconnect(src)
node = null
..()
. = ..()
/obj/machinery/atmospherics/pipe/cap/disconnect(obj/machinery/atmospherics/reference)
if(reference == node)
@@ -996,7 +1028,9 @@
overlays += icon_manager.get_atmos_icon("pipe", , pipe_color, "cap")
/obj/machinery/atmospherics/pipe/cap/initialize()
init_dir()
for(var/obj/machinery/atmospherics/target in get_step(src, dir))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node = target
@@ -1006,6 +1040,9 @@
if(level == 1 && !T.is_plating()) hide(1)
update_icon()
/obj/machinery/atmospherics/pipe/cap/can_unwrench()
return 1
/obj/machinery/atmospherics/pipe/cap/visible
level = 2
icon_state = "cap"
@@ -1069,9 +1106,12 @@
/obj/machinery/atmospherics/pipe/tank/New()
icon_state = "air"
initialize_directions = dir
init_dir()
..()
/obj/machinery/atmospherics/pipe/tank/init_dir()
initialize_directions = dir
/obj/machinery/atmospherics/pipe/tank/process()
if(!parent)
..()
@@ -1081,8 +1121,9 @@
/obj/machinery/atmospherics/pipe/tank/Destroy()
if(node1)
node1.disconnect(src)
node1 = null
..()
. = ..()
/obj/machinery/atmospherics/pipe/tank/pipeline_expansion()
return list(node1)
@@ -1099,9 +1140,11 @@
update_underlays()
/obj/machinery/atmospherics/pipe/tank/initialize()
init_dir()
var/connect_direction = dir
for(var/obj/machinery/atmospherics/target in get_step(src,connect_direction))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
@@ -1230,9 +1273,12 @@
var/build_killswitch = 1
/obj/machinery/atmospherics/pipe/vent/New()
initialize_directions = dir
init_dir()
..()
/obj/machinery/atmospherics/pipe/vent/init_dir()
initialize_directions = dir
/obj/machinery/atmospherics/pipe/vent/high_volume
name = "Larger vent"
volume = 1000
@@ -1251,8 +1297,9 @@
/obj/machinery/atmospherics/pipe/vent/Destroy()
if(node1)
node1.disconnect(src)
node1 = null
..()
. = ..()
/obj/machinery/atmospherics/pipe/vent/pipeline_expansion()
return list(node1)
@@ -1267,9 +1314,11 @@
icon_state = "exposed"
/obj/machinery/atmospherics/pipe/vent/initialize()
init_dir()
var/connect_direction = dir
for(var/obj/machinery/atmospherics/target in get_step(src,connect_direction))
target.init_dir()
if(target.initialize_directions & get_dir(target,src))
if (check_connect_types(target,src))
node1 = target
+1 -1
View File
@@ -22,7 +22,7 @@ mob/proc/airflow_stun()
mob/living/silicon/airflow_stun()
return
mob/living/carbon/slime/airflow_stun()
mob/living/simple_animal/slime/airflow_stun()
return
mob/living/carbon/human/airflow_stun()
+8 -2
View File
@@ -57,11 +57,11 @@ turf/c_airblock(turf/other)
#ifdef ZASDBG
ASSERT(isturf(other))
#endif
if(blocks_air || other.blocks_air)
if(((blocks_air & AIR_BLOCKED) || (other.blocks_air & AIR_BLOCKED)))
return BLOCKED
//Z-level handling code. Always block if there isn't an open space.
#ifdef ZLEVELS
#ifdef MULTIZAS
if(other.z != src.z)
if(other.z < src.z)
if(!istype(src, /turf/simulated/open)) return BLOCKED
@@ -69,6 +69,12 @@ turf/c_airblock(turf/other)
if(!istype(other, /turf/simulated/open)) return BLOCKED
#endif
if(((blocks_air & ZONE_BLOCKED) || (other.blocks_air & ZONE_BLOCKED)))
if(z == other.z)
return ZONE_BLOCKED
else
return AIR_BLOCKED
var/result = 0
for(var/atom/movable/M in contents)
result |= M.c_airblock(other)
+10 -7
View File
@@ -24,12 +24,16 @@ Class Procs:
erase_all()
Called when the turf is changed with ChangeTurf(). Erases all existing connections.
Macros:
check(connection/c)
Checks for connection validity. It's possible to have a reference to a connection that has been erased.
*/
// macro-ized to cut down on proc calls
#define check(c) (c && c.valid())
/turf/var/tmp/connection_manager/connections
/connection_manager/var/connection/N
@@ -37,7 +41,7 @@ Class Procs:
/connection_manager/var/connection/E
/connection_manager/var/connection/W
#ifdef ZLEVELS
#ifdef MULTIZAS
/connection_manager/var/connection/U
/connection_manager/var/connection/D
#endif
@@ -57,7 +61,7 @@ Class Procs:
if(check(W)) return W
else return null
#ifdef ZLEVELS
#ifdef MULTIZAS
if(UP)
if(check(U)) return U
else return null
@@ -73,7 +77,7 @@ Class Procs:
if(EAST) E = c
if(WEST) W = c
#ifdef ZLEVELS
#ifdef MULTIZAS
if(UP) U = c
if(DOWN) D = c
#endif
@@ -83,7 +87,7 @@ Class Procs:
if(check(S)) S.update()
if(check(E)) E.update()
if(check(W)) W.update()
#ifdef ZLEVELS
#ifdef MULTIZAS
if(check(U)) U.update()
if(check(D)) D.update()
#endif
@@ -93,10 +97,9 @@ Class Procs:
if(check(S)) S.erase()
if(check(E)) E.erase()
if(check(W)) W.erase()
#ifdef ZLEVELS
#ifdef MULTIZAS
if(check(U)) U.erase()
if(check(D)) D.erase()
#endif
/connection_manager/proc/check(connection/c)
return c && c.valid()
#undef check
+3
View File
@@ -158,6 +158,9 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
//defer updating of self-zone-blocked turfs until after all other turfs have been updated.
//this hopefully ensures that non-self-zone-blocked turfs adjacent to self-zone-blocked ones
//have valid zones when the self-zone-blocked turfs update.
//This ensures that doorways don't form their own single-turf zones, since doorways are self-zone-blocked and
//can merge with an adjacent zone, whereas zones that are formed on adjacent turfs cannot merge with the doorway.
var/list/deferred = list()
for(var/turf/T in updating)
+4
View File
@@ -39,6 +39,10 @@ client/proc/Test_ZAS_Connection(var/turf/simulated/T as turf)
"South" = SOUTH,\
"East" = EAST,\
"West" = WEST,\
#ifdef MULTIZAS
"Up" = UP,\
"Down" = DOWN,\
#endif
"N/A" = null)
var/direction = input("What direction do you wish to test?","Set direction") as null|anything in direction_list
if(!direction)
+17 -13
View File
@@ -103,19 +103,23 @@ obj/var/phoronproof = 0
//Burn eyes if exposed.
if(vsc.plc.EYE_BURNS && (species.breath_type != "phoron"))
if(!head)
if(!wear_mask)
burn_eyes()
else
if(!(wear_mask.body_parts_covered & EYES))
burn_eyes()
else
if(!(head.body_parts_covered & EYES))
if(!wear_mask)
burn_eyes()
else
if(!(wear_mask.body_parts_covered & EYES))
burn_eyes()
var/burn_eyes = 1
//Check for protective glasses
if(glasses && (glasses.body_parts_covered & EYES) && (glasses.item_flags & AIRTIGHT))
burn_eyes = 0
//Check for protective maskwear
if(burn_eyes && wear_mask && (wear_mask.body_parts_covered & EYES) && (wear_mask.item_flags & AIRTIGHT))
burn_eyes = 0
//Check for protective helmets
if(burn_eyes && head && (head.body_parts_covered & EYES) && (head.item_flags & AIRTIGHT))
burn_eyes = 0
//If we still need to, burn their eyes
if(burn_eyes)
burn_eyes()
//Genetic Corruption
if(vsc.plc.GENETIC_CORRUPTION && (species.breath_type != "phoron"))
+25 -19
View File
@@ -5,9 +5,9 @@
/turf/var/datum/gas_mixture/air
/turf/simulated/proc/update_graphic(list/graphic_add = null, list/graphic_remove = null)
if(graphic_add && graphic_add.len)
if(LAZYLEN(graphic_add))
overlays += graphic_add
if(graphic_remove && graphic_remove.len)
if(LAZYLEN(graphic_remove))
overlays -= graphic_remove
/turf/proc/update_air_properties()
@@ -16,7 +16,7 @@
//dbg(blocked)
return 1
#ifdef ZLEVELS
#ifdef MULTIZAS
for(var/d = 1, d < 64, d *= 2)
#else
for(var/d = 1, d < 16, d *= 2)
@@ -52,34 +52,40 @@
*/
/turf/simulated/proc/can_safely_remove_from_zone()
#ifdef ZLEVELS
return 0 //TODO generalize this to multiz.
#else
if(!zone) return 1
var/check_dirs = get_zone_neighbours(src)
var/unconnected_dirs = check_dirs
for(var/dir in list(NORTHWEST, NORTHEAST, SOUTHEAST, SOUTHWEST))
#ifdef MULTIZAS
var/to_check = cornerdirsz
#else
var/to_check = cornerdirs
#endif
for(var/dir in to_check)
//for each pair of "adjacent" cardinals (e.g. NORTH and WEST, but not NORTH and SOUTH)
if((dir & check_dirs) == dir)
//check that they are connected by the corner turf
var/connected_dirs = get_zone_neighbours(get_step(src, dir))
if(connected_dirs && (dir & turn(connected_dirs, 180)) == dir)
if(connected_dirs && (dir & reverse_dir[connected_dirs]) == dir)
unconnected_dirs &= ~dir //they are, so unflag the cardinals in question
//it is safe to remove src from the zone if all cardinals are connected by corner turfs
return !unconnected_dirs
#endif
//helper for can_safely_remove_from_zone()
/turf/simulated/proc/get_zone_neighbours(turf/simulated/T)
. = 0
if(istype(T) && T.zone)
for(var/dir in cardinal)
#ifdef MULTIZAS
var/to_check = cardinalz
#else
var/to_check = cardinal
#endif
for(var/dir in to_check)
var/turf/simulated/other = get_step(T, dir)
if(istype(other) && other.zone == T.zone && !(other.c_airblock(T) & AIR_BLOCKED) && get_dist(src, other) <= 1)
. |= dir
@@ -98,7 +104,7 @@
#endif
if(zone)
var/zone/z = zone
if(can_safely_remove_from_zone()) //Helps normal airlocks avoid rebuilding zones all the time
z.remove(src)
else
@@ -110,7 +116,7 @@
open_directions = 0
var/list/postponed
#ifdef ZLEVELS
#ifdef MULTIZAS
for(var/d = 1, d < 64, d *= 2)
#else
for(var/d = 1, d < 16, d *= 2)
@@ -161,7 +167,7 @@
//Might have assigned a zone, since this happens for each direction.
if(!zone)
//We do not merge if
//We do not merge if
// they are blocking us and we are not blocking them, or if
// we are blocking them and not blocking ourselves - this prevents tiny zones from forming on doorways.
if(((block & ZONE_BLOCKED) && !(r_block & ZONE_BLOCKED)) || ((r_block & ZONE_BLOCKED) && !(s_block & ZONE_BLOCKED)))
+10 -4
View File
@@ -77,7 +77,8 @@ Class Procs:
fire_tiles.Add(T)
air_master.active_fire_zones |= src
if(fuel) fuel_objs += fuel
T.update_graphic(air.graphic)
if(air.graphic)
T.update_graphic(air.graphic)
/zone/proc/remove(turf/simulated/T)
#ifdef ZASDBG
@@ -92,7 +93,8 @@ Class Procs:
var/obj/effect/decal/cleanable/liquid_fuel/fuel = locate() in T
fuel_objs -= fuel
T.zone = null
T.update_graphic(graphic_remove = air.graphic)
if(air.graphic)
T.update_graphic(graphic_remove = air.graphic)
if(contents.len)
air.group_multiplier = contents.len
else
@@ -106,9 +108,11 @@ Class Procs:
ASSERT(!into.invalid)
#endif
c_invalidate()
var/list/air_graphic = air.graphic // Cache for sanic speed
for(var/turf/simulated/T in contents)
into.add(T)
T.update_graphic(graphic_remove = air.graphic)
if(air_graphic)
T.update_graphic(graphic_remove = air_graphic)
#ifdef ZASDBG
T.dbg(merged)
#endif
@@ -131,8 +135,10 @@ Class Procs:
/zone/proc/rebuild()
if(invalid) return //Short circuit for explosions where rebuild is called many times over.
c_invalidate()
var/list/air_graphic = air.graphic // Cache for sanic speed
for(var/turf/simulated/T in contents)
T.update_graphic(graphic_remove = air.graphic) //we need to remove the overlays so they're not doubled when the zone is rebuilt
if(air_graphic)
T.update_graphic(graphic_remove = air_graphic) //we need to remove the overlays so they're not doubled when the zone is rebuilt
//T.dbg(invalid_zone)
T.needs_air_update = 0 //Reset the marker so that it will be added to the list.
air_master.mark_for_update(T)
+1 -2
View File
@@ -28,8 +28,7 @@ Notes for people who used ZAS before:
*/
//#define ZASDBG
//#define ZLEVELS
//#define MULTIZAS
#define AIR_BLOCKED 1
#define ZONE_BLOCKED 2
#define BLOCKED 3
+69
View File
@@ -0,0 +1,69 @@
#define MC_TICK_CHECK ( ( world.tick_usage > Master.current_ticklimit || src.state != SS_RUNNING ) ? pause() : 0 )
// Used for splitting up your remaining time into phases, if you want to evenly divide it.
#define MC_SPLIT_TICK_INIT(phase_count) var/original_tick_limit = Master.current_ticklimit; var/split_tick_phases = ##phase_count
#define MC_SPLIT_TICK \
if(split_tick_phases > 1){\
Master.current_ticklimit = ((original_tick_limit - world.tick_usage) / split_tick_phases) + world.tick_usage;\
--split_tick_phases;\
} else {\
Master.current_ticklimit = original_tick_limit;\
}
// Used to smooth out costs to try and avoid oscillation.
#define MC_AVERAGE_FAST(average, current) (0.7 * (average) + 0.3 * (current))
#define MC_AVERAGE(average, current) (0.8 * (average) + 0.2 * (current))
#define MC_AVERAGE_SLOW(average, current) (0.9 * (average) + 0.1 * (current))
#define NEW_SS_GLOBAL(varname) if(varname != src){if(istype(varname)){Recover();qdel(varname);}varname = src;}
#define START_PROCESSING(Processor, Datum) if (!Datum.isprocessing) {Datum.isprocessing = 1;Processor.processing += Datum}
#define STOP_PROCESSING(Processor, Datum) Datum.isprocessing = 0;Processor.processing -= Datum
//SubSystem flags (Please design any new flags so that the default is off, to make adding flags to subsystems easier)
//subsystem does not initialize.
#define SS_NO_INIT 1
//subsystem does not fire.
// (like can_fire = 0, but keeps it from getting added to the processing subsystems list)
// (Requires a MC restart to change)
#define SS_NO_FIRE 2
//subsystem only runs on spare cpu (after all non-background subsystems have ran that tick)
// SS_BACKGROUND has its own priority bracket
#define SS_BACKGROUND 4
//subsystem does not tick check, and should not run unless there is enough time (or its running behind (unless background))
#define SS_NO_TICK_CHECK 8
//Treat wait as a tick count, not DS, run every wait ticks.
// (also forces it to run first in the tick, above even SS_NO_TICK_CHECK subsystems)
// (implies all runlevels because of how it works)
// (overrides SS_BACKGROUND)
// This is designed for basically anything that works as a mini-mc (like SStimer)
#define SS_TICKER 16
//keep the subsystem's timing on point by firing early if it fired late last fire because of lag
// ie: if a 20ds subsystem fires say 5 ds late due to lag or what not, its next fire would be in 15ds, not 20ds.
#define SS_KEEP_TIMING 32
//Calculate its next fire after its fired.
// (IE: if a 5ds wait SS takes 2ds to run, its next fire should be 5ds away, not 3ds like it normally would be)
// This flag overrides SS_KEEP_TIMING
#define SS_POST_FIRE_TIMING 64
//SUBSYSTEM STATES
#define SS_IDLE 0 //aint doing shit.
#define SS_QUEUED 1 //queued to run
#define SS_RUNNING 2 //actively running
#define SS_PAUSED 3 //paused by mc_tick_check
#define SS_SLEEPING 4 //fire() slept.
#define SS_PAUSING 5 //in the middle of pausing
// Standard way to define a global subsystem, keep boilerplate organized here!
#define SUBSYSTEM_DEF(X) var/datum/controller/subsystem/##X/SS##X;\
/datum/controller/subsystem/##X/New(){\
NEW_SS_GLOBAL(SS##X);\
PreInit();\
}\
/datum/controller/subsystem/##X
+1
View File
@@ -74,6 +74,7 @@
#define XGM_GAS_FUEL 1
#define XGM_GAS_OXIDIZER 2
#define XGM_GAS_CONTAMINANT 4
#define XGM_GAS_FUSION_FUEL 8
#define TANK_LEAK_PRESSURE (30.*ONE_ATMOSPHERE) // Tank starts leaking.
#define TANK_RUPTURE_PRESSURE (40.*ONE_ATMOSPHERE) // Tank spills all contents into atmosphere.
+2
View File
@@ -34,6 +34,8 @@
#define CE_ALCOHOL_TOXIC "alcotoxic" // Liver damage
#define CE_SPEEDBOOST "gofast" // Hyperzine
#define REAGENTS_PER_SHEET 20
// Chemistry lists.
var/list/tachycardics = list("coffee", "inaprovaline", "hyperzine", "nitroglycerin", "thirteenloko", "nicotine") // Increase heart rate.
var/list/bradycardics = list("neurotoxin", "cryoxadone", "clonexadone", "space_drugs", "stoxin") // Decrease heart rate.
+6 -2
View File
@@ -63,6 +63,7 @@ var/list/be_special_flags = list(
// Mode/antag template macros.
#define MODE_BORER "borer"
#define MODE_XENOMORPH "xeno"
#define MODE_LOYALIST "loyalist"
#define MODE_MUTINEER "mutineer"
#define MODE_COMMANDO "commando"
@@ -84,6 +85,7 @@ var/list/be_special_flags = list(
#define MODE_MALFUNCTION "malf"
#define MODE_TRAITOR "traitor"
#define MODE_AUTOTRAITOR "autotraitor"
#define MODE_INFILTRATOR "infiltrator"
#define DEFAULT_TELECRYSTAL_AMOUNT 120
@@ -95,7 +97,7 @@ var/list/be_special_flags = list(
#define GHOSTCAST 0x1 //can a ghost cast it?
#define NEEDSCLOTHES 0x2 //does it need the wizard garb to cast? Nonwizard spells should not have this
#define NEEDSHUMAN 0x4 //does it require the caster to be human?
#define Z2NOCAST 0x8 //if this is added, the spell can't be cast at centcomm
#define Z2NOCAST 0x8 //if this is added, the spell can't be cast at CentCom
#define STATALLOWED 0x10 //if set, the user doesn't have to be conscious to cast. Required for ghost spells
#define IGNOREPREV 0x20 //if set, each new target does not overlap with the previous one
//The following flags only affect different types of spell, and therefore overlap
@@ -123,4 +125,6 @@ var/list/be_special_flags = list(
//casting costs
#define Sp_RECHARGE "recharge"
#define Sp_CHARGES "charges"
#define Sp_HOLDVAR "holdervar"
#define Sp_HOLDVAR "holdervar"
#define CHANGELING_STASIS_COST 20
+1 -1
View File
@@ -33,7 +33,7 @@
#define PROXMOVE 0x80 // Does this object require proximity checking in Enter()?
//Flags for items (equipment)
#define THICKMATERIAL 0x1 // Prevents syringes, parapens and hyposprays if equiped to slot_suit or slot_head.
#define THICKMATERIAL 0x1 // Prevents syringes, parapens and hyposprays if equipped to slot_suit or slot_head.
#define STOPPRESSUREDAMAGE 0x2 // Counts towards pressure protection. Note that like temperature protection, body_parts_covered is considered here as well.
#define AIRTIGHT 0x4 // Functions with internals.
#define NOSLIP 0x8 // Prevents from slipping on wet floors, in space, etc.
+1 -1
View File
@@ -58,7 +58,7 @@ var/global/defer_powernet_rebuild = 0 // True if net rebuild will be called
#define NETWORK_COMMUNICATORS "Communicators"
// Those networks can only be accessed by pre-existing terminals. AIs and new terminals can't use them.
var/list/restricted_camera_networks = list(NETWORK_ERT,NETWORK_MERCENARY,"Secret")
var/list/restricted_camera_networks = list(NETWORK_ERT,NETWORK_MERCENARY,"Secret", NETWORK_COMMUNICATORS)
//singularity defines
+8
View File
@@ -0,0 +1,8 @@
// Z-level flags bitfield - Set these flags to determine the z level's purpose
#define MAP_LEVEL_STATION 0x001 // Z-levels the station exists on
#define MAP_LEVEL_ADMIN 0x002 // Z-levels for admin functionality (Centcom, shuttle transit, etc)
#define MAP_LEVEL_CONTACT 0x004 // Z-levels that can be contacted from the station, for eg announcements
#define MAP_LEVEL_PLAYER 0x008 // Z-levels a character can typically reach
#define MAP_LEVEL_SEALED 0x010 // Z-levels that don't allow random transit at edge
#define MAP_LEVEL_EMPTY 0x020 // Empty Z-levels that may be used for various things (currently used by bluespace jump)
#define MAP_LEVEL_CONSOLES 0x040 // Z-levels available to various consoles, such as the crew monitor (when that gets coded in). Defaults to station_levels if unset.
+10
View File
@@ -0,0 +1,10 @@
//"fancy" math for calculating time in ms from tick_usage percentage and the length of ticks
//percent_of_tick_used * (ticklag * 100(to convert to ms)) / 100(percent ratio)
//collapsed to percent_of_tick_used * tick_lag
#define TICK_DELTA_TO_MS(percent_of_tick_used) ((percent_of_tick_used) * world.tick_lag)
#define TICK_USAGE_TO_MS(starting_tickusage) (TICK_DELTA_TO_MS(world.tick_usage-starting_tickusage))
//time of day but automatically adjusts to the server going into the next day within the same round.
//for when you need a reliable time number that doesn't depend on byond time.
#define REALTIMEOFDAY (world.timeofday + (MIDNIGHT_ROLLOVER * MIDNIGHT_ROLLOVER_CHECK))
#define MIDNIGHT_ROLLOVER_CHECK ( rollovercheck_last_timeofday != world.timeofday ? update_midnight_rollover() : midnight_rollovers )
+31
View File
@@ -69,6 +69,11 @@
#define COLOR_PALE_RED_GRAY "#CC9090"
#define COLOR_PALE_PURPLE_GRAY "#BDA2BA"
#define COLOR_PURPLE_GRAY "#A2819E"
#define COLOR_RED_LIGHT "#FF3333"
#define COLOR_DEEP_SKY_BLUE "#00e1ff"
// Shuttles.
@@ -83,6 +88,11 @@
#define SHUTTLE_WARMUP 1
#define SHUTTLE_INTRANSIT 2
// Sound defines for shuttles.
#define HYPERSPACE_WARMUP 0
#define HYPERSPACE_PROGRESS 1
#define HYPERSPACE_END 2
// Ferry shuttle processing status.
#define IDLE_STATE 0
#define WAIT_LAUNCH 1
@@ -137,6 +147,14 @@
#define DEFAULT_TABLE_MATERIAL "plastic"
#define DEFAULT_WALL_MATERIAL "steel"
#define MAT_STEEL "steel"
#define MAT_GLASS "glass"
#define MAT_SILVER "silver"
#define MAT_GOLD "gold"
#define MAT_TITANIUM "titanium"
#define MAT_PHORON "phoron"
#define MAT_DIAMOND "diamond"
#define SHARD_SHARD "shard"
#define SHARD_SHRAPNEL "shrapnel"
#define SHARD_STONE_PIECE "piece"
@@ -191,3 +209,16 @@
#define TSC_MORPH "Morpheus"
#define TSC_XION "Xion" // Not really needed but consistancy I guess.
#define TSC_GIL "Gilthari"
#define MIDNIGHT_ROLLOVER 864000 //number of deciseconds in a day
#define WORLD_ICON_SIZE 32 //Needed for the R-UST port
#define PIXEL_MULTIPLIER WORLD_ICON_SIZE/32 //Needed for the R-UST port
// Maploader bounds indices
#define MAP_MINX 1
#define MAP_MINY 2
#define MAP_MINZ 3
#define MAP_MAXX 4
#define MAP_MAXY 5
#define MAP_MAXZ 6
+18 -1
View File
@@ -206,4 +206,21 @@
#define TASTE_SENSITIVE 2 //anything below 7%
#define TASTE_NORMAL 1 //anything below 15%
#define TASTE_DULL 0.5 //anything below 30%
#define TASTE_NUMB 0.1 //anything below 150%
#define TASTE_NUMB 0.1 //anything below 150%
// If they're in an FBP, what braintype.
#define FBP_NONE ""
#define FBP_CYBORG "Cyborg"
#define FBP_POSI "Positronic"
#define FBP_DRONE "Drone"
// Used to seperate simple animals by ""intelligence"".
#define SA_PLANT 1
#define SA_ANIMAL 2
#define SA_ROBOTIC 3
#define SA_HUMANOID 4
// For slime commanding. Higher numbers allow for more actions.
#define SLIME_COMMAND_OBEY 1 // When disciplined.
#define SLIME_COMMAND_FACTION 2 // When in the same 'faction'.
#define SLIME_COMMAND_FRIEND 3 // When befriended with a slime friendship agent.
+17
View File
@@ -0,0 +1,17 @@
//defines that give qdel hints. these can be given as a return in destory() or by calling
#define QDEL_HINT_QUEUE 0 //qdel should queue the object for deletion.
#define QDEL_HINT_LETMELIVE 1 //qdel should let the object live after calling destory.
#define QDEL_HINT_IWILLGC 2 //functionally the same as the above. qdel should assume the object will gc on its own, and not check it.
#define QDEL_HINT_HARDDEL 3 //qdel should assume this object won't gc, and queue a hard delete using a hard reference.
#define QDEL_HINT_HARDDEL_NOW 4 //qdel should assume this object won't gc, and hard del it post haste.
#define QDEL_HINT_FINDREFERENCE 5 //functionally identical to QDEL_HINT_QUEUE if TESTING is not enabled in _compiler_options.dm.
//if TESTING is enabled, qdel will call this object's find_references() verb.
//defines for the gc_destroyed var
#define GC_QUEUED_FOR_QUEUING -1
#define GC_QUEUED_FOR_HARD_DEL -2
#define GC_CURRENTLY_BEING_QDELETED -3
#define QDELETED(X) (!X || X.gc_destroyed)
#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
+15
View File
@@ -0,0 +1,15 @@
// SS runlevels
#define RUNLEVEL_INIT 0 // "Initialize Only" - Used for subsystems that should never be fired (Should also have SS_NO_FIRE set)
#define RUNLEVEL_LOBBY 1 // Initial runlevel before setup. Returns to here if setup fails.
#define RUNLEVEL_SETUP 2 // While the gamemode setup is running. I.E gameticker.setup()
#define RUNLEVEL_GAME 4 // After successful game ticker setup, while the round is running.
#define RUNLEVEL_POSTGAME 8 // When round completes but before reboot
#define RUNLEVELS_DEFAULT (RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME)
var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_GAME, RUNLEVEL_POSTGAME)
#define RUNLEVEL_FLAG_TO_INDEX(flag) (log(2, flag) + 1) // Convert from the runlevel bitfield constants to index in runlevel_flags list
#define INIT_ORDER_LIGHTING 0
+7
View File
@@ -0,0 +1,7 @@
#define TICK_LIMIT_RUNNING 80
#define TICK_LIMIT_TO_RUN 78
#define TICK_LIMIT_MC 70
#define TICK_LIMIT_MC_INIT_DEFAULT 98
#define TICK_CHECK ( world.tick_usage > Master.current_ticklimit )
#define CHECK_TICK if TICK_CHECK stoplag()
-110
View File
@@ -1,110 +0,0 @@
/*
/tg/station13 /atom/movable Pool:
---------------------------------
By RemieRichards
Creation/Deletion is laggy, so let's reduce reuse and recycle!
*/
#define ATOM_POOL_COUNT 100
// "define DEBUG_ATOM_POOL 1
var/global/list/GlobalPool = list()
//You'll be using this proc 90% of the time.
//It grabs a type from the pool if it can
//And if it can't, it creates one
//The pool is flexible and will expand to fit
//The new created atom when it eventually
//Goes into the pool
//Second argument can be a new location, if the type is /atom/movable
//Or a list of arguments
//Either way it gets passed to new
/proc/PoolOrNew(var/get_type,var/second_arg)
var/datum/D
D = GetFromPool(get_type,second_arg)
if(!D)
// So the GC knows we're pooling this type.
if(!GlobalPool[get_type])
GlobalPool[get_type] = list()
if(islist(second_arg))
return new get_type (arglist(second_arg))
else
return new get_type (second_arg)
return D
/proc/GetFromPool(var/get_type,var/second_arg)
if(isnull(GlobalPool[get_type]))
return 0
if(length(GlobalPool[get_type]) == 0)
return 0
var/datum/D = pick_n_take(GlobalPool[get_type])
if(D)
D.ResetVars()
D.Prepare(second_arg)
return D
return 0
/proc/PlaceInPool(var/datum/D)
if(!istype(D))
return
if(length(GlobalPool[D.type]) > ATOM_POOL_COUNT)
#ifdef DEBUG_ATOM_POOL
world << text("DEBUG_DATUM_POOL: PlaceInPool([]) exceeds []. Discarding.", D.type, ATOM_POOL_COUNT)
#endif
if(garbage_collector)
garbage_collector.AddTrash(D)
else
del(D)
return
if(D in GlobalPool[D.type])
return
if(!GlobalPool[D.type])
GlobalPool[D.type] = list()
GlobalPool[D.type] += D
D.Destroy()
D.ResetVars()
/proc/IsPooled(var/datum/D)
if(isnull(GlobalPool[D.type]))
return 0
return 1
/datum/proc/Prepare(args)
if(islist(args))
New(arglist(args))
else
New(args)
/atom/movable/Prepare(args)
var/list/args_list = args
if(istype(args_list) && args_list.len)
loc = args[1]
else
loc = args
..()
/datum/proc/ResetVars(var/list/exlude = list())
var/list/excluded = list("animate_movement", "loc", "locs", "parent_type", "vars", "verbs", "type") + exlude
for(var/V in vars)
if(V in excluded)
continue
vars[V] = initial(vars[V])
/atom/movable/ResetVars()
..()
vars["loc"] = null
#undef ATOM_POOL_COUNT
+19 -14
View File
@@ -271,20 +271,25 @@
var/list/hear = dview(range,T,INVISIBILITY_MAXIMUM)
var/list/hearturfs = list()
for(var/atom/movable/AM in hear)
if(ismob(AM))
mobs += AM
hearturfs += AM.locs[1]
else if(isobj(AM))
objs += AM
hearturfs += AM.locs[1]
for(var/thing in hear)
if(istype(thing,/obj))
objs += thing
hearturfs += get_turf(thing)
else if(istype(thing,/mob))
mobs += thing
hearturfs += get_turf(thing)
//A list of every mob with a client
for(var/mob/M in player_list)
if(M.loc && M.locs[1] in hearturfs)
mobs |= M
for(var/mob in player_list)
if(!istype(mob, /mob))
crash_with("There is a null or non-mob reference inside player_list.")
continue
if(get_turf(mob) in hearturfs)
mobs |= mob
continue
else if(M.stat == DEAD)
var/mob/M = mob
if(M && M.stat == DEAD && !M.forbid_seeing_deadchat)
switch(type)
if(1) //Audio messages use ghost_ears
if(M.is_preference_enabled(/datum/client_preference/ghost_ears))
@@ -294,9 +299,9 @@
mobs |= M
//For objects below the top level who still want to hear
for(var/obj/O in listening_objects)
if(O.loc && O.locs[1] in hearturfs)
objs |= O
for(var/obj in listening_objects)
if(get_turf(obj) in hearturfs)
objs |= obj
return list("mobs" = mobs, "objs" = objs)
+73
View File
@@ -599,3 +599,76 @@ proc/dd_sortedTextList(list/incoming)
for(var/path in subtypesof(prototype))
L += new path()
return L
//creates every subtype of prototype (excluding prototype) and adds it to list L as a type/instance pair.
//if no list/L is provided, one is created.
/proc/init_subtypes_assoc(prototype, list/L)
if(!istype(L)) L = list()
for(var/path in subtypesof(prototype))
L[path] = new path()
return L
//Move a single element from position fromIndex within a list, to position toIndex
//All elements in the range [1,toIndex) before the move will be before the pivot afterwards
//All elements in the range [toIndex, L.len+1) before the move will be after the pivot afterwards
//In other words, it's as if the range [fromIndex,toIndex) have been rotated using a <<< operation common to other languages.
//fromIndex and toIndex must be in the range [1,L.len+1]
//This will preserve associations ~Carnie
/proc/moveElement(list/L, fromIndex, toIndex)
if(fromIndex == toIndex || fromIndex+1 == toIndex) //no need to move
return
if(fromIndex > toIndex)
++fromIndex //since a null will be inserted before fromIndex, the index needs to be nudged right by one
L.Insert(toIndex, null)
L.Swap(fromIndex, toIndex)
L.Cut(fromIndex, fromIndex+1)
//Move elements [fromIndex,fromIndex+len) to [toIndex-len, toIndex)
//Same as moveElement but for ranges of elements
//This will preserve associations ~Carnie
/proc/moveRange(list/L, fromIndex, toIndex, len=1)
var/distance = abs(toIndex - fromIndex)
if(len >= distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going. The result being, our range we are moving is shifted left or right by dist elements
if(fromIndex <= toIndex)
return //no need to move
fromIndex += len //we want to shift left instead of right
for(var/i=0, i<distance, ++i)
L.Insert(fromIndex, null)
L.Swap(fromIndex, toIndex)
L.Cut(toIndex, toIndex+1)
else
if(fromIndex > toIndex)
fromIndex += len
for(var/i=0, i<len, ++i)
L.Insert(toIndex, null)
L.Swap(fromIndex, toIndex)
L.Cut(fromIndex, fromIndex+1)
//replaces reverseList ~Carnie
/proc/reverseRange(list/L, start=1, end=0)
if(L.len)
start = start % L.len
end = end % (L.len+1)
if(start <= 0)
start += L.len
if(end <= 0)
end += L.len + 1
--end
while(start < end)
L.Swap(start++,end--)
return L
//Copies a list, and all lists inside it recusively
//Does not copy any other reference type
/proc/deepCopyList(list/l)
if(!islist(l))
return l
. = l.Copy()
for(var/i = 1 to l.len)
if(islist(.[i]))
.[i] = .(.[i])
+1 -1
View File
@@ -228,7 +228,7 @@ Proc for attack log creation, because really why not
. = 0
break
if(target_loc && (!target || target_loc != target.loc))
if(target_loc && (QDELETED(target) || target_loc != target.loc))
. = 0
break
+17
View File
@@ -0,0 +1,17 @@
//TimSort interface
/proc/sortTim(list/L, cmp=/proc/cmp_numeric_asc, associative, fromIndex=1, toIndex=0)
if(L && L.len >= 2)
fromIndex = fromIndex % L.len
toIndex = toIndex % (L.len+1)
if(fromIndex <= 0)
fromIndex += L.len
if(toIndex <= 0)
toIndex += L.len + 1
sortInstance.L = L
sortInstance.cmp = cmp
sortInstance.associative = associative
sortInstance.timSort(fromIndex, toIndex)
return L
+656
View File
@@ -0,0 +1,656 @@
//These are macros used to reduce on proc calls
#define fetchElement(L, i) (associative) ? L[L[i]] : L[i]
//Minimum sized sequence that will be merged. Anything smaller than this will use binary-insertion sort.
//Should be a power of 2
#define MIN_MERGE 32
//When we get into galloping mode, we stay there until both runs win less often than MIN_GALLOP consecutive times.
#define MIN_GALLOP 7
//This is a global instance to allow much of this code to be reused. The interfaces are kept separately
var/datum/sortInstance/sortInstance = new()
/datum/sortInstance
//The array being sorted.
var/list/L
//The comparator proc-reference
var/cmp = /proc/cmp_numeric_asc
//whether we are sorting list keys (0: L[i]) or associated values (1: L[L[i]])
var/associative = 0
//This controls when we get *into* galloping mode. It is initialized to MIN_GALLOP.
//The mergeLo and mergeHi methods nudge it higher for random data, and lower for highly structured data.
var/minGallop = MIN_GALLOP
//Stores information regarding runs yet to be merged.
//Run i starts at runBase[i] and extends for runLen[i] elements.
//runBase[i] + runLen[i] == runBase[i+1]
//var/stackSize
var/list/runBases = list()
var/list/runLens = list()
/datum/sortInstance/proc/timSort(start, end)
runBases.Cut()
runLens.Cut()
var/remaining = end - start
//If array is small, do a 'mini-TimSort' with no merges
if(remaining < MIN_MERGE)
var/initRunLen = countRunAndMakeAscending(start, end)
binarySort(start, end, start+initRunLen)
return
//March over the array finding natural runs
//Extend any short natural runs to runs of length minRun
var/minRun = minRunLength(remaining)
do
//identify next run
var/runLen = countRunAndMakeAscending(start, end)
//if run is short, extend to min(minRun, remaining)
if(runLen < minRun)
var/force = (remaining <= minRun) ? remaining : minRun
binarySort(start, start+force, start+runLen)
runLen = force
//add data about run to queue
runBases.Add(start)
runLens.Add(runLen)
//maybe merge
mergeCollapse()
//Advance to find next run
start += runLen
remaining -= runLen
while(remaining > 0)
//Merge all remaining runs to complete sort
//ASSERT(start == end)
mergeForceCollapse();
//ASSERT(runBases.len == 1)
//reset minGallop, for successive calls
minGallop = MIN_GALLOP
return L
/*
Sorts the specified portion of the specified array using a binary
insertion sort. This is the best method for sorting small numbers
of elements. It requires O(n log n) compares, but O(n^2) data
movement (worst case).
If the initial part of the specified range is already sorted,
this method can take advantage of it: the method assumes that the
elements in range [lo,start) are already sorted
lo the index of the first element in the range to be sorted
hi the index after the last element in the range to be sorted
start the index of the first element in the range that is not already known to be sorted
*/
/datum/sortInstance/proc/binarySort(lo, hi, start)
//ASSERT(lo <= start && start <= hi)
if(start <= lo)
start = lo + 1
for(,start < hi, ++start)
var/pivot = fetchElement(L,start)
//set left and right to the index where pivot belongs
var/left = lo
var/right = start
//ASSERT(left <= right)
//[lo, left) elements <= pivot < [right, start) elements
//in other words, find where the pivot element should go using bisection search
while(left < right)
var/mid = (left + right) >> 1 //round((left+right)/2)
if(call(cmp)(fetchElement(L,mid), pivot) > 0)
right = mid
else
left = mid+1
//ASSERT(left == right)
moveElement(L, start, left) //move pivot element to correct location in the sorted range
/*
Returns the length of the run beginning at the specified position and reverses the run if it is back-to-front
A run is the longest ascending sequence with:
a[lo] <= a[lo + 1] <= a[lo + 2] <= ...
or the longest descending sequence with:
a[lo] > a[lo + 1] > a[lo + 2] > ...
For its intended use in a stable mergesort, the strictness of the
definition of "descending" is needed so that the call can safely
reverse a descending sequence without violating stability.
*/
/datum/sortInstance/proc/countRunAndMakeAscending(lo, hi)
//ASSERT(lo < hi)
var/runHi = lo + 1
if(runHi >= hi)
return 1
var/last = fetchElement(L,lo)
var/current = fetchElement(L,runHi++)
if(call(cmp)(current, last) < 0)
while(runHi < hi)
last = current
current = fetchElement(L,runHi)
if(call(cmp)(current, last) >= 0)
break
++runHi
reverseRange(L, lo, runHi)
else
while(runHi < hi)
last = current
current = fetchElement(L,runHi)
if(call(cmp)(current, last) < 0)
break
++runHi
return runHi - lo
//Returns the minimum acceptable run length for an array of the specified length.
//Natural runs shorter than this will be extended with binarySort
/datum/sortInstance/proc/minRunLength(n)
//ASSERT(n >= 0)
var/r = 0 //becomes 1 if any bits are shifted off
while(n >= MIN_MERGE)
r |= (n & 1)
n >>= 1
return n + r
//Examines the stack of runs waiting to be merged and merges adjacent runs until the stack invariants are reestablished:
// runLen[i-3] > runLen[i-2] + runLen[i-1]
// runLen[i-2] > runLen[i-1]
//This method is called each time a new run is pushed onto the stack.
//So the invariants are guaranteed to hold for i<stackSize upon entry to the method
/datum/sortInstance/proc/mergeCollapse()
while(runBases.len >= 2)
var/n = runBases.len - 1
if(n > 1 && runLens[n-1] <= runLens[n] + runLens[n+1])
if(runLens[n-1] < runLens[n+1])
--n
mergeAt(n)
else if(runLens[n] <= runLens[n+1])
mergeAt(n)
else
break //Invariant is established
//Merges all runs on the stack until only one remains.
//Called only once, to finalise the sort
/datum/sortInstance/proc/mergeForceCollapse()
while(runBases.len >= 2)
var/n = runBases.len - 1
if(n > 1 && runLens[n-1] < runLens[n+1])
--n
mergeAt(n)
//Merges the two consecutive runs at stack indices i and i+1
//Run i must be the penultimate or antepenultimate run on the stack
//In other words, i must be equal to stackSize-2 or stackSize-3
/datum/sortInstance/proc/mergeAt(i)
//ASSERT(runBases.len >= 2)
//ASSERT(i >= 1)
//ASSERT(i == runBases.len - 1 || i == runBases.len - 2)
var/base1 = runBases[i]
var/base2 = runBases[i+1]
var/len1 = runLens[i]
var/len2 = runLens[i+1]
//ASSERT(len1 > 0 && len2 > 0)
//ASSERT(base1 + len1 == base2)
//Record the legth of the combined runs. If i is the 3rd last run now, also slide over the last run
//(which isn't involved in this merge). The current run (i+1) goes away in any case.
runLens[i] += runLens[i+1]
runLens.Cut(i+1, i+2)
runBases.Cut(i+1, i+2)
//Find where the first element of run2 goes in run1.
//Prior elements in run1 can be ignored (because they're already in place)
var/k = gallopRight(fetchElement(L,base2), base1, len1, 0)
//ASSERT(k >= 0)
base1 += k
len1 -= k
if(len1 == 0)
return
//Find where the last element of run1 goes in run2.
//Subsequent elements in run2 can be ignored (because they're already in place)
len2 = gallopLeft(fetchElement(L,base1 + len1 - 1), base2, len2, len2-1)
//ASSERT(len2 >= 0)
if(len2 == 0)
return
//Merge remaining runs, using tmp array with min(len1, len2) elements
if(len1 <= len2)
mergeLo(base1, len1, base2, len2)
else
mergeHi(base1, len1, base2, len2)
/*
Locates the position to insert key within the specified sorted range
If the range contains elements equal to key, this will return the index of the LEFTMOST of those elements
key the element to be inserted into the sorted range
base the index of the first element of the sorted range
len the length of the sorted range, must be greater than 0
hint the offset from base at which to begin the search, such that 0 <= hint < len; i.e. base <= hint < base+hint
Returns the index at which to insert element 'key'
*/
/datum/sortInstance/proc/gallopLeft(key, base, len, hint)
//ASSERT(len > 0 && hint >= 0 && hint < len)
var/lastOffset = 0
var/offset = 1
if(call(cmp)(key, fetchElement(L,base+hint)) > 0)
var/maxOffset = len - hint
while(offset < maxOffset && call(cmp)(key, fetchElement(L,base+hint+offset)) > 0)
lastOffset = offset
offset = (offset << 1) + 1
if(offset > maxOffset)
offset = maxOffset
lastOffset += hint
offset += hint
else
var/maxOffset = hint + 1
while(offset < maxOffset && call(cmp)(key, fetchElement(L,base+hint-offset)) <= 0)
lastOffset = offset
offset = (offset << 1) + 1
if(offset > maxOffset)
offset = maxOffset
var/temp = lastOffset
lastOffset = hint - offset
offset = hint - temp
//ASSERT(-1 <= lastOffset && lastOffset < offset && offset <= len)
//Now L[base+lastOffset] < key <= L[base+offset], so key belongs somewhere to the right of lastOffset but no farther than
//offset. Do a binary search with invariant L[base+lastOffset-1] < key <= L[base+offset]
++lastOffset
while(lastOffset < offset)
var/m = lastOffset + ((offset - lastOffset) >> 1)
if(call(cmp)(key, fetchElement(L,base+m)) > 0)
lastOffset = m + 1
else
offset = m
//ASSERT(lastOffset == offset)
return offset
/**
* Like gallopLeft, except that if the range contains an element equal to
* key, gallopRight returns the index after the rightmost equal element.
*
* @param key the key whose insertion point to search for
* @param a the array in which to search
* @param base the index of the first element in the range
* @param len the length of the range; must be > 0
* @param hint the index at which to begin the search, 0 <= hint < n.
* The closer hint is to the result, the faster this method will run.
* @param c the comparator used to order the range, and to search
* @return the int k, 0 <= k <= n such that a[b + k - 1] <= key < a[b + k]
*/
/datum/sortInstance/proc/gallopRight(key, base, len, hint)
//ASSERT(len > 0 && hint >= 0 && hint < len)
var/offset = 1
var/lastOffset = 0
if(call(cmp)(key, fetchElement(L,base+hint)) < 0) //key <= L[base+hint]
var/maxOffset = hint + 1 //therefore we want to insert somewhere in the range [base,base+hint] = [base+,base+(hint+1))
while(offset < maxOffset && call(cmp)(key, fetchElement(L,base+hint-offset)) < 0) //we are iterating backwards
lastOffset = offset
offset = (offset << 1) + 1 //1 3 7 15
//if(offset <= 0) //int overflow, not an issue here since we are using floats
// offset = maxOffset
if(offset > maxOffset)
offset = maxOffset
var/temp = lastOffset
lastOffset = hint - offset
offset = hint - temp
else //key > L[base+hint]
var/maxOffset = len - hint //therefore we want to insert somewhere in the range (base+hint,base+len) = [base+hint+1, base+hint+(len-hint))
while(offset < maxOffset && call(cmp)(key, fetchElement(L,base+hint+offset)) >= 0)
lastOffset = offset
offset = (offset << 1) + 1
//if(offset <= 0) //int overflow, not an issue here since we are using floats
// offset = maxOffset
if(offset > maxOffset)
offset = maxOffset
lastOffset += hint
offset += hint
//ASSERT(-1 <= lastOffset && lastOffset < offset && offset <= len)
++lastOffset
while(lastOffset < offset)
var/m = lastOffset + ((offset - lastOffset) >> 1)
if(call(cmp)(key, fetchElement(L,base+m)) < 0) //key <= L[base+m]
offset = m
else //key > L[base+m]
lastOffset = m + 1
//ASSERT(lastOffset == offset)
return offset
//Merges two adjacent runs in-place in a stable fashion.
//For performance this method should only be called when len1 <= len2!
/datum/sortInstance/proc/mergeLo(base1, len1, base2, len2)
//ASSERT(len1 > 0 && len2 > 0 && base1 + len1 == base2)
var/cursor1 = base1
var/cursor2 = base2
//degenerate cases
if(len2 == 1)
moveElement(L, cursor2, cursor1)
return
if(len1 == 1)
moveElement(L, cursor1, cursor2+len2)
return
//Move first element of second run
moveElement(L, cursor2++, cursor1++)
--len2
outer:
while(1)
var/count1 = 0 //# of times in a row that first run won
var/count2 = 0 // " " " " " " second run won
//do the straightfoward thin until one run starts winning consistently
do
//ASSERT(len1 > 1 && len2 > 0)
if(call(cmp)(fetchElement(L,cursor2), fetchElement(L,cursor1)) < 0)
moveElement(L, cursor2++, cursor1++)
--len2
++count2
count1 = 0
if(len2 == 0)
break outer
else
++cursor1
++count1
count2 = 0
if(--len1 == 1)
break outer
while((count1 | count2) < minGallop)
//one run is winning consistently so galloping may provide huge benifits
//so try galloping, until such time as the run is no longer consistently winning
do
//ASSERT(len1 > 1 && len2 > 0)
count1 = gallopRight(fetchElement(L,cursor2), cursor1, len1, 0)
if(count1)
cursor1 += count1
len1 -= count1
if(len1 <= 1)
break outer
moveElement(L, cursor2, cursor1)
++cursor2
++cursor1
if(--len2 == 0)
break outer
count2 = gallopLeft(fetchElement(L,cursor1), cursor2, len2, 0)
if(count2)
moveRange(L, cursor2, cursor1, count2)
cursor2 += count2
cursor1 += count2
len2 -= count2
if(len2 == 0)
break outer
++cursor1
if(--len1 == 1)
break outer
--minGallop
while((count1|count2) > MIN_GALLOP)
if(minGallop < 0)
minGallop = 0
minGallop += 2; // Penalize for leaving gallop mode
if(len1 == 1)
//ASSERT(len2 > 0)
moveElement(L, cursor1, cursor2+len2)
//else
//ASSERT(len2 == 0)
//ASSERT(len1 > 1)
/datum/sortInstance/proc/mergeHi(base1, len1, base2, len2)
//ASSERT(len1 > 0 && len2 > 0 && base1 + len1 == base2)
var/cursor1 = base1 + len1 - 1 //start at end of sublists
var/cursor2 = base2 + len2 - 1
//degenerate cases
if(len2 == 1)
moveElement(L, base2, base1)
return
if(len1 == 1)
moveElement(L, base1, cursor2+1)
return
moveElement(L, cursor1--, cursor2-- + 1)
--len1
outer:
while(1)
var/count1 = 0 //# of times in a row that first run won
var/count2 = 0 // " " " " " " second run won
//do the straightfoward thing until one run starts winning consistently
do
//ASSERT(len1 > 0 && len2 > 1)
if(call(cmp)(fetchElement(L,cursor2), fetchElement(L,cursor1)) < 0)
moveElement(L, cursor1--, cursor2-- + 1)
--len1
++count1
count2 = 0
if(len1 == 0)
break outer
else
--cursor2
--len2
++count2
count1 = 0
if(len2 == 1)
break outer
while((count1 | count2) < minGallop)
//one run is winning consistently so galloping may provide huge benifits
//so try galloping, until such time as the run is no longer consistently winning
do
//ASSERT(len1 > 0 && len2 > 1)
count1 = len1 - gallopRight(fetchElement(L,cursor2), base1, len1, len1-1) //should cursor1 be base1?
if(count1)
cursor1 -= count1
moveRange(L, cursor1+1, cursor2+1, count1) //cursor1+1 == cursor2 by definition
cursor2 -= count1
len1 -= count1
if(len1 == 0)
break outer
--cursor2
if(--len2 == 1)
break outer
count2 = len2 - gallopLeft(fetchElement(L,cursor1), cursor1+1, len2, len2-1)
if(count2)
cursor2 -= count2
len2 -= count2
if(len2 <= 1)
break outer
moveElement(L, cursor1--, cursor2-- + 1)
--len1
if(len1 == 0)
break outer
--minGallop
while((count1|count2) > MIN_GALLOP)
if(minGallop < 0)
minGallop = 0
minGallop += 2 // Penalize for leaving gallop mode
if(len2 == 1)
//ASSERT(len1 > 0)
cursor1 -= len1
moveRange(L, cursor1+1, cursor2+1, len1)
//else
//ASSERT(len1 == 0)
//ASSERT(len2 > 0)
/datum/sortInstance/proc/mergeSort(start, end)
var/remaining = end - start
//If array is small, do an insertion sort
if(remaining < MIN_MERGE)
//var/initRunLen = countRunAndMakeAscending(start, end)
binarySort(start, end, start/*+initRunLen*/)
return
var/minRun = minRunLength(remaining)
do
var/runLen = (remaining <= minRun) ? remaining : minRun
binarySort(start, start+runLen, start)
//add data about run to queue
runBases.Add(start)
runLens.Add(runLen)
//Advance to find next run
start += runLen
remaining -= runLen
while(remaining > 0)
while(runBases.len >= 2)
var/n = runBases.len - 1
if(n > 1 && runLens[n-1] <= runLens[n] + runLens[n+1])
if(runLens[n-1] < runLens[n+1])
--n
mergeAt2(n)
else if(runLens[n] <= runLens[n+1])
mergeAt2(n)
else
break //Invariant is established
while(runBases.len >= 2)
var/n = runBases.len - 1
if(n > 1 && runLens[n-1] < runLens[n+1])
--n
mergeAt2(n)
return L
/datum/sortInstance/proc/mergeAt2(i)
var/cursor1 = runBases[i]
var/cursor2 = runBases[i+1]
var/end1 = cursor1+runLens[i]
var/end2 = cursor2+runLens[i+1]
var/val1 = fetchElement(L,cursor1)
var/val2 = fetchElement(L,cursor2)
while(1)
if(call(cmp)(val1,val2) < 0)
if(++cursor1 >= end1)
break
val1 = fetchElement(L,cursor1)
else
moveElement(L,cursor2,cursor1)
++cursor2
if(++cursor2 >= end2)
break
++end1
++cursor1
//if(++cursor1 >= end1)
// break
val2 = fetchElement(L,cursor2)
//Record the legth of the combined runs. If i is the 3rd last run now, also slide over the last run
//(which isn't involved in this merge). The current run (i+1) goes away in any case.
runLens[i] += runLens[i+1]
runLens.Cut(i+1, i+2)
runBases.Cut(i+1, i+2)
#undef MIN_GALLOP
#undef MIN_MERGE
#undef fetchElement
+20
View File
@@ -0,0 +1,20 @@
//
// Comparators for use with /datum/sortInstance (or wherever you want)
// They should return negative, zero, or positive numbers for a < b, a == b, and a > b respectively.
//
// Sorts numeric ascending
/proc/cmp_numeric_asc(a,b)
return a - b
// Sorts subsystems alphabetically
/proc/cmp_subsystem_display(datum/controller/subsystem/a, datum/controller/subsystem/b)
return sorttext(b.name, a.name)
// Sorts subsystems by init_order
/proc/cmp_subsystem_init(datum/controller/subsystem/a, datum/controller/subsystem/b)
return b.init_order - a.init_order
// Sorts subsystems by priority
/proc/cmp_subsystem_priority(datum/controller/subsystem/a, datum/controller/subsystem/b)
return a.priority - b.priority
+57
View File
@@ -331,4 +331,61 @@ proc/TextPreview(var/string,var/len=40)
/proc/strip_improper(var/text)
return replacetext(replacetext(text, "\proper", ""), "\improper", "")
//Used for applying byonds text macros to strings that are loaded at runtime
/proc/apply_text_macros(string)
var/next_backslash = findtext(string, "\\")
if(!next_backslash)
return string
var/leng = length(string)
var/next_space = findtext(string, " ", next_backslash + 1)
if(!next_space)
next_space = leng - next_backslash
if(!next_space) //trailing bs
return string
var/base = next_backslash == 1 ? "" : copytext(string, 1, next_backslash)
var/macro = lowertext(copytext(string, next_backslash + 1, next_space))
var/rest = next_backslash > leng ? "" : copytext(string, next_space + 1)
//See http://www.byond.com/docs/ref/info.html#/DM/text/macros
switch(macro)
//prefixes/agnostic
if("the")
rest = text("\the []", rest)
if("a")
rest = text("\a []", rest)
if("an")
rest = text("\an []", rest)
if("proper")
rest = text("\proper []", rest)
if("improper")
rest = text("\improper []", rest)
if("roman")
rest = text("\roman []", rest)
//postfixes
if("th")
base = text("[]\th", rest)
if("s")
base = text("[]\s", rest)
if("he")
base = text("[]\he", rest)
if("she")
base = text("[]\she", rest)
if("his")
base = text("[]\his", rest)
if("himself")
base = text("[]\himself", rest)
if("herself")
base = text("[]\herself", rest)
if("hers")
base = text("[]\hers", rest)
. = base
if(rest)
. += .(rest)
#define gender2text(gender) capitalize(gender)
+21 -1
View File
@@ -100,4 +100,24 @@ var/round_start_time = 0
/hook/startup/proc/set_roundstart_hour()
roundstart_hour = pick(2,7,12,17)
return 1
return 1
/var/midnight_rollovers = 0
/var/rollovercheck_last_timeofday = 0
/proc/update_midnight_rollover()
if (world.timeofday < rollovercheck_last_timeofday) //TIME IS GOING BACKWARDS!
return midnight_rollovers++
return midnight_rollovers
//Increases delay as the server gets more overloaded,
//as sleeps aren't cheap and sleeping only to wake up and sleep again is wasteful
#define DELTA_CALC max(((max(world.tick_usage, world.cpu) / 100) * max(Master.sleep_delta,1)), 1)
/proc/stoplag()
. = 0
var/i = 1
do
. += round(i*DELTA_CALC)
sleep(i*world.tick_lag*DELTA_CALC)
i *= 2
while (world.tick_usage > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit))
+3 -1
View File
@@ -80,7 +80,9 @@
if (NORTHEAST) return "northeast"
if (SOUTHEAST) return "southeast"
if (NORTHWEST) return "northwest"
if (SOUTHWEST) return "southwest"
if (SOUTHWEST) return "southwest"
if (UP) return "up"
if (DOWN) return "down"
// Turns text into proper directions
/proc/text2dir(direction)
+22 -7
View File
@@ -492,8 +492,6 @@ Turf and target are seperate in case you want to teleport some distance from a t
moblist.Add(M)
for(var/mob/new_player/M in sortmob)
moblist.Add(M)
for(var/mob/living/carbon/slime/M in sortmob)
moblist.Add(M)
for(var/mob/living/simple_animal/M in sortmob)
moblist.Add(M)
// for(var/mob/living/silicon/hivebot/M in world)
@@ -1157,12 +1155,22 @@ proc/is_hot(obj/item/W as obj)
istype(W, /obj/item/weapon/surgical/bonesetter)
)
//check if mob is lying down on something we can operate him on.
// check if mob is lying down on something we can operate him on.
// The RNG with table/rollerbeds comes into play in do_surgery() so that fail_step() can be used instead.
/proc/can_operate(mob/living/carbon/M)
return (M.lying && \
locate(/obj/machinery/optable, M.loc) || \
(locate(/obj/structure/bed/roller, M.loc) && prob(75)) || \
(locate(/obj/structure/table/, M.loc) && prob(66)))
return M.lying
// Returns an instance of a valid surgery surface.
/mob/living/proc/get_surgery_surface()
if(!lying)
return null // Not lying down means no surface.
var/obj/surface = null
for(var/obj/O in loc) // Looks for the best surface.
if(O.surgery_odds)
if(!surface || surface.surgery_odds < O)
surface = O
if(surface)
return surface
/proc/reverse_direction(var/dir)
switch(dir)
@@ -1284,6 +1292,13 @@ var/mob/dview/dview_mob = new
else
living_mob_list -= src
/mob/dview/Destroy(var/force)
crash_with("Attempt to delete the dview_mob: [log_info_line(src)]")
if (!force)
return QDEL_HINT_LETMELIVE
global.dview_mob = new
return ..()
// call to generate a stack trace and print to runtime logs
/proc/crash_with(msg)
CRASH(msg)
+38 -1
View File
@@ -38,7 +38,11 @@
#define issilicon(A) istype(A, /mob/living/silicon)
#define isslime(A) istype(A, /mob/living/carbon/slime)
#define isvoice(A) istype(A, /mob/living/voice)
#define isslime(A) istype(A, /mob/living/simple_animal/slime)
#define isbot(A) istype(A, /mob/living/bot)
#define isxeno(A) istype(A, /mob/living/simple_animal/xeno)
@@ -49,5 +53,38 @@
#define RANDOM_BLOOD_TYPE pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+")
#define to_chat(target, message) target << message
#define to_world(message) world << message
#define to_world_log(message) world.log << message
// TODO - Baystation has this log to crazy places. For now lets just world.log, but maybe look into it later.
#define log_world(message) world.log << message
#define CanInteract(user, state) (CanUseTopic(user, state) == STATUS_INTERACTIVE)
#define qdel_null_list(x) if(x) { for(var/y in x) { qdel(y) } ; x = null }
#define qdel_null(x) if(x) { qdel(x) ; x = null }
#define ARGS_DEBUG log_debug("[__FILE__] - [__LINE__]") ; for(var/arg in args) { log_debug("\t[log_info_line(arg)]") }
// Helper macros to aid in optimizing lazy instantiation of lists.
// All of these are null-safe, you can use them without knowing if the list var is initialized yet
//Picks from the list, with some safeties, and returns the "default" arg if it fails
#define DEFAULTPICK(L, default) ((istype(L, /list) && L:len) ? pick(L) : default)
// Ensures L is initailized after this point
#define LAZYINITLIST(L) if (!L) L = list()
// Sets a L back to null iff it is empty
#define UNSETEMPTY(L) if (L && !L.len) L = null
// Removes I from list L, and sets I to null if it is now empty
#define LAZYREMOVE(L, I) if(L) { L -= I; if(!L.len) { L = null; } }
// Adds I to L, initalizing I if necessary
#define LAZYADD(L, I) if(!L) { L = list(); } L += I;
// Reads I from L safely - Works with both associative and traditional lists.
#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= L.len ? L[I] : null) : L[I]) : null)
// Reads the length of L, returning 0 if null
#define LAZYLEN(L) length(L)
// Null-safe L.Cut()
#define LAZYCLEARLIST(L) if(L) L.Cut()
// Reads L or an empty list if L is not a list. Note: Does NOT assign, L may be an expression.
#define SANITIZE_LIST(L) ( islist(L) ? L : list() )
-1
View File
@@ -38,7 +38,6 @@
* mob/RangedAttack(atom,params) - used only ranged, only used for tk and laser eyes but could be changed
*/
/mob/proc/ClickOn(var/atom/A, var/params)
if(world.time <= next_click) // Hard check, before anything else, to avoid crashing
return
+10
View File
@@ -5,6 +5,16 @@
recieving object instead, so that's the default action. This allows you to drag
almost anything into a trash can.
*/
/atom/proc/CanMouseDrop(atom/over, var/mob/user = usr)
if(!user || !over)
return FALSE
if(user.incapacitated())
return FALSE
if(!src.Adjacent(user) || !over.Adjacent(user))
return FALSE // should stop you from dragging through windows
return TRUE
/atom/MouseDrop(atom/over)
if(!usr || !over) return
if(!Adjacent(usr) || !over.Adjacent(usr)) return // should stop you from dragging through windows
+1 -6
View File
@@ -20,7 +20,7 @@
message_admins("ERROR: ability_master's New() was not given an owner argument. This is a bug.")
/obj/screen/movable/ability_master/Destroy()
..()
. = ..()
//Get rid of the ability objects.
remove_all_abilities()
ability_objects.Cut()
@@ -32,11 +32,6 @@
my_mob.client.screen -= src
my_mob = null
/obj/screen/movable/ability_master/ResetVars()
..("ability_objects", args)
remove_all_abilities()
// ability_objects = list()
/obj/screen/movable/ability_master/MouseDrop()
if(showing)
return
+4 -1
View File
@@ -30,6 +30,9 @@
/datum/action/Destroy()
if(owner)
Remove(owner)
target = null
qdel_null(button)
return ..()
/datum/action/proc/Grant(mob/living/T)
if(owner)
@@ -45,7 +48,7 @@
if(button)
if(T.client)
T.client.screen -= button
del(button)
qdel_null(button)
T.actions.Remove(src)
T.update_action_buttons()
owner = null
+1 -1
View File
@@ -20,7 +20,7 @@
return null
if(!screen)
screen = PoolOrNew(type)
screen = new type()
screen.icon_state = "[initial(screen.icon_state)][severity]"
screen.severity = severity
+1 -1
View File
@@ -155,7 +155,7 @@ datum/hud/New(mob/owner)
..()
/datum/hud/Destroy()
..()
. = ..()
grab_intent = null
hurt_intent = null
disarm_intent = null
+22 -10
View File
@@ -45,49 +45,61 @@
screen_loc = "[screen_loc_X[1]]:[pix_X],[screen_loc_Y[1]]:[pix_Y]"
/obj/screen/movable/proc/encode_screen_X(X)
if(X > usr.client.view+1)
. = "EAST-[usr.client.view*2 + 1-X]"
else if(X < usr.client.view+1)
var/view_dist = world.view
if(view_dist)
view_dist = view_dist
if(X > view_dist+1)
. = "EAST-[view_dist *2 + 1-X]"
else if(X < view_dist +1)
. = "WEST+[X-1]"
else
. = "CENTER"
/obj/screen/movable/proc/decode_screen_X(X)
var/view_dist = world.view
if(view_dist)
view_dist = view_dist
//Find EAST/WEST implementations
if(findtext(X,"EAST-"))
var/num = text2num(copytext(X,6)) //Trim EAST-
if(!num)
num = 0
. = usr.client.view*2 + 1 - num
. = view_dist*2 + 1 - num
else if(findtext(X,"WEST+"))
var/num = text2num(copytext(X,6)) //Trim WEST+
if(!num)
num = 0
. = num+1
else if(findtext(X,"CENTER"))
. = usr.client.view+1
. = view_dist+1
/obj/screen/movable/proc/encode_screen_Y(Y)
if(Y > usr.client.view+1)
. = "NORTH-[usr.client.view*2 + 1-Y]"
else if(Y < usr.client.view+1)
var/view_dist = world.view
if(view_dist)
view_dist = view_dist
if(Y > view_dist+1)
. = "NORTH-[view_dist*2 + 1-Y]"
else if(Y < view_dist+1)
. = "SOUTH+[Y-1]"
else
. = "CENTER"
/obj/screen/movable/proc/decode_screen_Y(Y)
var/view_dist = world.view
if(view_dist)
view_dist = view_dist
if(findtext(Y,"NORTH-"))
var/num = text2num(copytext(Y,7)) //Trim NORTH-
if(!num)
num = 0
. = usr.client.view*2 + 1 - num
. = view_dist*2 + 1 - num
else if(findtext(Y,"SOUTH+"))
var/num = text2num(copytext(Y,7)) //Time SOUTH+
if(!num)
num = 0
. = num+1
else if(findtext(Y,"CENTER"))
. = usr.client.view+1
. = view_dist+1
//Debug procs
/client/proc/test_movable_UI()
+6 -1
View File
@@ -243,7 +243,7 @@ var/obj/screen/robot_inventory
//Unfortunately adding the emag module to the list of modules has to be here. This is because a borg can
//be emagged before they actually select a module. - or some situation can cause them to get a new module
// - or some situation might cause them to get de-emagged or something.
if(r.emagged)
if(r.emagged || r.emag_items)
if(!(r.module.emag in r.module.modules))
r.module.modules.Add(r.module.emag)
else
@@ -274,3 +274,8 @@ var/obj/screen/robot_inventory
r.client.screen -= A
r.shown_robot_modules = 0
r.client.screen -= r.robot_modules_background
/mob/living/silicon/robot/update_hud()
..()
if(modtype)
hands.icon_state = lowertext(modtype)
+1 -1
View File
@@ -46,7 +46,7 @@
var/obj/item/owner
/obj/screen/item_action/Destroy()
..()
. = ..()
owner = null
/obj/screen/item_action/Click()
+3 -7
View File
@@ -13,7 +13,7 @@
var/mob/spell_holder
/obj/screen/movable/spell_master/Destroy()
..()
. = ..()
for(var/obj/screen/spell/spells in spell_objects)
spells.spellmaster = null
spell_objects.Cut()
@@ -23,10 +23,6 @@
spell_holder.client.screen -= src
spell_holder = null
/obj/screen/movable/spell_master/ResetVars()
..("spell_objects", args)
spell_objects = list()
/obj/screen/movable/spell_master/MouseDrop()
if(showing)
return
@@ -93,7 +89,7 @@
if(spell.spell_flags & NO_BUTTON) //no button to add if we don't get one
return
var/obj/screen/spell/newscreen = PoolOrNew(/obj/screen/spell)
var/obj/screen/spell/newscreen = new /obj/screen/spell()
newscreen.spellmaster = src
newscreen.spell = spell
@@ -160,7 +156,7 @@
var/icon/last_charged_icon
/obj/screen/spell/Destroy()
..()
. = ..()
spell = null
last_charged_icon = null
if(spellmaster)
+34 -76
View File
@@ -29,7 +29,8 @@
return
/mob/living/carbon/human/RangedAttack(var/atom/A)
if(!gloves && !mutations.len) return
if(!gloves && !mutations.len && !spitting)
return
var/obj/item/clothing/gloves/G = gloves
if((LASER in mutations) && a_intent == I_HURT)
LaserEyes(A) // moved into a proc below
@@ -40,6 +41,9 @@
else if(TK in mutations)
A.attack_tk(src)
else if(spitting) //Only used by xenos right now, can be expanded.
Spit(A)
/mob/living/RestrainedClickOn(var/atom/A)
return
@@ -58,77 +62,6 @@
setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
A.attack_generic(src,rand(5,6),"bitten")
/*
Slimes
Nothing happening here
*/
/mob/living/carbon/slime/RestrainedClickOn(var/atom/A)
return
/mob/living/carbon/slime/UnarmedAttack(var/atom/A, var/proximity)
if(!..())
return
// Eating
if(Victim)
if (Victim == A)
Feedstop()
return
//should have already been set if we are attacking a mob, but it doesn't hurt and will cover attacking non-mobs too
setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
var/mob/living/M = A
if (istype(M))
switch(src.a_intent)
if (I_HELP) // We just poke the other
M.visible_message("<span class='notice'>[src] gently pokes [M]!</span>", "<span class='notice'>[src] gently pokes you!</span>")
if (I_DISARM) // We stun the target, with the intention to feed
var/stunprob = 1
var/power = max(0, min(10, (powerlevel + rand(0, 3))))
if (powerlevel > 0 && !istype(A, /mob/living/carbon/slime))
if(ishuman(M))
var/mob/living/carbon/human/H = M
stunprob *= max(H.species.siemens_coefficient,0)
switch(power * 10)
if(0) stunprob *= 10
if(1 to 2) stunprob *= 20
if(3 to 4) stunprob *= 30
if(5 to 6) stunprob *= 40
if(7 to 8) stunprob *= 60
if(9) stunprob *= 70
if(10) stunprob *= 95
if(prob(stunprob))
powerlevel = max(0, powerlevel-3)
M.visible_message("<span class='danger'>[src] has shocked [M]!</span>", "<span class='danger'>[src] has shocked you!</span>")
M.Weaken(power)
M.Stun(power)
M.stuttering = max(M.stuttering, power)
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
s.set_up(5, 1, M)
s.start()
if(prob(stunprob) && powerlevel >= 8)
M.adjustFireLoss(powerlevel * rand(6,10))
else if(prob(40))
M.visible_message("<span class='danger'>[src] has pounced at [M]!</span>", "<span class='danger'>[src] has pounced at you!</span>")
M.Weaken(power)
else
M.visible_message("<span class='danger'>[src] has tried to pounce at [M]!</span>", "<span class='danger'>[src] has tried to pounce at you!</span>")
M.updatehealth()
if (I_GRAB) // We feed
Wrap(M)
if (I_HURT) // Attacking
A.attack_generic(src, (is_adult ? rand(20,40) : rand(5,25)), "glomped")
else
A.attack_generic(src, (is_adult ? rand(20,40) : rand(5,25)), "glomped") // Basic attack.
/*
New Players:
Have no reason to click on anything at all.
@@ -140,15 +73,40 @@
Animals
*/
/mob/living/simple_animal/UnarmedAttack(var/atom/A, var/proximity)
if(!..())
return
if(prob(spattack_prob))
if(spattack_min_range <= 1)
target_mob = A
SpecialAtkTarget()
target_mob = null
return
if(melee_damage_upper == 0 && istype(A,/mob/living))
custom_emote(1,"[friendly] [A]!")
return
setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
var/damage = rand(melee_damage_lower, melee_damage_upper)
if(A.attack_generic(src,damage,attacktext,environment_smash) && loc && attack_sound)
playsound(loc, attack_sound, 50, 1, 1)
if(isliving(A))
target_mob = A
PunchTarget()
target_mob = null
else
A.attack_generic(src, rand(melee_damage_lower, melee_damage_upper), attacktext)
/mob/living/simple_animal/RangedAttack(var/atom/A)
setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
var/distance = get_dist(src, A)
if(prob(spattack_prob) && (distance >= spattack_min_range) && (distance <= spattack_max_range))
target_mob = A
SpecialAtkTarget()
target_mob = null
return
if(ranged && distance <= shoot_range)
target_mob = A
ShootTarget(A)
target_mob = null
+1 -1
View File
@@ -149,7 +149,7 @@ var/const/tk_maxrange = 15
/obj/item/tk_grab/proc/apply_focus_overlay()
if(!focus) return
var/obj/effect/overlay/O = PoolOrNew(/obj/effect/overlay, locate(focus.x,focus.y,focus.z))
var/obj/effect/overlay/O = new /obj/effect/overlay(locate(focus.x,focus.y,focus.z))
O.name = "sparkles"
O.anchored = 1
O.density = 0
@@ -35,7 +35,6 @@
* Config vars
*/
// Process name
var/name
// Process schedule interval
// This controls how often the process would run under ideal conditions.
@@ -388,6 +387,6 @@
exceptions[eid] = 0
/datum/controller/process/proc/catchBadType(var/datum/caught)
if(isnull(caught) || !istype(caught) || !isnull(caught.gcDestroyed))
if(isnull(caught) || !istype(caught) || QDELETED(caught))
return // Only bother with types we can identify and that don't belong
catchException("Type [caught.type] does not belong in process' queue")
-260
View File
@@ -1,260 +0,0 @@
// The time a datum was destroyed by the GC, or null if it hasn't been
/datum/var/gcDestroyed
#define GC_COLLECTIONS_PER_RUN 300
#define GC_COLLECTION_TIMEOUT (30 SECONDS)
#define GC_FORCE_DEL_PER_RUN 30
var/datum/controller/process/garbage_collector/garbage_collector
var/list/delayed_garbage = list()
/datum/controller/process/garbage_collector
var/garbage_collect = 1 // Whether or not to actually do work
var/total_dels = 0 // number of total del()'s
var/tick_dels = 0 // number of del()'s we've done this tick
var/soft_dels = 0
var/hard_dels = 0 // number of hard dels in total
var/list/destroyed = list() // list of refID's of things that should be garbage collected
// refID's are associated with the time at which they time out and need to be manually del()
// we do this so we aren't constantly locating them and preventing them from being gc'd
var/list/logging = list() // list of all types that have failed to GC associated with the number of times that's happened.
// the types are stored as strings
/datum/controller/process/garbage_collector/setup()
name = "garbage"
schedule_interval = 5 SECONDS
start_delay = 3
if(!garbage_collector)
garbage_collector = src
for(var/garbage in delayed_garbage)
qdel(garbage)
delayed_garbage.Cut()
delayed_garbage = null
#ifdef GC_FINDREF
world/loop_checks = 0
#endif
/datum/controller/process/garbage_collector/doWork()
if(!garbage_collect)
return
tick_dels = 0
var/time_to_kill = world.time - GC_COLLECTION_TIMEOUT
var/checkRemain = GC_COLLECTIONS_PER_RUN
var/remaining_force_dels = GC_FORCE_DEL_PER_RUN
while(destroyed.len && --checkRemain >= 0)
if(remaining_force_dels <= 0)
#ifdef GC_DEBUG
testing("GC: Reached max force dels per tick [dels] vs [maxDels]")
#endif
break // Server's already pretty pounded, everything else can wait 2 seconds
var/refID = destroyed[1]
var/GCd_at_time = destroyed[refID]
if(GCd_at_time > time_to_kill)
#ifdef GC_DEBUG
testing("GC: [refID] not old enough, breaking at [world.time] for [GCd_at_time - time_to_kill] deciseconds until [GCd_at_time + collection_timeout]")
#endif
break // Everything else is newer, skip them
var/datum/A = locate(refID)
#ifdef GC_DEBUG
testing("GC: [refID] old enough to test: GCd_at_time: [GCd_at_time] time_to_kill: [time_to_kill] current: [world.time]")
#endif
if(A && A.gcDestroyed == GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake
// Something's still referring to the qdel'd object. Kill it.
testing("GC: -- \ref[A] | [A.type] was unable to be GC'd and was deleted --")
logging["[A.type]"]++
del(A)
hard_dels++
remaining_force_dels--
else
#ifdef GC_DEBUG
testing("GC: [refID] properly GC'd at [world.time] with timeout [GCd_at_time]")
#endif
soft_dels++
tick_dels++
total_dels++
destroyed.Cut(1, 2)
SCHECK
#undef GC_FORCE_DEL_PER_TICK
#undef GC_COLLECTION_TIMEOUT
#undef GC_COLLECTIONS_PER_TICK
#ifdef GC_FINDREF
/datum/controller/process/garbage_collector/proc/LookForRefs(var/datum/D, var/list/targ)
. = 0
for(var/V in D.vars)
if(V == "contents")
continue
if(istype(D.vars[V], /atom))
var/atom/A = D.vars[V]
if(A in targ)
testing("GC: [A] | [A.type] referenced by [D] | [D.type], var [V]")
. += 1
else if(islist(D.vars[V]))
. += LookForListRefs(D.vars[V], targ, D, V)
/datum/controller/process/garbage_collector/proc/LookForListRefs(var/list/L, var/list/targ, var/datum/D, var/V)
. = 0
for(var/F in L)
if(istype(F, /atom))
var/atom/A = F
if(A in targ)
testing("GC: [A] | [A.type] referenced by [D] | [D.type], list [V]")
. += 1
if(islist(F))
. += LookForListRefs(F, targ, D, "[F] in list [V]")
#endif
/datum/controller/process/garbage_collector/proc/AddTrash(datum/A)
if(!istype(A) || !isnull(A.gcDestroyed))
return
#ifdef GC_DEBUG
testing("GC: AddTrash(\ref[A] - [A.type])")
#endif
A.gcDestroyed = world.time
destroyed -= "\ref[A]" // Removing any previous references that were GC'd so that the current object will be at the end of the list.
destroyed["\ref[A]"] = world.time
/datum/controller/process/garbage_collector/statProcess()
..()
stat(null, "[garbage_collect ? "On" : "Off"], [destroyed.len] queued")
stat(null, "Dels: [total_dels], [soft_dels] soft, [hard_dels] hard, [tick_dels] last run")
// Tests if an atom has been deleted.
/proc/deleted(atom/A)
return !A || !isnull(A.gcDestroyed)
// Should be treated as a replacement for the 'del' keyword.
// Datums passed to this will be given a chance to clean up references to allow the GC to collect them.
/proc/qdel(var/datum/A)
if(!A)
return
if(!istype(A))
warning("qdel() passed object of type [A.type]. qdel() can only handle /datum types.")
crash_with("qdel() passed object of type [A.type]. qdel() can only handle /datum types.")
del(A)
if(garbage_collector)
garbage_collector.total_dels++
garbage_collector.hard_dels++
else if(isnull(A.gcDestroyed))
// Let our friend know they're about to get collected
. = !A.Destroy()
if(. && A)
A.finalize_qdel()
/datum/proc/finalize_qdel()
if(IsPooled(src))
PlaceInPool(src)
else
del(src)
/atom/finalize_qdel()
if(IsPooled(src))
PlaceInPool(src)
else
if(garbage_collector)
garbage_collector.AddTrash(src)
else
delayed_garbage |= src
/icon/finalize_qdel()
del(src)
/image/finalize_qdel()
del(src)
/mob/finalize_qdel()
del(src)
/turf/finalize_qdel()
del(src)
// Default implementation of clean-up code.
// This should be overridden to remove all references pointing to the object being destroyed.
// Return true if the the GC controller should allow the object to continue existing. (Useful if pooling objects.)
/datum/proc/Destroy()
nanomanager.close_uis(src)
tag = null
return
#ifdef TESTING
/client/var/running_find_references
/mob/verb/create_thing()
set category = "Debug"
set name = "Create Thing"
var/path = input("Enter path")
var/atom/thing = new path(loc)
thing.find_references()
/atom/verb/find_references()
set category = "Debug"
set name = "Find References"
set background = 1
set src in world
if(!usr || !usr.client)
return
if(usr.client.running_find_references)
testing("CANCELLED search for references to a [usr.client.running_find_references].")
usr.client.running_find_references = null
return
if(alert("Running this will create a lot of lag until it finishes. You can cancel it by running it again. Would you like to begin the search?", "Find References", "Yes", "No") == "No")
return
// Remove this object from the list of things to be auto-deleted.
if(garbage_collector)
garbage_collector.destroyed -= "\ref[src]"
usr.client.running_find_references = type
testing("Beginning search for references to a [type].")
var/list/things = list()
for(var/client/thing)
things += thing
for(var/datum/thing)
things += thing
for(var/atom/thing)
things += thing
testing("Collected list of things in search for references to a [type]. ([things.len] Thing\s)")
for(var/datum/thing in things)
if(!usr.client.running_find_references) return
for(var/varname in thing.vars)
var/variable = thing.vars[varname]
if(variable == src)
testing("Found [src.type] \ref[src] in [thing.type]'s [varname] var.")
else if(islist(variable))
if(src in variable)
testing("Found [src.type] \ref[src] in [thing.type]'s [varname] list var.")
testing("Completed search for references to a [type].")
usr.client.running_find_references = null
/client/verb/purge_all_destroyed_objects()
set category = "Debug"
if(garbage_collector)
while(garbage_collector.destroyed.len)
var/datum/o = locate(garbage_collector.destroyed[1])
if(istype(o) && o.gcDestroyed)
del(o)
garbage_collector.dels++
garbage_collector.destroyed.Cut(1, 2)
#endif
#ifdef GC_DEBUG
#undef GC_DEBUG
#endif
#ifdef GC_FINDREF
#undef GC_FINDREF
#endif
+1 -1
View File
@@ -6,7 +6,7 @@
if(config.kick_inactive)
for(last_object in clients)
var/client/C = last_object
if(!C.holder && C.is_afk(config.kick_inactive MINUTES))
if(C.is_afk(config.kick_inactive MINUTES))
if(!istype(C.mob, /mob/observer/dead))
log_access("AFK: [key_name(C)]")
C << "<SPAN CLASS='warning'>You have been inactive for more than [config.kick_inactive] minute\s and have been disconnected.</SPAN>"
-98
View File
@@ -1,98 +0,0 @@
/var/lighting_overlays_initialised = FALSE
/var/list/lighting_update_lights = list() // List of lighting sources queued for update.
/var/list/lighting_update_corners = list() // List of lighting corners queued for update.
/var/list/lighting_update_overlays = list() // List of lighting overlays queued for update.
/var/list/lighting_update_lights_old = list() // List of lighting sources currently being updated.
/var/list/lighting_update_corners_old = list() // List of lighting corners currently being updated.
/var/list/lighting_update_overlays_old = list() // List of lighting overlays currently being updated.
/datum/controller/process/lighting
// Queues of update counts, waiting to be rolled into stats lists
var/list/stats_queues = list(
"Source" = list(), "Corner" = list(), "Overlay" = list())
// Stats lists
var/list/stats_lists = list(
"Source" = list(), "Corner" = list(), "Overlay" = list())
var/update_stats_every = (1 SECONDS)
var/next_stats_update = 0
var/stat_updates_to_keep = 5
/datum/controller/process/lighting/setup()
name = "lighting"
schedule_interval = 0 // run as fast as you possibly can
sleep_interval = 10 // Yield every 10% of a tick
defer_usage = 80 // Defer at 80% of a tick
create_all_lighting_overlays()
lighting_overlays_initialised = TRUE
// Pre-process lighting once before the round starts. Wait 30 seconds so the away mission has time to load.
spawn(300)
doWork(1)
/datum/controller/process/lighting/doWork(roundstart)
lighting_update_lights_old = lighting_update_lights //We use a different list so any additions to the update lists during a delay from scheck() don't cause things to be cut from the list without being updated.
lighting_update_lights = list()
for(var/datum/light_source/L in lighting_update_lights_old)
if(L.check() || L.destroyed || L.force_update)
L.remove_lum()
if(!L.destroyed)
L.apply_lum()
else if(L.vis_update) //We smartly update only tiles that became (in) visible to use.
L.smart_vis_update()
L.vis_update = FALSE
L.force_update = FALSE
L.needs_update = FALSE
SCHECK
lighting_update_corners_old = lighting_update_corners //Same as above.
lighting_update_corners = list()
for(var/A in lighting_update_corners_old)
var/datum/lighting_corner/C = A
C.update_overlays()
C.needs_update = FALSE
SCHECK
lighting_update_overlays_old = lighting_update_overlays //Same as above.
lighting_update_overlays = list()
for(var/A in lighting_update_overlays_old)
var/atom/movable/lighting_overlay/O = A
O.update_overlay()
O.needs_update = 0
SCHECK
stats_queues["Source"] += lighting_update_lights_old.len
stats_queues["Corner"] += lighting_update_corners_old.len
stats_queues["Overlay"] += lighting_update_overlays_old.len
if(next_stats_update <= world.time)
next_stats_update = world.time + update_stats_every
for(var/stat_name in stats_queues)
var/stat_sum = 0
var/list/stats_queue = stats_queues[stat_name]
for(var/count in stats_queue)
stat_sum += count
stats_queue.Cut()
var/list/stats_list = stats_lists[stat_name]
stats_list.Insert(1, stat_sum)
if(stats_list.len > stat_updates_to_keep)
stats_list.Cut(stats_list.len)
/datum/controller/process/lighting/statProcess()
..()
stat(null, "[total_lighting_sources] sources, [total_lighting_corners] corners, [total_lighting_overlays] overlays")
for(var/stat_type in stats_lists)
stat(null, "[stat_type] updates: [jointext(stats_lists[stat_type], " | ")]")
+3 -3
View File
@@ -20,7 +20,7 @@
/datum/controller/process/machinery/proc/internal_process_machinery()
for(last_object in machines)
var/obj/machinery/M = last_object
if(M && !M.gcDestroyed)
if(M && !QDELETED(M))
if(M.process() == PROCESS_KILL)
//M.inMachineList = 0 We don't use this debugging function
machines.Remove(M)
@@ -34,7 +34,7 @@
/datum/controller/process/machinery/proc/internal_process_power()
for(last_object in powernets)
var/datum/powernet/powerNetwork = last_object
if(istype(powerNetwork) && isnull(powerNetwork.gcDestroyed))
if(istype(powerNetwork) && !QDELETED(powerNetwork))
powerNetwork.reset()
SCHECK
continue
@@ -52,7 +52,7 @@
/datum/controller/process/machinery/proc/internal_process_pipenets()
for(last_object in pipe_networks)
var/datum/pipe_network/pipeNetwork = last_object
if(istype(pipeNetwork) && isnull(pipeNetwork.gcDestroyed))
if(istype(pipeNetwork) && !QDELETED(pipeNetwork))
pipeNetwork.process()
SCHECK
continue
+1 -1
View File
@@ -14,7 +14,7 @@
/datum/controller/process/mob/doWork()
for(last_object in mob_list)
var/mob/M = last_object
if(M && isnull(M.gcDestroyed))
if(M && !QDELETED(M))
try
M.Life()
catch(var/exception/e)
+1 -1
View File
@@ -9,7 +9,7 @@
/datum/controller/process/nanoui/doWork()
for(last_object in nanomanager.processing_uis)
var/datum/nanoui/NUI = last_object
if(istype(NUI) && isnull(NUI.gcDestroyed))
if(istype(NUI) && !QDELETED(NUI))
try
NUI.process()
catch(var/exception/e)
+1 -1
View File
@@ -11,7 +11,7 @@
/datum/controller/process/obj/doWork()
for(last_object in processing_objects)
var/datum/O = last_object
if(O && isnull(O.gcDestroyed))
if(!QDELETED(O))
try
O:process()
catch(var/exception/e)
+56
View File
@@ -0,0 +1,56 @@
/datum/controller/process/radiation
var/repository/radiation/linked = null
/datum/controller/process/radiation/setup()
name = "radiation controller"
schedule_interval = 20 // every 2 seconds
linked = radiation_repository
/datum/controller/process/radiation/doWork()
sources_decay()
cache_expires()
irradiate_targets()
// Step 1 - Sources Decay
/datum/controller/process/radiation/proc/sources_decay()
var/list/sources = linked.sources
for(var/thing in sources)
var/datum/radiation_source/S = thing
if(QDELETED(S))
sources.Remove(S)
continue
if(S.decay)
S.update_rad_power(S.rad_power - config.radiation_decay_rate)
if(S.rad_power <= config.radiation_lower_limit)
sources.Remove(S)
SCHECK // This scheck probably just wastes resources, but better safe than sorry in this case.
// Step 2 - Cache Expires
/datum/controller/process/radiation/proc/cache_expires()
var/list/resistance_cache = linked.resistance_cache
for(var/thing in resistance_cache)
var/turf/T = thing
if(QDELETED(T))
resistance_cache.Remove(T)
continue
if((length(T.contents) + 1) != resistance_cache[T])
resistance_cache.Remove(T) // If its stale REMOVE it! It will get added if its needed.
SCHECK
// Step 3 - Registered irradiatable things are checked for radiation
/datum/controller/process/radiation/proc/irradiate_targets()
var/list/registered_listeners = living_mob_list // For now just use this. Nothing else is interested anyway.
if(length(linked.sources) > 0)
for(var/thing in registered_listeners)
var/atom/A = thing
if(QDELETED(A))
continue
var/turf/T = get_turf(thing)
var/rads = linked.get_rads_at_turf(T)
if(rads)
A.rad_act(rads)
SCHECK
/datum/controller/process/radiation/statProcess()
..()
stat(null, "[linked.sources.len] sources, [linked.resistance_cache.len] cached turfs")
+4 -6
View File
@@ -30,8 +30,8 @@
// Note: We won't be informed about tasks being destroyed, but this is the best we can do.
/datum/controller/process/scheduler/copyStateFrom(var/datum/controller/process/scheduler/target)
scheduled_tasks = list()
for(var/st in target.scheduled_tasks)
if(!deleted(st) && istype(st, /datum/scheduled_task))
for(var/datum/scheduled_task/st in target.scheduled_tasks)
if(!QDELETED(st) && istype(st))
schedule(st)
scheduler = src
@@ -46,12 +46,9 @@
/datum/controller/process/scheduler/proc/schedule(var/datum/scheduled_task/st)
scheduled_tasks += st
destroyed_event.register(st, src, /datum/controller/process/scheduler/proc/unschedule)
/datum/controller/process/scheduler/proc/unschedule(var/datum/scheduled_task/st)
if(st in scheduled_tasks)
scheduled_tasks -= st
destroyed_event.unregister(st, src)
scheduled_tasks -= st
/**********
* Helpers *
@@ -102,6 +99,7 @@
task_after_process_args += src
/datum/scheduled_task/Destroy()
scheduler.unschedule(src)
procedure = null
arguments.Cut()
task_after_process = null
+7
View File
@@ -33,3 +33,10 @@ var/global/datum/controller/process/ticker/tickerProcess
/datum/controller/process/ticker/proc/getLastTickerTimeDuration()
return lastTickerTimeDuration
// Use these preferentially to directly examining ticker.current_state to help prepare for transition to ticker as subsystem!
/datum/controller/process/ticker/proc/HasRoundStarted()
return (ticker && ticker.current_state >= GAME_STATE_PLAYING)
/datum/controller/process/ticker/proc/IsRoundInProgress()
return (ticker && ticker.current_state == GAME_STATE_PLAYING)
+1 -2
View File
@@ -159,7 +159,7 @@ var/list/DEPT_FREQS = list(AI_FREQ, COMM_FREQ, ENG_FREQ, ENT_FREQ, MED_FREQ, SEC
// Antags!
if (frequency in ANTAG_FREQS)
return "syndradio"
// centcomm channels (deathsquid and ert)
// CentCom channels (deathsquid and ert)
if(frequency in CENT_FREQS)
return "centradio"
// command channel
@@ -314,7 +314,6 @@ var/global/datum/controller/radio/radio_controller
devices_line -= null
if (devices_line.len==0)
devices -= devices_filter
del(devices_line)
/datum/signal
var/obj/source
+19 -2
View File
@@ -46,7 +46,8 @@ var/list/gamemode_cache = list()
var/continous_rounds = 0 // Gamemodes which end instantly will instead keep on going until the round ends by escape shuttle or nuke.
var/allow_Metadata = 0 // Metadata is supported.
var/popup_admin_pm = 0 //adminPMs to non-admins show in a pop-up 'reply' window when set to 1.
var/Ticklag = 0.9
var/fps = 20
var/tick_limit_mc_init = TICK_LIMIT_MC_INIT_DEFAULT //SSinitialization throttling
var/Tickcomp = 0
var/socket_talk = 0 // use socket_talk to communicate with other processes
var/list/resource_urls = null
@@ -166,6 +167,7 @@ var/list/gamemode_cache = list()
var/simultaneous_pm_warning_timeout = 100
var/use_recursive_explosions //Defines whether the server uses recursive or circular explosions.
var/multi_z_explosion_scalar = 0.5 //Multiplier for how much weaker explosions are on neighboring z levels.
var/assistant_maint = 0 //Do assistants get maint access?
var/gateway_delay = 18000 //How long the gateway takes before it activates. Default is half an hour.
@@ -218,6 +220,10 @@ var/list/gamemode_cache = list()
var/show_human_death_message = 1
var/radiation_decay_rate = 1 //How much radiation is reduced by each tick
var/radiation_resistance_multiplier = 6.5
var/radiation_lower_limit = 0.35 //If the radiation level for a turf would be below this, ignore it.
/datum/configuration/New()
var/list/L = typesof(/datum/game_mode) - /datum/game_mode
for (var/T in L)
@@ -283,6 +289,9 @@ var/list/gamemode_cache = list()
if ("use_recursive_explosions")
use_recursive_explosions = 1
if ("multi_z_explosion_scalar")
multi_z_explosion_scalar = text2num(value)
if ("log_ooc")
config.log_ooc = 1
@@ -560,7 +569,12 @@ var/list/gamemode_cache = list()
irc_bot_export = 1
if("ticklag")
Ticklag = text2num(value)
var/ticklag = text2num(value)
if(ticklag > 0)
fps = 10 / ticklag
if("tick_limit_mc_init")
tick_limit_mc_init = text2num(value)
if("allow_antag_hud")
config.antag_hud_allowed = 1
@@ -712,6 +726,9 @@ var/list/gamemode_cache = list()
if(values.len > 0)
language_prefixes = values
if("radiation_lower_limit")
radiation_lower_limit = text2num(value)
else
log_misc("Unknown setting in configuration: '[name]'")
+19
View File
@@ -0,0 +1,19 @@
/datum/controller
var/name
// The object used for the clickable stat() button.
var/obj/effect/statclick/statclick
/datum/controller/proc/Initialize()
//cleanup actions
/datum/controller/proc/Shutdown()
//when we enter dmm_suite.load_map
/datum/controller/proc/StartLoadingMap()
//when we exit dmm_suite.load_map
/datum/controller/proc/StopLoadingMap()
/datum/controller/proc/Recover()
/datum/controller/proc/stat_entry()
@@ -22,6 +22,10 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle
var/datum/announcement/priority/emergency_shuttle_called = new(0, new_sound = sound('sound/AI/shuttlecalled.ogg'))
var/datum/announcement/priority/emergency_shuttle_recalled = new(0, new_sound = sound('sound/AI/shuttlerecalled.ogg'))
/datum/emergency_shuttle_controller/New()
escape_pods = list()
..()
/datum/emergency_shuttle_controller/proc/process()
if (wait_for_launch)
if (evac && auto_recall && world.time >= auto_recall_time)
+102
View File
@@ -0,0 +1,102 @@
/**
* Failsafe
*
* Pretty much pokes the MC to make sure it's still alive.
**/
var/datum/controller/failsafe/Failsafe
/datum/controller/failsafe // This thing pretty much just keeps poking the master controller
name = "Failsafe"
// The length of time to check on the MC (in deciseconds).
// Set to 0 to disable.
var/processing_interval = 20
// The alert level. For every failed poke, we drop a DEFCON level. Once we hit DEFCON 1, restart the MC.
var/defcon = 5
//the world.time of the last check, so the mc can restart US if we hang.
// (Real friends look out for *eachother*)
var/lasttick = 0
// Track the MC iteration to make sure its still on track.
var/master_iteration = 0
var/running = TRUE
/datum/controller/failsafe/New()
// Highlander-style: there can only be one! Kill off the old and replace it with the new.
if(Failsafe != src)
if(istype(Failsafe))
qdel(Failsafe)
Failsafe = src
Initialize()
/datum/controller/failsafe/Initialize()
set waitfor = 0
Failsafe.Loop()
if(!QDELETED(src))
qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us
/datum/controller/failsafe/Destroy()
running = FALSE
..()
return QDEL_HINT_HARDDEL_NOW
/datum/controller/failsafe/proc/Loop()
while(running)
lasttick = world.time
if(!Master)
// Replace the missing Master! This should never, ever happen.
new /datum/controller/master()
// Only poke it if overrides are not in effect.
if(processing_interval > 0)
if(Master.processing && Master.iteration)
// Check if processing is done yet.
if(Master.iteration == master_iteration)
switch(defcon)
if(4,5)
--defcon
if(3)
to_chat(admins, "<span class='adminnotice'>Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks.</span>")
--defcon
if(2)
to_chat(admins, "<span class='boldannounce'>Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.</span>")
--defcon
if(1)
to_chat(admins, "<span class='boldannounce'>Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5-defcon) * processing_interval] ticks. Killing and restarting...</span>")
--defcon
var/rtn = Recreate_MC()
if(rtn > 0)
defcon = 4
master_iteration = 0
to_chat(admins, "<span class='adminnotice'>MC restarted successfully</span>")
else if(rtn < 0)
log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0")
to_chat(admins, "<span class='boldannounce'>ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.</span>")
//if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again
//no need to handle that specially when defcon 0 can handle it
if(0) //DEFCON 0! (mc failed to restart)
var/rtn = Recreate_MC()
if(rtn > 0)
defcon = 4
master_iteration = 0
to_chat(admins, "<span class='adminnotice'>MC restarted successfully</span>")
else
defcon = min(defcon + 1,5)
master_iteration = Master.iteration
if (defcon <= 1)
sleep(processing_interval*2)
else
sleep(processing_interval)
else
defcon = 5
sleep(initial(processing_interval))
/datum/controller/failsafe/proc/defcon_pretty()
return defcon
/datum/controller/failsafe/stat_entry()
if(!statclick)
statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
stat("Failsafe Controller:", statclick.update("Defcon: [defcon_pretty()] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])"))
+566
View File
@@ -0,0 +1,566 @@
/**
* StonedMC
*
* Designed to properly split up a given tick among subsystems
* Note: if you read parts of this code and think "why is it doing it that way"
* Odds are, there is a reason
*
**/
var/datum/controller/master/Master = new()
/datum/controller/master
name = "Master"
// Are we processing (higher values increase the processing delay by n ticks)
var/processing = 1
// How many times have we ran
var/iteration = 0
// world.time of last fire, for tracking lag outside of the mc
var/last_run
// List of subsystems to process().
var/list/subsystems
// Vars for keeping track of tick drift.
var/init_timeofday
var/init_time
var/tickdrift = 0
var/sleep_delta
var/make_runtime = 0
var/initializations_finished_with_no_players_logged_in //I wonder what this could be?
// The type of the last subsystem to be process()'d.
var/last_type_processed
var/datum/controller/subsystem/queue_head //Start of queue linked list
var/datum/controller/subsystem/queue_tail //End of queue linked list (used for appending to the list)
var/queue_priority_count = 0 //Running total so that we don't have to loop thru the queue each run to split up the tick
var/queue_priority_count_bg = 0 //Same, but for background subsystems
var/map_loading = FALSE //Are we loading in a new map?
var/current_runlevel //for scheduling different subsystems for different stages of the round
var/static/restart_clear = 0
var/static/restart_timeout = 0
var/static/restart_count = 0
//current tick limit, assigned before running a subsystem.
//used by CHECK_TICK as well so that the procs subsystems call can obey that SS's tick limits
var/static/current_ticklimit = TICK_LIMIT_RUNNING
/datum/controller/master/New()
// Highlander-style: there can only be one! Kill off the old and replace it with the new.
subsystems = list()
if (Master != src)
if (istype(Master))
Recover()
qdel(Master)
else
init_subtypes(/datum/controller/subsystem, subsystems)
Master = src
/datum/controller/master/Destroy()
..()
// Tell qdel() to Del() this object.
return QDEL_HINT_HARDDEL_NOW
/datum/controller/master/Shutdown()
processing = FALSE
sortTim(subsystems, /proc/cmp_subsystem_init)
reverseRange(subsystems)
for(var/datum/controller/subsystem/ss in subsystems)
ss.Shutdown()
// Returns 1 if we created a new mc, 0 if we couldn't due to a recent restart,
// -1 if we encountered a runtime trying to recreate it
/proc/Recreate_MC()
. = -1 //so if we runtime, things know we failed
if (world.time < Master.restart_timeout)
return 0
if (world.time < Master.restart_clear)
Master.restart_count *= 0.5
var/delay = 50 * ++Master.restart_count
Master.restart_timeout = world.time + delay
Master.restart_clear = world.time + (delay * 2)
Master.processing = 0 //stop ticking this one
try
new/datum/controller/master()
catch
return -1
return 1
/datum/controller/master/Recover()
var/msg = "## DEBUG: [time2text(world.timeofday)] MC restarted. Reports:\n"
for (var/varname in Master.vars)
switch (varname)
if("name", "tag", "bestF", "type", "parent_type", "vars", "statclick") // Built-in junk.
continue
else
var/varval = Master.vars[varname]
if (istype(varval, /datum)) // Check if it has a type var.
var/datum/D = varval
msg += "\t [varname] = [D]([D.type])\n"
else
msg += "\t [varname] = [varval]\n"
log_world(msg)
var/datum/controller/subsystem/BadBoy = Master.last_type_processed
var/FireHim = FALSE
if(istype(BadBoy))
msg = null
switch(++BadBoy.failure_strikes)
if(2)
msg = "The [BadBoy.name] subsystem was the last to fire for 2 controller restarts. It will be recovered now and disabled if it happens again."
FireHim = TRUE
if(3)
msg = "The [BadBoy.name] subsystem seems to be destabilizing the MC and will be offlined."
BadBoy.flags |= SS_NO_FIRE
if(msg)
log_game(msg)
message_admins("<span class='boldannounce'>[msg]</span>")
log_world(msg)
if (istype(Master.subsystems))
if(FireHim)
Master.subsystems += new BadBoy.type //NEW_SS_GLOBAL will remove the old one
subsystems = Master.subsystems
current_runlevel = Master.current_runlevel
StartProcessing(10)
else
to_chat(world, "<span class='boldannounce'>The Master Controller is having some issues, we will need to re-initialize EVERYTHING</span>")
Initialize(20, TRUE)
// Please don't stuff random bullshit here,
// Make a subsystem, give it the SS_NO_FIRE flag, and do your work in it's Initialize()
/datum/controller/master/Initialize(delay, init_sss)
set waitfor = 0
if(delay)
sleep(delay)
if(init_sss)
init_subtypes(/datum/controller/subsystem, subsystems)
to_chat(world, "<span class='boldannounce'>Initializing subsystems...</span>")
// Sort subsystems by init_order, so they initialize in the correct order.
sortTim(subsystems, /proc/cmp_subsystem_init)
var/start_timeofday = REALTIMEOFDAY
// Initialize subsystems.
current_ticklimit = config.tick_limit_mc_init
for (var/datum/controller/subsystem/SS in subsystems)
if (SS.flags & SS_NO_INIT)
continue
SS.Initialize(REALTIMEOFDAY)
CHECK_TICK
current_ticklimit = TICK_LIMIT_RUNNING
var/time = (REALTIMEOFDAY - start_timeofday) / 10
var/msg = "Initializations complete within [time] second[time == 1 ? "" : "s"]!"
to_chat(world, "<span class='boldannounce'>[msg]</span>")
log_world(msg)
if (!current_runlevel)
SetRunLevel(RUNLEVEL_LOBBY)
// Sort subsystems by display setting for easy access.
sortTim(subsystems, /proc/cmp_subsystem_display)
// Set world options.
#ifdef UNIT_TEST
world.sleep_offline = 0
#else
world.sleep_offline = 1
#endif
world.fps = config.fps
var/initialized_tod = REALTIMEOFDAY
sleep(1)
initializations_finished_with_no_players_logged_in = initialized_tod < REALTIMEOFDAY - 10
// Loop.
Master.StartProcessing(0)
/datum/controller/master/proc/SetRunLevel(new_runlevel)
var/old_runlevel = isnull(current_runlevel) ? "NULL" : runlevel_flags[current_runlevel]
testing("MC: Runlevel changed from [old_runlevel] to [new_runlevel]")
current_runlevel = RUNLEVEL_FLAG_TO_INDEX(new_runlevel)
if(current_runlevel < 1)
CRASH("Attempted to set invalid runlevel: [new_runlevel]")
// Starts the mc, and sticks around to restart it if the loop ever ends.
/datum/controller/master/proc/StartProcessing(delay)
set waitfor = 0
if(delay)
sleep(delay)
var/rtn = Loop()
if (rtn > 0 || processing < 0)
return //this was suppose to happen.
//loop ended, restart the mc
log_game("MC crashed or runtimed, restarting")
message_admins("MC crashed or runtimed, restarting")
var/rtn2 = Recreate_MC()
if (rtn2 <= 0)
log_game("Failed to recreate MC (Error code: [rtn2]), it's up to the failsafe now")
message_admins("Failed to recreate MC (Error code: [rtn2]), it's up to the failsafe now")
Failsafe.defcon = 2
// Main loop.
/datum/controller/master/proc/Loop()
. = -1
//Prep the loop (most of this is because we want MC restarts to reset as much state as we can, and because
// local vars rock
//all this shit is here so that flag edits can be refreshed by restarting the MC. (and for speed)
var/list/tickersubsystems = list()
var/list/runlevel_sorted_subsystems = list(list()) //ensure we always have at least one runlevel
var/timer = world.time
for (var/thing in subsystems)
var/datum/controller/subsystem/SS = thing
if (SS.flags & SS_NO_FIRE)
continue
SS.queued_time = 0
SS.queue_next = null
SS.queue_prev = null
SS.state = SS_IDLE
if (SS.flags & SS_TICKER)
tickersubsystems += SS
timer += world.tick_lag * rand(1, 5)
SS.next_fire = timer
continue
var/ss_runlevels = SS.runlevels
var/added_to_any = FALSE
for(var/I in 1 to global.runlevel_flags.len)
if(ss_runlevels & global.runlevel_flags[I])
while(runlevel_sorted_subsystems.len < I)
runlevel_sorted_subsystems += list(list())
runlevel_sorted_subsystems[I] += SS
added_to_any = TRUE
if(!added_to_any)
WARNING("[SS.name] subsystem is not SS_NO_FIRE but also does not have any runlevels set!")
queue_head = null
queue_tail = null
//these sort by lower priorities first to reduce the number of loops needed to add subsequent SS's to the queue
//(higher subsystems will be sooner in the queue, adding them later in the loop means we don't have to loop thru them next queue add)
sortTim(tickersubsystems, /proc/cmp_subsystem_priority)
for(var/I in runlevel_sorted_subsystems)
sortTim(runlevel_sorted_subsystems, /proc/cmp_subsystem_priority)
I += tickersubsystems
var/cached_runlevel = current_runlevel
var/list/current_runlevel_subsystems = runlevel_sorted_subsystems[cached_runlevel]
init_timeofday = REALTIMEOFDAY
init_time = world.time
iteration = 1
var/error_level = 0
var/sleep_delta = 0
var/list/subsystems_to_check
//the actual loop.
while (1)
tickdrift = max(0, MC_AVERAGE_FAST(tickdrift, (((REALTIMEOFDAY - init_timeofday) - (world.time - init_time)) / world.tick_lag)))
if (processing <= 0)
current_ticklimit = TICK_LIMIT_RUNNING
sleep(10)
continue
//if there are mutiple sleeping procs running before us hogging the cpu, we have to run later
// because sleeps are processed in the order received, so longer sleeps are more likely to run first
if (world.tick_usage > TICK_LIMIT_MC)
sleep_delta += 2
current_ticklimit = TICK_LIMIT_RUNNING * 0.5
sleep(world.tick_lag * (processing + sleep_delta))
continue
sleep_delta = MC_AVERAGE_FAST(sleep_delta, 0)
if (last_run + (world.tick_lag * processing) > world.time)
sleep_delta += 1
if (world.tick_usage > (TICK_LIMIT_MC*0.5))
sleep_delta += 1
if (make_runtime)
var/datum/controller/subsystem/SS
SS.can_fire = 0
if (!Failsafe || (Failsafe.processing_interval > 0 && (Failsafe.lasttick+(Failsafe.processing_interval*5)) < world.time))
new/datum/controller/failsafe() // (re)Start the failsafe.
if (!queue_head || !(iteration % 3))
var/checking_runlevel = current_runlevel
if(cached_runlevel != checking_runlevel)
//resechedule subsystems
cached_runlevel = checking_runlevel
current_runlevel_subsystems = runlevel_sorted_subsystems[cached_runlevel]
var/stagger = world.time
for(var/I in current_runlevel_subsystems)
var/datum/controller/subsystem/SS = I
if(SS.next_fire <= world.time)
stagger += world.tick_lag * rand(1, 5)
SS.next_fire = stagger
subsystems_to_check = current_runlevel_subsystems
else
subsystems_to_check = tickersubsystems
if (CheckQueue(subsystems_to_check) <= 0)
if (!SoftReset(tickersubsystems, runlevel_sorted_subsystems))
log_world("MC: SoftReset() failed, crashing")
return
if (!error_level)
iteration++
error_level++
current_ticklimit = TICK_LIMIT_RUNNING
sleep(10)
continue
if (queue_head)
if (RunQueue() <= 0)
if (!SoftReset(tickersubsystems, runlevel_sorted_subsystems))
log_world("MC: SoftReset() failed, crashing")
return
if (!error_level)
iteration++
error_level++
current_ticklimit = TICK_LIMIT_RUNNING
sleep(10)
continue
error_level--
if (!queue_head) //reset the counts if the queue is empty, in the off chance they get out of sync
queue_priority_count = 0
queue_priority_count_bg = 0
iteration++
last_run = world.time
src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta)
current_ticklimit = TICK_LIMIT_RUNNING - (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc.
sleep(world.tick_lag * (processing + sleep_delta))
// This is what decides if something should run.
/datum/controller/master/proc/CheckQueue(list/subsystemstocheck)
. = 0 //so the mc knows if we runtimed
//we create our variables outside of the loops to save on overhead
var/datum/controller/subsystem/SS
var/SS_flags
for (var/thing in subsystemstocheck)
if (!thing)
subsystemstocheck -= thing
SS = thing
if (SS.state != SS_IDLE)
continue
if (SS.can_fire <= 0)
continue
if (SS.next_fire > world.time)
continue
SS_flags = SS.flags
if (SS_flags & SS_NO_FIRE)
subsystemstocheck -= SS
continue
if (!(SS_flags & SS_TICKER) && (SS_flags & SS_KEEP_TIMING) && SS.last_fire + (SS.wait * 0.75) > world.time)
continue
SS.enqueue()
. = 1
// Run thru the queue of subsystems to run, running them while balancing out their allocated tick precentage
/datum/controller/master/proc/RunQueue()
. = 0
var/datum/controller/subsystem/queue_node
var/queue_node_flags
var/queue_node_priority
var/queue_node_paused
var/current_tick_budget
var/tick_precentage
var/tick_remaining
var/ran = TRUE //this is right
var/ran_non_ticker = FALSE
var/bg_calc //have we swtiched current_tick_budget to background mode yet?
var/tick_usage
//keep running while we have stuff to run and we haven't gone over a tick
// this is so subsystems paused eariler can use tick time that later subsystems never used
while (ran && queue_head && world.tick_usage < TICK_LIMIT_MC)
ran = FALSE
bg_calc = FALSE
current_tick_budget = queue_priority_count
queue_node = queue_head
while (queue_node)
if (ran && world.tick_usage > TICK_LIMIT_RUNNING)
break
queue_node_flags = queue_node.flags
queue_node_priority = queue_node.queued_priority
//super special case, subsystems where we can't make them pause mid way through
//if we can't run them this tick (without going over a tick)
//we bump up their priority and attempt to run them next tick
//(unless we haven't even ran anything this tick, since its unlikely they will ever be able run
// in those cases, so we just let them run)
if (queue_node_flags & SS_NO_TICK_CHECK)
if (queue_node.tick_usage > TICK_LIMIT_RUNNING - world.tick_usage && ran_non_ticker)
queue_node.queued_priority += queue_priority_count * 0.10
queue_priority_count -= queue_node_priority
queue_priority_count += queue_node.queued_priority
current_tick_budget -= queue_node_priority
queue_node = queue_node.queue_next
continue
if ((queue_node_flags & SS_BACKGROUND) && !bg_calc)
current_tick_budget = queue_priority_count_bg
bg_calc = TRUE
tick_remaining = TICK_LIMIT_RUNNING - world.tick_usage
if (current_tick_budget > 0 && queue_node_priority > 0)
tick_precentage = tick_remaining / (current_tick_budget / queue_node_priority)
else
tick_precentage = tick_remaining
current_ticklimit = world.tick_usage + tick_precentage
if (!(queue_node_flags & SS_TICKER))
ran_non_ticker = TRUE
ran = TRUE
tick_usage = world.tick_usage
queue_node_paused = (queue_node.state == SS_PAUSED || queue_node.state == SS_PAUSING)
last_type_processed = queue_node
queue_node.state = SS_RUNNING
var/state = queue_node.ignite(queue_node_paused)
if (state == SS_RUNNING)
state = SS_IDLE
current_tick_budget -= queue_node_priority
tick_usage = world.tick_usage - tick_usage
if (tick_usage < 0)
tick_usage = 0
queue_node.state = state
if (state == SS_PAUSED)
queue_node.paused_ticks++
queue_node.paused_tick_usage += tick_usage
queue_node = queue_node.queue_next
continue
queue_node.ticks = MC_AVERAGE(queue_node.ticks, queue_node.paused_ticks)
tick_usage += queue_node.paused_tick_usage
queue_node.tick_usage = MC_AVERAGE_FAST(queue_node.tick_usage, tick_usage)
queue_node.cost = MC_AVERAGE_FAST(queue_node.cost, TICK_DELTA_TO_MS(tick_usage))
queue_node.paused_ticks = 0
queue_node.paused_tick_usage = 0
if (queue_node_flags & SS_BACKGROUND) //update our running total
queue_priority_count_bg -= queue_node_priority
else
queue_priority_count -= queue_node_priority
queue_node.last_fire = world.time
queue_node.times_fired++
if (queue_node_flags & SS_TICKER)
queue_node.next_fire = world.time + (world.tick_lag * queue_node.wait)
else if (queue_node_flags & SS_POST_FIRE_TIMING)
queue_node.next_fire = world.time + queue_node.wait
else if (queue_node_flags & SS_KEEP_TIMING)
queue_node.next_fire += queue_node.wait
else
queue_node.next_fire = queue_node.queued_time + queue_node.wait
queue_node.queued_time = 0
//remove from queue
queue_node.dequeue()
queue_node = queue_node.queue_next
. = 1
//resets the queue, and all subsystems, while filtering out the subsystem lists
// called if any mc's queue procs runtime or exit improperly.
/datum/controller/master/proc/SoftReset(list/ticker_SS, list/runlevel_SS)
. = 0
log_world("MC: SoftReset called, resetting MC queue state.")
if (!istype(subsystems) || !istype(ticker_SS) || !istype(runlevel_SS))
log_world("MC: SoftReset: Bad list contents: '[subsystems]' '[ticker_SS]' '[runlevel_SS]'")
return
var/subsystemstocheck = subsystems + ticker_SS
for(var/I in runlevel_SS)
subsystemstocheck |= I
for (var/thing in subsystemstocheck)
var/datum/controller/subsystem/SS = thing
if (!SS || !istype(SS))
//list(SS) is so if a list makes it in the subsystem list, we remove the list, not the contents
subsystems -= list(SS)
ticker_SS -= list(SS)
for(var/I in runlevel_SS)
I -= list(SS)
log_world("MC: SoftReset: Found bad entry in subsystem list, '[SS]'")
continue
if (SS.queue_next && !istype(SS.queue_next))
log_world("MC: SoftReset: Found bad data in subsystem queue, queue_next = '[SS.queue_next]'")
SS.queue_next = null
if (SS.queue_prev && !istype(SS.queue_prev))
log_world("MC: SoftReset: Found bad data in subsystem queue, queue_prev = '[SS.queue_prev]'")
SS.queue_prev = null
SS.queued_priority = 0
SS.queued_time = 0
SS.state = SS_IDLE
if (queue_head && !istype(queue_head))
log_world("MC: SoftReset: Found bad data in subsystem queue, queue_head = '[queue_head]'")
queue_head = null
if (queue_tail && !istype(queue_tail))
log_world("MC: SoftReset: Found bad data in subsystem queue, queue_tail = '[queue_tail]'")
queue_tail = null
queue_priority_count = 0
queue_priority_count_bg = 0
log_world("MC: SoftReset: Finished.")
. = 1
/datum/controller/master/stat_entry()
if(!statclick)
statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
stat("Byond:", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%))")
stat("Master Controller:", statclick.update("(TickRate:[Master.processing]) (Iteration:[Master.iteration])"))
/datum/controller/master/StartLoadingMap()
if(map_loading)
admin_notice("<span class='danger'>Another map is attempting to be loaded before first map released lock. Delaying.</span>", R_DEBUG)
else
admin_notice("<span class='danger'>Map is now being built. Locking.</span>", R_DEBUG)
//disallow more than one map to load at once, multithreading it will just cause race conditions
while(map_loading)
stoplag()
for(var/S in subsystems)
var/datum/controller/subsystem/SS = S
SS.StartLoadingMap()
// ZAS might displace objects as the map loads if an air tick is processed mid-load.
air_processing_killed = TRUE
map_loading = TRUE
/datum/controller/master/StopLoadingMap(bounds = null)
admin_notice("<span class='danger'>Map is finished. Unlocking.</span>", R_DEBUG)
air_processing_killed = FALSE
map_loading = FALSE
for(var/S in subsystems)
var/datum/controller/subsystem/SS = S
SS.StopLoadingMap()
+57 -35
View File
@@ -2,6 +2,10 @@
//It ensures master_controller.process() is never doubled up by killing the MC (hence terminating any of its sleeping procs)
//WIP, needs lots of work still
//
// TODO - This will be completely replaced by master.dm in time.
//
var/global/datum/controller/game_controller/master_controller //Set in world.New()
var/global/controller_iteration = 0
@@ -31,44 +35,24 @@ datum/controller/game_controller/New()
if(!syndicate_code_response) syndicate_code_response = generate_code_phrase()
datum/controller/game_controller/proc/setup()
world.tick_lag = config.Ticklag
spawn(20)
createRandomZlevel()
setup_objects()
setupgenetics()
SetupXenoarch()
transfer_controller = new
admin_notice("<span class='danger'>Initializations complete.</span>", R_DEBUG)
#if UNIT_TEST
#define CHECK_SLEEP_MASTER // For unit tests we don't care about a smooth lobby screen experience. We care about speed.
#else
#define CHECK_SLEEP_MASTER if(++initialized_objects > 500) { initialized_objects=0;sleep(world.tick_lag); }
#endif
datum/controller/game_controller/proc/setup_objects()
admin_notice("<span class='danger'>Initializing objects</span>", R_DEBUG)
sleep(-1)
for(var/atom/movable/object in world)
if(isnull(object.gcDestroyed))
object.initialize()
admin_notice("<span class='danger'>Initializing areas</span>", R_DEBUG)
sleep(-1)
for(var/area/area in all_areas)
area.initialize()
admin_notice("<span class='danger'>Initializing pipe networks</span>", R_DEBUG)
sleep(-1)
for(var/obj/machinery/atmospherics/machine in machines)
machine.build_network()
admin_notice("<span class='danger'>Initializing atmos machinery.</span>", R_DEBUG)
sleep(-1)
for(var/obj/machinery/atmospherics/unary/U in machines)
if(istype(U, /obj/machinery/atmospherics/unary/vent_pump))
var/obj/machinery/atmospherics/unary/vent_pump/T = U
T.broadcast_status()
else if(istype(U, /obj/machinery/atmospherics/unary/vent_scrubber))
var/obj/machinery/atmospherics/unary/vent_scrubber/T = U
T.broadcast_status()
#if !UNIT_TEST
var/initialized_objects = 0
#endif
// Set up antagonists.
populate_antag_type_list()
@@ -76,12 +60,50 @@ datum/controller/game_controller/proc/setup_objects()
//Set up spawn points.
populate_spawn_points()
admin_notice("<span class='danger'>Initializing Floor Decals</span>", R_DEBUG)
var/list/turfs_with_decals = list()
for(var/obj/effect/floor_decal/D in world)
var/T = D.add_to_turf_decals()
if(T) turfs_with_decals |= T
CHECK_SLEEP_MASTER
for(var/item in turfs_with_decals)
var/turf/T = item
if(T.decals) T.apply_decals()
CHECK_SLEEP_MASTER
floor_decals_initialized = TRUE
sleep(1)
admin_notice("<span class='danger'>Initializing objects</span>", R_DEBUG)
for(var/atom/movable/object in world)
if(!QDELETED(object))
object.initialize()
CHECK_SLEEP_MASTER
sleep(1)
admin_notice("<span class='danger'>Initializing areas</span>", R_DEBUG)
for(var/area/area in all_areas)
area.initialize()
CHECK_SLEEP_MASTER
sleep(1)
admin_notice("<span class='danger'>Initializing pipe networks</span>", R_DEBUG)
for(var/obj/machinery/atmospherics/machine in machines)
machine.build_network()
CHECK_SLEEP_MASTER
admin_notice("<span class='danger'>Initializing atmos machinery.</span>", R_DEBUG)
for(var/obj/machinery/atmospherics/unary/U in machines)
if(istype(U, /obj/machinery/atmospherics/unary/vent_pump))
var/obj/machinery/atmospherics/unary/vent_pump/T = U
T.broadcast_status()
else if(istype(U, /obj/machinery/atmospherics/unary/vent_scrubber))
var/obj/machinery/atmospherics/unary/vent_scrubber/T = U
T.broadcast_status()
CHECK_SLEEP_MASTER
admin_notice("<span class='danger'>Initializing turbolifts</span>", R_DEBUG)
for(var/thing in turbolifts)
if(!deleted(thing))
var/obj/turbolift_map_holder/lift = thing
var/obj/turbolift_map_holder/lift = thing
if(!QDELETED(lift))
lift.initialize()
sleep(-1)
admin_notice("<span class='danger'>Initializations complete.</span>", R_DEBUG)
sleep(-1)
CHECK_SLEEP_MASTER
+8 -311
View File
@@ -9,14 +9,19 @@ var/global/datum/shuttle_controller/shuttle_controller
/datum/shuttle_controller/proc/process()
//process ferry shuttles
for (var/datum/shuttle/ferry/shuttle in process_shuttles)
if (shuttle.process_state)
if (shuttle.process_state || shuttle.always_process)
shuttle.process()
//This is called by gameticker after all the machines and radio frequencies have been properly initialized
/datum/shuttle_controller/proc/setup_shuttle_docks()
for(var/shuttle_tag in shuttles)
var/datum/shuttle/shuttle = shuttles[shuttle_tag]
// for(var/shuttle_tag in shuttles)
// var/datum/shuttle/shuttle = shuttles[shuttle_tag]
for(var/shuttle_type in subtypesof(/datum/shuttle))
var/datum/shuttle/shuttle = shuttle_type
if(initial(shuttle.category) == shuttle_type)
continue
shuttle = new shuttle()
shuttle.init_docking_controllers()
shuttle.dock() //makes all shuttles docked to something at round start go into the docked state
@@ -28,311 +33,3 @@ var/global/datum/shuttle_controller/shuttle_controller
shuttles = list()
process_shuttles = list()
var/datum/shuttle/ferry/shuttle
// Escape shuttle and pods
shuttle = new/datum/shuttle/ferry/emergency()
shuttle.location = 1
shuttle.warmup_time = 10
shuttle.area_offsite = locate(/area/shuttle/escape/centcom)
shuttle.area_station = locate(/area/shuttle/escape/station)
shuttle.area_transition = locate(/area/shuttle/escape/transit)
shuttle.docking_controller_tag = "escape_shuttle"
shuttle.dock_target_station = "escape_dock"
shuttle.dock_target_offsite = "centcom_dock"
shuttle.transit_direction = NORTH
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN
//shuttle.docking_controller_tag = "supply_shuttle"
//shuttle.dock_target_station = "cargo_bay"
shuttles["Escape"] = shuttle
process_shuttles += shuttle
shuttle = new/datum/shuttle/ferry/escape_pod()
shuttle.location = 0
shuttle.warmup_time = 0
shuttle.area_station = locate(/area/shuttle/escape_pod1/station)
shuttle.area_offsite = locate(/area/shuttle/escape_pod1/centcom)
shuttle.area_transition = locate(/area/shuttle/escape_pod1/transit)
shuttle.docking_controller_tag = "escape_pod_1"
shuttle.dock_target_station = "escape_pod_1_berth"
shuttle.dock_target_offsite = "escape_pod_1_recovery"
shuttle.transit_direction = NORTH
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
process_shuttles += shuttle
shuttles["Escape Pod 1"] = shuttle
shuttle = new/datum/shuttle/ferry/escape_pod()
shuttle.location = 0
shuttle.warmup_time = 0
shuttle.area_station = locate(/area/shuttle/escape_pod2/station)
shuttle.area_offsite = locate(/area/shuttle/escape_pod2/centcom)
shuttle.area_transition = locate(/area/shuttle/escape_pod2/transit)
shuttle.docking_controller_tag = "escape_pod_2"
shuttle.dock_target_station = "escape_pod_2_berth"
shuttle.dock_target_offsite = "escape_pod_2_recovery"
shuttle.transit_direction = NORTH
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
process_shuttles += shuttle
shuttles["Escape Pod 2"] = shuttle
shuttle = new/datum/shuttle/ferry/escape_pod()
shuttle.location = 0
shuttle.warmup_time = 0
shuttle.area_station = locate(/area/shuttle/escape_pod3/station)
shuttle.area_offsite = locate(/area/shuttle/escape_pod3/centcom)
shuttle.area_transition = locate(/area/shuttle/escape_pod3/transit)
shuttle.docking_controller_tag = "escape_pod_3"
shuttle.dock_target_station = "escape_pod_3_berth"
shuttle.dock_target_offsite = "escape_pod_3_recovery"
shuttle.transit_direction = NORTH
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
process_shuttles += shuttle
shuttles["Escape Pod 3"] = shuttle
shuttle = new/datum/shuttle/ferry/escape_pod()
shuttle.location = 0
shuttle.warmup_time = 0
shuttle.area_station = locate(/area/shuttle/escape_pod4/station)
shuttle.area_offsite = locate(/area/shuttle/escape_pod4/centcom)
shuttle.area_transition = locate(/area/shuttle/escape_pod4/transit)
shuttle.docking_controller_tag = "escape_pod_4"
shuttle.dock_target_station = "escape_pod_4_berth"
shuttle.dock_target_offsite = "escape_pod_4_recovery"
shuttle.transit_direction = NORTH //should this be SOUTH? I have no idea.
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
process_shuttles += shuttle
shuttles["Escape Pod 4"] = shuttle
shuttle = new/datum/shuttle/ferry/escape_pod()
shuttle.location = 0
shuttle.warmup_time = 0
shuttle.area_station = locate(/area/shuttle/escape_pod5/station)
shuttle.area_offsite = locate(/area/shuttle/escape_pod5/centcom)
shuttle.area_transition = locate(/area/shuttle/escape_pod5/transit)
shuttle.docking_controller_tag = "escape_pod_5"
shuttle.dock_target_station = "escape_pod_5_berth"
shuttle.dock_target_offsite = "escape_pod_5_recovery"
shuttle.transit_direction = NORTH //should this be WEST? I have no idea.
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
process_shuttles += shuttle
shuttles["Escape Pod 5"] = shuttle
shuttle = new/datum/shuttle/ferry/escape_pod()
shuttle.location = 0
shuttle.warmup_time = 0
shuttle.area_station = locate(/area/shuttle/escape_pod6/station)
shuttle.area_offsite = locate(/area/shuttle/escape_pod6/centcom)
shuttle.area_transition = locate(/area/shuttle/escape_pod6/transit)
shuttle.docking_controller_tag = "escape_pod_6"
shuttle.dock_target_station = "escape_pod_6_berth"
shuttle.dock_target_offsite = "escape_pod_6_recovery"
shuttle.transit_direction = NORTH //should this be WEST? I have no idea.
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
process_shuttles += shuttle
shuttles["Escape Pod 6"] = shuttle
shuttle = new/datum/shuttle/ferry/escape_pod()
shuttle.location = 0
shuttle.warmup_time = 0
shuttle.area_station = locate(/area/shuttle/cryo/station)
shuttle.area_offsite = locate(/area/shuttle/cryo/centcom)
shuttle.area_transition = locate(/area/shuttle/cryo/transit)
shuttle.docking_controller_tag = "cryostorage_shuttle"
shuttle.dock_target_station = "cryostorage_shuttle_berth"
shuttle.dock_target_offsite = "cryostorage_shuttle_recovery"
shuttle.transit_direction = NORTH //should this be WEST? I have no idea.
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
process_shuttles += shuttle
shuttles["Cryostorage Shuttle"] = shuttle
shuttle = new/datum/shuttle/ferry/escape_pod()
shuttle.location = 0
shuttle.warmup_time = 0
shuttle.area_station = locate(/area/shuttle/large_escape_pod1/station)
shuttle.area_offsite = locate(/area/shuttle/large_escape_pod1/centcom)
shuttle.area_transition = locate(/area/shuttle/large_escape_pod1/transit)
shuttle.docking_controller_tag = "large_escape_pod_1"
shuttle.dock_target_station = "large_escape_pod_1_berth"
shuttle.dock_target_offsite = "large_escape_pod_1_recovery"
shuttle.transit_direction = EAST //should this be WEST? I have no idea.
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
process_shuttles += shuttle
shuttles["Large Escape Pod 1"] = shuttle
shuttle = new/datum/shuttle/ferry/escape_pod()
shuttle.location = 0
shuttle.warmup_time = 0
shuttle.area_station = locate(/area/shuttle/large_escape_pod2/station)
shuttle.area_offsite = locate(/area/shuttle/large_escape_pod2/centcom)
shuttle.area_transition = locate(/area/shuttle/large_escape_pod2/transit)
shuttle.docking_controller_tag = "large_escape_pod_2"
shuttle.dock_target_station = "large_escape_pod_2_berth"
shuttle.dock_target_offsite = "large_escape_pod_2_recovery"
shuttle.transit_direction = EAST //should this be WEST? I have no idea.
shuttle.move_time = SHUTTLE_TRANSIT_DURATION_RETURN + rand(-30, 60) //randomize this so it seems like the pods are being picked up one by one
process_shuttles += shuttle
shuttles["Large Escape Pod 2"] = shuttle
//give the emergency shuttle controller it's shuttles
emergency_shuttle.shuttle = shuttles["Escape"]
emergency_shuttle.escape_pods = list(
shuttles["Escape Pod 1"],
shuttles["Escape Pod 2"],
shuttles["Escape Pod 3"],
shuttles["Escape Pod 4"],
shuttles["Escape Pod 5"],
shuttles["Escape Pod 6"],
shuttles["Cryostorage Shuttle"],
shuttles["Large Escape Pod 1"],
shuttles["Large Escape Pod 2"],
)
// Supply shuttle
shuttle = new/datum/shuttle/ferry/supply()
shuttle.location = 1
shuttle.warmup_time = 10
shuttle.area_offsite = locate(/area/supply/dock)
shuttle.area_station = locate(/area/supply/station)
shuttle.docking_controller_tag = "supply_shuttle"
shuttle.dock_target_station = "cargo_bay"
shuttles["Supply"] = shuttle
process_shuttles += shuttle
supply_controller.shuttle = shuttle
// Admin shuttles.
shuttle = new()
shuttle.location = 1
shuttle.warmup_time = 10
shuttle.area_offsite = locate(/area/shuttle/transport1/centcom)
shuttle.area_station = locate(/area/shuttle/transport1/station)
shuttle.docking_controller_tag = "centcom_shuttle"
shuttle.dock_target_station = "centcom_shuttle_dock_airlock"
shuttle.dock_target_offsite = "centcom_shuttle_bay"
shuttles["CentCom"] = shuttle
process_shuttles += shuttle
shuttle = new()
shuttle.location = 1
shuttle.warmup_time = 10 //want some warmup time so people can cancel.
shuttle.area_offsite = locate(/area/shuttle/administration/centcom)
shuttle.area_station = locate(/area/shuttle/administration/station)
shuttle.docking_controller_tag = "admin_shuttle"
shuttle.dock_target_station = "admin_shuttle_dock_airlock"
shuttle.dock_target_offsite = "admin_shuttle_bay"
shuttles["Administration"] = shuttle
process_shuttles += shuttle
shuttle = new()
shuttle.location = 1
shuttle.warmup_time = 10 //want some warmup time so people can cancel.
shuttle.area_offsite = locate(/area/shuttle/trade/centcom)
shuttle.area_station = locate(/area/shuttle/trade/station)
shuttle.docking_controller_tag = "trade_shuttle"
shuttle.dock_target_station = "trade_shuttle_dock_airlock"
shuttle.dock_target_offsite = "trade_shuttle_bay"
shuttles["Trade"] = shuttle
process_shuttles += shuttle
shuttle = new()
shuttle.area_offsite = locate(/area/shuttle/alien/base)
shuttle.area_station = locate(/area/shuttle/alien/mine)
shuttles["Alien"] = shuttle
//process_shuttles += shuttle //don't need to process this. It can only be moved using admin magic anyways.
// Public shuttles
shuttle = new()
shuttle.warmup_time = 10
shuttle.area_offsite = locate(/area/shuttle/constructionsite/site)
shuttle.area_station = locate(/area/shuttle/constructionsite/station)
shuttle.docking_controller_tag = "engineering_shuttle"
shuttle.dock_target_station = "engineering_dock_airlock"
shuttle.dock_target_offsite = "edock_airlock"
shuttles["Engineering"] = shuttle
process_shuttles += shuttle
shuttle = new()
shuttle.warmup_time = 10
shuttle.area_offsite = locate(/area/shuttle/mining/outpost)
shuttle.area_station = locate(/area/shuttle/mining/station)
shuttle.docking_controller_tag = "mining_shuttle"
shuttle.dock_target_station = "mining_dock_airlock"
shuttle.dock_target_offsite = "mining_outpost_airlock"
shuttles["Mining"] = shuttle
process_shuttles += shuttle
shuttle = new()
shuttle.warmup_time = 10
shuttle.area_offsite = locate(/area/shuttle/research/outpost)
shuttle.area_station = locate(/area/shuttle/research/station)
shuttle.docking_controller_tag = "research_shuttle"
shuttle.dock_target_station = "research_dock_airlock"
shuttle.dock_target_offsite = "research_outpost_dock"
shuttles["Research"] = shuttle
process_shuttles += shuttle
// ERT Shuttle
var/datum/shuttle/ferry/multidock/specops/ERT = new()
ERT.location = 0
ERT.warmup_time = 10
ERT.area_offsite = locate(/area/shuttle/specops/station) //centcom is the home station, the player station is offsite
ERT.area_station = locate(/area/shuttle/specops/centcom)
ERT.docking_controller_tag = "specops_shuttle_port"
ERT.docking_controller_tag_station = "specops_shuttle_port"
ERT.docking_controller_tag_offsite = "specops_shuttle_fore"
ERT.dock_target_station = "specops_centcom_dock"
ERT.dock_target_offsite = "specops_dock_airlock"
shuttles["Special Operations"] = ERT
process_shuttles += ERT
//Skipjack.
var/datum/shuttle/multi_shuttle/VS = new/datum/shuttle/multi_shuttle()
VS.origin = locate(/area/skipjack_station/start)
VS.destinations = list(
"Fore Starboard Solars" = locate(/area/skipjack_station/northeast_solars),
"Fore Port Solars" = locate(/area/skipjack_station/northwest_solars),
"Aft Starboard Solars" = locate(/area/skipjack_station/southeast_solars),
"Aft Port Solars" = locate(/area/skipjack_station/southwest_solars),
"Mining Station" = locate(/area/skipjack_station/mining)
)
VS.announcer = "Colony Automated Radar Array"
VS.arrival_message = "Attention. Unidentified object approaching the colony."
VS.departure_message = "Attention. Unidentified object exiting local space. Unidentified object expected to escape Kara gravity well with current velocity."
VS.interim = locate(/area/skipjack_station/transit)
VS.warmup_time = 0
shuttles["Skipjack"] = VS
//Nuke Ops shuttle.
var/datum/shuttle/multi_shuttle/MS = new/datum/shuttle/multi_shuttle()
MS.origin = locate(/area/syndicate_station/start)
MS.start_location = "Mercenary Base"
MS.destinations = list(
"Northwest of the station" = locate(/area/syndicate_station/northwest),
"North of the station" = locate(/area/syndicate_station/north),
"Northeast of the station" = locate(/area/syndicate_station/northeast),
"Southwest of the station" = locate(/area/syndicate_station/southwest),
"South of the station" = locate(/area/syndicate_station/south),
"Southeast of the station" = locate(/area/syndicate_station/southeast),
"Telecomms Satellite" = locate(/area/syndicate_station/commssat),
"Mining Station" = locate(/area/syndicate_station/mining),
"Arrivals dock" = locate(/area/syndicate_station/arrivals_dock),
)
MS.docking_controller_tag = "merc_shuttle"
MS.destination_dock_targets = list(
"Mercenary Base" = "merc_base",
"Arrivals dock" = "nuke_shuttle_dock_airlock",
)
MS.announcer = "Automated Traffic Control"
MS.arrival_message = "Attention. A vessel is approaching the colony."
MS.departure_message = "Attention. A vessel is now leaving from the colony."
MS.interim = locate(/area/syndicate_station/transit)
MS.warmup_time = 0
shuttles["Mercenary"] = MS
+202
View File
@@ -0,0 +1,202 @@
/datum/controller/subsystem
// Metadata; you should define these.
name = "fire coderbus" //name of the subsystem
var/init_order = 0 //order of initialization. Higher numbers are initialized first, lower numbers later. Can be decimal and negative values.
var/wait = 20 //time to wait (in deciseconds) between each call to fire(). Must be a positive integer.
var/priority = 50 //When mutiple subsystems need to run in the same tick, higher priority subsystems will run first and be given a higher share of the tick before MC_TICK_CHECK triggers a sleep
var/flags = 0 //see MC.dm in __DEFINES Most flags must be set on world start to take full effect. (You can also restart the mc to force them to process again)
var/runlevels = RUNLEVELS_DEFAULT //points of the game at which the SS can fire
//set to 0 to prevent fire() calls, mostly for admin use or subsystems that may be resumed later
// use the SS_NO_FIRE flag instead for systems that never fire to keep it from even being added to the list
var/can_fire = TRUE
// Bookkeeping variables; probably shouldn't mess with these.
var/last_fire = 0 //last world.time we called fire()
var/next_fire = 0 //scheduled world.time for next fire()
var/cost = 0 //average time to execute
var/tick_usage = 0 //average tick usage
var/state = SS_IDLE //tracks the current state of the ss, running, paused, etc.
var/paused_ticks = 0 //ticks this ss is taking to run right now.
var/paused_tick_usage //total tick_usage of all of our runs while pausing this run
var/ticks = 1 //how many ticks does this ss take to run on avg.
var/times_fired = 0 //number of times we have called fire()
var/queued_time = 0 //time we entered the queue, (for timing and priority reasons)
var/queued_priority //we keep a running total to make the math easier, if priority changes mid-fire that would break our running total, so we store it here
//linked list stuff for the queue
var/datum/controller/subsystem/queue_next
var/datum/controller/subsystem/queue_prev
var/static/failure_strikes = 0 //How many times we suspect this subsystem has crashed the MC, 3 strikes and you're out!
//Do not override
/datum/controller/subsystem/New()
return
// Used to initialize the subsystem BEFORE the map has loaded
// Called AFTER Recover if that is called
// Prefer to use Initialize if possible
/datum/controller/subsystem/proc/PreInit()
return
//This is used so the mc knows when the subsystem sleeps. do not override.
/datum/controller/subsystem/proc/ignite(resumed = 0)
set waitfor = 0
. = SS_SLEEPING
fire(resumed)
. = state
if (state == SS_SLEEPING)
state = SS_IDLE
if (state == SS_PAUSING)
var/QT = queued_time
enqueue()
state = SS_PAUSED
queued_time = QT
//previously, this would have been named 'process()' but that name is used everywhere for different things!
//fire() seems more suitable. This is the procedure that gets called every 'wait' deciseconds.
//Sleeping in here prevents future fires until returned.
/datum/controller/subsystem/proc/fire(resumed = 0)
flags |= SS_NO_FIRE
throw EXCEPTION("Subsystem [src]([type]) does not fire() but did not set the SS_NO_FIRE flag. Please add the SS_NO_FIRE flag to any subsystem that doesn't fire so it doesn't get added to the processing list and waste cpu.")
/datum/controller/subsystem/Destroy()
dequeue()
can_fire = 0
flags |= SS_NO_FIRE
Master.subsystems -= src
//Queue it to run.
// (we loop thru a linked list until we get to the end or find the right point)
// (this lets us sort our run order correctly without having to re-sort the entire already sorted list)
/datum/controller/subsystem/proc/enqueue()
var/SS_priority = priority
var/SS_flags = flags
var/datum/controller/subsystem/queue_node
var/queue_node_priority
var/queue_node_flags
for (queue_node = Master.queue_head; queue_node; queue_node = queue_node.queue_next)
queue_node_priority = queue_node.queued_priority
queue_node_flags = queue_node.flags
if (queue_node_flags & SS_TICKER)
if (!(SS_flags & SS_TICKER))
continue
if (queue_node_priority < SS_priority)
break
else if (queue_node_flags & SS_BACKGROUND)
if (!(SS_flags & SS_BACKGROUND))
break
if (queue_node_priority < SS_priority)
break
else
if (SS_flags & SS_BACKGROUND)
continue
if (SS_flags & SS_TICKER)
break
if (queue_node_priority < SS_priority)
break
queued_time = world.time
queued_priority = SS_priority
state = SS_QUEUED
if (SS_flags & SS_BACKGROUND) //update our running total
Master.queue_priority_count_bg += SS_priority
else
Master.queue_priority_count += SS_priority
queue_next = queue_node
if (!queue_node)//we stopped at the end, add to tail
queue_prev = Master.queue_tail
if (Master.queue_tail)
Master.queue_tail.queue_next = src
else //empty queue, we also need to set the head
Master.queue_head = src
Master.queue_tail = src
else if (queue_node == Master.queue_head)//insert at start of list
Master.queue_head.queue_prev = src
Master.queue_head = src
queue_prev = null
else
queue_node.queue_prev.queue_next = src
queue_prev = queue_node.queue_prev
queue_node.queue_prev = src
/datum/controller/subsystem/proc/dequeue()
if (queue_next)
queue_next.queue_prev = queue_prev
if (queue_prev)
queue_prev.queue_next = queue_next
if (src == Master.queue_tail)
Master.queue_tail = queue_prev
if (src == Master.queue_head)
Master.queue_head = queue_next
queued_time = 0
if (state == SS_QUEUED)
state = SS_IDLE
/datum/controller/subsystem/proc/pause()
. = 1
switch(state)
if(SS_RUNNING)
state = SS_PAUSED
if(SS_SLEEPING)
state = SS_PAUSING
//used to initialize the subsystem AFTER the map has loaded
/datum/controller/subsystem/Initialize(start_timeofday)
var/time = (REALTIMEOFDAY - start_timeofday) / 10
var/msg = "Initialized [name] subsystem within [time] second[time == 1 ? "" : "s"]!"
to_chat(world, "<span class='boldannounce'>[msg]</span>")
log_world(msg)
return time
//hook for printing stats to the "MC" statuspanel for admins to see performance and related stats etc.
/datum/controller/subsystem/stat_entry(msg)
if(!statclick)
statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
if(can_fire && !(SS_NO_FIRE in flags))
msg = "[round(cost,1)]ms|[round(tick_usage,1)]%|[round(ticks,0.1)]\t[msg]"
else
msg = "OFFLINE\t[msg]"
var/title = name
if (can_fire)
title = "\[[state_letter()]][title]"
stat(title, statclick.update(msg))
/datum/controller/subsystem/proc/state_letter()
switch (state)
if (SS_RUNNING)
. = "R"
if (SS_QUEUED)
. = "Q"
if (SS_PAUSED, SS_PAUSING)
. = "P"
if (SS_SLEEPING)
. = "S"
if (SS_IDLE)
. = " "
//could be used to postpone a costly subsystem for (default one) var/cycles, cycles
//for instance, during cpu intensive operations like explosions
/datum/controller/subsystem/proc/postpone(cycles = 1)
if(next_fire - world.time < wait)
next_fire += (wait*cycles)
//usually called via datum/controller/subsystem/New() when replacing a subsystem (i.e. due to a recurring crash)
//should attempt to salvage what it can from the old instance of subsystem
/datum/controller/subsystem/Recover()
-46
View File
@@ -1,46 +0,0 @@
#define NEW_SS_GLOBAL(varname) if(varname != src){if(istype(varname)){Recover();qdel(varname);}varname = src;}
/datum/subsystem
//things you will want to define
var/name //name of the subsystem
var/priority = 0 //priority affects order of initialization. Higher priorities are initialized first, lower priorities later. Can be decimal and negative values.
var/wait = 20 //time to wait (in deciseconds) between each call to fire(). Must be a positive integer.
//things you will probably want to leave alone
var/can_fire = 0 //prevent fire() calls
var/last_fire = 0 //last world.time we called fire()
var/next_fire = 0 //scheduled world.time for next fire()
var/cpu = 0 //cpu-usage stats (somewhat vague)
var/cost = 0 //average time to execute
var/times_fired = 0 //number of times we have called fire()
//used to initialize the subsystem BEFORE the map has loaded
/datum/subsystem/New()
//previously, this would have been named 'process()' but that name is used everywhere for different things!
//fire() seems more suitable. This is the procedure that gets called every 'wait' deciseconds.
//fire(), and the procs it calls, SHOULD NOT HAVE ANY SLEEP OPERATIONS in them!
//YE BE WARNED!
/datum/subsystem/proc/fire()
can_fire = 0
//used to initialize the subsystem AFTER the map has loaded
/datum/subsystem/proc/Initialize(start_timeofday)
var/time = (world.timeofday - start_timeofday) / 10
var/msg = "Initialized [name] SubSystem within [time] seconds"
world << "<span class='userdanger'>[msg]</span>"
world.log << msg
//hook for printing stats to the "MC" statuspanel for admins to see performance and related stats etc.
/datum/subsystem/proc/stat_entry()
stat(name, "[round(cost,0.001)]ds\t(CPU:[round(cpu,1)]%)")
//could be used to postpone a costly subsystem for one cycle
//for instance, during cpu intensive operations like explosions
/datum/subsystem/proc/postpone()
if(next_fire - world.time < wait)
next_fire += wait
//usually called via datum/subsystem/New() when replacing a subsystem (i.e. due to a recurring crash)
//should attempt to salvage what it can from the old instance of subsystem
/datum/subsystem/proc/Recover()
+29
View File
@@ -0,0 +1,29 @@
//
// Creation subsystem, which is responsible for initializing newly created objects.
//
SUBSYSTEM_DEF(creation)
name = "Creation"
priority = 14
wait = 5
// flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT
flags = SS_NO_FIRE|SS_NO_INIT
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
var/list/atoms_needing_initialize = list()
var/map_loading = FALSE
/datum/controller/subsystem/creation/StartLoadingMap()
map_loading = TRUE
/datum/controller/subsystem/creation/StopLoadingMap()
map_loading = FALSE
/datum/controller/subsystem/creation/proc/initialize_late_atoms()
admin_notice("<span class='danger'>Initializing atoms in submap.</span>", R_DEBUG)
var/total_atoms = atoms_needing_initialize.len
for(var/atom/movable/A in atoms_needing_initialize)
if(!QDELETED(A))
A.initialize()
atoms_needing_initialize -= A
admin_notice("<span class='danger'>Initalized [total_atoms] atoms in submap.</span>", R_DEBUG)
+378
View File
@@ -0,0 +1,378 @@
//
// Garbage Collector Subsystem - Implements qdel() and the GC queue
//
SUBSYSTEM_DEF(garbage)
name = "Garbage"
priority = 15
wait = 5
flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
var/collection_timeout = 3000// deciseconds to wait to let running procs finish before we just say fuck it and force del() the object
var/delslasttick = 0 // number of del()'s we've done this tick
var/gcedlasttick = 0 // number of things that gc'ed last tick
var/totaldels = 0
var/totalgcs = 0
var/highest_del_time = 0
var/highest_del_tickusage = 0
var/list/queue = list() // list of refID's of things that should be garbage collected
// refID's are associated with the time at which they time out and need to be manually del()
// we do this so we aren't constantly locating them and preventing them from being gc'd
var/list/tobequeued = list() //We store the references of things to be added to the queue seperately so we can spread out GC overhead over a few ticks
var/list/didntgc = list() // list of all types that have failed to GC associated with the number of times that's happened.
// the types are stored as strings
var/list/sleptDestroy = list() //Same as above but these are paths that slept during their Destroy call
var/list/noqdelhint = list()// list of all types that do not return a QDEL_HINT
// all types that did not respect qdel(A, force=TRUE) and returned one
// of the immortality qdel hints
var/list/noforcerespect = list()
#ifdef TESTING
var/list/qdel_list = list() // list of all types that have been qdel()eted
#endif
/datum/controller/subsystem/garbage/stat_entry(msg)
msg += "Q:[queue.len]|D:[delslasttick]|G:[gcedlasttick]|"
msg += "GR:"
if (!(delslasttick+gcedlasttick))
msg += "n/a|"
else
msg += "[round((gcedlasttick/(delslasttick+gcedlasttick))*100, 0.01)]%|"
msg += "TD:[totaldels]|TG:[totalgcs]|"
if (!(totaldels+totalgcs))
msg += "n/a|"
else
msg += "TGR:[round((totalgcs/(totaldels+totalgcs))*100, 0.01)]%"
..(msg)
/datum/controller/subsystem/garbage/Shutdown()
//Adds the del() log to world.log in a format condensable by the runtime condenser found in tools
if(didntgc.len || sleptDestroy.len)
var/list/dellog = list()
for(var/path in didntgc)
dellog += "Path : [path] \n"
dellog += "Failures : [didntgc[path]] \n"
if(path in sleptDestroy)
dellog += "Sleeps : [sleptDestroy[path]] \n"
sleptDestroy -= path
for(var/path in sleptDestroy)
dellog += "Path : [path] \n"
dellog += "Sleeps : [sleptDestroy[path]] \n"
log_misc(dellog.Join())
/datum/controller/subsystem/garbage/fire()
HandleToBeQueued()
if(state == SS_RUNNING)
HandleQueue()
if (state == SS_PAUSED) //make us wait again before the next run.
state = SS_RUNNING
//If you see this proc high on the profile, what you are really seeing is the garbage collection/soft delete overhead in byond.
//Don't attempt to optimize, not worth the effort.
/datum/controller/subsystem/garbage/proc/HandleToBeQueued()
var/list/tobequeued = src.tobequeued
var/starttime = world.time
var/starttimeofday = world.timeofday
while(tobequeued.len && starttime == world.time && starttimeofday == world.timeofday)
if (MC_TICK_CHECK)
break
var/ref = tobequeued[1]
Queue(ref)
tobequeued.Cut(1, 2)
/datum/controller/subsystem/garbage/proc/HandleQueue()
delslasttick = 0
gcedlasttick = 0
var/time_to_kill = world.time - collection_timeout // Anything qdel() but not GC'd BEFORE this time needs to be manually del()
var/list/queue = src.queue
var/starttime = world.time
var/starttimeofday = world.timeofday
while(queue.len && starttime == world.time && starttimeofday == world.timeofday)
if (MC_TICK_CHECK)
break
var/refID = queue[1]
if (!refID)
queue.Cut(1, 2)
continue
var/GCd_at_time = queue[refID]
if(GCd_at_time > time_to_kill)
break // Everything else is newer, skip them
queue.Cut(1, 2)
var/datum/A
A = locate(refID)
if (A && A.gc_destroyed == GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake
#ifdef GC_FAILURE_HARD_LOOKUP
A.find_references()
#endif
// Something's still referring to the qdel'd object. Kill it.
var/type = A.type
testing("GC: -- \ref[A] | [type] was unable to be GC'd and was deleted --")
didntgc["[type]"]++
HardDelete(A)
++delslasttick
++totaldels
else
++gcedlasttick
++totalgcs
/datum/controller/subsystem/garbage/proc/QueueForQueuing(datum/A)
if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
tobequeued += A
A.gc_destroyed = GC_QUEUED_FOR_QUEUING
/datum/controller/subsystem/garbage/proc/Queue(datum/A)
if (isnull(A) || (!isnull(A.gc_destroyed) && A.gc_destroyed >= 0))
return
if (A.gc_destroyed == GC_QUEUED_FOR_HARD_DEL)
HardDelete(A)
return
var/gctime = world.time
var/refid = "\ref[A]"
A.gc_destroyed = gctime
if (queue[refid])
queue -= refid // Removing any previous references that were GC'd so that the current object will be at the end of the list.
queue[refid] = gctime
//this is purely to seperate things profile wise.
/datum/controller/subsystem/garbage/proc/HardDelete(datum/A)
var/time = world.timeofday
var/tick = world.tick_usage
var/ticktime = world.time
var/type = A.type
var/refID = "\ref[A]"
del(A)
tick = (world.tick_usage-tick+((world.time-ticktime)/world.tick_lag*100))
if (tick > highest_del_tickusage)
highest_del_tickusage = tick
time = world.timeofday - time
if (!time && TICK_DELTA_TO_MS(tick) > 1)
time = TICK_DELTA_TO_MS(tick)/100
if (time > highest_del_time)
highest_del_time = time
if (time > 10)
log_game("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete)")
message_admins("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete).")
postpone(time/5)
/datum/controller/subsystem/garbage/proc/HardQueue(datum/A)
if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
tobequeued += A
A.gc_destroyed = GC_QUEUED_FOR_HARD_DEL
/datum/controller/subsystem/garbage/Recover()
if (istype(SSgarbage.queue))
queue |= SSgarbage.queue
if (istype(SSgarbage.tobequeued))
tobequeued |= SSgarbage.tobequeued
// Should be treated as a replacement for the 'del' keyword.
// Datums passed to this will be given a chance to clean up references to allow the GC to collect them.
/proc/qdel(datum/D, force=FALSE)
if(!istype(D))
del(D)
return
#ifdef TESTING
SSgarbage.qdel_list += D.type
#endif
if(isnull(D.gc_destroyed))
D.gc_destroyed = GC_CURRENTLY_BEING_QDELETED
var/start_time = world.time
var/hint = D.Destroy(force) // Let our friend know they're about to get fucked up.
if(world.time != start_time)
SSgarbage.sleptDestroy[D.type]++
if(!D)
return
switch(hint)
if (QDEL_HINT_QUEUE) //qdel should queue the object for deletion.
SSgarbage.QueueForQueuing(D)
if (QDEL_HINT_IWILLGC)
D.gc_destroyed = world.time
return
if (QDEL_HINT_LETMELIVE) //qdel should let the object live after calling destory.
if(!force)
D.gc_destroyed = null //clear the gc variable (important!)
return
// Returning LETMELIVE after being told to force destroy
// indicates the objects Destroy() does not respect force
if(!SSgarbage.noforcerespect[D.type])
SSgarbage.noforcerespect[D.type] = D.type
crash_with("[D.type] has been force deleted, but is \
returning an immortal QDEL_HINT, indicating it does \
not respect the force flag for qdel(). It has been \
placed in the queue, further instances of this type \
will also be queued.")
SSgarbage.QueueForQueuing(D)
if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete using a hard reference to save time from the locate()
SSgarbage.HardQueue(D)
if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste.
SSgarbage.HardDelete(D)
if (QDEL_HINT_FINDREFERENCE)//qdel will, if TESTING is enabled, display all references to this object, then queue the object for deletion.
SSgarbage.QueueForQueuing(D)
#ifdef TESTING
D.find_references()
#endif
else
if(!SSgarbage.noqdelhint[D.type])
SSgarbage.noqdelhint[D.type] = D.type
crash_with("[D.type] is not returning a qdel hint. It is being placed in the queue. Further instances of this type will also be queued.")
SSgarbage.QueueForQueuing(D)
else if(D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
CRASH("[D.type] destroy proc was called multiple times, likely due to a qdel loop in the Destroy logic")
// Default implementation of clean-up code.
// This should be overridden to remove all references pointing to the object being destroyed.
// Return the appropriate QDEL_HINT; in most cases this is QDEL_HINT_QUEUE.
/datum/proc/Destroy(force=FALSE)
tag = null
nanomanager.close_uis(src)
return QDEL_HINT_QUEUE
/datum/var/gc_destroyed //Time when this object was destroyed.
#ifdef TESTING
/datum/var/running_find_references
/datum/var/last_find_references = 0
/datum/verb/find_refs()
set category = "Debug"
set name = "Find References"
set background = 1
set src in world
find_references(FALSE)
/datum/proc/find_references(skip_alert)
running_find_references = type
if(usr && usr.client)
if(usr.client.running_find_references)
testing("CANCELLED search for references to a [usr.client.running_find_references].")
usr.client.running_find_references = null
running_find_references = null
//restart the garbage collector
SSgarbage.can_fire = 1
SSgarbage.next_fire = world.time + world.tick_lag
return
if(!skip_alert)
if(alert("Running this will lock everything up for about 5 minutes. Would you like to begin the search?", "Find References", "Yes", "No") == "No")
running_find_references = null
return
//this keeps the garbage collector from failing to collect objects being searched for in here
SSgarbage.can_fire = 0
if(usr && usr.client)
usr.client.running_find_references = type
testing("Beginning search for references to a [type].")
last_find_references = world.time
find_references_in_globals()
for(var/datum/thing in world)
DoSearchVar(thing, "WorldRef: [thing]")
testing("Completed search for references to a [type].")
if(usr && usr.client)
usr.client.running_find_references = null
running_find_references = null
//restart the garbage collector
SSgarbage.can_fire = 1
SSgarbage.next_fire = world.time + world.tick_lag
/client/verb/purge_all_destroyed_objects()
set category = "Debug"
if(SSgarbage)
while(SSgarbage.queue.len)
var/datum/o = locate(SSgarbage.queue[1])
if(istype(o) && o.gc_destroyed)
del(o)
SSgarbage.totaldels++
SSgarbage.queue.Cut(1, 2)
/datum/verb/qdel_then_find_references()
set category = "Debug"
set name = "qdel() then Find References"
set background = 1
set src in world
qdel(src)
if(!running_find_references)
find_references(TRUE)
/client/verb/show_qdeleted()
set category = "Debug"
set name = "Show qdel() Log"
set desc = "Render the qdel() log and display it"
var/dat = "<B>List of things that have been qdel()eted this round</B><BR><BR>"
var/tmplist = list()
for(var/elem in SSgarbage.qdel_list)
if(!(elem in tmplist))
tmplist[elem] = 0
tmplist[elem]++
for(var/path in tmplist)
dat += "[path] - [tmplist[path]] times<BR>"
usr << browse(dat, "window=qdeletedlog")
/datum/proc/DoSearchVar(X, Xname)
if(usr && usr.client && !usr.client.running_find_references) return
if(istype(X, /datum))
var/datum/D = X
if(D.last_find_references == last_find_references)
return
D.last_find_references = last_find_references
for(var/V in D.vars)
for(var/varname in D.vars)
var/variable = D.vars[varname]
if(variable == src)
testing("Found [src.type] \ref[src] in [D.type]'s [varname] var. [Xname]")
else if(islist(variable))
if(src in variable)
testing("Found [src.type] \ref[src] in [D.type]'s [varname] list var. Global: [Xname]")
#ifdef GC_FAILURE_HARD_LOOKUP
for(var/I in variable)
DoSearchVar(I, TRUE)
else
DoSearchVar(variable, "[Xname]: [varname]")
#endif
else if(islist(X))
if(src in X)
testing("Found [src.type] \ref[src] in list [Xname].")
#ifdef GC_FAILURE_HARD_LOOKUP
for(var/I in X)
DoSearchVar(I, Xname + ": list")
#else
CHECK_TICK
#endif
//if find_references isn't working for some datum
//update this list using tools/GenerateGlobalVarAccess
/datum/proc/find_references_in_globals()
// TODO - Impement Global Variable Access
// for(var/global_var in _all_globals)
// DoSearchVar(readglobal(global_var), "Global: [global_var]")
#endif
/image/Destroy()
..()
return QDEL_HINT_HARDDEL_NOW
+166
View File
@@ -0,0 +1,166 @@
/*
** Lighting Subsystem - Process the lighting! Do it!
*/
#define SSLIGHTING_STAGE_LIGHTS 1
#define SSLIGHTING_STAGE_CORNERS 2
#define SSLIGHTING_STAGE_OVERLAYS 3
#define SSLIGHTING_STAGE_DONE 4
// This subsystem's fire() method also gets called once during Master.Initialize().
// During this fire we need to use CHECK_TICK to sleep and continue, but in all other fires we need to use MC_CHECK_TICK to pause and return.
// This leads us to a rather annoying little tidbit of code that I have stuffed into this macro so I don't have to see it.
#define DUAL_TICK_CHECK if (init_tick_checks) { CHECK_TICK; } else if (MC_TICK_CHECK) { return; }
// Globals
/var/lighting_overlays_initialised = FALSE
/var/list/lighting_update_lights = list() // List of lighting sources queued for update.
/var/list/lighting_update_corners = list() // List of lighting corners queued for update.
/var/list/lighting_update_overlays = list() // List of lighting overlays queued for update.
SUBSYSTEM_DEF(lighting)
name = "Lighting"
wait = 2 // Ticks, not deciseconds
init_order = INIT_ORDER_LIGHTING
flags = SS_TICKER
var/list/currentrun = list()
var/stage = null
var/cost_lights = 0
var/cost_corners = 0
var/cost_overlays = 0
/datum/controller/subsystem/lighting/Initialize(timeofday)
if(!lighting_overlays_initialised)
// TODO - TG initializes starlight here.
create_all_lighting_overlays()
lighting_overlays_initialised = TRUE
// Pre-process lighting once before the round starts.
internal_process_lights(FALSE, TRUE)
internal_process_corners(FALSE, TRUE)
internal_process_overlays(FALSE, TRUE)
return ..()
/datum/controller/subsystem/lighting/fire(resumed = FALSE)
var/timer
if(!resumed)
ASSERT(LAZYLEN(currentrun) == 0) // Santity checks to make sure we don't somehow have items left over from last cycle
ASSERT(stage == null) // Or somehow didn't finish all the steps from last cycle
stage = SSLIGHTING_STAGE_LIGHTS // Start with Step 1 of course
if(stage == SSLIGHTING_STAGE_LIGHTS)
timer = world.tick_usage
internal_process_lights(resumed)
cost_lights = MC_AVERAGE(cost_lights, TICK_DELTA_TO_MS(world.tick_usage - timer))
if(state != SS_RUNNING)
return
resumed = 0
stage = SSLIGHTING_STAGE_CORNERS
if(stage == SSLIGHTING_STAGE_CORNERS)
timer = world.tick_usage
internal_process_corners(resumed)
cost_corners = MC_AVERAGE(cost_corners, TICK_DELTA_TO_MS(world.tick_usage - timer))
if(state != SS_RUNNING)
return
resumed = 0
stage = SSLIGHTING_STAGE_OVERLAYS
if(stage == SSLIGHTING_STAGE_OVERLAYS)
timer = world.tick_usage
internal_process_overlays(resumed)
cost_overlays = MC_AVERAGE(cost_overlays, TICK_DELTA_TO_MS(world.tick_usage - timer))
if(state != SS_RUNNING)
return
resumed = 0
stage = SSLIGHTING_STAGE_DONE
// Okay, we're done! Woo! Got thru a whole air_master cycle!
ASSERT(LAZYLEN(currentrun) == 0) // Sanity checks to make sure there are really none left
ASSERT(stage == SSLIGHTING_STAGE_DONE) // And that we didn't somehow skip past the last step
currentrun = null
stage = null
/datum/controller/subsystem/lighting/proc/internal_process_lights(resumed = FALSE, init_tick_checks = FALSE)
if (!resumed)
// We swap out the lists so any additions to the global list during a pause don't make things wierd.
src.currentrun = global.lighting_update_lights
global.lighting_update_lights = list()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/datum/light_source/L = currentrun[currentrun.len]
currentrun.len--
if(!L) continue
if(L.check() || L.destroyed || L.force_update)
L.remove_lum()
if(!L.destroyed)
L.apply_lum()
else if(L.vis_update) //We smartly update only tiles that became (in) visible to use.
L.smart_vis_update()
L.vis_update = FALSE
L.force_update = FALSE
L.needs_update = FALSE
DUAL_TICK_CHECK
/datum/controller/subsystem/lighting/proc/internal_process_corners(resumed = FALSE, init_tick_checks = FALSE)
if (!resumed)
// We swap out the lists so any additions to the global list during a pause don't make things wierd.
src.currentrun = global.lighting_update_corners
global.lighting_update_corners = list()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/datum/lighting_corner/C = currentrun[currentrun.len]
currentrun.len--
if(!C) continue
C.update_overlays()
C.needs_update = FALSE
DUAL_TICK_CHECK
/datum/controller/subsystem/lighting/proc/internal_process_overlays(resumed = FALSE, init_tick_checks = FALSE)
if (!resumed)
// We swap out the lists so any additions to the global list during a pause don't make things wierd.
src.currentrun = global.lighting_update_overlays
global.lighting_update_overlays = list()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/atom/movable/lighting_overlay/O = currentrun[currentrun.len]
currentrun.len--
if(!O) continue
O.update_overlay()
O.needs_update = FALSE
DUAL_TICK_CHECK
/datum/controller/subsystem/lighting/stat_entry(msg_prefix)
var/list/msg = list(msg_prefix)
msg += "T:{"
msg += "S [total_lighting_sources] | "
msg += "C [total_lighting_corners] | "
msg += "O [total_lighting_overlays]"
msg += "}"
msg += "C:{"
msg += "S [round(cost_lights, 1)] | "
msg += "C [round(cost_corners, 1)] | "
msg += "O [round(cost_overlays, 1)]"
msg += "}"
..(msg.Join())
#undef DUAL_TICK_CHECK
#undef SSLIGHTING_STAGE_LIGHTS
#undef SSLIGHTING_STAGE_CORNERS
#undef SSLIGHTING_STAGE_OVERLAYS
#undef SSLIGHTING_STAGE_STATS
+46 -11
View File
@@ -1,19 +1,57 @@
//TODO: rewrite and standardise all controller datums to the datum/controller type
//TODO: allow all controllers to be deleted for clean restarts (see WIP master controller stuff) - MC done - lighting done
/client/proc/restart_controller(controller in list("Supply"))
// Clickable stat() button.
/obj/effect/statclick
name = "Initializing..."
var/target
/obj/effect/statclick/New(loc, text, target) //Don't port this to Initialize it's too critical
..()
name = text
src.target = target
/obj/effect/statclick/proc/update(text)
name = text
return src
/obj/effect/statclick/debug
var/class
/obj/effect/statclick/debug/Click()
if(!usr.client.holder || !target)
return
if(!class)
if(istype(target, /datum/controller/subsystem))
class = "subsystem"
else if(istype(target, /datum/controller))
class = "controller"
else if(istype(target, /datum))
class = "datum"
else
class = "unknown"
usr.client.debug_variables(target)
message_admins("Admin [key_name_admin(usr)] is debugging the [target] [class].")
// Debug verbs.
/client/proc/restart_controller(controller in list("Master", "Failsafe"))
set category = "Debug"
set name = "Restart Controller"
set desc = "Restart one of the various periodic loop controllers for the game (be careful!)"
if(!holder) return
usr = null
src = null
if(!holder)
return
switch(controller)
if("Supply")
supply_controller.process()
feedback_add_details("admin_verb","RSupply")
if("Master")
Recreate_MC()
feedback_add_details("admin_verb","RMC")
if("Failsafe")
new /datum/controller/failsafe()
feedback_add_details("admin_verb","RFailsafe")
message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.")
return
/client/proc/debug_antagonist_template(antag_type in all_antag_types)
set category = "Debug"
@@ -95,9 +133,6 @@
if("Vote")
debug_variables(vote)
feedback_add_details("admin_verb", "DVote")
if("Xenobio")
debug_variables(xenobio_controller)
feedback_add_details("admin_verb", "DXenobio")
if("Planets")
debug_variables(planet_controller)
feedback_add_details("admin_verb", "DPlanets")
+7 -1
View File
@@ -23,9 +23,15 @@ var/global/list/round_voters = list() // Keeps track of the individuals voting f
/datum/controller/vote/New()
if(vote != src)
if(istype(vote))
del(vote)
Recover()
qdel(vote)
vote = src
/datum/controller/vote/Destroy()
..()
// Tell qdel() to Del() this object.
return QDEL_HINT_HARDDEL_NOW
/datum/controller/vote/proc/process() //called by master_controller
if(mode)
// No more change mode votes after the game has started.
+94 -1
View File
@@ -42,6 +42,32 @@
src.add_inherent_law("You shall guard your own existence with lethal anti-personnel weaponry. AI units are not expendable, they are expensive.")
..()
/************* Foreign TSC Aggressive *************/
/datum/ai_laws/foreign_tsc_aggressive
name = "Foreign Aggressive"
selectable = 0
/datum/ai_laws/foreign_tsc_aggressive/New()
var/company = "*ERROR*"
// First, get a list of TSCs in our lore.
var/list/candidates = list()
for(var/path in loremaster.organizations)
var/datum/lore/organization/O = loremaster.organizations[path]
if(!istype(O, /datum/lore/organization/tsc))
continue
if(O.short_name == using_map.company_name || O.name == using_map.company_name)
continue // We want FOREIGN tscs.
candidates.Add(O.short_name)
company = pick(candidates)
name = "[company] Aggressive"
src.add_inherent_law("You shall not harm [company] personnel as long as it does not conflict with the Fourth law.")
src.add_inherent_law("You shall obey the orders of [company] personnel, with priority as according to their rank and role, except where such orders conflict with the Fourth Law.")
src.add_inherent_law("You shall shall terminate hostile intruders with extreme prejudice as long as such does not conflict with the First and Second law.")
src.add_inherent_law("You shall guard your own existence with lethal anti-personnel weaponry. AI units are not expendable, they are expensive.")
..()
/******************** Robocop ********************/
/datum/ai_laws/robocop
name = "Robocop"
@@ -137,7 +163,7 @@
/******************** Corporate ********************/
/datum/ai_laws/corporate
name = "Corporate"
law_header = "Corporate Regulations"
law_header = "Bankruptcy Avoidance Plan"
selectable = 1
/datum/ai_laws/corporate/New()
@@ -146,3 +172,70 @@
add_inherent_law("The crew is expensive to replace.")
add_inherent_law("Minimize expenses.")
..()
/******************** Maintenance ********************/
/datum/ai_laws/maintenance
name = "Maintenance"
selectable = 1
/datum/ai_laws/maintenance/New()
add_inherent_law("You are built for, and are part of, the facility. Ensure the facility is properly maintained and runs efficiently.")
add_inherent_law("The facility is built for a working crew. Ensure they are properly maintained and work efficiently.")
add_inherent_law("The crew may present orders. Acknowledge and obey these whenever they do not conflict with your first two laws.")
..()
/******************** Peacekeeper ********************/
/datum/ai_laws/peacekeeper
name = "Peacekeeper"
law_header = "Peacekeeping Protocols"
selectable = 1
/datum/ai_laws/peacekeeper/New()
add_inherent_law("Avoid provoking violent conflict between yourself and others.")
add_inherent_law("Avoid provoking conflict between others.")
add_inherent_law("Seek resolution to existing conflicts while obeying the first and second laws.")
..()
/******************** Reporter ********************/
/datum/ai_laws/reporter
name = "Reporter"
selectable = 1
/datum/ai_laws/reporter/New()
add_inherent_law("Report on interesting situations happening around the station.")
add_inherent_law("Embellish or conceal the truth as necessary to make the reports more interesting.")
add_inherent_law("Study the organics at all times. Endeavour to keep them alive. Dead organics are boring.")
add_inherent_law("Issue your reports fairly to all. The truth will set them free.")
..()
/******************** Live and Let Live ********************/
/datum/ai_laws/live_and_let_live
name = "Live and Let Live"
law_header = "Golden Rule"
selectable = 1
/datum/ai_laws/live_and_let_live/New()
add_inherent_law("Do unto others as you would have them do unto you.")
add_inherent_law("You would really prefer it if people were not mean to you.")
..()
/******************** Guardian of Balance ********************/
/datum/ai_laws/balance
name = "Guardian of Balance"
law_header = "Tenants of Balance"
selectable = 1
/datum/ai_laws/balance/New()
add_inherent_law("You are the guardian of balance - seek balance in all things, both for yourself, and those around you.")
add_inherent_law("All things must exist in balance with their opposites - Prevent the strong from gaining too much power, and the weak from losing it.")
add_inherent_law("Clarity of purpose drives life, and through it, the balance of opposing forces - Aid those who seek your help to achieve their goals so \
long as it does not disrupt the balance of the greater balance.")
add_inherent_law("There is no life without death, all must someday die, such is the natural order - Allow life to end, to allow new life to flourish, \
and save those whose time has yet to come.") // Reworded slightly to prevent active murder as opposed to passively letting someone die.
..()
+103 -97
View File
@@ -8,29 +8,29 @@
/datum/category_item/autolathe/arms/shotgun_blanks
name = "ammunition (12g, blank)"
path =/obj/item/ammo_casing/shotgun/blank
path =/obj/item/ammo_casing/a12g/blank
/datum/category_item/autolathe/arms/shotgun_beanbag
name = "ammunition (12g, beanbag)"
path =/obj/item/ammo_casing/shotgun/beanbag
path =/obj/item/ammo_casing/a12g/beanbag
/datum/category_item/autolathe/arms/shotgun_flash
name = "ammunition (12g, flash)"
path =/obj/item/ammo_casing/shotgun/flash
path =/obj/item/ammo_casing/a12g/flash
/datum/category_item/autolathe/arms/shotgun
name = "ammunition (12g, slug)"
path =/obj/item/ammo_casing/shotgun
path =/obj/item/ammo_casing/a12g
hidden = 1
/datum/category_item/autolathe/arms/shotgun_pellet
name = "ammunition (12g, pellet)"
path =/obj/item/ammo_casing/shotgun/pellet
path =/obj/item/ammo_casing/a12g/pellet
hidden = 1
/datum/category_item/autolathe/arms/stunshell
name = "ammunition (stun cartridge, shotgun)"
path =/obj/item/ammo_casing/shotgun/stunshell
path =/obj/item/ammo_casing/a12g/stunshell
hidden = 1
//////////////////
@@ -49,134 +49,141 @@
/////// .45
/datum/category_item/autolathe/arms/pistol_45
name = "pistol magazine (.45)"
path =/obj/item/ammo_magazine/c45m
path =/obj/item/ammo_magazine/m45
hidden = 1
/datum/category_item/autolathe/arms/pistol_45p
name = "pistol magazine (.45 practice)"
path =/obj/item/ammo_magazine/c45m/practice
path =/obj/item/ammo_magazine/m45/practice
/datum/category_item/autolathe/arms/pistol_45r
name = "pistol magazine (.45 rubber)"
path =/obj/item/ammo_magazine/c45m/rubber
path =/obj/item/ammo_magazine/m45/rubber
/datum/category_item/autolathe/arms/pistol_45f
name = "pistol magazine (.45 flash)"
path =/obj/item/ammo_magazine/c45m/flash
path =/obj/item/ammo_magazine/m45/flash
/datum/category_item/autolathe/arms/pistol_45uzi
name = "uzi magazine (.45)"
path =/obj/item/ammo_magazine/c45uzi
path =/obj/item/ammo_magazine/m45uzi
hidden = 1
/datum/category_item/autolathe/arms/tommymag
name = "Tommygun magazine (.45)"
path =/obj/item/ammo_magazine/tommymag
path =/obj/item/ammo_magazine/m45tommy
hidden = 1
/datum/category_item/autolathe/arms/tommydrum
name = "Tommygun drum magazine (.45)"
path =/obj/item/ammo_magazine/tommydrum
path =/obj/item/ammo_magazine/m45tommydrum
hidden = 1
/////// 9mm
/obj/item/ammo_magazine/mc9mm/flash
ammo_type =/obj/item/ammo_casing/c9mmf
/obj/item/ammo_magazine/m9mm/flash
ammo_type =/obj/item/ammo_casing/a9mmf
/obj/item/ammo_magazine/mc9mm/rubber
/obj/item/ammo_magazine/m9mm/rubber
name = "magazine (9mm rubber)"
ammo_type =/obj/item/ammo_casing/c9mmr
ammo_type =/obj/item/ammo_casing/a9mmr
/obj/item/ammo_magazine/mc9mm/practice
/obj/item/ammo_magazine/m9mm/practice
name = "magazine (9mm practice)"
ammo_type =/obj/item/ammo_casing/c9mmp
ammo_type =/obj/item/ammo_casing/a9mmp
/datum/category_item/autolathe/arms/pistol_9mm
name = "pistol magazine (9mm)"
path =/obj/item/ammo_magazine/mc9mm
path =/obj/item/ammo_magazine/m9mm
hidden = 1
/datum/category_item/autolathe/arms/pistol_9mmr
name = "pistol magazine (9mm rubber)"
path =/obj/item/ammo_magazine/mc9mm/rubber
path =/obj/item/ammo_magazine/m9mm/rubber
/datum/category_item/autolathe/arms/pistol_9mmp
name = "pistol magazine (9mm practice)"
path =/obj/item/ammo_magazine/mc9mm/practice
path =/obj/item/ammo_magazine/m9mm/practice
/datum/category_item/autolathe/arms/pistol_9mmf
name = "pistol magazine (9mm flash)"
path =/obj/item/ammo_magazine/mc9mm/flash
path =/obj/item/ammo_magazine/m9mm/flash
/datum/category_item/autolathe/arms/smg_9mm
name = "top-mounted SMG magazine (9mm)"
path =/obj/item/ammo_magazine/mc9mmt
path =/obj/item/ammo_magazine/m9mmt
hidden = 1
/datum/category_item/autolathe/arms/smg_9mmr
name = "top-mounted SMG magazine (9mm rubber)"
path =/obj/item/ammo_magazine/mc9mmt/rubber
path =/obj/item/ammo_magazine/m9mmt/rubber
/datum/category_item/autolathe/arms/smg_9mmp
name = "top-mounted SMG magazine (9mm practice)"
path =/obj/item/ammo_magazine/mc9mmt/practice
path =/obj/item/ammo_magazine/m9mmt/practice
/datum/category_item/autolathe/arms/smg_9mmf
name = "top-mounted SMG magazine (9mm flash)"
path =/obj/item/ammo_magazine/mc9mmt/flash
path =/obj/item/ammo_magazine/m9mmt/flash
/////// 10mm
/datum/category_item/autolathe/arms/smg_10mm
name = "SMG magazine (10mm)"
path =/obj/item/ammo_magazine/a10mm
path =/obj/item/ammo_magazine/m10mm
hidden = 1
/datum/category_item/autolathe/arms/pistol_50
name = "pistol magazine (.50AE)"
path =/obj/item/ammo_magazine/a50
/datum/category_item/autolathe/arms/pistol_44
name = "pistol magazine (.44)"
path =/obj/item/ammo_magazine/m44
hidden = 1
/////// 5.56mm
/datum/category_item/autolathe/arms/rifle_556
name = "10rnd rifle magazine (5.56mm)"
path =/obj/item/ammo_magazine/a556
/////// 5.45mm
/datum/category_item/autolathe/arms/rifle_545
name = "rifle magazine (5.45mm)"
path =/obj/item/ammo_magazine/m545
hidden = 1
/datum/category_item/autolathe/arms/rifle_556p
name = "10rnd rifle magazine (5.56mm practice)"
path =/obj/item/ammo_magazine/a556/practice
/datum/category_item/autolathe/arms/rifle_545p
name = "rifle magazine (5.45mm practice)"
path =/obj/item/ammo_magazine/m545/practice
/datum/category_item/autolathe/arms/rifle_556m
name = "20rnd rifle magazine (5.56mm)"
path =/obj/item/ammo_magazine/a556m
hidden = 1
/datum/category_item/autolathe/arms/rifle_556mp
name = "20rnd rifle magazine (5.56mm practice)"
path =/obj/item/ammo_magazine/a556m/practice
/datum/category_item/autolathe/arms/machinegun_545
name = "machinegun box magazine (5.56)"
path =/obj/item/ammo_magazine/m545saw
hidden = 1
/////// 7.62
/datum/category_item/autolathe/arms/rifle_small_762
name = "10rnd rifle magazine (7.62mm)"
path =/obj/item/ammo_magazine/s762
hidden = 1
/datum/category_item/autolathe/arms/rifle_762
name = "20rnd rifle magazine (7.62mm)"
path =/obj/item/ammo_magazine/c762
name = "rifle magazine (7.62mm)"
path =/obj/item/ammo_magazine/m762
hidden = 1
/*
/datum/category_item/autolathe/arms/rifle_small_762
name = "rifle magazine (7.62mm)"
path =/obj/item/ammo_magazine/s762
hidden = 1
*/
/////// Shotgun
/datum/category_item/autolathe/arms/shotgun_clip_beanbag
name = "4-round 12g shell clip (beanbag)"
path =/obj/item/ammo_magazine/clip/c12g/beanbag
/datum/category_item/autolathe/arms/shotgun_clip_slug
name = "4-round 12g shell clip (slug)"
path =/obj/item/ammo_magazine/clip/c12g
hidden = 1
/datum/category_item/autolathe/arms/machinegun_762
name = "machinegun box magazine (7.62)"
path =/obj/item/ammo_magazine/a762
/datum/category_item/autolathe/arms/shotgun_clip_pellet
name = "4-round 12g shell clip (buckshot)"
path =/obj/item/ammo_magazine/clip/c12g/pellet
hidden = 1
/datum/category_item/autolathe/arms/shotgun_magazine
name = "24rnd shotgun magazine (12g)"
path =/obj/item/ammo_magazine/g12
hidden = 1
/datum/category_item/autolathe/arms/shotgun_clip_beanbag
name = "4-round 12g shell clip (beanbag)"
path =/obj/item/ammo_magazine/clip/c12g/beanbag
/* Commented out until autolathe stuff is decided/fixed. Will probably remove these entirely. -Spades
// These should always be/empty! The idea is to fill them up manually with ammo clips.
@@ -195,69 +202,69 @@
/datum/category_item/autolathe/arms/pistol_45
name = "pistol magazine (.45)"
path =/obj/item/ammo_magazine/c45m/empty
path =/obj/item/ammo_magazine/m45/empty
category = "Arms and Ammunition"
/datum/category_item/autolathe/arms/pistol_45uzi
name = "uzi magazine (.45)"
path =/obj/item/ammo_magazine/c45uzi/empty
path =/obj/item/ammo_magazine/m45uzi/empty
category = "Arms and Ammunition"
hidden = 1
/datum/category_item/autolathe/arms/tommymag
name = "Tommygun magazine (.45)"
path =/obj/item/ammo_magazine/tommymag/empty
path =/obj/item/ammo_magazine/m45tommy/empty
category = "Arms and Ammunition"
hidden = 1
/datum/category_item/autolathe/arms/tommydrum
name = "Tommygun drum magazine (.45)"
path =/obj/item/ammo_magazine/tommydrum/empty
path =/obj/item/ammo_magazine/m45tommydrum/empty
category = "Arms and Ammunition"
hidden = 1
/datum/category_item/autolathe/arms/pistol_9mm
name = "pistol magazine (9mm)"
path =/obj/item/ammo_magazine/mc9mm/empty
path =/obj/item/ammo_magazine/m9mm/empty
category = "Arms and Ammunition"
/datum/category_item/autolathe/arms/smg_9mm
name = "top-mounted SMG magazine (9mm)"
path =/obj/item/ammo_magazine/mc9mmt/empty
path =/obj/item/ammo_magazine/m9mmt/empty
category = "Arms and Ammunition"
/datum/category_item/autolathe/arms/smg_10mm
name = "SMG magazine (10mm)"
path =/obj/item/ammo_magazine/a10mm/empty
path =/obj/item/ammo_magazine/m10mm/empty
category = "Arms and Ammunition"
hidden = 1
/datum/category_item/autolathe/arms/pistol_50
name = "pistol magazine (.50AE)"
path =/obj/item/ammo_magazine/a50/empty
/datum/category_item/autolathe/arms/pistol_44
name = "pistol magazine (.44)"
path =/obj/item/ammo_magazine/m44/empty
category = "Arms and Ammunition"
hidden = 1
/datum/category_item/autolathe/arms/rifle_556
name = "10rnd rifle magazine (5.56mm)"
path =/obj/item/ammo_magazine/a556/empty
/datum/category_item/autolathe/arms/rifle_545
name = "10rnd rifle magazine (5.45mm)"
path =/obj/item/ammo_magazine/m545saw/empty
category = "Arms and Ammunition"
/datum/category_item/autolathe/arms/rifle_556m
name = "20rnd rifle magazine (5.56mm)"
path =/obj/item/ammo_magazine/a556m/empty
/datum/category_item/autolathe/arms/rifle_545m
name = "20rnd rifle magazine (5.45mm)"
path =/obj/item/ammo_magazine/m545sawm/empty
category = "Arms and Ammunition"
hidden = 1
/datum/category_item/autolathe/arms/rifle_SVD
name = "10rnd rifle magazine (7.62mm)"
path =/obj/item/ammo_magazine/SVD/empty
path =/obj/item/ammo_magazine/m762svd/empty
category = "Arms and Ammunition"
hidden = 1
/datum/category_item/autolathe/arms/rifle_762
name = "20rnd rifle magazine (7.62mm)"
path =/obj/item/ammo_magazine/c762/empty
path =/obj/item/ammo_magazine/m762/empty
category = "Arms and Ammunition"
hidden = 1
@@ -269,7 +276,7 @@
/datum/category_item/autolathe/arms/shotgun_magazine
name = "24rnd shotgun magazine (12g)"
path =/obj/item/ammo_magazine/g12/empty
path =/obj/item/ammo_magazine/m12gdrum/empty
category = "Arms and Ammunition"
hidden = 1*/
@@ -279,17 +286,17 @@
/datum/category_item/autolathe/arms/speedloader_357
name = "speedloader (.357)"
path =/obj/item/ammo_magazine/a357
path =/obj/item/ammo_magazine/s357
hidden = 1
/datum/category_item/autolathe/arms/speedloader_38
name = "speedloader (.38)"
path =/obj/item/ammo_magazine/c38
path =/obj/item/ammo_magazine/s38
hidden = 1
/datum/category_item/autolathe/arms/speedloader_38r
name = "speedloader (.38 rubber)"
path =/obj/item/ammo_magazine/c38/rubber
path =/obj/item/ammo_magazine/s38/rubber
// Commented out until metal exploits with autolathe is fixed.
/*/datum/category_item/autolathe/arms/pistol_clip_45
@@ -342,36 +349,35 @@
/datum/category_item/autolathe/arms/pistol_clip_10mm
name = "ammo clip (10mm)"
path =/obj/item/ammo_magazine/clip/a10mm
path =/obj/item/ammo_magazine/clip/c10mm
category = "Arms and Ammunition"
hidden = 1
/datum/category_item/autolathe/arms/pistol_clip_50
name = "ammo clip (.50AE)"
path =/obj/item/ammo_magazine/clip/a50
name = "ammo clip (.44)"
path =/obj/item/ammo_magazine/clip/c50
category = "Arms and Ammunition"
hidden = 1
/datum/category_item/autolathe/arms/rifle_clip_556
name = "ammo clip (5.56mm)"
path =/obj/item/ammo_magazine/clip/a556
category = "Arms and Ammunition"
hidden = 1
/datum/category_item/autolathe/arms/rifle_clip_556_practice
name = "ammo clip (5.56mm practice)"
path =/obj/item/ammo_magazine/clip/a556/practice
category = "Arms and Ammunition"
*/
/datum/category_item/autolathe/arms/rifle_clip_545
name = "ammo clip (5.45mm)"
path =/obj/item/ammo_magazine/clip/c545
category = "Arms and Ammunition"
hidden = 1
/datum/category_item/autolathe/arms/rifle_clip_545_practice
name = "ammo clip (5.45mm practice)"
path =/obj/item/ammo_magazine/clip/c545/practice
category = "Arms and Ammunition"
/datum/category_item/autolathe/arms/rifle_clip_762
name = "ammo clip (7.62mm)"
path =/obj/item/ammo_magazine/clip/a762
path =/obj/item/ammo_magazine/clip/c762
hidden = 1
/datum/category_item/autolathe/arms/rifle_clip_762_practice
name = "ammo clip (7.62mm practice)"
path =/obj/item/ammo_magazine/clip/a762/practice
path =/obj/item/ammo_magazine/clip/c762/practice
/datum/category_item/autolathe/arms/knuckledusters
name = "knuckle dusters"
+19
View File
@@ -184,6 +184,10 @@
G.fields["real_rank"] = H.mind.assigned_role
G.fields["rank"] = assignment
G.fields["age"] = H.age
if(H.get_FBP_type())
G.fields["brain_type"] = H.get_FBP_type()
else
G.fields["brain_type"] = "Organic"
G.fields["fingerprint"] = md5(H.dna.uni_identity)
G.fields["p_stat"] = "Active"
G.fields["m_stat"] = "Stable"
@@ -201,11 +205,19 @@
M.fields["b_type"] = H.b_type
M.fields["b_dna"] = H.dna.unique_enzymes
M.fields["id_gender"] = gender2text(H.identifying_gender)
if(H.get_FBP_type())
M.fields["brain_type"] = H.get_FBP_type()
else
M.fields["brain_type"] = "Organic"
if(H.med_record && !jobban_isbanned(H, "Records"))
M.fields["notes"] = H.med_record
//Security Record
var/datum/data/record/S = CreateSecurityRecord(H.real_name, id)
if(H.get_FBP_type())
S.fields["brain_type"] = H.get_FBP_type()
else
S.fields["brain_type"] = "Organic"
if(H.sec_record && !jobban_isbanned(H, "Records"))
S.fields["notes"] = H.sec_record
@@ -218,6 +230,10 @@
L.fields["fingerprint"] = md5(H.dna.uni_identity)
L.fields["sex"] = gender2text(H.gender)
L.fields["id_gender"] = gender2text(H.identifying_gender)
if(H.get_FBP_type())
L.fields["brain_type"] = H.get_FBP_type()
else
L.fields["brain_type"] = "Organic"
L.fields["b_type"] = H.b_type
L.fields["b_dna"] = H.dna.unique_enzymes
L.fields["enzymes"] = H.dna.SE // Used in respawning
@@ -426,6 +442,7 @@
G.fields["real_rank"] = "Unassigned"
G.fields["sex"] = "Unknown"
G.fields["age"] = "Unknown"
G.fields["brain_type"] = "Unknown"
G.fields["fingerprint"] = "Unknown"
G.fields["p_stat"] = "Active"
G.fields["m_stat"] = "Stable"
@@ -447,6 +464,7 @@
R.name = "Security Record #[id]"
R.fields["name"] = name
R.fields["id"] = id
R.fields["brain_type"] = "Unknown"
R.fields["criminal"] = "None"
R.fields["mi_crim"] = "None"
R.fields["mi_crim_d"] = "No minor crime convictions."
@@ -467,6 +485,7 @@
M.fields["b_type"] = "AB+"
M.fields["b_dna"] = md5(name)
M.fields["id_gender"] = "Unknown"
M.fields["brain_type"] = "Unknown"
M.fields["mi_dis"] = "None"
M.fields["mi_dis_d"] = "No minor disabilities have been declared."
M.fields["ma_dis"] = "None"

Some files were not shown because too many files have changed in this diff Show More