Merge branch 'master' into upstream-merge-5897

This commit is contained in:
Novacat
2019-03-28 14:39:41 -04:00
committed by GitHub
1471 changed files with 49032 additions and 48743 deletions

View File

@@ -200,7 +200,7 @@
"moles" = round(air2.gas[output_gas], 0.01))
// update the ui if it exists, returns null if no ui is passed/found
ui = GLOB.nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "algae_farm_vr.tmpl", "Algae Farm Control Panel", 500, 600)
ui.set_initial_data(data)

View File

@@ -125,24 +125,24 @@
..()
/obj/machinery/atmospherics/binary/circulator/verb/rotate_clockwise()
set name = "Rotate Circulator Clockwise"
set category = "Object"
set src in view(1)
if (usr.stat || usr.restrained() || anchored)
return
src.set_dir(turn(src.dir, 270))
desc = initial(desc) + " Its outlet port is to the [dir2text(dir)]."
/obj/machinery/atmospherics/binary/circulator/verb/rotate_counterclockwise()
set name = "Rotate Circulator Counterclockwise"
set category = "Object"
set name = "Rotate Circulator (Clockwise)"
set src in view(1)
if (usr.stat || usr.restrained() || anchored)
return
src.set_dir(turn(src.dir, 90))
desc = initial(desc) + " Its outlet port is to the [dir2text(dir)]."
/obj/machinery/atmospherics/binary/circulator/verb/rotate_anticlockwise()
set category = "Object"
set name = "Rotate Circulator (Counterclockwise)"
set src in view(1)
if (usr.stat || usr.restrained() || anchored)
return
src.set_dir(turn(src.dir, -90))
desc = initial(desc) + " Its outlet port is to the [dir2text(dir)]."

View File

@@ -194,7 +194,7 @@
return 1
/obj/machinery/atmospherics/binary/dp_vent_pump/initialize()
/obj/machinery/atmospherics/binary/dp_vent_pump/Initialize()
. = ..()
if(frequency)
set_frequency(frequency)

View File

@@ -129,7 +129,7 @@
return 1
/obj/machinery/atmospherics/binary/passive_gate/initialize()
/obj/machinery/atmospherics/binary/passive_gate/Initialize()
. = ..()
if(frequency)
set_frequency(frequency)
@@ -197,7 +197,7 @@
)
// update the ui if it exists, returns null if no ui is passed/found
ui = GLOB.nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
// the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm

View File

@@ -20,209 +20,210 @@
var/datum/pipe_network/network1
var/datum/pipe_network/network2
New()
..()
air_in.volume = 200
air_out.volume = 800
volume_ratio = air_in.volume / (air_in.volume + air_out.volume)
switch(dir)
if(NORTH)
/obj/machinery/atmospherics/pipeturbine/New()
..()
air_in.volume = 200
air_out.volume = 800
volume_ratio = air_in.volume / (air_in.volume + air_out.volume)
switch(dir)
if(NORTH)
initialize_directions = EAST|WEST
if(SOUTH)
initialize_directions = EAST|WEST
if(EAST)
initialize_directions = NORTH|SOUTH
if(WEST)
initialize_directions = NORTH|SOUTH
/obj/machinery/atmospherics/pipeturbine/Destroy()
. = ..()
if(node1)
node1.disconnect(src)
qdel(network1)
if(node2)
node2.disconnect(src)
qdel(network2)
node1 = null
node2 = null
/obj/machinery/atmospherics/pipeturbine/process()
..()
if(anchored && !(stat&BROKEN))
kin_energy *= 1 - kin_loss
dP = max(air_in.return_pressure() - air_out.return_pressure(), 0)
if(dP > 10)
kin_energy += 1/ADIABATIC_EXPONENT * dP * air_in.volume * (1 - volume_ratio**ADIABATIC_EXPONENT) * efficiency
air_in.temperature *= volume_ratio**ADIABATIC_EXPONENT
var/datum/gas_mixture/air_all = new
air_all.volume = air_in.volume + air_out.volume
air_all.merge(air_in.remove_ratio(1))
air_all.merge(air_out.remove_ratio(1))
air_in.merge(air_all.remove(volume_ratio))
air_out.merge(air_all)
update_icon()
if (network1)
network1.update = 1
if (network2)
network2.update = 1
/obj/machinery/atmospherics/pipeturbine/update_icon()
overlays.Cut()
if (dP > 10)
overlays += image('icons/obj/pipeturbine.dmi', "moto-turb")
if (kin_energy > 100000)
overlays += image('icons/obj/pipeturbine.dmi', "low-turb")
if (kin_energy > 500000)
overlays += image('icons/obj/pipeturbine.dmi', "med-turb")
if (kin_energy > 1000000)
overlays += image('icons/obj/pipeturbine.dmi', "hi-turb")
/obj/machinery/atmospherics/pipeturbine/attackby(obj/item/weapon/W as obj, mob/user as mob)
if(W.is_wrench())
anchored = !anchored
playsound(src, W.usesound, 50, 1)
to_chat(user, "<span class='notice'>You [anchored ? "secure" : "unsecure"] the bolts holding \the [src] to the floor.</span>")
if(anchored)
if(dir & (NORTH|SOUTH))
initialize_directions = EAST|WEST
if(SOUTH)
initialize_directions = EAST|WEST
if(EAST)
initialize_directions = NORTH|SOUTH
if(WEST)
else if(dir & (EAST|WEST))
initialize_directions = NORTH|SOUTH
Destroy()
. = ..()
if(node1)
node1.disconnect(src)
qdel(network1)
if(node2)
node2.disconnect(src)
qdel(network2)
node1 = null
node2 = null
process()
..()
if(anchored && !(stat&BROKEN))
kin_energy *= 1 - kin_loss
dP = max(air_in.return_pressure() - air_out.return_pressure(), 0)
if(dP > 10)
kin_energy += 1/ADIABATIC_EXPONENT * dP * air_in.volume * (1 - volume_ratio**ADIABATIC_EXPONENT) * efficiency
air_in.temperature *= volume_ratio**ADIABATIC_EXPONENT
var/datum/gas_mixture/air_all = new
air_all.volume = air_in.volume + air_out.volume
air_all.merge(air_in.remove_ratio(1))
air_all.merge(air_out.remove_ratio(1))
air_in.merge(air_all.remove(volume_ratio))
air_out.merge(air_all)
update_icon()
if (network1)
network1.update = 1
if (network2)
network2.update = 1
update_icon()
overlays.Cut()
if (dP > 10)
overlays += image('icons/obj/pipeturbine.dmi', "moto-turb")
if (kin_energy > 100000)
overlays += image('icons/obj/pipeturbine.dmi', "low-turb")
if (kin_energy > 500000)
overlays += image('icons/obj/pipeturbine.dmi', "med-turb")
if (kin_energy > 1000000)
overlays += image('icons/obj/pipeturbine.dmi', "hi-turb")
attackby(obj/item/weapon/W as obj, mob/user as mob)
if(W.is_wrench())
anchored = !anchored
playsound(src, W.usesound, 50, 1)
to_chat(user, "<span class='notice'>You [anchored ? "secure" : "unsecure"] the bolts holding \the [src] to the floor.</span>")
if(anchored)
if(dir & (NORTH|SOUTH))
initialize_directions = EAST|WEST
else if(dir & (EAST|WEST))
initialize_directions = NORTH|SOUTH
atmos_init()
build_network()
if (node1)
node1.atmos_init()
node1.build_network()
if (node2)
node2.atmos_init()
node2.build_network()
else
if(node1)
node1.disconnect(src)
qdel(network1)
if(node2)
node2.disconnect(src)
qdel(network2)
node1 = null
node2 = null
atmos_init()
build_network()
if (node1)
node1.atmos_init()
node1.build_network()
if (node2)
node2.atmos_init()
node2.build_network()
else
..()
if(node1)
node1.disconnect(src)
qdel(network1)
if(node2)
node2.disconnect(src)
qdel(network2)
verb/rotate_clockwise()
set category = "Object"
set name = "Rotate Circulator (Clockwise)"
set src in view(1)
if (usr.stat || usr.restrained() || anchored)
return
src.set_dir(turn(src.dir, -90))
verb/rotate_anticlockwise()
set category = "Object"
set name = "Rotate Circulator (Counterclockwise)"
set src in view(1)
if (usr.stat || usr.restrained() || anchored)
return
src.set_dir(turn(src.dir, 90))
//Goddamn copypaste from binary base class because atmospherics machinery API is not damn flexible
get_neighbor_nodes_for_init()
return list(node1, node2)
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
if(new_network.normal_members.Find(src))
return 0
new_network.normal_members += src
return null
atmos_init()
if(node1 && node2) return
var/node2_connect = turn(dir, -90)
var/node1_connect = turn(dir, 90)
for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect))
if(target.initialize_directions & get_dir(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))
node2 = target
break
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 += air_in
if(network2 == reference)
results += air_out
return results
disconnect(obj/machinery/atmospherics/reference)
if(reference==node1)
qdel(network1)
node1 = null
else if(reference==node2)
qdel(network2)
node2 = null
return null
return
..()
/obj/machinery/atmospherics/pipeturbine/verb/rotate_clockwise()
set name = "Rotate Turbine Clockwise"
set category = "Object"
set src in view(1)
if (usr.stat || usr.restrained() || anchored)
return
src.set_dir(turn(src.dir, 270))
/obj/machinery/atmospherics/pipeturbine/verb/rotate_counterclockwise()
set name = "Rotate Turbine Counterclockwise"
set category = "Object"
set src in view(1)
if (usr.stat || usr.restrained() || anchored)
return
src.set_dir(turn(src.dir, 90))
//Goddamn copypaste from binary base class because atmospherics machinery API is not damn flexible
/obj/machinery/atmospherics/pipeturbine/get_neighbor_nodes_for_init()
return list(node1, node2)
/obj/machinery/atmospherics/pipeturbine/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
if(new_network.normal_members.Find(src))
return 0
new_network.normal_members += src
return null
/obj/machinery/atmospherics/pipeturbine/atmos_init()
if(node1 && node2)
return
var/node2_connect = turn(dir, -90)
var/node1_connect = turn(dir, 90)
for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect))
if(target.initialize_directions & get_dir(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))
node2 = target
break
/obj/machinery/atmospherics/pipeturbine/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/pipeturbine/return_network(obj/machinery/atmospherics/reference)
build_network()
if(reference==node1)
return network1
if(reference==node2)
return network2
return null
/obj/machinery/atmospherics/pipeturbine/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/pipeturbine/return_network_air(datum/pipe_network/reference)
var/list/results = list()
if(network1 == reference)
results += air_in
if(network2 == reference)
results += air_out
return results
/obj/machinery/atmospherics/pipeturbine/disconnect(obj/machinery/atmospherics/reference)
if(reference==node1)
qdel(network1)
node1 = null
else if(reference==node2)
qdel(network2)
node2 = null
return null
/obj/machinery/power/turbinemotor
@@ -236,54 +237,53 @@
var/kin_to_el_ratio = 0.1 //How much kinetic energy will be taken from turbine and converted into electricity
var/obj/machinery/atmospherics/pipeturbine/turbine
New()
..()
spawn(1)
updateConnection()
proc/updateConnection()
turbine = null
if(src.loc && anchored)
turbine = locate(/obj/machinery/atmospherics/pipeturbine) in get_step(src,dir)
if (turbine.stat & (BROKEN) || !turbine.anchored || turn(turbine.dir,180) != dir)
turbine = null
process()
/obj/machinery/power/turbinemotor/New()
..()
spawn(1)
updateConnection()
if(!turbine || !anchored || stat & (BROKEN))
return
var/power_generated = kin_to_el_ratio * turbine.kin_energy
turbine.kin_energy -= power_generated
add_avail(power_generated)
attackby(obj/item/weapon/W as obj, mob/user as mob)
if(W.is_wrench())
anchored = !anchored
playsound(src, W.usesound, 50, 1)
/obj/machinery/power/turbinemotor/proc/updateConnection()
turbine = null
if(src.loc && anchored)
turbine = locate(/obj/machinery/atmospherics/pipeturbine) in get_step(src,dir)
if (turbine.stat & (BROKEN) || !turbine.anchored || turn(turbine.dir,180) != dir)
turbine = null
to_chat(user, "<span class='notice'>You [anchored ? "secure" : "unsecure"] the bolts holding \the [src] to the floor.</span>")
updateConnection()
else
..()
verb/rotate_clock()
set category = "Object"
set name = "Rotate Motor Clockwise"
set src in view(1)
/obj/machinery/power/turbinemotor/process()
updateConnection()
if(!turbine || !anchored || stat & (BROKEN))
return
if (usr.stat || usr.restrained() || anchored)
return
var/power_generated = kin_to_el_ratio * turbine.kin_energy
turbine.kin_energy -= power_generated
add_avail(power_generated)
src.set_dir(turn(src.dir, -90))
/obj/machinery/power/turbinemotor/attackby(obj/item/weapon/W as obj, mob/user as mob)
if(W.is_wrench())
anchored = !anchored
playsound(src, W.usesound, 50, 1)
turbine = null
to_chat(user, "<span class='notice'>You [anchored ? "secure" : "unsecure"] the bolts holding \the [src] to the floor.</span>")
updateConnection()
else
..()
verb/rotate_anticlock()
set category = "Object"
set name = "Rotate Motor Counterclockwise"
set src in view(1)
/obj/machinery/power/turbinemotor/verb/rotate_clockwise()
set name = "Rotate Motor Clockwise"
set category = "Object"
set src in view(1)
if (usr.stat || usr.restrained() || anchored)
return
if (usr.stat || usr.restrained() || anchored)
return
src.set_dir(turn(src.dir, 90))
src.set_dir(turn(src.dir, 270))
/obj/machinery/power/turbinemotor/verb/rotate_counterclockwise()
set name = "Rotate Motor Counterclockwise"
set category = "Object"
set src in view(1)
if (usr.stat || usr.restrained() || anchored)
return
src.set_dir(turn(src.dir, 90))

View File

@@ -140,7 +140,7 @@ Thus, the two variables affect pump operation are set in New():
)
// update the ui if it exists, returns null if no ui is passed/found
ui = GLOB.nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
// the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
@@ -149,7 +149,7 @@ Thus, the two variables affect pump operation are set in New():
ui.open() // open the new ui window
ui.set_auto_update(1) // auto update every Master Controller tick
/obj/machinery/atmospherics/binary/pump/initialize()
/obj/machinery/atmospherics/binary/pump/Initialize()
. = ..()
if(frequency)
set_frequency(frequency)

View File

@@ -93,7 +93,7 @@
data = build_uidata()
ui = GLOB.nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "omni_filter.tmpl", "Omni Filter Control", 330, 330)
@@ -181,7 +181,7 @@
switch_filter(dir_flag(href_list["dir"]), mode_return_switch(new_filter))
update_icon()
GLOB.nanomanager.update_uis(src)
SSnanoui.update_uis(src)
return
/obj/machinery/atmospherics/omni/atmos_filter/proc/mode_return_switch(var/mode)

View File

@@ -131,7 +131,7 @@
data = build_uidata()
ui = GLOB.nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, "omni_mixer.tmpl", "Omni Mixer Control", 360, 330)
@@ -200,7 +200,7 @@
con_lock(dir_flag(href_list["dir"]))
update_icon()
GLOB.nanomanager.update_uis(src)
SSnanoui.update_uis(src)
return
/obj/machinery/atmospherics/omni/mixer/proc/switch_mode(var/port = NORTH, var/mode = ATM_NONE)

View File

@@ -105,7 +105,7 @@
return 1
/obj/machinery/atmospherics/trinary/atmos_filter/initialize()
/obj/machinery/atmospherics/trinary/atmos_filter/Initialize()
. = ..()
if(frequency)
set_frequency(frequency)

View File

@@ -302,7 +302,7 @@
/obj/machinery/atmospherics/tvalve/digital/initialize()
/obj/machinery/atmospherics/tvalve/digital/Initialize()
. = ..()
if(frequency)
set_frequency(frequency)

View File

@@ -83,7 +83,7 @@
data["gasTemperatureClass"] = temp_class
// update the ui if it exists, returns null if no ui is passed/found
ui = GLOB.nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if(!ui)
// the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm

View File

@@ -103,7 +103,7 @@
data["gasTemperatureClass"] = temp_class
// update the ui if it exists, returns null if no ui is passed/found
ui = GLOB.nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if(!ui)
// the ui does not exist, so we'll create a new() one
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm

View File

@@ -122,7 +122,7 @@
return 1
/obj/machinery/atmospherics/unary/outlet_injector/initialize()
/obj/machinery/atmospherics/unary/outlet_injector/Initialize()
. = ..()
if(frequency)
set_frequency(frequency)

View File

@@ -263,7 +263,7 @@
if(frequency)
radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA)
/obj/machinery/atmospherics/valve/digital/initialize()
/obj/machinery/atmospherics/valve/digital/Initialize()
. = ..()
if(frequency)
set_frequency(frequency)

View File

@@ -20,7 +20,7 @@ var/global/list/datum/pipe_network/pipe_networks = list() // TODO - Move into SS
gases.Cut() // Do not qdel the gases, we don't own them
return ..()
proc/process()
process()
//Equalize gases amongst pipe if called for
if(update)
update = 0
@@ -75,7 +75,7 @@ var/global/list/datum/pipe_network/pipe_networks = list() // TODO - Move into SS
for(var/datum/pipeline/line_member in line_members)
gases += line_member.air
for(var/datum/gas_mixture/air in gases)
volume += air.volume

View File

@@ -20,7 +20,7 @@ datum/pipeline
edges = null
. = ..()
proc/process()//This use to be called called from the pipe networks
process()//This use to be called called from the pipe networks
//Check to see if pressure is within acceptable limits
var/pressure = air.return_pressure()

View File

@@ -571,7 +571,7 @@ obj/machinery/atmospherics/mains_pipe/valve
else
icon_state = "[hide?"h":""]mvalve[open]"
initialize()
Initialize()
normalize_dir()
var/node1_dir
var/node2_dir
@@ -609,7 +609,7 @@ obj/machinery/atmospherics/mains_pipe/valve
open = 1
update_icon()
initialize()
Initialize()
return 1
@@ -668,7 +668,7 @@ obj/machinery/atmospherics/mains_pipe/valve
var/id = null
var/datum/radio_frequency/radio_connection
initialize()
Initialize()
..()
if(frequency)
set_frequency(frequency)

View File

@@ -22,9 +22,6 @@ mob/proc/airflow_stun()
mob/living/silicon/airflow_stun()
return
mob/living/simple_animal/slime/airflow_stun()
return
mob/living/carbon/human/airflow_stun()
if(shoes && (shoes.item_flags & NOSLIP))
to_chat(src, "<span class='notice'>Air suddenly rushes past you!</span>")

View File

@@ -1,15 +1,81 @@
#define MC_TICK_CHECK ( ( TICK_USAGE > GLOB.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 = GLOB.CURRENT_TICKLIMIT; var/split_tick_phases = ##phase_count
#define MC_TICK_CHECK ( ( TICK_USAGE > Master.current_ticklimit || src.state != SS_RUNNING ) ? pause() : 0 )
#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){\
GLOB.CURRENT_TICKLIMIT = ((original_tick_limit - world.tick_usage) / split_tick_phases) + world.tick_usage;\
--split_tick_phases;\
} else {\
GLOB.CURRENT_TICKLIMIT = original_tick_limit;\
}
if(split_tick_phases > 1){\
Master.current_ticklimit = ((original_tick_limit - TICK_USAGE) / split_tick_phases) + 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 MC_AVG_FAST_UP_SLOW_DOWN(average, current) (average > current ? MC_AVERAGE_SLOW(average, current) : MC_AVERAGE_FAST(average, current))
#define MC_AVG_SLOW_UP_FAST_DOWN(average, current) (average < current ? MC_AVERAGE_SLOW(average, current) : MC_AVERAGE_FAST(average, current))
#define NEW_SS_GLOBAL(varname) if(varname != src){if(istype(varname)){Recover();qdel(varname);}varname = src;}
#define START_PROCESSING(Processor, Datum) if (!(Datum.datum_flags & DF_ISPROCESSING)) {Datum.datum_flags |= DF_ISPROCESSING;Processor.processing += Datum}
#define STOP_PROCESSING(Processor, Datum) Datum.datum_flags &= ~DF_ISPROCESSING;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
#define SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/##X);\
/datum/controller/subsystem/##X/New(){\
NEW_SS_GLOBAL(SS##X);\
PreInit();\
}\
/datum/controller/subsystem/##X
#define PROCESSING_SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/processing/##X);\
/datum/controller/subsystem/processing/##X/New(){\
NEW_SS_GLOBAL(SS##X);\
PreInit();\
}\
/datum/controller/subsystem/processing/##X
// Boilerplate code for multi-step processors. See machines.dm for example use.
#define INTERNAL_PROCESS_STEP(this_step, initial_step, proc_to_call, cost_var, next_step)\
@@ -23,71 +89,3 @@ if(current_step == this_step || (initial_step && !resumed)) /* So we start at st
resumed = 0;\
current_step = next_step;\
}
// 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 MC_AVG_FAST_UP_SLOW_DOWN(average, current) (average > current ? MC_AVERAGE_SLOW(average, current) : MC_AVERAGE_FAST(average, current))
#define MC_AVG_SLOW_UP_FAST_DOWN(average, current) (average < current ? MC_AVERAGE_SLOW(average, current) : MC_AVERAGE_FAST(average, 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) GLOBAL_REAL(SS##X, /datum/controller/subsystem/##X);\
/datum/controller/subsystem/##X/New(){\
NEW_SS_GLOBAL(SS##X);\
PreInit();\
}\
/datum/controller/subsystem/##X
#define PROCESSING_SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/processing/##X);\
/datum/controller/subsystem/processing/##X/New(){\
NEW_SS_GLOBAL(SS##X);\
PreInit();\
}\
/datum/controller/subsystem/processing/##X

54
code/__defines/_lists.dm Normal file
View File

@@ -0,0 +1,54 @@
// 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 && !length(L)) 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(!length(L)) { L = null; } }
// Adds I to L, initalizing I if necessary
#define LAZYADD(L, I) if(!L) { L = list(); } L += I;
#define LAZYOR(L, I) if(!L) { L = list(); } L |= I;
#define LAZYFIND(L, V) L ? L.Find(V) : 0
// Reads I from L safely - Works with both associative and traditional lists.
#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= length(L) ? L[I] : null) : L[I]) : null)
// Turns LAZYINITLIST(L) L[K] = V into ... for associated lists
#define LAZYSET(L, K, V) if(!L) { L = list(); } L[K] = V;
// 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() )
#define reverseList(L) reverseRange(L.Copy())
// binary search sorted insert
// IN: Object to be inserted
// LIST: List to insert object into
// TYPECONT: The typepath of the contents of the list
// COMPARE: The variable on the objects to compare
#define BINARY_INSERT(IN, LIST, TYPECONT, COMPARE) \
var/__BIN_CTTL = length(LIST);\
if(!__BIN_CTTL) {\
LIST += IN;\
} else {\
var/__BIN_LEFT = 1;\
var/__BIN_RIGHT = __BIN_CTTL;\
var/__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\
var/##TYPECONT/__BIN_ITEM;\
while(__BIN_LEFT < __BIN_RIGHT) {\
__BIN_ITEM = LIST[__BIN_MID];\
if(__BIN_ITEM.##COMPARE <= IN.##COMPARE) {\
__BIN_LEFT = __BIN_MID + 1;\
} else {\
__BIN_RIGHT = __BIN_MID;\
};\
__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\
};\
__BIN_ITEM = LIST[__BIN_MID];\
__BIN_MID = __BIN_ITEM.##COMPARE > IN.##COMPARE ? __BIN_MID : __BIN_MID + 1;\
LIST.Insert(__BIN_MID, IN);\
}

View File

@@ -58,7 +58,10 @@ What is the naming convention for planes or layers?
#define ATMOS_LAYER 2.4 // Pipe-like atmos machinery that goes on the floor, like filters.
#define ABOVE_UTILITY 2.5 // Above stuff like pipes and wires
#define TURF_PLANE -45 // Turfs themselves, most flooring
#define ABOVE_TURF_LAYER 2.1 // Snow and wallmounted/floormounted equipment
#define WATER_FLOOR_LAYER 2.0 // The 'bottom' of water tiles.
#define UNDERWATER_LAYER 2.5 // Anything on this layer will render under the water layer.
#define WATER_LAYER 3.0 // Layer for water overlays.
#define ABOVE_TURF_LAYER 3.1 // Snow and wallmounted/floormounted equipment
#define DECAL_PLANE -44 // Permanent decals
#define DIRTY_PLANE -43 // Nonpermanent decals
#define BLOOD_PLANE -42 // Blood is really dirty, but we can do special stuff if we separate it
@@ -73,8 +76,8 @@ What is the naming convention for planes or layers?
#define ABOVE_JUNK_LAYER 3.1 // Things that want to be slightly above common objects
#define DOOR_CLOSED_LAYER 3.1 // Doors when closed
#define WINDOW_LAYER 3.2 // Windows
#define ABOVE_WINDOW_LAYER 3.25 //Above full tile windows so wall items are clickable
#define ON_WINDOW_LAYER 3.3 // Ontop of a window
#define ABOVE_WINDOW_LAYER 3.4 //Above full tile windows so wall items are clickable
// Mob planes
#define MOB_PLANE -25

View File

@@ -1,9 +1,15 @@
#define TICK_LIMIT_RUNNING 80
#define TICK_LIMIT_TO_RUN 70
#define TICK_LIMIT_MC 70
#define TICK_LIMIT_MC_INIT_DEFAULT 98
#define TICK_CHECK ( TICK_USAGE > GLOB.CURRENT_TICKLIMIT )
#define CHECK_TICK if TICK_CHECK stoplag()
#define TICK_USAGE world.tick_usage
#define TICK_CHECK ( TICK_USAGE > Master.current_ticklimit )
#define CHECK_TICK ( TICK_CHECK ? stoplag() : 0 )
#define TICK_CHECK_HIGH_PRIORITY ( TICK_USAGE > 95 )
#define CHECK_TICK_HIGH_PRIORITY ( TICK_CHECK_HIGH_PRIORITY? stoplag() : 0 )
#define UNTIL(X) while(!(X)) stoplag()

View File

@@ -7,6 +7,7 @@
#define CLONE "clone"
#define HALLOSS "halloss"
#define ELECTROCUTE "electrocute"
#define BIOACID "bioacid"
#define CUT "cut"
#define BRUISE "bruise"

19
code/__defines/flags.dm Normal file
View File

@@ -0,0 +1,19 @@
//MARK ALL FLAG CHANGES IN _globals/bitfields.dm!
//All flags should go in here if possible.
#define ALL (~0) //For convenience.
#define NONE 0
//for convenience
#define ENABLE_BITFIELD(variable, flag) (variable |= (flag))
#define DISABLE_BITFIELD(variable, flag) (variable &= ~(flag))
#define CHECK_BITFIELD(variable, flag) (variable & flag)
//check if all bitflags specified are present
#define CHECK_MULTIPLE_BITFIELDS(flagvar, flags) ((flagvar & (flags)) == flags)
GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768))
// datum_flags
#define DF_VAR_EDITED (1<<0)
#define DF_ISPROCESSING (1<<1)

View File

@@ -45,8 +45,6 @@ var/list/be_special_flags = list(
"pAI" = BE_PAI
)
#define IS_MODE_COMPILED(MODE) (ispath(text2path("/datum/game_mode/"+(MODE))))
// Antagonist datum flags.
#define ANTAG_OVERRIDE_JOB 0x1 // Assigned job is set to MODE when spawning.

View File

@@ -0,0 +1,53 @@
#define isdatum(D) istype(D, /datum)
#define isweakref(A) istype(A, /weakref)
#define islist(D) istype(D, /list)
//---------------
#define isatom(D) istype(D, /atom)
//---------------
//#define isobj(D) istype(D, /obj) //Built in
#define isitem(D) istype(D, /obj/item)
#define isairlock(A) istype(A, /obj/machinery/door/airlock)
#define isorgan(A) istype(A, /obj/item/organ/external)
//---------------
//#define isarea(D) istype(D, /area) //Built in
//---------------
//#define ismob(D) istype(D, /mob) //Built in
#define isliving(A) istype(A, /mob/living)
#define isobserver(A) istype(A, /mob/observer/dead)
#define isEye(A) istype(A, /mob/observer/eye)
#define isnewplayer(A) istype(A, /mob/new_player)
#define isanimal(A) istype(A, /mob/living/simple_mob)
#define ismouse(A) istype(A, /mob/living/simple_mob/animal/passive/mouse)
#define iscorgi(A) istype(A, /mob/living/simple_mob/animal/passive/dog/corgi)
#define isslime(A) istype(A, /mob/living/simple_mob/slime)
#define isxeno(A) istype(A, /mob/living/simple_mob/animal/space/alien)
#define iscarbon(A) istype(A, /mob/living/carbon)
#define isalien(A) istype(A, /mob/living/carbon/alien)
#define isbrain(A) istype(A, /mob/living/carbon/brain)
#define ishuman(A) istype(A, /mob/living/carbon/human)
#define issilicon(A) istype(A, /mob/living/silicon)
#define isAI(A) istype(A, /mob/living/silicon/ai)
#define isrobot(A) istype(A, /mob/living/silicon/robot)
#define ispAI(A) istype(A, /mob/living/silicon/pai)
#define isbot(A) istype(A, /mob/living/bot)
#define isvoice(A) istype(A, /mob/living/voice)
//---------------
//#define isturf(D) istype(D, /turf) //Built in
#define isopenspace(A) istype(A, /turf/simulated/open)

View File

@@ -53,11 +53,10 @@
//Flags for items (equipment) - Used in /obj/item/var/item_flags
#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.
#define BLOCK_GAS_SMOKE_EFFECT 0x10 // Blocks the effect that chemical clouds would have on a mob -- glasses, mask and helmets ONLY! (NOTE: flag shared with ONESIZEFITSALL)
#define FLEXIBLEMATERIAL 0x20 // At the moment, masks with this flag will not prevent eating even if they are covering your face.
#define AIRTIGHT 0x2 // Functions with internals.
#define NOSLIP 0x4 // Prevents from slipping on wet floors, in space, etc.
#define BLOCK_GAS_SMOKE_EFFECT 0x8 // Blocks the effect that chemical clouds would have on a mob -- glasses, mask and helmets ONLY! (NOTE: flag shared with ONESIZEFITSALL)
#define FLEXIBLEMATERIAL 0x10 // At the moment, masks with this flag will not prevent eating even if they are covering your face.
// Flags for pass_flags. - Used in /atom/var/pass_flags
#define PASSTABLE 0x1

View File

@@ -106,26 +106,15 @@ var/list/restricted_camera_networks = list(NETWORK_ERT,NETWORK_MERCENARY,"Secret
#define ATMOS_DEFAULT_VOLUME_MIXER 200 // L.
#define ATMOS_DEFAULT_VOLUME_PIPE 70 // L.
//wIP - PORT ALL OF THESE TO SUBSYSTEMS AND GET RID OF THE WHOLE LIST PROCESS THING
// Fancy-pants START/STOP_PROCESSING() macros that lets us custom define what the list is.
#define START_PROCESSING_IN_LIST(DATUM, LIST) \
if (DATUM.is_processing) {\
if(DATUM.is_processing != #LIST)\
{\
crash_with("Failed to start processing. [log_info_line(DATUM)] is already being processed by [DATUM.is_processing] but queue attempt occured on [#LIST]."); \
}\
} else {\
DATUM.is_processing = #LIST;\
if (!(DATUM.datum_flags & DF_ISPROCESSING)) {\
LIST += DATUM;\
DATUM.datum_flags |= DF_ISPROCESSING\
}
#define STOP_PROCESSING_IN_LIST(DATUM, LIST) \
if(DATUM.is_processing) {\
if(LIST.Remove(DATUM)) {\
DATUM.is_processing = null;\
} else {\
crash_with("Failed to stop processing. [log_info_line(DATUM)] is being processed by [is_processing] and not found in SSmachines.[#LIST]"); \
}\
}
#define STOP_PROCESSING_IN_LIST(DATUM, LIST) LIST.Remove(DATUM);DATUM.datum_flags &= ~DF_ISPROCESSING
// Note - I would prefer these be defined machines.dm, but some are used prior in file order. ~Leshana
#define START_MACHINE_PROCESSING(Datum) START_PROCESSING_IN_LIST(Datum, global.processing_machines)

View File

@@ -1,18 +1,228 @@
// Credits to Nickr5 for the useful procs I've taken from his library resource.
// This file is quadruple wrapped for your pleasure
// (
#define NUM_E 2.71828183
#define M_PI (3.14159265)
#define INFINITY (1.#INF) //closer then enough
#define SHORT_REAL_LIMIT 16777216
//"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(TICK_USAGE-starting_tickusage))
#define TICK_USAGE_TO_MS(starting_tickusage) (TICK_DELTA_TO_MS(world.tick_usage - starting_tickusage))
#define PERCENT(val) (round((val)*100, 0.1))
#define CLAMP01(x) (CLAMP(x, 0, 1))
//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 )
#define SHORT_REAL_LIMIT 16777216 // 2^24 - Maximum integer that can be exactly represented in a float (BYOND num var)
#define SIGN(x) ( (x)!=0 ? (x) / abs(x) : 0 )
#define CEILING(x, y) ( -round(-(x) / (y)) * (y) )
// round() acts like floor(x, 1) by default but can't handle other values
#define FLOOR(x, y) ( round((x) / (y)) * (y) )
// Check if a BYOND dir var is a cardinal direction (power of two)
#define CLAMP(CLVALUE,CLMIN,CLMAX) ( max( (CLMIN), min((CLVALUE), (CLMAX)) ) )
// Similar to clamp but the bottom rolls around to the top and vice versa. min is inclusive, max is exclusive
#define WRAP(val, min, max) ( min == max ? min : (val) - (round(((val) - (min))/((max) - (min))) * ((max) - (min))) )
// Real modulus that handles decimals
#define MODULUS(x, y) ( (x) - (y) * round((x) / (y)) )
// Tangent
#define TAN(x) (sin(x) / cos(x))
// Cotangent
#define COT(x) (1 / TAN(x))
// Secant
#define SEC(x) (1 / cos(x))
// Cosecant
#define CSC(x) (1 / sin(x))
#define ATAN2(x, y) ( !(x) && !(y) ? 0 : (y) >= 0 ? arccos((x) / sqrt((x)*(x) + (y)*(y))) : -arccos((x) / sqrt((x)*(x) + (y)*(y))) )
// Greatest Common Divisor - Euclid's algorithm
/proc/GCD(a, b)
return b ? GCD(b, (a) % (b)) : a
// Least Common Multiple
#define LCM(a, b) (abs(a) / GCD(a, b) * abs(b))
#define IS_CARDINAL(x) ((x & (x - 1)) == 0)
#define INVERSE(x) ( 1/(x) )
// Used for calculating the radioactive strength falloff
#define INVERSE_SQUARE(initial_strength,cur_distance,initial_distance) ( (initial_strength)*((initial_distance)**2/(cur_distance)**2) )
#define ISABOUTEQUAL(a, b, deviation) (deviation ? abs((a) - (b)) <= deviation : abs((a) - (b)) <= 0.1)
#define ISEVEN(x) (x % 2 == 0)
#define ISODD(x) (x % 2 != 0)
// Returns true if val is from min to max, inclusive.
#define ISINRANGE(val, min, max) (min <= val && val <= max)
// Same as above, exclusive.
#define ISINRANGE_EX(val, min, max) (min < val && val > max)
#define ISINTEGER(x) (round(x) == x)
#define ISMULTIPLE(x, y) ((x) % (y) == 0)
// Performs a linear interpolation between a and b.
// Note that amount=0 returns a, amount=1 returns b, and
// amount=0.5 returns the mean of a and b.
#define LERP(a, b, amount) ( amount ? ((a) + ((b) - (a)) * (amount)) : a )
// Returns the nth root of x.
#define ROOT(n, x) ((x) ** (1 / (n)))
/proc/Mean(...)
var/sum = 0
for(var/val in args)
sum += val
return sum / args.len
// The quadratic formula. Returns a list with the solutions, or an empty list
// if they are imaginary.
/proc/SolveQuadratic(a, b, c)
ASSERT(a)
. = list()
var/d = b*b - 4 * a * c
var/bottom = 2 * a
// Return if the roots are imaginary.
if(d < 0)
return
var/root = sqrt(d)
. += (-b + root) / bottom
// If discriminant == 0, there would be two roots at the same position.
if(!d)
return
. += (-b - root) / bottom
// 180 / Pi ~ 57.2957795
#define TODEGREES(radians) ((radians) * 57.2957795)
// Pi / 180 ~ 0.0174532925
#define TORADIANS(degrees) ((degrees) * 0.0174532925)
// Will filter out extra rotations and negative rotations
// E.g: 540 becomes 180. -180 becomes 180.
#define SIMPLIFY_DEGREES(degrees) (MODULUS((degrees), 360))
#define GET_ANGLE_OF_INCIDENCE(face, input) (MODULUS((face) - (input), 360))
//Finds the shortest angle that angle A has to change to get to angle B. Aka, whether to move clock or counterclockwise.
/proc/closer_angle_difference(a, b)
if(!isnum(a) || !isnum(b))
return
a = SIMPLIFY_DEGREES(a)
b = SIMPLIFY_DEGREES(b)
var/inc = b - a
if(inc < 0)
inc += 360
var/dec = a - b
if(dec < 0)
dec += 360
. = inc > dec? -dec : inc
//A logarithm that converts an integer to a number scaled between 0 and 1.
//Currently, this is used for hydroponics-produce sprite transforming, but could be useful for other transform functions.
#define TRANSFORM_USING_VARIABLE(input, max) ( sin((90*(input))/(max))**2 )
//converts a uniform distributed random number into a normal distributed one
//since this method produces two random numbers, one is saved for subsequent calls
//(making the cost negligble for every second call)
//This will return +/- decimals, situated about mean with standard deviation stddev
//68% chance that the number is within 1stddev
//95% chance that the number is within 2stddev
//98% chance that the number is within 3stddev...etc
#define ACCURACY 10000
/proc/gaussian(mean, stddev)
var/static/gaussian_next
var/R1;var/R2;var/working
if(gaussian_next != null)
R1 = gaussian_next
gaussian_next = null
else
do
R1 = rand(-ACCURACY,ACCURACY)/ACCURACY
R2 = rand(-ACCURACY,ACCURACY)/ACCURACY
working = R1*R1 + R2*R2
while(working >= 1 || working==0)
working = sqrt(-2 * log(working) / working)
R1 *= working
gaussian_next = R2 * working
return (mean + stddev * R1)
#undef ACCURACY
/proc/get_turf_in_angle(angle, turf/starting, increments)
var/pixel_x = 0
var/pixel_y = 0
for(var/i in 1 to increments)
pixel_x += sin(angle)+16*sin(angle)*2
pixel_y += cos(angle)+16*cos(angle)*2
var/new_x = starting.x
var/new_y = starting.y
while(pixel_x > 16)
pixel_x -= 32
new_x++
while(pixel_x < -16)
pixel_x += 32
new_x--
while(pixel_y > 16)
pixel_y -= 32
new_y++
while(pixel_y < -16)
pixel_y += 32
new_y--
new_x = CLAMP(new_x, 0, world.maxx)
new_y = CLAMP(new_y, 0, world.maxy)
return locate(new_x, new_y, starting.z)
// Returns a list where [1] is all x values and [2] is all y values that overlap between the given pair of rectangles
/proc/get_overlap(x1, y1, x2, y2, x3, y3, x4, y4)
var/list/region_x1 = list()
var/list/region_y1 = list()
var/list/region_x2 = list()
var/list/region_y2 = list()
// These loops create loops filled with x/y values that the boundaries inhabit
// ex: list(5, 6, 7, 8, 9)
for(var/i in min(x1, x2) to max(x1, x2))
region_x1["[i]"] = TRUE
for(var/i in min(y1, y2) to max(y1, y2))
region_y1["[i]"] = TRUE
for(var/i in min(x3, x4) to max(x3, x4))
region_x2["[i]"] = TRUE
for(var/i in min(y3, y4) to max(y3, y4))
region_y2["[i]"] = TRUE
return list(region_x1 & region_x2, region_y1 & region_y2)
// )
#define RAND_F(LOW, HIGH) (rand()*(HIGH-LOW) + LOW)
#define SQUARE(x) (x*x)
//Vector Algebra
#define SQUAREDNORM(x, y) (x*x+y*y)
#define NORM(x, y) (sqrt(SQUAREDNORM(x,y)))
#define ISPOWEROFTWO(x) ((x & (x - 1)) == 0)
#define ROUNDUPTOPOWEROFTWO(x) (2 ** -round(-log(2,x)))
#define DEFAULT(a, b) (a? a : b)

View File

@@ -1,10 +1,13 @@
// Math constants.
#define M_PI 3.14159265
#define R_IDEAL_GAS_EQUATION 8.31 // kPa*L/(K*mol).
#define ONE_ATMOSPHERE 101.325 // kPa.
#define IDEAL_GAS_ENTROPY_CONSTANT 1164 // (mol^3 * s^3) / (kg^3 * L).
#define T0C 273.15 // 0.0 degrees celcius
#define T20C 293.15 // 20.0 degrees celcius
#define TCMB 2.7 // -270.3 degrees celcius
#define TN60C 213.15 // -60 degrees celcius
// Radiation constants.
#define STEFAN_BOLTZMANN_CONSTANT 5.6704e-8 // W/(m^2*K^4).
#define COSMIC_RADIATION_TEMPERATURE 3.15 // K.
@@ -15,18 +18,7 @@
#define RADIATOR_EXPOSED_SURFACE_AREA_RATIO 0.04 // (3 cm + 100 cm * sin(3deg))/(2*(3+100 cm)). Unitless ratio.
#define HUMAN_EXPOSED_SURFACE_AREA 5.2 //m^2, surface area of 1.7m (H) x 0.46m (D) cylinder
#define T0C 273.15 // 0.0 degrees celcius
#define T20C 293.15 // 20.0 degrees celcius
#define TCMB 2.7 // -270.3 degrees celcius
#define TN60C 213.15 // -60 degrees celcius
#define CLAMP01(x) max(0, min(1, x))
#define QUANTIZE(variable) (round(variable,0.0001))
#define INFINITY 1.#INF
#define TICKS_IN_DAY 24*60*60*10
#define TICKS_IN_SECOND 10
#define SIMPLE_SIGN(X) ((X) < 0 ? -1 : 1)
#define SIGN(X) ((X) ? SIMPLE_SIGN(X) : 0)
#define TICKS_IN_DAY (TICKS_IN_SECOND * 60 * 60 * 24)
#define TICKS_IN_SECOND (world.fps)

View File

@@ -258,7 +258,7 @@
// If the GLOB system is ever ported, you can change this macro in one place and have less work to do than you otherwise would.
#define GLOBAL_LIST_BOILERPLATE(LIST_NAME, PATH)\
var/global/list/##LIST_NAME = list();\
##PATH/initialize(mapload, ...)\
##PATH/Initialize(mapload, ...)\
{\
##LIST_NAME += src;\
return ..();\
@@ -302,4 +302,10 @@ var/global/list/##LIST_NAME = list();\
#define RCD_SHEETS_PER_MATTER_UNIT 4 // Each physical material sheet is worth four matter units.
#define RCD_MAX_CAPACITY 30 * RCD_SHEETS_PER_MATTER_UNIT
#define RCD_MAX_CAPACITY 30 * RCD_SHEETS_PER_MATTER_UNIT
// Radiation 'levels'. Used for the geiger counter, for visuals and sound. They are in different files so this goes here.
#define RAD_LEVEL_LOW 0.01 // Around the level at which radiation starts to become harmful
#define RAD_LEVEL_MODERATE 10
#define RAD_LEVEL_HIGH 25
#define RAD_LEVEL_VERY_HIGH 50

View File

@@ -27,6 +27,9 @@
#define BORGXRAY 0x4
#define BORGMATERIAL 8
#define STANCE_ATTACK 11 // Backwards compatability
#define STANCE_ATTACKING 12 // Ditto
/*
#define STANCE_IDLE 1 // Looking for targets if hostile. Does idle wandering.
#define STANCE_ALERT 2 // Bears
#define STANCE_ATTACK 3 // Attempting to get into attack position
@@ -34,6 +37,20 @@
#define STANCE_TIRED 5 // Bears
#define STANCE_FOLLOW 6 // Following somone
#define STANCE_BUSY 7 // Do nothing on life ticks (Other code is running)
*/
#define STANCE_SLEEP 0 // Doing (almost) nothing, to save on CPU because nobody is around to notice or the mob died.
#define STANCE_IDLE 1 // The more or less default state. Wanders around, looks for baddies, and spouts one-liners.
#define STANCE_ALERT 2 // A baddie is visible but not too close, and essentially we tell them to go away or die.
#define STANCE_APPROACH 3 // Attempting to get into range to attack them.
#define STANCE_FIGHT 4 // Actually fighting, with melee or ranged.
#define STANCE_BLINDFIGHT 5 // Fighting something that cannot be seen by the mob, from invisibility or out of sight.
#define STANCE_REPOSITION 6 // Relocating to a better position while in combat. Also used when moving away from a danger like grenades.
#define STANCE_MOVE 7 // Similar to above but for out of combat. If a baddie is seen, they'll cancel and fight them.
#define STANCE_FOLLOW 8 // Following somone, without trying to murder them.
#define STANCE_FLEE 9 // Run away from the target because they're too spooky/we're dying/some other reason.
#define STANCE_DISABLED 10 // Used when the holder is afflicted with certain status effects, such as stuns or confusion.
#define STANCES_COMBAT list(STANCE_ALERT, STANCE_APPROACH, STANCE_FIGHT, STANCE_BLINDFIGHT, STANCE_REPOSITION)
#define LEFT 0x1
#define RIGHT 0x2
@@ -279,11 +296,34 @@
#define SA_ROBOTIC 3
#define SA_HUMANOID 4
// More refined version of SA_* ""intelligence"" seperators.
// Now includes bitflags, so to target two classes you just do 'MOB_CLASS_ANIMAL|MOB_CLASS_HUMANOID'
#define MOB_CLASS_NONE 0 // Default value, and used to invert for _ALL.
#define MOB_CLASS_PLANT 1 // Unused at the moment.
#define MOB_CLASS_ANIMAL 2 // Animals and beasts like spiders, saviks, and bears.
#define MOB_CLASS_HUMANOID 4 // Non-robotic humanoids, including /simple_mob and /carbon/humans and their alien variants.
#define MOB_CLASS_SYNTHETIC 8 // Silicons, mechanical simple mobs, FBPs, and anything else that would pass is_synthetic()
#define MOB_CLASS_SLIME 16 // Everyone's favorite xenobiology specimen (and maybe prometheans?).
#define MOB_CLASS_ABERRATION 32 // Weird shit.
#define MOB_CLASS_DEMONIC 64 // Cult stuff.
#define MOB_CLASS_BOSS 128 // Future megafauna hopefully someday.
#define MOB_CLASS_ILLUSION 256 // Fake mobs, e.g. Technomancer illusions.
#define MOB_CLASS_PHOTONIC 512 // Holographic mobs like holocarp, similar to _ILLUSION, but that make no attempt to hide their true nature.
#define MOB_CLASS_ALL (~MOB_CLASS_NONE)
// 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.
// Threshold for mobs being able to damage things like airlocks or reinforced glass windows.
// If the damage is below this, nothing will happen besides a message saying that the attack was ineffective.
// Generally, this was not a define but was commonly set to 10, however 10 may be too low now since simple_mobs now attack twice as fast,
// at half damage compared to the old mob system, meaning mobs who could hurt structures may not be able to now, so now it is 5.
#define STRUCTURE_MIN_DAMAGE_THRESHOLD 5
//Vision flags, for dealing with plane visibility
#define VIS_FULLBRIGHT 1
#define VIS_LIGHTING 2

View File

@@ -1,6 +1,5 @@
/*
* Defines used for miscellaneous objects.
* Currently only the home of the Multiool.
*/
// Multitool Mode Defines.
@@ -8,3 +7,25 @@
#define MULTITOOL_MODE_STANDARD "Standard"
#define MULTITOOL_MODE_INTCIRCUITS "Modular Wiring"
#define MULTITOOL_MODE_DOORHACK "Advanced Jacking"
// Identity system defines.
#define IDENTITY_UNKNOWN 0 // Nothing is known so far.
#define IDENTITY_PROPERTIES 1 // Basic function of the item, and amount of charges available if it uses them.
#define IDENTITY_QUALITY 2 // Blessed/Uncursed/Cursed status. Some things don't use this.
#define IDENTITY_FULL IDENTITY_PROPERTIES|IDENTITY_QUALITY // Know everything.
#define IDENTITY_TYPE_NONE "generic"
#define IDENTITY_TYPE_TECH "technological"
#define IDENTITY_TYPE_CHEMICAL "chemical"
// Roguelike object quality defines. Not used at the moment.
#define ROGUELIKE_ITEM_ARTIFACT 2 // Cannot degrade, very rare.
#define ROGUELIKE_ITEM_BLESSED 1 // Better than average and resists cursing.
#define ROGUELIKE_ITEM_UNCURSED 0 // Normal.
#define ROGUELIKE_ITEM_CURSED -1 // Does bad things, clothing cannot be taken off.
// Consistant messages for certain events.
// Consistancy is import in order to avoid giving too much information away when using an
// unidentified object due to a typo or some other unique difference in message output.
#define ROGUELIKE_MESSAGE_NOTHING "Nothing happens."
#define ROGUELIKE_MESSAGE_UNKNOWN "Something happened, but you're not sure what."

View File

@@ -9,6 +9,9 @@
#define WEATHER_WINDY "windy"
#define WEATHER_HOT "hot"
#define WEATHER_BLOOD_MOON "blood moon" // For admin fun or cult later on.
#define WEATHER_EMBERFALL "emberfall" // More adminbuse, from TG. Harmless.
#define WEATHER_ASH_STORM "ash storm" // Ripped from TG, like the above. Less harmless.
#define WEATHER_FALLOUT "fallout" // Modified emberfall, actually harmful. Admin only.
#define MOON_PHASE_NEW_MOON "new moon"
#define MOON_PHASE_WAXING_CRESCENT "waxing crescent"

View File

@@ -22,7 +22,13 @@
#define QDELETED(X) (!X || X.gc_destroyed)
#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
//Qdel helper macros.
#define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE)
#define QDEL_IN_CLIENT_TIME(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE | TIMER_CLIENT_TIME)
#define QDEL_NULL(item) qdel(item); item = null
#define QDEL_LIST_NULL(x) if(x) { for(var/y in x) { qdel(y) } ; x = null }
#define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); }
#define QDEL_LIST_IN(L, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/______qdel_list_wrapper, L), time, TIMER_STOPPABLE)
#define QDEL_LIST_ASSOC(L) if(L) { for(var/I in L) { qdel(L[I]); qdel(I); } L.Cut(); }
#define QDEL_LIST_ASSOC_VAL(L) if(L) { for(var/I in L) qdel(L[I]); L.Cut(); }

View File

@@ -51,6 +51,7 @@
#define LANGUAGE_EVENT1 "Occursus"
#define LANGUAGE_AKHANI "Akhani"
#define LANGUAGE_ALAI "Alai"
#define LANGUAGE_GIBBERISH "Babel"
// Language flags.
#define WHITELISTED 1 // Language is available if the speaker is whitelisted.

View File

@@ -1,18 +1,23 @@
//Timing subsystem
//Don't run if there is an identical unique timer active
#define TIMER_UNIQUE 0x1
//if the arguments to addtimer are the same as an existing timer, it doesn't create a new timer, and returns the id of the existing timer
#define TIMER_UNIQUE (1<<0)
//For unique timers: Replace the old timer rather then not start this one
#define TIMER_OVERRIDE 0x2
#define TIMER_OVERRIDE (1<<1)
//Timing should be based on how timing progresses on clients, not the sever.
// tracking this is more expensive,
// should only be used in conjuction with things that have to progress client side, such as animate() or sound()
#define TIMER_CLIENT_TIME 0x4
#define TIMER_CLIENT_TIME (1<<2)
//Timer can be stopped using deltimer()
#define TIMER_STOPPABLE 0x8
#define TIMER_STOPPABLE (1<<3)
//To be used with TIMER_UNIQUE
//prevents distinguishing identical timers with the wait variable
#define TIMER_NO_HASH_WAIT 0x10
#define TIMER_NO_INVOKE_WARNING 600 //number of byond ticks that are allowed to pass before the timer subsystem thinks it hung on something
#define TIMER_NO_HASH_WAIT (1<<4)
//Loops the timer repeatedly until qdeleted
//In most cases you want a subsystem instead
#define TIMER_LOOP (1<<5)
#define TIMER_ID_NULL -1
#define INITIALIZATION_INSSATOMS 0 //New should not call Initialize
#define INITIALIZATION_INNEW_MAPLOAD 1 //New should call Initialize(TRUE)
@@ -47,19 +52,22 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G
// Subsystem init_order, from highest priority to lowest priority
// Subsystems shutdown in the reverse of the order they initialize in
// The numbers just define the ordering, they are meaningless otherwise.
#define INIT_ORDER_MAPPING 20 // VOREStation Edit
#define INIT_ORDER_DECALS 16
#define INIT_ORDER_ATOMS 15
#define INIT_ORDER_MACHINES 10
#define INIT_ORDER_SHUTTLES 3
#define INIT_ORDER_DEFAULT 0
#define INIT_ORDER_LIGHTING 0
#define INIT_ORDER_AIR -1
#define INIT_ORDER_PLANETS -4
#define INIT_ORDER_HOLOMAPS -5
#define INIT_ORDER_OVERLAY -6
#define INIT_ORDER_XENOARCH -20
#define INIT_ORDER_CIRCUIT -21
#define INIT_ORDER_CHEMISTRY 18
#define INIT_ORDER_MAPPING 17
#define INIT_ORDER_DECALS 16
#define INIT_ORDER_ATOMS 15
#define INIT_ORDER_MACHINES 10
#define INIT_ORDER_SHUTTLES 3
#define INIT_ORDER_TIMER 1
#define INIT_ORDER_DEFAULT 0
#define INIT_ORDER_LIGHTING 0
#define INIT_ORDER_AIR -1
#define INIT_ORDER_PLANETS -4
#define INIT_ORDER_HOLOMAPS -5
#define INIT_ORDER_OVERLAY -6
#define INIT_ORDER_XENOARCH -20
#define INIT_ORDER_CIRCUIT -21
#define INIT_ORDER_AI -22
// Subsystem fire priority, from lowest to highest priority
@@ -67,9 +75,12 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G
#define FIRE_PRIORITY_SHUTTLES 5
#define FIRE_PRIORITY_ORBIT 8
#define FIRE_PRIORITY_VOTE 9
#define FIRE_PRIORITY_AI 10
#define FIRE_PRIORITY_GARBAGE 15
#define FIRE_PRIORITY_AIRFLOW 30
#define FIRE_PRIORITY_AIR 35
#define FIRE_PRIORITY_OBJ 40
#define FIRE_PRIORITY_PROCESS 45
#define FIRE_PRIORITY_DEFAULT 50
#define FIRE_PRIORITY_PLANETS 75
#define FIRE_PRIORITY_MACHINES 100

46
code/__defines/vv.dm Normal file
View File

@@ -0,0 +1,46 @@
#define VV_NUM "Number"
#define VV_TEXT "Text"
#define VV_MESSAGE "Mutiline Text"
#define VV_ICON "Icon"
#define VV_ATOM_REFERENCE "Atom Reference"
#define VV_DATUM_REFERENCE "Datum Reference"
#define VV_MOB_REFERENCE "Mob Reference"
#define VV_CLIENT "Client"
#define VV_ATOM_TYPE "Atom Typepath"
#define VV_DATUM_TYPE "Datum Typepath"
#define VV_TYPE "Custom Typepath"
#define VV_FILE "File"
#define VV_LIST "List"
#define VV_NEW_ATOM "New Atom"
#define VV_NEW_DATUM "New Datum"
#define VV_NEW_TYPE "New Custom Typepath"
#define VV_NEW_LIST "New List"
#define VV_NULL "NULL"
#define VV_RESTORE_DEFAULT "Restore to Default"
#define VV_MARKED_DATUM "Marked Datum"
#define VV_BITFIELD "Bitfield"
#define VV_MSG_MARKED "<br><font size='1' color='red'><b>Marked Object</b></font>"
#define VV_MSG_EDITED "<br><font size='1' color='red'><b>Var Edited</b></font>"
#define VV_MSG_DELETED "<br><font size='1' color='red'><b>Deleted</b></font>"
#define VV_NORMAL_LIST_NO_EXPAND_THRESHOLD 50
#define VV_SPECIAL_LIST_NO_EXPAND_THRESHOLD 150
#define IS_VALID_ASSOC_KEY(V) (istext(V) || isdatum(V) || islist(V))
//Helpers for vv_get_dropdown()
#define VV_DROPDOWN_OPTION(href_key, name) . += "<option value='?_src_=vars;[href_key]=TRUE;target=\ref[src]'>[name]</option>"
//Helpers for vv_do_topic(list/href_list)
#define IF_VV_OPTION(href_key) if(href_list[href_key])
// /datum
#define VV_HK_DELETE "delete"
#define VV_HK_EXPOSE "expose"
#define VV_HK_CALLPROC "proc_call"
#define VV_HK_MARK "mark"
// /atom
#define VV_HK_ATOM_EXPLODE "turf_explode"
#define VV_HK_ATOM_EMP "turf_emp"

View File

@@ -0,0 +1,7 @@
GLOBAL_LIST_INIT(bitfields, list(
"datum_flags" = list(
"DF_VAR_EDITED" = DF_VAR_EDITED,
"DF_ISPROCESSING" = DF_ISPROCESSING
)
))

View File

@@ -1,4 +1,8 @@
GLOBAL_LIST_EMPTY(all_observable_events)
GLOBAL_LIST_EMPTY(error_last_seen)
GLOBAL_LIST_EMPTY(error_cooldown)
GLOBAL_DATUM_INIT(all_observable_events, /datum/all_observable_events, new) // This is a datum. It is not a list.
GLOBAL_DATUM_INIT(destroyed_event, /decl/observ/destroyed, new())
GLOBAL_VAR_INIT(timezoneOffset, 0) // The difference betwen midnight (of the host computer) and 0 world.ticks.

View File

@@ -2,4 +2,6 @@ GLOBAL_LIST_EMPTY(admins) //all clients whom are admins
GLOBAL_PROTECT(admins)
GLOBAL_LIST_EMPTY(deadmins) //all ckeys who have used the de-admin verb.
GLOBAL_LIST_EMPTY(stealthminID)
GLOBAL_LIST_EMPTY(directory) //all ckeys with associated client
GLOBAL_LIST_EMPTY(directory) //all ckeys with associated client
GLOBAL_LIST_EMPTY(clients)
GLOBAL_LIST_EMPTY(players_by_zlevel)

View File

@@ -432,7 +432,7 @@ proc/isInSight(var/atom/A, var/atom/B)
/proc/Show2Group4Delay(obj/O, list/group, delay=0)
if(!isobj(O)) return
if(!group) group = clients
if(!group) group = GLOB.clients
for(var/client/C in group)
C.screen += O
if(delay)

View File

@@ -1,6 +1,4 @@
var/list/clients = list() //list of all clients
var/list/admins = list() //list of all clients whom are admins
var/list/directory = list() //list of all ckeys with associated client
//Since it didn't really belong in any other category, I'm putting this here
//This is for procs to replace all the goddamn 'in world's that are chilling around the code
@@ -15,8 +13,6 @@ var/global/list/dead_mob_list = list() //List of all dead mobs, including cli
var/global/list/listening_objects = list() //List of all objects which care about receiving messages (communicators, radios, etc)
var/global/list/cable_list = list() //Index for all cables, so that powernets don't have to look through the entire world all the time
var/global/list/chemical_reactions_list //list of all /datum/chemical_reaction datums. Used during chemical reactions
var/global/list/chemical_reagents_list //list of all /datum/reagent datums indexed by reagent id. Used by chemistry stuff
var/global/list/landmarks_list = list() //list of all landmarks created
var/global/list/surgery_steps = list() //list of all surgery steps |BS12
var/global/list/side_effects = list() //list of all medical sideeffects types by thier names |BS12

View File

@@ -94,7 +94,7 @@ var/global/list/struggle_sounds = list(
"Squish4" = 'sound/vore/squish4.ogg')
var/global/list/global_egg_types = list(
var/global/list/global_vore_egg_types = list(
"Unathi" = UNATHI_EGG,
"Tajaran" = TAJARAN_EGG,
"Akula" = AKULA_EGG,
@@ -107,7 +107,7 @@ var/global/list/global_egg_types = list(
"Xenochimera" = XENOCHIMERA_EGG,
"Xenomorph" = XENOMORPH_EGG)
var/global/list/tf_egg_types = list(
var/global/list/tf_vore_egg_types = list(
"Unathi" = /obj/structure/closet/secure_closet/egg/unathi,
"Tajara" = /obj/structure/closet/secure_closet/egg/tajaran,
"Akula" = /obj/structure/closet/secure_closet/egg/shark,

View File

@@ -627,22 +627,44 @@ proc/ColorTone(rgb, tone)
if(gray <= tone_gray) return BlendRGB("#000000", tone, gray/(tone_gray || 1))
else return BlendRGB(tone, "#ffffff", (gray-tone_gray)/((255-tone_gray) || 1))
/*
Get flat icon by DarkCampainger. As it says on the tin, will return an icon with all the overlays
as a single icon. Useful for when you want to manipulate an icon via the above as overlays are not normally included.
The _flatIcons list is a cache for generated icon files.
*/
// Ported from /tg/station
// Creates a single icon from a given /atom or /image. Only the first argument is required.
/proc/getFlatIcon(image/A, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE)
// We start with a blank canvas, otherwise some icon procs crash silently
var/icon/flat = icon('icons/effects/effects.dmi', "nothing") // Final flattened icon
if(!A)
return flat
if(A.alpha <= 0)
return flat
var/noIcon = FALSE
//Define... defines.
var/static/icon/flat_template = icon('icons/effects/effects.dmi', "nothing")
#define BLANK icon(flat_template)
#define SET_SELF(SETVAR) do { \
var/icon/SELF_ICON=icon(icon(curicon, curstate, base_icon_dir),"",SOUTH,no_anim?1:null); \
if(A.alpha<255) { \
SELF_ICON.Blend(rgb(255,255,255,A.alpha),ICON_MULTIPLY);\
} \
if(A.color) { \
if(islist(A.color)){ \
SELF_ICON.MapColors(arglist(A.color))} \
else{ \
SELF_ICON.Blend(A.color,ICON_MULTIPLY)} \
} \
##SETVAR=SELF_ICON;\
} while (0)
#define INDEX_X_LOW 1
#define INDEX_X_HIGH 2
#define INDEX_Y_LOW 3
#define INDEX_Y_HIGH 4
#define flatX1 flat_size[INDEX_X_LOW]
#define flatX2 flat_size[INDEX_X_HIGH]
#define flatY1 flat_size[INDEX_Y_LOW]
#define flatY2 flat_size[INDEX_Y_HIGH]
#define addX1 add_size[INDEX_X_LOW]
#define addX2 add_size[INDEX_X_HIGH]
#define addY1 add_size[INDEX_Y_LOW]
#define addY2 add_size[INDEX_Y_HIGH]
if(!A || A.alpha <= 0)
return BLANK
var/noIcon = FALSE
if(start)
if(!defdir)
defdir = A.dir
@@ -653,26 +675,16 @@ The _flatIcons list is a cache for generated icon files.
if(!defblend)
defblend = A.blend_mode
var/curicon
if(A.icon)
curicon = A.icon
else
curicon = deficon
var/curicon = A.icon || deficon
var/curstate = A.icon_state || defstate
if(!curicon)
noIcon = TRUE // Do not render this object.
var/curstate
if(A.icon_state)
curstate = A.icon_state
else
curstate = defstate
if(!noIcon && !(curstate in icon_states(curicon)))
if("" in icon_states(curicon))
curstate = ""
else
noIcon = TRUE // Do not render this object.
if(!((noIcon = (!curicon))))
var/curstates = icon_states(curicon)
if(!(curstate in curstates))
if("" in curstates)
curstate = ""
else
noIcon = TRUE // Do not render this object.
var/curdir
var/base_icon_dir //We'll use this to get the icon state to display if not null BUT NOT pass it to overlays as the dir we have
@@ -683,148 +695,140 @@ The _flatIcons list is a cache for generated icon files.
else
curdir = A.dir
//Let's check if the icon actually contains any diagonals, just skip if it's south to save (lot of) time
if(curdir != SOUTH)
var/icon/test_icon
var/directionals_exist = FALSE
var/list/dirs_to_check = cardinal - SOUTH
outer:
for(var/possible_dir in dirs_to_check)
test_icon = icon(curicon,curstate,possible_dir,frame=1)
for(var/x in 1 to world.icon_size)
for(var/y in 1 to world.icon_size)
if(!isnull(test_icon.GetPixel(x,y)))
directionals_exist = TRUE
break outer
if(!directionals_exist)
//Try to remove/optimize this section ASAP, CPU hog.
//Determines if there's directionals.
if(!noIcon && curdir != SOUTH)
var/exist = FALSE
var/static/list/checkdirs = list(NORTH, EAST, WEST)
for(var/i in checkdirs) //Not using GLOB for a reason.
if(length(icon_states(icon(curicon, curstate, i))))
exist = TRUE
break
if(!exist)
base_icon_dir = SOUTH
//
if(!base_icon_dir)
base_icon_dir = curdir
var/curblend
if(A.blend_mode == BLEND_DEFAULT)
curblend = defblend
else
curblend = A.blend_mode
ASSERT(!BLEND_DEFAULT) //I might just be stupid but lets make sure this define is 0.
// Before processing overlays, make sure any pending overlays are applied
if (isloc(A))
var/atom/aAtom = A
if(aAtom.flags & OVERLAY_QUEUED)
COMPILE_OVERLAYS(aAtom)
var/curblend = A.blend_mode || defblend
// Layers will be a sorted list of icons/overlays, based on the order in which they are displayed
var/list/layers = list()
var/image/copy
// Add the atom's icon itself, without pixel_x/y offsets.
if(!noIcon)
copy = image(icon=curicon, icon_state=curstate, layer=A.layer, dir=base_icon_dir)
copy.color = A.color
copy.alpha = A.alpha
copy.blend_mode = curblend
layers[copy] = A.layer
if(A.overlays.len || A.underlays.len)
var/icon/flat = BLANK
// Layers will be a sorted list of icons/overlays, based on the order in which they are displayed
var/list/layers = list()
var/image/copy
// Add the atom's icon itself, without pixel_x/y offsets.
if(!noIcon)
copy = image(icon=curicon, icon_state=curstate, layer=A.layer, dir=base_icon_dir)
copy.color = A.color
copy.alpha = A.alpha
copy.blend_mode = curblend
layers[copy] = A.layer
// Loop through the underlays, then overlays, sorting them into the layers list
var/list/process = A.underlays // Current list being processed
var/pSet=0 // Which list is being processed: 0 = underlays, 1 = overlays
var/curIndex=1 // index of 'current' in list being processed
var/current // Current overlay being sorted
var/currentLayer // Calculated layer that overlay appears on (special case for FLOAT_LAYER)
var/compare // The overlay 'add' is being compared against
var/cmpIndex // The index in the layers list of 'compare'
while(TRUE)
if(curIndex<=process.len)
current = process[curIndex]
if(!current)
curIndex++ //Try the next layer
// Loop through the underlays, then overlays, sorting them into the layers list
for(var/process_set in 0 to 1)
var/list/process = process_set? A.overlays : A.underlays
for(var/i in 1 to process.len)
var/image/current = process[i]
if(!current)
continue
if(current.plane != FLOAT_PLANE && current.plane != A.plane)
continue
var/current_layer = current.layer
if(current_layer < 0)
if(current_layer <= -1000)
return flat
current_layer = process_set + A.layer + current_layer / 1000
for(var/p in 1 to layers.len)
var/image/cmp = layers[p]
if(current_layer < layers[cmp])
layers.Insert(p, current)
break
layers[current] = current_layer
//sortTim(layers, /proc/cmp_image_layer_asc)
var/icon/add // Icon of overlay being added
// Current dimensions of flattened icon
var/list/flat_size = list(1, flat.Width(), 1, flat.Height())
// Dimensions of overlay being added
var/list/add_size[4]
for(var/V in layers)
var/image/I = V
if(I.alpha == 0)
continue
var/image/I = current
if(I.plane != FLOAT_PLANE && I.plane != A.plane)
curIndex++
if(I == copy) // 'I' is an /image based on the object being flattened.
curblend = BLEND_OVERLAY
add = icon(I.icon, I.icon_state, base_icon_dir)
else // 'I' is an appearance object.
add = getFlatIcon(image(I), curdir, curicon, curstate, curblend, FALSE, no_anim)
if(!add)
continue
currentLayer = I.layer
if(currentLayer<0) // Special case for FLOAT_LAYER
if(currentLayer <= -1000)
return flat
if(pSet == 0) // Underlay
currentLayer = A.layer+currentLayer/1000
else // Overlay
currentLayer = A.layer+(1000+currentLayer)/1000
// Find the new dimensions of the flat icon to fit the added overlay
add_size = list(
min(flatX1, I.pixel_x+1),
max(flatX2, I.pixel_x+add.Width()),
min(flatY1, I.pixel_y+1),
max(flatY2, I.pixel_y+add.Height())
)
// Sort add into layers list
for(cmpIndex=1,cmpIndex<=layers.len,cmpIndex++)
compare = layers[cmpIndex]
if(currentLayer < layers[compare]) // Associated value is the calculated layer
layers.Insert(cmpIndex,current)
layers[current] = currentLayer
break
if(cmpIndex>layers.len) // Reached end of list without inserting
layers[current]=currentLayer // Place at end
if(flat_size ~! add_size)
// Resize the flattened icon so the new icon fits
flat.Crop(
addX1 - flatX1 + 1,
addY1 - flatY1 + 1,
addX2 - flatX1 + 1,
addY2 - flatY1 + 1
)
flat_size = add_size.Copy()
curIndex++
// Blend the overlay into the flattened icon
flat.Blend(add, blendMode2iconMode(curblend), I.pixel_x + 2 - flatX1, I.pixel_y + 2 - flatY1)
if(curIndex>process.len)
if(pSet == 0) // Switch to overlays
curIndex = 1
pSet = 1
process = A.overlays
else // All done
break
if(A.color)
if(islist(A.color))
flat.MapColors(arglist(A.color))
else
flat.Blend(A.color, ICON_MULTIPLY)
var/icon/add // Icon of overlay being added
if(A.alpha < 255)
flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY)
// Current dimensions of flattened icon
var/flatX1=1
var/flatX2=flat.Width()
var/flatY1=1
var/flatY2=flat.Height()
// Dimensions of overlay being added
var/addX1
var/addX2
var/addY1
var/addY2
for(var/V in layers)
var/image/I = V
if(I.alpha == 0)
continue
if(I == copy) // 'I' is an /image based on the object being flattened.
curblend = BLEND_OVERLAY
add = icon(I.icon, I.icon_state, base_icon_dir)
else // 'I' is an appearance object.
add = getFlatIcon(new/image(I), curdir, curicon, curstate, curblend, FALSE, no_anim)
// Find the new dimensions of the flat icon to fit the added overlay
addX1 = min(flatX1, I.pixel_x+1)
addX2 = max(flatX2, I.pixel_x+add.Width())
addY1 = min(flatY1, I.pixel_y+1)
addY2 = max(flatY2, I.pixel_y+add.Height())
if(addX1!=flatX1 || addX2!=flatX2 || addY1!=flatY1 || addY2!=flatY2)
// Resize the flattened icon so the new icon fits
flat.Crop(addX1-flatX1+1, addY1-flatY1+1, addX2-flatX1+1, addY2-flatY1+1)
flatX1=addX1;flatX2=addX2
flatY1=addY1;flatY2=addY2
// Blend the overlay into the flattened icon
flat.Blend(add, blendMode2iconMode(curblend), I.pixel_x + 2 - flatX1, I.pixel_y + 2 - flatY1)
if(A.color)
flat.Blend(A.color, ICON_MULTIPLY)
if(A.alpha < 255)
flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY)
if(no_anim)
//Clean up repeated frames
var/icon/cleaned = new /icon()
cleaned.Insert(flat, "", SOUTH, 1, 0)
return cleaned
else
return icon(flat, "", SOUTH)
if(no_anim)
//Clean up repeated frames
var/icon/cleaned = new /icon()
cleaned.Insert(flat, "", SOUTH, 1, 0)
. = cleaned
else
. = icon(flat, "", SOUTH)
else //There's no overlays.
if(!noIcon)
SET_SELF(.)
//Clear defines
#undef flatX1
#undef flatX2
#undef flatY1
#undef flatY2
#undef addX1
#undef addX2
#undef addY1
#undef addY2
#undef INDEX_X_LOW
#undef INDEX_X_HIGH
#undef INDEX_Y_LOW
#undef INDEX_Y_HIGH
#undef BLANK
#undef SET_SELF
/proc/getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N
var/icon/alpha_mask = new(A.icon,A.icon_state)//So we want the default icon and icon state of A.
@@ -900,9 +904,9 @@ proc/adjust_brightness(var/color, var/value)
if (!value) return color
var/list/RGB = ReadRGB(color)
RGB[1] = Clamp(RGB[1]+value,0,255)
RGB[2] = Clamp(RGB[2]+value,0,255)
RGB[3] = Clamp(RGB[3]+value,0,255)
RGB[1] = CLAMP(RGB[1]+value,0,255)
RGB[2] = CLAMP(RGB[2]+value,0,255)
RGB[3] = CLAMP(RGB[3]+value,0,255)
return rgb(RGB[1],RGB[2],RGB[3])
proc/sort_atoms_by_layer(var/list/atoms)

View File

@@ -27,9 +27,6 @@ proc/listgetindex(var/list/list,index)
return list[index]
return
proc/islist(list/list)
return(istype(list))
//Return either pick(list) or null if list is not of type /list or is empty
proc/safepick(list/list)
if(!islist(list) || !list.len)
@@ -250,7 +247,7 @@ proc/listclearnulls(list/list)
else
L[key] = temp[key]
//Mergesort: divides up the list into halves to begin the sort
/proc/sortKey(var/list/client/L, var/order = 1)
if(isnull(L) || L.len < 2)
@@ -753,3 +750,9 @@ proc/dd_sortedTextList(list/incoming)
for(var/i = 1 to l.len)
if(islist(.[i]))
.[i] = .(.[i])
//Return a list with no duplicate entries
/proc/uniqueList(list/L)
. = list()
for(var/i in L)
. |= i

View File

@@ -1,131 +0,0 @@
// Macro functions.
#define RAND_F(LOW, HIGH) (rand()*(HIGH-LOW) + LOW)
#define ceil(x) (-round(-(x)))
// min is inclusive, max is exclusive
/proc/Wrap(val, min, max)
var/d = max - min
var/t = Floor((val - min) / d)
return val - (t * d)
/proc/Default(a, b)
return a ? a : b
// Trigonometric functions.
/proc/Tan(x)
return sin(x) / cos(x)
/proc/Csc(x)
return 1 / sin(x)
/proc/Sec(x)
return 1 / cos(x)
/proc/Cot(x)
return 1 / Tan(x)
/proc/Atan2(x, y)
if(!x && !y) return 0
var/a = arccos(x / sqrt(x*x + y*y))
return y >= 0 ? a : -a
/proc/Floor(x)
return round(x)
/proc/Ceiling(x)
return -round(-x)
// Greatest Common Divisor: Euclid's algorithm.
/proc/Gcd(a, b)
while (1)
if (!b) return a
a %= b
if (!a) return b
b %= a
// Least Common Multiple. The formula is a consequence of: a*b = LCM*GCD.
/proc/Lcm(a, b)
return abs(a) * abs(b) / Gcd(a, b)
// Useful in the cases when x is a large expression, e.g. x = 3a/2 + b^2 + Function(c)
/proc/Square(x)
return x*x
/proc/Inverse(x)
return 1 / x
// Condition checks.
/proc/IsAboutEqual(a, b, delta = 0.1)
return abs(a - b) <= delta
// Returns true if val is from min to max, inclusive.
/proc/IsInRange(val, min, max)
return (val >= min) && (val <= max)
/proc/IsInteger(x)
return Floor(x) == x
/proc/IsMultiple(x, y)
return x % y == 0
/proc/IsEven(x)
return !(x & 0x1)
/proc/IsOdd(x)
return (x & 0x1)
// Performs a linear interpolation between a and b.
// Note: weight=0 returns a, weight=1 returns b, and weight=0.5 returns the mean of a and b.
/proc/Interpolate(a, b, weight = 0.5)
return a + (b - a) * weight // Equivalent to: a*(1 - weight) + b*weight
/proc/Mean(...)
var/sum = 0
for(var/val in args)
sum += val
return sum / args.len
// Returns the nth root of x.
/proc/Root(n, x)
return x ** (1 / n)
// The quadratic formula. Returns a list with the solutions, or an empty list
// if they are imaginary.
/proc/SolveQuadratic(a, b, c)
ASSERT(a)
. = list()
var/discriminant = b*b - 4*a*c
var/bottom = 2*a
// Return if the roots are imaginary.
if(discriminant < 0)
return
var/root = sqrt(discriminant)
. += (-b + root) / bottom
// If discriminant == 0, there would be two roots at the same position.
if(discriminant != 0)
. += (-b - root) / bottom
/proc/ToDegrees(radians)
// 180 / Pi ~ 57.2957795
return radians * 57.2957795
/proc/ToRadians(degrees)
// Pi / 180 ~ 0.0174532925
return degrees * 0.0174532925
// Vector algebra.
/proc/squaredNorm(x, y)
return x*x + y*y
/proc/norm(x, y)
return sqrt(squaredNorm(x, y))
/proc/IsPowerOfTwo(var/val)
return (val & (val-1)) == 0
/proc/RoundUpToPowerOfTwo(var/val)
return 2 ** -round(-log(2,val))

View File

@@ -50,7 +50,7 @@
/proc/color_rotation(angle)
if(angle == 0)
return color_identity()
angle = Clamp(angle, -180, 180)
angle = CLAMP(angle, -180, 180)
var/cos = cos(angle)
var/sin = sin(angle)
@@ -65,7 +65,7 @@
//Makes everything brighter or darker without regard to existing color or brightness
/proc/color_brightness(power)
power = Clamp(power, -255, 255)
power = CLAMP(power, -255, 255)
power = power/255
return list(1,0,0, 0,1,0, 0,0,1, power,power,power)
@@ -85,7 +85,7 @@
//Exxagerates or removes brightness
/proc/color_contrast(value)
value = Clamp(value, -100, 100)
value = CLAMP(value, -100, 100)
if(value == 0)
return color_identity()
@@ -108,7 +108,7 @@
/proc/color_saturation(value as num)
if(value == 0)
return color_identity()
value = Clamp(value, -100, 100)
value = CLAMP(value, -100, 100)
if(value > 0)
value *= 3
var/x = 1 + value / 100

View File

@@ -42,3 +42,6 @@
. = B[STAT_ENTRY_TIME] - A[STAT_ENTRY_TIME]
if (!.)
. = B[STAT_ENTRY_COUNT] - A[STAT_ENTRY_COUNT]
/proc/cmp_timer(datum/timedevent/a, datum/timedevent/b)
return a.timeToRun - b.timeToRun

View File

@@ -16,8 +16,8 @@
#define TICK *world.tick_lag
#define TICKS *world.tick_lag
#define DS2TICKS(DS) (DS/world.tick_lag) // Convert deciseconds to ticks
#define TICKS2DS(T) (T TICKS) // Convert ticks to deciseconds
#define DS2TICKS(DS) ((DS)/world.tick_lag) // Convert deciseconds to ticks
#define TICKS2DS(T) ((T) TICKS) // Convert ticks to deciseconds
/proc/get_game_time()
var/global/time_offset = 0
@@ -140,103 +140,36 @@ var/round_start_time = 0
. += CEILING(i*DELTA_CALC, 1)
sleep(i*world.tick_lag*DELTA_CALC)
i *= 2
while (TICK_USAGE > min(TICK_LIMIT_TO_RUN, GLOB.CURRENT_TICKLIMIT))
while (TICK_USAGE > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit))
#undef DELTA_CALC
//Takes a value of time in deciseconds.
//Returns a text value of that number in hours, minutes, or seconds.
/proc/DisplayTimeText(time_value, truncate = FALSE)
var/second = (time_value)*0.1
var/second_adjusted = null
var/second_rounded = FALSE
var/minute = null
var/hour = null
var/day = null
/proc/DisplayTimeText(time_value, round_seconds_to = 0.1)
var/second = round(time_value * 0.1, round_seconds_to)
if(!second)
return "0 seconds"
if(second >= 60)
minute = FLOOR(second/60, 1)
second = round(second - (minute*60), 0.1)
second_rounded = TRUE
if(second) //check if we still have seconds remaining to format, or if everything went into minute.
second_adjusted = round(second) //used to prevent '1 seconds' being shown
if(day || hour || minute)
if(second_adjusted == 1 && second >= 1)
second = " and 1 second"
else if(second > 1)
second = " and [second_adjusted] seconds"
else //shows a fraction if seconds is < 1
if(second_rounded) //no sense rounding again if it's already done
second = " and [second] seconds"
else
second = " and [round(second, 0.1)] seconds"
else
if(second_adjusted == 1 && second >= 1)
second = "[truncate ? "second" : "1 second"]"
else if(second > 1)
second = "[second_adjusted] seconds"
else
if(second_rounded)
second = "[second] seconds"
else
second = "[round(second, 0.1)] seconds"
else
second = null
if(!minute)
return "[second]"
if(minute >= 60)
hour = FLOOR(minute/60, 1)
minute = (minute - (hour*60))
if(minute) //alot simpler from here since you don't have to worry about fractions
if(minute != 1)
if((day || hour) && second)
minute = ", [minute] minutes"
else if((day || hour) && !second)
minute = " and [minute] minutes"
else
minute = "[minute] minutes"
else
if((day || hour) && second)
minute = ", 1 minute"
else if((day || hour) && !second)
minute = " and 1 minute"
else
minute = "[truncate ? "minute" : "1 minute"]"
else
minute = null
if(!hour)
return "[minute][second]"
if(hour >= 24)
day = FLOOR(hour/24, 1)
hour = (hour - (day*24))
return "right now"
if(second < 60)
return "[second] second[(second != 1)? "s":""]"
var/minute = FLOOR(second / 60, 1)
second = MODULUS(second, 60)
var/secondT
if(second)
secondT = " and [second] second[(second != 1)? "s":""]"
if(minute < 60)
return "[minute] minute[(minute != 1)? "s":""][secondT]"
var/hour = FLOOR(minute / 60, 1)
minute = MODULUS(minute, 60)
var/minuteT
if(minute)
minuteT = " and [minute] minute[(minute != 1)? "s":""]"
if(hour < 24)
return "[hour] hour[(hour != 1)? "s":""][minuteT][secondT]"
var/day = FLOOR(hour / 24, 1)
hour = MODULUS(hour, 24)
var/hourT
if(hour)
if(hour != 1)
if(day && (minute || second))
hour = ", [hour] hours"
else if(day && (!minute || !second))
hour = " and [hour] hours"
else
hour = "[hour] hours"
else
if(day && (minute || second))
hour = ", 1 hour"
else if(day && (!minute || !second))
hour = " and 1 hour"
else
hour = "[truncate ? "hour" : "1 hour"]"
else
hour = null
if(!day)
return "[hour][minute][second]"
if(day > 1)
day = "[day] days"
else
day = "[truncate ? "day" : "1 day"]"
return "[day][hour][minute][second]"
hourT = " and [hour] hour[(hour != 1)? "s":""]"
return "[day] day[(day != 1)? "s":""][hourT][minuteT][secondT]"

View File

@@ -242,3 +242,42 @@
/proc/isLeap(y)
return ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
//Takes a string and a datum
//The string is well, obviously the string being checked
//The datum is used as a source for var names, to check validity
//Otherwise every single word could technically be a variable!
/proc/string2listofvars(var/t_string, var/datum/var_source)
if(!t_string || !var_source)
return list()
. = list()
var/var_found = findtext(t_string,"\[") //Not the actual variables, just a generic "should we even bother" check
if(var_found)
//Find var names
// "A dog said hi [name]!"
// splittext() --> list("A dog said hi ","name]!"
// jointext() --> "A dog said hi name]!"
// splittext() --> list("A","dog","said","hi","name]!")
t_string = replacetext(t_string,"\[","\[ ")//Necessary to resolve "word[var_name]" scenarios
var/list/list_value = splittext(t_string,"\[")
var/intermediate_stage = jointext(list_value, null)
list_value = splittext(intermediate_stage," ")
for(var/value in list_value)
if(findtext(value,"]"))
value = splittext(value,"]") //"name]!" --> list("name","!")
for(var/A in value)
if(var_source.vars.Find(A))
. += A
/proc/get_end_section_of_type(type)
var/strtype = "[type]"
var/delim_pos = findlasttext(strtype, "/")
if(delim_pos == 0)
return strtype
return copytext(strtype, delim_pos)

View File

@@ -501,7 +501,7 @@ 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/simple_animal/M in sortmob)
for(var/mob/living/simple_mob/M in sortmob)
moblist.Add(M)
// for(var/mob/living/silicon/hivebot/M in sortmob)
// mob_list.Add(M)
@@ -831,6 +831,7 @@ proc/GaussRandRound(var/sigma,var/roundto)
//Move the objects. Not forceMove because the object isn't "moving" really, it's supposed to be on the "same" turf.
for(var/obj/O in T)
O.loc = X
O.update_light()
//Move the mobs unless it's an AI eye or other eye type.
for(var/mob/M in T)
@@ -1461,3 +1462,94 @@ var/mob/dview/dview_mob = new
/proc/pass()
return
#define NAMEOF(datum, X) (#X || ##datum.##X)
/proc/pick_closest_path(value, list/matches = get_fancy_list_of_atom_types())
if (value == FALSE) //nothing should be calling us with a number, so this is safe
value = input("Enter type to find (blank for all, cancel to cancel)", "Search for type") as null|text
if (isnull(value))
return
value = trim(value)
if(!isnull(value) && value != "")
matches = filter_fancy_list(matches, value)
if(matches.len==0)
return
var/chosen
if(matches.len==1)
chosen = matches[1]
else
chosen = input("Select a type", "Pick Type", matches[1]) as null|anything in matches
if(!chosen)
return
chosen = matches[chosen]
return chosen
/proc/get_fancy_list_of_atom_types()
var/static/list/pre_generated_list
if (!pre_generated_list) //init
pre_generated_list = make_types_fancy(typesof(/atom))
return pre_generated_list
/proc/get_fancy_list_of_datum_types()
var/static/list/pre_generated_list
if (!pre_generated_list) //init
pre_generated_list = make_types_fancy(sortList(typesof(/datum) - typesof(/atom)))
return pre_generated_list
/proc/filter_fancy_list(list/L, filter as text)
var/list/matches = new
for(var/key in L)
var/value = L[key]
if(findtext("[key]", filter) || findtext("[value]", filter))
matches[key] = value
return matches
/proc/make_types_fancy(var/list/types)
if (ispath(types))
types = list(types)
. = list()
for(var/type in types)
var/typename = "[type]"
var/static/list/TYPES_SHORTCUTS = list(
/obj/effect/decal/cleanable = "CLEANABLE",
/obj/item/device/radio/headset = "HEADSET",
/obj/item/clothing/head/helmet/space = "SPESSHELMET",
/obj/item/weapon/book/manual = "MANUAL",
/obj/item/weapon/reagent_containers/food/drinks = "DRINK",
/obj/item/weapon/reagent_containers/food = "FOOD",
/obj/item/weapon/reagent_containers = "REAGENT_CONTAINERS",
/obj/machinery/atmospherics = "ATMOS_MECH",
/obj/machinery/portable_atmospherics = "PORT_ATMOS",
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack = "MECHA_MISSILE_RACK",
/obj/item/mecha_parts/mecha_equipment = "MECHA_EQUIP",
/obj/item/organ = "ORGAN",
/obj/item = "ITEM",
/obj/machinery = "MACHINERY",
/obj/effect = "EFFECT",
/obj = "O",
/datum = "D",
/turf/simulated/wall = "S-WALL",
/turf/simulated/floor = "S-FLOOR",
/turf/simulated = "SIMULATED",
/turf/unsimulated/wall = "US-WALL",
/turf/unsimulated/floor = "US-FLOOR",
/turf/unsimulated = "UNSIMULATED",
/turf = "T",
/mob/living/carbon = "CARBON",
/mob/living/simple_mob = "SIMPLE",
/mob/living = "LIVING",
/mob = "M"
)
for (var/tn in TYPES_SHORTCUTS)
if (copytext(typename,1, length("[tn]/")+1)=="[tn]/" /*findtextEx(typename,"[tn]/",1,2)*/ )
typename = TYPES_SHORTCUTS[tn]+copytext(typename,length("[tn]/"))
break
.[typename] = type
/proc/IsValidSrc(datum/D)
if(istype(D))
return !QDELETED(D)
return FALSE

View File

@@ -78,13 +78,13 @@ return_location()
return
// calculate the angle
angle = Atan2(dx, dy) + angle_offset
angle = ATAN2(dx, dy) + angle_offset
// and some rounding to stop the increments jumping whole turfs - because byond favours certain angles
if(angle > -135 && angle < 45)
angle = Ceiling(angle)
angle = CEILING(angle, 1)
else
angle = Floor(angle)
angle = FLOOR(angle, 1)
// calculate the offset per increment step
if(abs(angle) in list(0, 45, 90, 135, 180)) // check if the angle is a cardinal
@@ -93,11 +93,11 @@ return_location()
if(abs(angle) in list(45, 90, 135))
offset_y = sign(dy)
else if(abs(dy) > abs(dx))
offset_x = Cot(abs(angle)) // otherwise set the offsets
offset_x = COT(abs(angle)) // otherwise set the offsets
offset_y = sign(dy)
else
offset_x = sign(dx)
offset_y = Tan(angle)
offset_y = TAN(angle)
if(dx < 0)
offset_y = -offset_y

View File

@@ -1,57 +1,7 @@
#define Clamp(x, y, z) (x <= y ? y : (x >= z ? z : x))
#define CLAMP01(x) (Clamp(x, 0, 1))
#define span(class, text) ("<span class='[class]'>[text]</span>")
#define get_turf(A) get_step(A,0)
#define isAI(A) istype(A, /mob/living/silicon/ai)
#define isalien(A) istype(A, /mob/living/carbon/alien)
#define isanimal(A) istype(A, /mob/living/simple_animal)
#define isairlock(A) istype(A, /obj/machinery/door/airlock)
#define isbrain(A) istype(A, /mob/living/carbon/brain)
#define iscarbon(A) istype(A, /mob/living/carbon)
#define iscorgi(A) istype(A, /mob/living/simple_animal/corgi)
#define isEye(A) istype(A, /mob/observer/eye)
#define ishuman(A) istype(A, /mob/living/carbon/human)
#define isliving(A) istype(A, /mob/living)
#define ismouse(A) istype(A, /mob/living/simple_animal/mouse)
#define isnewplayer(A) istype(A, /mob/new_player)
#define isobserver(A) istype(A, /mob/observer/dead)
#define isorgan(A) istype(A, /obj/item/organ/external)
#define ispAI(A) istype(A, /mob/living/silicon/pai)
#define isrobot(A) istype(A, /mob/living/silicon/robot)
#define issilicon(A) istype(A, /mob/living/silicon)
#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)
#define isopenspace(A) istype(A, /turf/simulated/open)
#define isweakref(A) istype(A, /weakref)
#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
@@ -62,35 +12,12 @@
#define to_file(file_entry, source_var) file_entry << source_var
#define from_file(file_entry, target_var) file_entry >> target_var
// From TG, might be useful to have.
// Didn't port SEND_TEXT() since to_chat() appears to serve the same purpose.
#define DIRECT_OUTPUT(A, B) A << B
#define SEND_IMAGE(target, image) DIRECT_OUTPUT(target, image)
#define SEND_SOUND(target, sound) DIRECT_OUTPUT(target, sound)
#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() )
// Turns LAZYINITLIST(L) L[K] = V into ... for associated lists
#define LAZYSET(L, K, V) if(!L) { L = list(); } L[K] = V;

View File

@@ -98,7 +98,7 @@
/obj/screen/fullscreen/flash
icon = 'icons/mob/screen1.dmi'
screen_loc = "WEST,SOUTH to EAST,NORTH"
icon_state = "flash"
icon_state = "flash_static"
/obj/screen/fullscreen/flash/noise
icon_state = "noise"

View File

@@ -316,8 +316,6 @@ datum/hud/New(mob/owner)
mymob.instantiate_hud(src)
else if(isalien(mymob))
larva_hud()
else if(isslime(mymob))
slime_hud()
else if(isAI(mymob))
ai_hud()
else if(isobserver(mymob))

View File

@@ -23,7 +23,7 @@
mymob.client.screen += list(blobpwrdisplay, blobhealthdisplay)
mymob.client.screen += mymob.client.void
/*
/datum/hud/proc/slime_hud(ui_style = 'icons/mob/screen1_Midnight.dmi')
src.adding = list()
@@ -92,23 +92,20 @@
mymob.client.screen += mymob.client.void
return
/mob/living/simple_animal/construct/instantiate_hud(var/datum/hud/HUD)
..(HUD)
*/
// HUD.construct_hud() //Archaic.
/*
/datum/hud/proc/construct_hud()
var/constructtype
if(istype(mymob,/mob/living/simple_animal/construct/armoured) || istype(mymob,/mob/living/simple_animal/construct/behemoth))
if(istype(mymob,/mob/living/simple_mob/construct/armoured) || istype(mymob,/mob/living/simple_mob/construct/behemoth))
constructtype = "juggernaut"
else if(istype(mymob,/mob/living/simple_animal/construct/builder))
else if(istype(mymob,/mob/living/simple_mob/construct/builder))
constructtype = "artificer"
else if(istype(mymob,/mob/living/simple_animal/construct/wraith))
else if(istype(mymob,/mob/living/simple_mob/construct/wraith))
constructtype = "wraith"
else if(istype(mymob,/mob/living/simple_animal/construct/harvester))
else if(istype(mymob,/mob/living/simple_mob/construct/harvester))
constructtype = "harvester"
if(constructtype)

View File

@@ -1,15 +1,15 @@
/obj/screen/proc/Click_vr(location, control, params)
/obj/screen/proc/Click_vr(location, control, params) //VORESTATION AI TEMPORARY REMOVAL
if(!usr) return 1
switch(name)
//Shadekin
if("darkness")
var/mob/living/simple_animal/shadekin/sk = usr
var/mob/living/simple_mob/shadekin/sk = usr
var/turf/T = get_turf(sk)
var/darkness = round(1 - T.get_lumcount(),0.1)
to_chat(usr,"<span class='notice'><b>Darkness:</b> [darkness]</span>")
if("energy")
var/mob/living/simple_animal/shadekin/sk = usr
var/mob/living/simple_mob/shadekin/sk = usr
to_chat(usr,"<span class='notice'><b>Energy:</b> [sk.energy] ([sk.dark_gains])</span>")

View File

@@ -60,7 +60,7 @@ avoid code duplication. This includes items that may sometimes act as a standard
// Same as above but actually does useful things.
// W is the item being used in the attack, if any. modifier is if the attack should be longer or shorter than usual, for whatever reason.
/mob/living/get_attack_speed(var/obj/item/W)
var/speed = DEFAULT_ATTACK_COOLDOWN
var/speed = base_attack_cooldown
if(W && istype(W))
speed = W.attackspeed
for(var/datum/modifier/M in modifiers)

View File

@@ -68,58 +68,3 @@
*/
/mob/new_player/ClickOn()
return
/*
Animals
*/
/mob/living/simple_animal/UnarmedAttack(var/atom/A, var/proximity)
if(!(. = ..()))
return
setClickCooldown(get_attack_speed())
if(has_hands && istype(A,/obj) && a_intent != I_HURT)
var/obj/O = A
return O.attack_hand(src)
switch(a_intent)
if(I_HELP)
if(isliving(A))
custom_emote(1,"[pick(friendly)] [A]!")
if(I_HURT)
if(prob(spattack_prob))
if(spattack_min_range <= 1)
SpecialAtkTarget()
else if(melee_damage_upper == 0 && istype(A,/mob/living))
custom_emote(1,"[pick(friendly)] [A]!")
else
DoPunch(A)
if(I_GRAB)
if(has_hands)
A.attack_hand(src)
if(I_DISARM)
if(has_hands)
A.attack_hand(src)
/mob/living/simple_animal/RangedAttack(var/atom/A)
setClickCooldown(get_attack_speed())
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

View File

@@ -155,7 +155,7 @@
/datum/controller/process/proc/setup()
/datum/controller/process/proc/process()
/datum/controller/process/process()
started()
doWork()
finished()

View File

@@ -70,7 +70,7 @@ var/global/datum/controller/processScheduler/processScheduler
spawn(0)
process()
/datum/controller/processScheduler/proc/process()
/datum/controller/processScheduler/process()
while(isRunning)
checkRunningProcesses()
queueProcesses()

View File

@@ -1,23 +0,0 @@
/datum/controller/process/air/setup()
name = "air"
schedule_interval = 20 // every 2 seconds
start_delay = 4
if(!air_master)
air_master = new
air_master.Setup()
/datum/controller/process/air/doWork()
if(!air_processing_killed)
if(!air_master.Tick()) //Runtimed.
air_master.failed_ticks++
if(air_master.failed_ticks > 5)
world << "<SPAN CLASS='danger'>RUNTIMES IN ATMOS TICKER. Killing air simulation!</SPAN>"
world.log << "### ZAS SHUTDOWN"
message_admins("ZASALERT: Shutting down! status: [air_master.tick_progress]")
log_admin("ZASALERT: Shutting down! status: [air_master.tick_progress]")
air_processing_killed = TRUE
air_master.failed_ticks = 0

View File

@@ -11,7 +11,7 @@
var/datum/controller/process/alarm/alarm_manager
/datum/controller/process/alarm
var/list/datum/alarm/all_handlers
var/list/datum/alarm/all_handlers = list()
/datum/controller/process/alarm/setup()
name = "alarm"

View File

@@ -1,33 +0,0 @@
var/datum/controller/process/chemistry/chemistryProcess
/datum/controller/process/chemistry
var/list/active_holders
var/list/chemical_reactions
var/list/chemical_reagents
/datum/controller/process/chemistry/setup()
name = "chemistry"
schedule_interval = 20 // every 2 seconds
chemistryProcess = src
active_holders = list()
chemical_reactions = chemical_reactions_list
chemical_reagents = chemical_reagents_list
/datum/controller/process/chemistry/statProcess()
..()
stat(null, "[active_holders.len] reagent holder\s")
/datum/controller/process/chemistry/doWork()
for(last_object in active_holders)
var/datum/reagents/holder = last_object
if(!holder.process_reactions())
active_holders -= holder
SCHECK
/datum/controller/process/chemistry/proc/mark_for_update(var/datum/reagents/holder)
if(holder in active_holders)
return
//Process once, right away. If we still need to continue then add to the active_holders list and continue later
if(holder.process_reactions())
active_holders += holder

View File

@@ -1,6 +0,0 @@
/datum/controller/process/event/setup()
name = "event controller"
schedule_interval = 20 // every 2 seconds
/datum/controller/process/event/doWork()
event_manager.process()

View File

@@ -1,19 +0,0 @@
/datum/controller/process/nanoui/setup()
name = "nanoui"
schedule_interval = 20 // every 2 seconds
/datum/controller/process/nanoui/statProcess()
..()
stat(null, "[GLOB.nanomanager.processing_uis.len] UIs")
/datum/controller/process/nanoui/doWork()
for(last_object in GLOB.nanomanager.processing_uis)
var/datum/nanoui/NUI = last_object
if(istype(NUI) && !QDELETED(NUI))
try
NUI.process()
catch(var/exception/e)
catchException(e, NUI)
else
catchBadType(NUI)
GLOB.nanomanager.processing_uis -= NUI

View File

@@ -1,26 +0,0 @@
/datum/controller/process/obj/setup()
name = "obj"
schedule_interval = 20 // every 2 seconds
start_delay = 8
/datum/controller/process/obj/started()
..()
if(!processing_objects)
processing_objects = list()
/datum/controller/process/obj/doWork()
for(last_object in processing_objects)
var/datum/O = last_object
if(!QDELETED(O))
try
O:process()
catch(var/exception/e)
catchException(e, O)
SCHECK
else
catchBadType(O)
processing_objects -= O
/datum/controller/process/obj/statProcess()
..()
stat(null, "[processing_objects.len] objects")

View File

@@ -40,7 +40,7 @@
// We are being killed. Least we can do is deregister all those events we registered
/datum/controller/process/scheduler/onKill()
for(var/st in scheduled_tasks)
destroyed_event.unregister(st, src)
GLOB.destroyed_event.unregister(st, src)
/datum/controller/process/scheduler/statProcess()
..()
@@ -148,7 +148,7 @@
/datum/scheduled_task/source/New(var/trigger_time, var/datum/source, var/procedure, var/list/arguments, var/proc/task_after_process, var/list/task_after_process_args)
src.source = source
destroyed_event.register(src.source, src, /datum/scheduled_task/source/proc/source_destroyed)
GLOB.destroyed_event.register(src.source, src, /datum/scheduled_task/source/proc/source_destroyed)
..(trigger_time, procedure, arguments, task_after_process, task_after_process_args)
/datum/scheduled_task/source/Destroy()

View File

@@ -1,7 +0,0 @@
/datum/controller/process/sun/setup()
name = "sun"
schedule_interval = 20 // every second
sun = new
/datum/controller/process/sun/doWork()
sun.calc_position()

View File

@@ -65,7 +65,7 @@ var/datum/controller/supply/supply_controller = new()
// Supply shuttle ticker - handles supply point regeneration
// This is called by the process scheduler every thirty seconds
/datum/controller/supply/proc/process()
/datum/controller/supply/process()
points += points_per_process
//To stop things being sent to CentCom which should not be sent to centcomm. Recursively checks for these types.

View File

@@ -1,16 +0,0 @@
var/global/list/turf/processing_turfs = list()
/datum/controller/process/turf/setup()
name = "turf"
schedule_interval = 20 // every 2 seconds
/datum/controller/process/turf/doWork()
for(last_object in processing_turfs)
var/turf/T = last_object
if(T.process() == PROCESS_KILL)
processing_turfs.Remove(T)
SCHECK
/datum/controller/process/turf/statProcess()
..()
stat(null, "[processing_turfs.len] turf\s")

View File

@@ -9,12 +9,12 @@ datum/controller/transfer_controller/New()
timerbuffer = config.vote_autotransfer_initial
shift_hard_end = config.vote_autotransfer_initial + (config.vote_autotransfer_interval * 1) //VOREStation Edit //Change this "1" to how many extend votes you want there to be.
shift_last_vote = shift_hard_end - config.vote_autotransfer_interval //VOREStation Edit
processing_objects += src
START_PROCESSING(SSobj, src)
datum/controller/transfer_controller/Destroy()
processing_objects -= src
STOP_PROCESSING(SSobj, src)
datum/controller/transfer_controller/proc/process()
datum/controller/transfer_controller/process()
currenttick = currenttick + 1
//VOREStation Edit START
if (round_duration_in_ticks >= shift_last_vote - 2 MINUTES)

View File

@@ -220,7 +220,7 @@ var/list/gamemode_cache = list()
var/aggressive_changelog = 0
var/list/language_prefixes = list(",","#","-")//Default language prefixes
var/list/language_prefixes = list(",","#")//Default language prefixes
var/show_human_death_message = 1
@@ -228,6 +228,8 @@ var/list/gamemode_cache = list()
var/radiation_resistance_multiplier = 8.5 //VOREstation edit
var/radiation_lower_limit = 0.35 //If the radiation level for a turf would be below this, ignore it.
var/random_submap_orientation = FALSE // If true, submaps loaded automatically can be rotated.
/datum/configuration/New()
var/list/L = typesof(/datum/game_mode) - /datum/game_mode
for (var/T in L)
@@ -748,6 +750,9 @@ var/list/gamemode_cache = list()
if ("paranoia_logging")
config.paranoia_logging = 1
if("random_submap_orientation")
config.random_submap_orientation = 1
else
log_misc("Unknown setting in configuration: '[name]'")

View File

@@ -26,7 +26,7 @@ var/global/datum/emergency_shuttle_controller/emergency_shuttle
escape_pods = list()
..()
/datum/emergency_shuttle_controller/proc/process()
/datum/emergency_shuttle_controller/process()
if (wait_for_launch)
if (evac && auto_recall && world.time >= auto_recall_time)
recall()

View File

@@ -38,8 +38,10 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars)
stat("Globals:", statclick.update("Edit"))
/datum/controller/global_vars/VV_hidden()
return ..() + gvars_datum_protected_varlist
/datum/controller/global_vars/vv_edit_var(var_name, var_value)
if(gvars_datum_protected_varlist[var_name])
return FALSE
return ..()
/datum/controller/global_vars/Initialize(var/exclude_these)
gvars_datum_init_order = list()

View File

@@ -13,14 +13,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
//THIS IS THE INIT ORDER
//Master -> SSPreInit -> GLOB -> world -> config -> SSInit -> Failsafe
//GOT IT MEMORIZED?
GLOBAL_VAR_INIT(MC_restart_clear, 0)
GLOBAL_VAR_INIT(MC_restart_timeout, 0)
GLOBAL_VAR_INIT(MC_restart_count, 0)
//current tick limit, assigned by the queue controller before running a subsystem.
//used by check_tick as well so that the procs subsystems call can obey that SS's tick limits
GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
/datum/controller/master
name = "Master"
@@ -62,6 +54,10 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
var/static/restart_timeout = 0
var/static/restart_count = 0
//current tick limit, assigned by the queue controller 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
/datum/controller/master/New()
// Highlander-style: there can only be one! Kill off the old and replace it with the new.
var/list/_subsystems = list()
@@ -98,14 +94,14 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
// -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 < GLOB.MC_restart_timeout)
if (world.time < Master.restart_timeout)
return 0
if (world.time < GLOB.MC_restart_clear)
GLOB.MC_restart_count *= 0.5
if (world.time < Master.restart_clear)
Master.restart_count *= 0.5
var/delay = 50 * ++GLOB.MC_restart_count
GLOB.MC_restart_timeout = world.time + delay
GLOB.MC_restart_clear = world.time + (delay * 2)
var/delay = 50 * ++Master.restart_count
Master.restart_timeout = world.time + delay
Master.restart_clear = world.time + (delay * 2)
Master.processing = FALSE //stop ticking this one
try
new/datum/controller/master()
@@ -176,13 +172,13 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
var/start_timeofday = REALTIMEOFDAY
// Initialize subsystems.
GLOB.CURRENT_TICKLIMIT = config.tick_limit_mc_init
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
GLOB.CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
current_ticklimit = TICK_LIMIT_RUNNING
var/time = (REALTIMEOFDAY - start_timeofday) / 10
var/msg = "Initializations complete within [time] second[time == 1 ? "" : "s"]!"
@@ -291,7 +287,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
tickdrift = max(0, MC_AVERAGE_FAST(tickdrift, (((REALTIMEOFDAY - init_timeofday) - (world.time - init_time)) / world.tick_lag)))
var/starting_tick_usage = TICK_USAGE
if (processing <= 0)
GLOB.CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
current_ticklimit = TICK_LIMIT_RUNNING
sleep(10)
continue
@@ -300,7 +296,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
// (because sleeps are processed in the order received, longer sleeps are more likely to run first)
if (starting_tick_usage > TICK_LIMIT_MC) //if there isn't enough time to bother doing anything this tick, sleep a bit.
sleep_delta *= 2
GLOB.CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING * 0.5
current_ticklimit = TICK_LIMIT_RUNNING * 0.5
sleep(world.tick_lag * (processing * sleep_delta))
continue
@@ -346,7 +342,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
if (!error_level)
iteration++
error_level++
GLOB.CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
current_ticklimit = TICK_LIMIT_RUNNING
sleep(10)
continue
@@ -358,7 +354,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
if (!error_level)
iteration++
error_level++
GLOB.CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
current_ticklimit = TICK_LIMIT_RUNNING
sleep(10)
continue
error_level--
@@ -369,9 +365,9 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
iteration++
last_run = world.time
src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta)
GLOB.CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
current_ticklimit = TICK_LIMIT_RUNNING
if (processing * sleep_delta <= world.tick_lag)
GLOB.CURRENT_TICKLIMIT -= (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc if we plan on running next tick
current_ticklimit -= (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc if we plan on running next tick
sleep(world.tick_lag * (processing * sleep_delta))
@@ -463,7 +459,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
// Reduce tick allocation for subsystems that overran on their last tick.
tick_precentage = max(tick_precentage*0.5, tick_precentage-queue_node.tick_overrun)
GLOB.CURRENT_TICKLIMIT = round(TICK_USAGE + tick_precentage)
current_ticklimit = round(TICK_USAGE + tick_precentage)
if (!(queue_node_flags & SS_TICKER))
ran_non_ticker = TRUE

View File

@@ -6,6 +6,7 @@
var/priority = FIRE_PRIORITY_DEFAULT //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/subsystem_initialized = FALSE //set to TRUE after it has been initialized, will obviously never be set if the subsystem doesn't initialize
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
@@ -154,6 +155,7 @@
//used to initialize the subsystem AFTER the map has loaded
/datum/controller/subsystem/Initialize(start_timeofday)
subsystem_initialized = TRUE
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>")

View File

@@ -0,0 +1,36 @@
SUBSYSTEM_DEF(ai)
name = "AI"
init_order = INIT_ORDER_AI
priority = FIRE_PRIORITY_AI
wait = 5 // This gets run twice a second, however this is technically two loops in one, with the second loop being run every four iterations.
flags = SS_NO_INIT|SS_TICKER
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/list/processing = list()
var/list/currentrun = list()
/datum/controller/subsystem/ai/stat_entry(msg_prefix)
var/list/msg = list(msg_prefix)
msg += "P:[processing.len]"
..(msg.Join())
/datum/controller/subsystem/ai/fire(resumed = 0)
if (!resumed)
src.currentrun = processing.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
// var/mob/living/L = currentrun[currentrun.len]
var/datum/ai_holder/A = currentrun[currentrun.len]
--currentrun.len
if(!A || QDELETED(A)) // Doesn't exist or won't exist soon.
continue
if(times_fired % 4 == 0 && A.holder.stat != DEAD)
A.handle_strategicals()
if(A.holder.stat != DEAD) // The /TG/ version checks stat twice, presumably in-case processing somehow got the mob killed in that instant.
A.handle_tactics()
if(MC_TICK_CHECK)
return

View File

@@ -77,7 +77,7 @@ SUBSYSTEM_DEF(atoms)
var/start_tick = world.time
var/result = A.initialize(arglist(arguments))
var/result = A.Initialize(arglist(arguments))
if(start_tick != world.time)
BadInitializeCalls[the_type] |= BAD_INIT_SLEPT

View File

@@ -1,29 +0,0 @@
//
// 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(var/quiet)
map_loading = TRUE
/datum/controller/subsystem/creation/StopLoadingMap(var/quiet)
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)

View File

@@ -0,0 +1,72 @@
SUBSYSTEM_DEF(events)
name = "Events"
wait = 20
var/list/datum/event/active_events = list()
var/list/datum/event/finished_events = list()
var/list/datum/event/allEvents
var/list/datum/event_container/event_containers
var/datum/event_meta/new_event = new
/datum/controller/subsystem/events/Initialize()
event_containers = list(
EVENT_LEVEL_MUNDANE = new/datum/event_container/mundane,
EVENT_LEVEL_MODERATE = new/datum/event_container/moderate,
EVENT_LEVEL_MAJOR = new/datum/event_container/major
)
allEvents = typesof(/datum/event) - /datum/event
return ..()
/datum/controller/subsystem/events/fire(resumed)
for(var/datum/event/E in active_events)
E.process()
for(var/i = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR)
var/list/datum/event_container/EC = event_containers[i]
EC.process()
/datum/controller/subsystem/events/Recover()
if(SSevents.active_events)
active_events |= SSevents.active_events
if(SSevents.finished_events)
finished_events |= SSevents.finished_events
/datum/controller/subsystem/events/proc/event_complete(var/datum/event/E)
if(!E.event_meta || !E.severity) // datum/event is used here and there for random reasons, maintaining "backwards compatibility"
log_debug("Event of '[E.type]' with missing meta-data has completed.")
return
finished_events += E
// Add the event back to the list of available events
var/datum/event_container/EC = event_containers[E.severity]
var/datum/event_meta/EM = E.event_meta
if(EM.add_to_queue)
EC.available_events += EM
log_debug("Event '[EM.name]' has completed at [worldtime2stationtime(world.time)].")
/datum/controller/subsystem/events/proc/delay_events(var/severity, var/delay)
var/list/datum/event_container/EC = event_containers[severity]
EC.next_event_time += delay
/datum/controller/subsystem/events/proc/RoundEnd()
if(!report_at_round_end)
return
to_chat(world, "<br><br><br><font size=3><b>Random Events This Round:</b></font>")
for(var/datum/event/E in active_events|finished_events)
var/datum/event_meta/EM = E.event_meta
if(EM.name == "Nothing")
continue
var/message = "'[EM.name]' began at [worldtime2stationtime(E.startedAt)] "
if(E.isRunning)
message += "and is still running."
else
if(E.endedAt - E.startedAt > MinutesToTicks(5)) // Only mention end time if the entire duration was more than 5 minutes
message += "and ended at [worldtime2stationtime(E.endedAt)]."
else
message += "and ran to completion."
to_chat(world, message)

View File

@@ -1,11 +1,12 @@
/datum/controller/process/inactivity/setup()
name = "inactivity"
schedule_interval = 600 // Once every minute (approx.)
SUBSYSTEM_DEF(inactivity)
name = "AFK Kick"
wait = 600
flags = SS_BACKGROUND | SS_NO_TICK_CHECK
/datum/controller/process/inactivity/doWork()
/datum/controller/subsystem/inactivity/fire()
if(config.kick_inactive)
for(last_object in clients)
var/client/C = last_object
for(var/i in GLOB.clients)
var/client/C = i
if(C.is_afk(config.kick_inactive MINUTES) && !C.holder) // VOREStation Edit - Allow admins to idle
to_chat(C,"<span class='warning'>You have been inactive for more than [config.kick_inactive] minute\s and have been disconnected.</span>")
var/information
@@ -33,4 +34,3 @@
log_and_message_admins("being kicked for AFK[information][adminlinks]", C.mob)
qdel(C)
SCHECK

View File

@@ -109,7 +109,7 @@ SUBSYSTEM_DEF(machines)
else
global.pipe_networks.Remove(PN)
if(!QDELETED(PN))
PN.is_processing = null
DISABLE_BITFIELD(PN.datum_flags, DF_ISPROCESSING)
if(MC_TICK_CHECK)
return
@@ -127,7 +127,7 @@ SUBSYSTEM_DEF(machines)
else
global.processing_machines.Remove(M)
if(!QDELETED(M))
M.is_processing = null
DISABLE_BITFIELD(M.datum_flags, DF_ISPROCESSING)
if(MC_TICK_CHECK)
return
@@ -144,7 +144,7 @@ SUBSYSTEM_DEF(machines)
else
global.powernets.Remove(PN)
if(!QDELETED(PN))
PN.is_processing = null
DISABLE_BITFIELD(PN.datum_flags, DF_ISPROCESSING)
if(MC_TICK_CHECK)
return
@@ -160,7 +160,7 @@ SUBSYSTEM_DEF(machines)
current_run.len--
if(!I.pwr_drain(wait)) // 0 = Process Kill, remove from processing list.
global.processing_power_items.Remove(I)
I.is_processing = null
DISABLE_BITFIELD(I.datum_flags, DF_ISPROCESSING)
if(MC_TICK_CHECK)
return

View File

@@ -0,0 +1,30 @@
// Handles map-related tasks, mostly here to ensure it does so after the MC initializes.
SUBSYSTEM_DEF(mapping)
name = "Mapping"
init_order = INIT_ORDER_MAPPING
flags = SS_NO_FIRE
var/list/map_templates = list()
var/dmm_suite/maploader = null
/datum/controller/subsystem/mapping/Initialize(timeofday)
if(subsystem_initialized)
return
world.max_z_changed() // This is to set up the player z-level list, maxz hasn't actually changed (probably)
maploader = new()
load_map_templates()
if(config.generate_map)
// Map-gen is still very specific to the map, however putting it here should ensure it loads in the correct order.
if(using_map.perform_map_generation())
using_map.refresh_mining_turfs()
/datum/controller/subsystem/mapping/proc/load_map_templates()
for(var/T in subtypesof(/datum/map_template))
var/datum/map_template/template = T
if(!(initial(template.mappath))) // If it's missing the actual path its probably a base type or being used for inheritence.
continue
template = new T()
map_templates[template.name] = template
return TRUE

View File

@@ -7,8 +7,9 @@ SUBSYSTEM_DEF(mapping)
init_order = INIT_ORDER_MAPPING
flags = SS_NO_FIRE
var/list/map_templates = list()
var/dmm_suite/maploader = null
var/obj/effect/landmark/engine_loader/engine_loader
var/list/shelter_templates = list()
/datum/controller/subsystem/mapping/Recover()
@@ -16,6 +17,17 @@ SUBSYSTEM_DEF(mapping)
shelter_templates = SSmapping.shelter_templates
/datum/controller/subsystem/mapping/Initialize(timeofday)
if(subsystem_initialized)
return
world.max_z_changed() // This is to set up the player z-level list, maxz hasn't actually changed (probably)
maploader = new()
load_map_templates()
if(config.generate_map)
// Map-gen is still very specific to the map, however putting it here should ensure it loads in the correct order.
if(using_map.perform_map_generation())
using_map.refresh_mining_turfs()
loadEngine()
preloadShelterTemplates()
// Mining generation probably should be here too
@@ -24,6 +36,15 @@ SUBSYSTEM_DEF(mapping)
loadLateMaps()
..()
/datum/controller/subsystem/mapping/proc/load_map_templates()
for(var/T in subtypesof(/datum/map_template))
var/datum/map_template/template = T
if(!(initial(template.mappath))) // If it's missing the actual path its probably a base type or being used for inheritence.
continue
template = new T()
map_templates[template.name] = template
return TRUE
/datum/controller/subsystem/mapping/proc/loadEngine()
if(!engine_loader)
return // Seems this map doesn't need an engine loaded.

View File

@@ -0,0 +1,42 @@
SUBSYSTEM_DEF(nanoui)
name = "NanoUI"
wait = 20
// a list of current open /nanoui UIs, grouped by src_object and ui_key
var/list/open_uis = list()
// a list of current open /nanoui UIs, not grouped, for use in processing
var/list/processing_uis = list()
// a list of asset filenames which are to be sent to the client on user logon
var/list/asset_files = list()
/datum/controller/subsystem/nanoui/Initialize()
var/list/nano_asset_dirs = list(\
"nano/css/",\
"nano/images/",\
"nano/js/",\
"nano/templates/"\
)
var/list/filenames = null
for (var/path in nano_asset_dirs)
filenames = flist(path)
for(var/filename in filenames)
if(copytext(filename, length(filename)) != "/") // filenames which end in "/" are actually directories, which we want to ignore
if(fexists(path + filename))
asset_files.Add(fcopy_rsc(path + filename)) // add this file to asset_files for sending to clients when they connect
return ..()
/datum/controller/subsystem/nanoui/Recover()
if(SSnanoui.open_uis)
open_uis |= SSnanoui.open_uis
if(SSnanoui.processing_uis)
processing_uis |= SSnanoui.processing_uis
if(SSnanoui.asset_files)
asset_files |= SSnanoui.asset_files
/datum/controller/subsystem/nanoui/stat_entry()
return ..("[processing_uis.len] UIs")
/datum/controller/subsystem/nanoui/fire(resumed)
for(var/thing in processing_uis)
var/datum/nanoui/UI = thing
UI.process()

View File

@@ -168,7 +168,7 @@ var/global/image/appearance_bro = new() // Temporarily super-global because of B
* Adds specific overlay(s) to the atom.
* It is designed so any of the types allowed to be added to /atom/overlays can be added here too. More details below.
*
* @param overlays The overlay(s) to add. These may be
* @param overlays The overlay(s) to add. These may be
* - A string: In which case it is treated as an icon_state of the atom's icon.
* - An icon: It is treated as an icon.
* - An atom: Its own overlays are compiled and then it's appearance is added. (Meaning its current apperance is frozen).
@@ -205,7 +205,7 @@ var/global/image/appearance_bro = new() // Temporarily super-global because of B
/**
* Copy the overlays from another atom, either replacing all of ours or appending to our existing overlays.
* Note: This copies only the normal overlays, not the "priority" overlays.
*
*
* @param other The atom to copy overlays from.
* @param cut_old If true, all of our overlays will be *replaced* by the other's. If other is null, that means cutting all ours.
*/

View File

@@ -0,0 +1,43 @@
PROCESSING_SUBSYSTEM_DEF(chemistry)
name = "Chemistry"
wait = 20
flags = SS_BACKGROUND|SS_POST_FIRE_TIMING
init_order = INIT_ORDER_CHEMISTRY
var/list/chemical_reactions = list()
var/list/chemical_reagents = list()
/datum/controller/subsystem/processing/chemistry/Recover()
chemical_reactions = SSchemistry.chemical_reactions
chemical_reagents = SSchemistry.chemical_reagents
/datum/controller/subsystem/processing/chemistry/Initialize()
initialize_chemical_reactions()
initialize_chemical_reagents()
//Chemical Reactions - Initialises all /datum/chemical_reaction into a list
// It is filtered into multiple lists within a list.
// For example:
// chemical_reaction_list["phoron"] is a list of all reactions relating to phoron
// Note that entries in the list are NOT duplicated. So if a reaction pertains to
// more than one chemical it will still only appear in only one of the sublists.
/datum/controller/subsystem/processing/chemistry/proc/initialize_chemical_reactions()
var/paths = typesof(/datum/chemical_reaction) - /datum/chemical_reaction
SSchemistry.chemical_reactions = list()
for(var/path in paths)
var/datum/chemical_reaction/D = new path()
if(D.required_reagents && D.required_reagents.len)
var/reagent_id = D.required_reagents[1]
if(!chemical_reactions[reagent_id])
chemical_reactions[reagent_id] = list()
chemical_reactions[reagent_id] += D
//Chemical Reagents - Initialises all /datum/reagent into a list indexed by reagent id
/datum/controller/subsystem/processing/chemistry/proc/initialize_chemical_reagents()
var/paths = typesof(/datum/reagent) - /datum/reagent
chemical_reagents = list()
for(var/path in paths)
var/datum/reagent/D = new path()
if(!D.name)
continue
chemical_reagents[D.id] = D

View File

@@ -0,0 +1,6 @@
//Fires five times every second.
PROCESSING_SUBSYSTEM_DEF(fastprocess)
name = "Fast Processing"
wait = 2
stat_tag = "FP"

View File

@@ -0,0 +1,5 @@
PROCESSING_SUBSYSTEM_DEF(obj)
name = "Objects"
priority = FIRE_PRIORITY_OBJ
flags = SS_NO_INIT
wait = 20

View File

@@ -0,0 +1,35 @@
//Used to process objects. Fires once every second.
SUBSYSTEM_DEF(processing)
name = "Processing"
priority = FIRE_PRIORITY_PROCESS
flags = SS_BACKGROUND|SS_POST_FIRE_TIMING|SS_NO_INIT
wait = 10
var/stat_tag = "P" //Used for logging
var/list/processing = list()
var/list/currentrun = list()
/datum/controller/subsystem/processing/stat_entry()
..("[stat_tag]:[processing.len]")
/datum/controller/subsystem/processing/fire(resumed = 0)
if (!resumed)
currentrun = processing.Copy()
//cache for sanic speed (lists are references anyways)
var/list/current_run = currentrun
while(current_run.len)
var/datum/thing = current_run[current_run.len]
current_run.len--
if(QDELETED(thing))
processing -= thing
else if(thing.process(wait) == PROCESS_KILL)
// fully stop so that a future START_PROCESSING will work
STOP_PROCESSING(src, thing)
if (MC_TICK_CHECK)
return
/datum/proc/process()
set waitfor = 0
return PROCESS_KILL

View File

@@ -0,0 +1,3 @@
PROCESSING_SUBSYSTEM_DEF(turfs)
name = "Turf Processing"
wait = 20

View File

@@ -0,0 +1,7 @@
SUBSYSTEM_DEF(sun)
name = "Sun"
wait = 600
var/static/datum/sun/sun = new
/datum/controller/subsystem/sun/fire()
sun.calc_position()

View File

@@ -0,0 +1,37 @@
SUBSYSTEM_DEF(time_track)
name = "Time Tracking"
wait = 600
flags = SS_NO_INIT|SS_NO_TICK_CHECK
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
var/time_dilation_current = 0
var/time_dilation_avg_fast = 0
var/time_dilation_avg = 0
var/time_dilation_avg_slow = 0
var/first_run = TRUE
var/last_tick_realtime = 0
var/last_tick_byond_time = 0
var/last_tick_tickcount = 0
/datum/controller/subsystem/time_track/fire()
var/current_realtime = REALTIMEOFDAY
var/current_byondtime = world.time
var/current_tickcount = world.time/world.tick_lag
if (!first_run)
var/tick_drift = max(0, (((current_realtime - last_tick_realtime) - (current_byondtime - last_tick_byond_time)) / world.tick_lag))
time_dilation_current = tick_drift / (current_tickcount - last_tick_tickcount) * 100
time_dilation_avg_fast = MC_AVERAGE_FAST(time_dilation_avg_fast, time_dilation_current)
time_dilation_avg = MC_AVERAGE(time_dilation_avg, time_dilation_avg_fast)
time_dilation_avg_slow = MC_AVERAGE_SLOW(time_dilation_avg_slow, time_dilation_avg)
else
first_run = FALSE
last_tick_realtime = current_realtime
last_tick_byond_time = current_byondtime
last_tick_tickcount = current_tickcount

View File

@@ -0,0 +1,522 @@
#define BUCKET_LEN (world.fps*1*60) //how many ticks should we keep in the bucket. (1 minutes worth)
#define BUCKET_POS(timer) ((round((timer.timeToRun - SStimer.head_offset) / world.tick_lag) % BUCKET_LEN)||BUCKET_LEN)
#define TIMER_MAX (world.time + TICKS2DS(min(BUCKET_LEN-(SStimer.practical_offset-DS2TICKS(world.time - SStimer.head_offset))-1, BUCKET_LEN-1)))
#define TIMER_ID_MAX (2**24) //max float with integer precision
SUBSYSTEM_DEF(timer)
name = "Timer"
wait = 1 //SS_TICKER subsystem, so wait is in ticks
init_order = INIT_ORDER_TIMER
flags = SS_TICKER|SS_NO_INIT
var/list/datum/timedevent/second_queue = list() //awe, yes, you've had first queue, but what about second queue?
var/list/hashes = list()
var/head_offset = 0 //world.time of the first entry in the the bucket.
var/practical_offset = 1 //index of the first non-empty item in the bucket.
var/bucket_resolution = 0 //world.tick_lag the bucket was designed for
var/bucket_count = 0 //how many timers are in the buckets
var/list/bucket_list = list() //list of buckets, each bucket holds every timer that has to run that byond tick.
var/list/timer_id_dict = list() //list of all active timers assoicated to their timer id (for easy lookup)
var/list/clienttime_timers = list() //special snowflake timers that run on fancy pansy "client time"
var/last_invoke_tick = 0
var/static/last_invoke_warning = 0
var/static/bucket_auto_reset = TRUE
/datum/controller/subsystem/timer/PreInit()
bucket_list.len = BUCKET_LEN
head_offset = world.time
bucket_resolution = world.tick_lag
/datum/controller/subsystem/timer/stat_entry(msg)
..("B:[bucket_count] P:[length(second_queue)] H:[length(hashes)] C:[length(clienttime_timers)] S:[length(timer_id_dict)]")
/datum/controller/subsystem/timer/fire(resumed = FALSE)
var/lit = last_invoke_tick
var/last_check = world.time - TICKS2DS(BUCKET_LEN*1.5)
var/list/bucket_list = src.bucket_list
if(!bucket_count)
last_invoke_tick = world.time
if(lit && lit < last_check && head_offset < last_check && last_invoke_warning < last_check)
last_invoke_warning = world.time
var/msg = "No regular timers processed in the last [BUCKET_LEN*1.5] ticks[bucket_auto_reset ? ", resetting buckets" : ""]!"
message_admins(msg)
WARNING(msg)
if(bucket_auto_reset)
bucket_resolution = 0
log_world("Timer bucket reset. world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
for (var/i in 1 to length(bucket_list))
var/datum/timedevent/bucket_head = bucket_list[i]
if (!bucket_head)
continue
log_world("Active timers at index [i]:")
var/datum/timedevent/bucket_node = bucket_head
var/anti_loop_check = 1000
do
log_world(get_timer_debug_string(bucket_node))
bucket_node = bucket_node.next
anti_loop_check--
while(bucket_node && bucket_node != bucket_head && anti_loop_check)
log_world("Active timers in the second_queue queue:")
for(var/I in second_queue)
log_world(get_timer_debug_string(I))
var/cut_start_index = 1
var/next_clienttime_timer_index = 0
var/len = length(clienttime_timers)
for (next_clienttime_timer_index in 1 to len)
if (MC_TICK_CHECK)
next_clienttime_timer_index--
break
var/datum/timedevent/ctime_timer = clienttime_timers[next_clienttime_timer_index]
if (ctime_timer.timeToRun > REALTIMEOFDAY)
next_clienttime_timer_index--
break
var/datum/callback/callBack = ctime_timer.callBack
if (!callBack)
clienttime_timers.Cut(next_clienttime_timer_index,next_clienttime_timer_index+1)
CRASH("Invalid timer: [get_timer_debug_string(ctime_timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset], REALTIMEOFDAY: [REALTIMEOFDAY]")
ctime_timer.spent = REALTIMEOFDAY
callBack.InvokeAsync()
if(ctime_timer.flags & TIMER_LOOP)
ctime_timer.spent = 0
clienttime_timers.Insert(ctime_timer, 1)
cut_start_index++
else
qdel(ctime_timer)
if (next_clienttime_timer_index)
clienttime_timers.Cut(cut_start_index,next_clienttime_timer_index+1)
if (MC_TICK_CHECK)
return
var/static/list/spent = list()
var/static/datum/timedevent/timer
if (practical_offset > BUCKET_LEN)
head_offset += TICKS2DS(BUCKET_LEN)
practical_offset = 1
resumed = FALSE
if ((length(bucket_list) != BUCKET_LEN) || (world.tick_lag != bucket_resolution))
reset_buckets()
bucket_list = src.bucket_list
resumed = FALSE
if (!resumed)
timer = null
while (practical_offset <= BUCKET_LEN && head_offset + ((practical_offset-1)*world.tick_lag) <= world.time)
var/datum/timedevent/head = bucket_list[practical_offset]
if (!timer || !head || timer == head)
head = bucket_list[practical_offset]
timer = head
while (timer)
var/datum/callback/callBack = timer.callBack
if (!callBack)
bucket_resolution = null //force bucket recreation
CRASH("Invalid timer: [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
if (!timer.spent)
spent += timer
timer.spent = world.time
callBack.InvokeAsync()
last_invoke_tick = world.time
if (MC_TICK_CHECK)
return
timer = timer.next
if (timer == head)
break
bucket_list[practical_offset++] = null
//we freed up a bucket, lets see if anything in second_queue needs to be shifted to that bucket.
var/i = 0
var/L = length(second_queue)
for (i in 1 to L)
timer = second_queue[i]
if (timer.timeToRun >= TIMER_MAX)
i--
break
if (timer.timeToRun < head_offset)
bucket_resolution = null //force bucket recreation
CRASH("[i] Invalid timer state: Timer in long run queue with a time to run less then head_offset. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
if (timer.callBack && !timer.spent)
timer.callBack.InvokeAsync()
spent += timer
bucket_count++
else if(!QDELETED(timer))
qdel(timer)
continue
if (timer.timeToRun < head_offset + TICKS2DS(practical_offset-1))
bucket_resolution = null //force bucket recreation
CRASH("[i] Invalid timer state: Timer in long run queue that would require a backtrack to transfer to short run queue. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
if (timer.callBack && !timer.spent)
timer.callBack.InvokeAsync()
spent += timer
bucket_count++
else if(!QDELETED(timer))
qdel(timer)
continue
bucket_count++
var/bucket_pos = max(1, BUCKET_POS(timer))
var/datum/timedevent/bucket_head = bucket_list[bucket_pos]
if (!bucket_head)
bucket_list[bucket_pos] = timer
timer.next = null
timer.prev = null
continue
if (!bucket_head.prev)
bucket_head.prev = bucket_head
timer.next = bucket_head
timer.prev = bucket_head.prev
timer.next.prev = timer
timer.prev.next = timer
if (i)
second_queue.Cut(1, i+1)
timer = null
bucket_count -= length(spent)
for (var/i in spent)
var/datum/timedevent/qtimer = i
if(QDELETED(qtimer))
bucket_count++
continue
if(!(qtimer.flags & TIMER_LOOP))
qdel(qtimer)
else
bucket_count++
qtimer.spent = 0
qtimer.bucketEject()
if(qtimer.flags & TIMER_CLIENT_TIME)
qtimer.timeToRun = REALTIMEOFDAY + qtimer.wait
else
qtimer.timeToRun = world.time + qtimer.wait
qtimer.bucketJoin()
spent.len = 0
//formated this way to be runtime resistant
/datum/controller/subsystem/timer/proc/get_timer_debug_string(datum/timedevent/TE)
. = "Timer: [TE]"
. += "Prev: [TE.prev ? TE.prev : "NULL"], Next: [TE.next ? TE.next : "NULL"]"
if(TE.spent)
. += ", SPENT([TE.spent])"
if(QDELETED(TE))
. += ", QDELETED"
if(!TE.callBack)
. += ", NO CALLBACK"
/datum/controller/subsystem/timer/proc/reset_buckets()
var/list/bucket_list = src.bucket_list
var/list/alltimers = list()
//collect the timers currently in the bucket
for (var/bucket_head in bucket_list)
if (!bucket_head)
continue
var/datum/timedevent/bucket_node = bucket_head
do
alltimers += bucket_node
bucket_node = bucket_node.next
while(bucket_node && bucket_node != bucket_head)
bucket_list.len = 0
bucket_list.len = BUCKET_LEN
practical_offset = 1
bucket_count = 0
head_offset = world.time
bucket_resolution = world.tick_lag
alltimers += second_queue
if (!length(alltimers))
return
sortTim(alltimers, .proc/cmp_timer)
var/datum/timedevent/head = alltimers[1]
if (head.timeToRun < head_offset)
head_offset = head.timeToRun
var/new_bucket_count
var/i = 1
for (i in 1 to length(alltimers))
var/datum/timedevent/timer = alltimers[1]
if (!timer)
continue
var/bucket_pos = BUCKET_POS(timer)
if (timer.timeToRun >= TIMER_MAX)
i--
break
if (!timer.callBack || timer.spent)
WARNING("Invalid timer: [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
if (timer.callBack)
qdel(timer)
continue
new_bucket_count++
var/datum/timedevent/bucket_head = bucket_list[bucket_pos]
if (!bucket_head)
bucket_list[bucket_pos] = timer
timer.next = null
timer.prev = null
continue
if (!bucket_head.prev)
bucket_head.prev = bucket_head
timer.next = bucket_head
timer.prev = bucket_head.prev
timer.next.prev = timer
timer.prev.next = timer
if (i)
alltimers.Cut(1, i+1)
second_queue = alltimers
bucket_count = new_bucket_count
/datum/controller/subsystem/timer/Recover()
second_queue |= SStimer.second_queue
hashes |= SStimer.hashes
timer_id_dict |= SStimer.timer_id_dict
bucket_list |= SStimer.bucket_list
/datum/timedevent
var/id
var/datum/callback/callBack
var/timeToRun
var/wait
var/hash
var/list/flags
var/spent = 0 //time we ran the timer.
var/name //for easy debugging.
//cicular doublely linked list
var/datum/timedevent/next
var/datum/timedevent/prev
/datum/timedevent/New(datum/callback/callBack, wait, flags, hash)
var/static/nextid = 1
id = TIMER_ID_NULL
src.callBack = callBack
src.wait = wait
src.flags = flags
src.hash = hash
if (flags & TIMER_CLIENT_TIME)
timeToRun = REALTIMEOFDAY + wait
else
timeToRun = world.time + wait
if (flags & TIMER_UNIQUE)
SStimer.hashes[hash] = src
if (flags & TIMER_STOPPABLE)
id = num2text(nextid, 100)
if (nextid >= SHORT_REAL_LIMIT)
nextid += min(1, 2**round(nextid/SHORT_REAL_LIMIT))
else
nextid++
SStimer.timer_id_dict[id] = src
name = "Timer: [id] (\ref[src]), TTR: [timeToRun], Flags: [jointext(bitfield2list(flags, list("TIMER_UNIQUE", "TIMER_OVERRIDE", "TIMER_CLIENT_TIME", "TIMER_STOPPABLE", "TIMER_NO_HASH_WAIT", "TIMER_LOOP")), ", ")], callBack: \ref[callBack], callBack.object: [callBack.object]\ref[callBack.object]([getcallingtype()]), callBack.delegate:[callBack.delegate]([callBack.arguments ? callBack.arguments.Join(", ") : ""])"
if ((timeToRun < world.time || timeToRun < SStimer.head_offset) && !(flags & TIMER_CLIENT_TIME))
CRASH("Invalid timer state: Timer created that would require a backtrack to run (addtimer would never let this happen): [SStimer.get_timer_debug_string(src)]")
if (callBack.object != GLOBAL_PROC && !QDESTROYING(callBack.object))
LAZYADD(callBack.object.active_timers, src)
bucketJoin()
/datum/timedevent/Destroy()
..()
if (flags & TIMER_UNIQUE && hash)
SStimer.hashes -= hash
if (callBack && callBack.object && callBack.object != GLOBAL_PROC && callBack.object.active_timers)
callBack.object.active_timers -= src
UNSETEMPTY(callBack.object.active_timers)
callBack = null
if (flags & TIMER_STOPPABLE)
SStimer.timer_id_dict -= id
if (flags & TIMER_CLIENT_TIME)
if (!spent)
spent = world.time
SStimer.clienttime_timers -= src
return QDEL_HINT_IWILLGC
if (!spent)
spent = world.time
bucketEject()
else
if (prev && prev.next == src)
prev.next = next
if (next && next.prev == src)
next.prev = prev
next = null
prev = null
return QDEL_HINT_IWILLGC
/datum/timedevent/proc/bucketEject()
var/bucketpos = BUCKET_POS(src)
var/list/bucket_list = SStimer.bucket_list
var/list/second_queue = SStimer.second_queue
var/datum/timedevent/buckethead
if(bucketpos > 0)
buckethead = bucket_list[bucketpos]
if(buckethead == src)
bucket_list[bucketpos] = next
SStimer.bucket_count--
else if(timeToRun < TIMER_MAX || next || prev)
SStimer.bucket_count--
else
var/l = length(second_queue)
second_queue -= src
if(l == length(second_queue))
SStimer.bucket_count--
if(prev != next)
prev.next = next
next.prev = prev
else
prev?.next = null
next?.prev = null
prev = next = null
/datum/timedevent/proc/bucketJoin()
var/list/L
if (flags & TIMER_CLIENT_TIME)
L = SStimer.clienttime_timers
else if (timeToRun >= TIMER_MAX)
L = SStimer.second_queue
if(L)
BINARY_INSERT(src, L, datum/timedevent, timeToRun)
return
//get the list of buckets
var/list/bucket_list = SStimer.bucket_list
//calculate our place in the bucket list
var/bucket_pos = BUCKET_POS(src)
//get the bucket for our tick
var/datum/timedevent/bucket_head = bucket_list[bucket_pos]
SStimer.bucket_count++
//empty bucket, we will just add ourselves
if (!bucket_head)
bucket_list[bucket_pos] = src
return
//other wise, lets do a simplified linked list add.
if (!bucket_head.prev)
bucket_head.prev = bucket_head
next = bucket_head
prev = bucket_head.prev
next.prev = src
prev.next = src
/datum/timedevent/proc/getcallingtype()
. = "ERROR"
if (callBack.object == GLOBAL_PROC)
. = "GLOBAL_PROC"
else
. = "[callBack.object.type]"
/proc/addtimer(datum/callback/callback, wait = 0, flags = 0)
if (!callback)
CRASH("addtimer called without a callback")
if (wait < 0)
crash_with("addtimer called with a negative wait. Converting to [world.tick_lag]")
if (callback.object != GLOBAL_PROC && QDELETED(callback.object) && !QDESTROYING(callback.object))
crash_with("addtimer called with a callback assigned to a qdeleted object. In the future such timers will not be supported and may refuse to run or run with a 0 wait")
wait = max(CEILING(wait, world.tick_lag), world.tick_lag)
if(wait >= INFINITY)
CRASH("Attempted to create timer with INFINITY delay")
var/hash
if (flags & TIMER_UNIQUE)
var/list/hashlist
if(flags & TIMER_NO_HASH_WAIT)
hashlist = list(callback.object, "(\ref[callback.object])", callback.delegate, flags & TIMER_CLIENT_TIME)
else
hashlist = list(callback.object, "(\ref[callback.object])", callback.delegate, wait, flags & TIMER_CLIENT_TIME)
hashlist += callback.arguments
hash = hashlist.Join("|||||||")
var/datum/timedevent/hash_timer = SStimer.hashes[hash]
if(hash_timer)
if (hash_timer.spent) //it's pending deletion, pretend it doesn't exist.
hash_timer.hash = null //but keep it from accidentally deleting us
else
if (flags & TIMER_OVERRIDE)
hash_timer.hash = null //no need having it delete it's hash if we are going to replace it
qdel(hash_timer)
else
if (hash_timer.flags & TIMER_STOPPABLE)
. = hash_timer.id
return
else if(flags & TIMER_OVERRIDE)
crash_with("TIMER_OVERRIDE used without TIMER_UNIQUE")
var/datum/timedevent/timer = new(callback, wait, flags, hash)
return timer.id
/proc/deltimer(id)
if (!id)
return FALSE
if (id == TIMER_ID_NULL)
CRASH("Tried to delete a null timerid. Use TIMER_STOPPABLE flag")
if (!istext(id))
if (istype(id, /datum/timedevent))
qdel(id)
return TRUE
//id is string
var/datum/timedevent/timer = SStimer.timer_id_dict[id]
if (timer && !timer.spent)
qdel(timer)
return TRUE
return FALSE
#undef BUCKET_LEN
#undef BUCKET_POS
#undef TIMER_MAX
#undef TIMER_ID_MAX

View File

@@ -1,373 +1,373 @@
SUBSYSTEM_DEF(vote)
name = "Vote"
wait = 10
priority = FIRE_PRIORITY_VOTE
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
flags = SS_KEEP_TIMING | SS_NO_INIT
var/list/round_voters = list()
//Current vote
var/initiator
var/started_time
var/time_remaining
var/duration
var/mode
var/question
var/list/choices = list()
var/list/gamemode_names = list()
var/list/voted = list()
var/list/current_votes = list()
var/list/additional_text = list()
/datum/controller/subsystem/vote/fire(resumed)
if(mode)
time_remaining = round((started_time + duration - world.time)/10)
if(mode == VOTE_GAMEMODE && ticker.current_state >= GAME_STATE_SETTING_UP)
to_chat(world, "<b>Gamemode vote aborted: Game has already started.</b>")
reset()
return
if(time_remaining <= 0)
result()
reset()
/datum/controller/subsystem/vote/proc/autotransfer()
initiate_vote(VOTE_CREW_TRANSFER, "the server", 1)
log_debug("The server has called a crew transfer vote.")
/datum/controller/subsystem/vote/proc/autogamemode()
initiate_vote(VOTE_GAMEMODE, "the server", 1)
log_debug("The server has called a gamemode vote.")
/datum/controller/subsystem/vote/proc/reset()
initiator = null
started_time = null
duration = null
time_remaining = null
mode = null
question = null
choices.Cut()
voted.Cut()
current_votes.Cut()
additional_text.Cut()
/datum/controller/subsystem/vote/proc/get_result() // Get the highest number of votes
var/greatest_votes = 0
var/total_votes = 0
for(var/option in choices)
var/votes = choices[option]
total_votes += votes
if(votes > greatest_votes)
greatest_votes = votes
if(!config.vote_no_default && choices.len) // Default-vote for everyone who didn't vote
var/non_voters = (clients.len - total_votes)
if(non_voters > 0)
if(mode == VOTE_RESTART)
choices["Continue Playing"] += non_voters
if(choices["Continue Playing"] >= greatest_votes)
greatest_votes = choices["Continue Playing"]
else if(mode == VOTE_GAMEMODE)
if(master_mode in choices)
choices[master_mode] += non_voters
if(choices[master_mode] >= greatest_votes)
greatest_votes = choices[master_mode]
else if(mode == VOTE_CREW_TRANSFER)
var/factor = 0.5
switch(world.time / (10 * 60)) // minutes
if(0 to 60)
factor = 0.5
if(61 to 120)
factor = 0.8
if(121 to 240)
factor = 1
if(241 to 300)
factor = 1.2
else
factor = 1.4
choices["Initiate Crew Transfer"] = round(choices["Initiate Crew Transfer"] * factor)
world << "<font color='purple'>Crew Transfer Factor: [factor]</font>"
greatest_votes = max(choices["Initiate Crew Transfer"], choices["Continue The Round"])
. = list() // Get all options with that many votes and return them in a list
if(greatest_votes)
for(var/option in choices)
if(choices[option] == greatest_votes)
. += option
/datum/controller/subsystem/vote/proc/announce_result()
var/list/winners = get_result()
var/text
if(winners.len > 0)
if(winners.len > 1)
if(mode != VOTE_GAMEMODE || ticker.hide_mode == 0) // Here we are making sure we don't announce potential game modes
text = "<b>Vote Tied Between:</b>\n"
for(var/option in winners)
text += "\t[option]\n"
. = pick(winners)
for(var/key in current_votes)
if(choices[current_votes[key]] == .)
round_voters += key // Keep track of who voted for the winning round.
if(mode != VOTE_GAMEMODE || . == "Extended" || ticker.hide_mode == 0) // Announce Extended gamemode, but not other gamemodes
text += "<b>Vote Result: [mode == VOTE_GAMEMODE ? gamemode_names[.] : .]</b>"
else
text += "<b>The vote has ended.</b>"
else
text += "<b>Vote Result: Inconclusive - No Votes!</b>"
if(mode == VOTE_ADD_ANTAGONIST)
antag_add_failed = 1
log_vote(text)
to_chat(world, "<font color='purple'>[text]</font>")
/datum/controller/subsystem/vote/proc/result()
. = announce_result()
var/restart = 0
if(.)
switch(mode)
if(VOTE_RESTART)
if(. == "Restart Round")
restart = 1
if(VOTE_GAMEMODE)
if(master_mode != .)
world.save_mode(.)
if(ticker && ticker.mode)
restart = 1
else
master_mode = .
if(VOTE_CREW_TRANSFER)
if(. == "Initiate Crew Transfer")
init_shift_change(null, 1)
if(VOTE_ADD_ANTAGONIST)
if(isnull(.) || . == "None")
antag_add_failed = 1
else
additional_antag_types |= antag_names_to_ids[.]
if(mode == VOTE_GAMEMODE) //fire this even if the vote fails.
if(!round_progressing)
round_progressing = 1
world << "<font color='red'><b>The round will start soon.</b></font>"
if(restart)
world << "World restarting due to vote..."
feedback_set_details("end_error", "restart vote")
if(blackbox)
blackbox.save_all_data_to_sql()
sleep(50)
log_game("Rebooting due to restart vote")
world.Reboot()
/datum/controller/subsystem/vote/proc/submit_vote(ckey, newVote)
if(mode)
if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder)
return
if(current_votes[ckey])
choices[choices[current_votes[ckey]]]--
if(newVote && newVote >= 1 && newVote <= choices.len)
choices[choices[newVote]]++
current_votes[ckey] = newVote
else
current_votes[ckey] = null
/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, automatic = FALSE, time = config.vote_period)
if(!mode)
if(started_time != null && !(check_rights(R_ADMIN) || automatic))
var/next_allowed_time = (started_time + config.vote_delay)
if(next_allowed_time > world.time)
return 0
reset()
switch(vote_type)
if(VOTE_RESTART)
choices.Add("Restart Round", "Continue Playing")
if(VOTE_GAMEMODE)
if(ticker.current_state >= GAME_STATE_SETTING_UP)
return 0
choices.Add(config.votable_modes)
for(var/F in choices)
var/datum/game_mode/M = gamemode_cache[F]
if(!M)
continue
gamemode_names[M.config_tag] = capitalize(M.name) //It's ugly to put this here but it works
additional_text.Add("<td align = 'center'>[M.required_players]</td>")
gamemode_names["secret"] = "Secret"
if(VOTE_CREW_TRANSFER)
if(!check_rights(R_ADMIN|R_MOD, 0)) // The gods care not for the affairs of the mortals
if(get_security_level() == "red" || get_security_level() == "delta")
initiator_key << "The current alert status is too high to call for a crew transfer!"
return 0
if(ticker.current_state <= GAME_STATE_SETTING_UP)
initiator_key << "The crew transfer button has been disabled!"
return 0
question = "End the shift?"
choices.Add("Initiate Crew Transfer", "Continue The Round")
if(VOTE_ADD_ANTAGONIST)
if(!config.allow_extra_antags || ticker.current_state >= GAME_STATE_SETTING_UP)
return 0
for(var/antag_type in all_antag_types)
var/datum/antagonist/antag = all_antag_types[antag_type]
if(!(antag.id in additional_antag_types) && antag.is_votable())
choices.Add(antag.role_text)
choices.Add("None")
if(VOTE_CUSTOM)
question = sanitizeSafe(input(usr, "What is the vote for?") as text|null)
if(!question)
return 0
for(var/i = 1 to 10)
var/option = capitalize(sanitize(input(usr, "Please enter an option or hit cancel to finish") as text|null))
if(!option || mode || !usr.client)
break
choices.Add(option)
else
return 0
mode = vote_type
initiator = initiator_key
started_time = world.time
duration = time
var/text = "[capitalize(mode)] vote started by [initiator]."
if(mode == VOTE_CUSTOM)
text += "\n[question]"
log_vote(text)
world << "<font color='purple'><b>[text]</b>\nType <b>vote</b> or click <a href='?src=\ref[src]'>here</a> to place your votes.\nYou have [config.vote_period / 10] seconds to vote.</font>"
if(vote_type == VOTE_CREW_TRANSFER || vote_type == VOTE_GAMEMODE || vote_type == VOTE_CUSTOM)
world << sound('sound/ambience/alarm4.ogg', repeat = 0, wait = 0, volume = 50, channel = 3)
if(mode == VOTE_GAMEMODE && round_progressing)
round_progressing = 0
world << "<font color='red'><b>Round start has been delayed.</b></font>"
time_remaining = round(config.vote_period / 10)
return 1
return 0
/datum/controller/subsystem/vote/proc/interface(var/client/C)
if(!istype(C))
return
var/admin = FALSE
if(C.holder)
if(C.holder.rights & R_ADMIN)
admin = TRUE
. = "<html><head><title>Voting Panel</title></head><body>"
if(mode)
if(question)
. += "<h2>Vote: '[question]'</h2>"
else
. += "<h2>Vote: [capitalize(mode)]</h2>"
. += "Time Left: [time_remaining] s<hr>"
. += "<table width = '100%'><tr><td align = 'center'><b>Choices</b></td><td align = 'center'><b>Votes</b></td>"
if(mode == VOTE_GAMEMODE)
.+= "<td align = 'center'><b>Minimum Players</b></td></tr>"
for(var/i = 1 to choices.len)
var/votes = choices[choices[i]]
if(!votes)
votes = 0
. += "<tr>"
var/thisVote = (current_votes[C.ckey] == i)
if(mode == VOTE_GAMEMODE)
. += "<td>[thisVote ? "<b>" : ""]<a href='?src=\ref[src];vote=[i]'>[gamemode_names[choices[i]]]</a>[thisVote ? "</b>" : ""]</td><td align = 'center'>[votes]</td>"
else
. += "<td>[thisVote ? "<b>" : ""]<a href='?src=\ref[src];vote=[i]'>[choices[i]]</a>[thisVote ? "</b>" : ""]</td><td align = 'center'>[votes]</td>"
if (additional_text.len >= i)
. += additional_text[i]
. += "</tr>"
. += "<tr><td><a href='?src=\ref[src];vote=unvote'>Unvote</a></td></tr>"
. += "</table><hr>"
if(admin)
. += "(<a href='?src=\ref[src];vote=cancel'>Cancel Vote</a>) "
else
. += "<h2>Start a vote:</h2><hr><ul><li>"
if(admin || config.allow_vote_restart)
. += "<a href='?src=\ref[src];vote=restart'>Restart</a>"
else
. += "<font color='grey'>Restart (Disallowed)</font>"
. += "</li><li>"
if(admin || config.allow_vote_restart)
. += "<a href='?src=\ref[src];vote=crew_transfer'>Crew Transfer</a>"
else
. += "<font color='grey'>Crew Transfer (Disallowed)</font>"
if(admin)
. += "\t(<a href='?src=\ref[src];vote=toggle_restart'>[config.allow_vote_restart ? "Allowed" : "Disallowed"]</a>)"
. += "</li><li>"
if(admin || config.allow_vote_mode)
. += "<a href='?src=\ref[src];vote=gamemode'>GameMode</a>"
else
. += "<font color='grey'>GameMode (Disallowed)</font>"
if(admin)
. += "\t(<a href='?src=\ref[src];vote=toggle_gamemode'>[config.allow_vote_mode ? "Allowed" : "Disallowed"]</a>)"
. += "</li><li>"
if(!antag_add_failed && config.allow_extra_antags)
. += "<a href='?src=\ref[src];vote=add_antagonist'>Add Antagonist Type</a>"
else
. += "<font color='grey'>Add Antagonist (Disallowed)</font>"
. += "</li>"
if(admin)
. += "<li><a href='?src=\ref[src];vote=custom'>Custom</a></li>"
. += "</ul><hr>"
. += "<a href='?src=\ref[src];vote=close' style='position:absolute;right:50px'>Close</a></body></html>"
/datum/controller/subsystem/vote/Topic(href, href_list[])
if(!usr || !usr.client)
return
switch(href_list["vote"])
if("close")
usr << browse(null, "window=vote")
return
if("cancel")
if(usr.client.holder)
reset()
if("toggle_restart")
if(usr.client.holder)
config.allow_vote_restart = !config.allow_vote_restart
if("toggle_gamemode")
if(usr.client.holder)
config.allow_vote_mode = !config.allow_vote_mode
if(VOTE_RESTART)
if(config.allow_vote_restart || usr.client.holder)
initiate_vote(VOTE_RESTART, usr.key)
if(VOTE_GAMEMODE)
if(config.allow_vote_mode || usr.client.holder)
initiate_vote(VOTE_GAMEMODE, usr.key)
if(VOTE_CREW_TRANSFER)
if(config.allow_vote_restart || usr.client.holder)
initiate_vote(VOTE_CREW_TRANSFER, usr.key)
if(VOTE_ADD_ANTAGONIST)
if(config.allow_extra_antags || usr.client.holder)
initiate_vote(VOTE_ADD_ANTAGONIST, usr.key)
if(VOTE_CUSTOM)
if(usr.client.holder)
initiate_vote(VOTE_CUSTOM, usr.key)
if("unvote")
submit_vote(usr.ckey, null)
else
var/t = round(text2num(href_list["vote"]))
if(t) // It starts from 1, so there's no problem
submit_vote(usr.ckey, t)
usr.client.vote()
/client/verb/vote()
set category = "OOC"
set name = "Vote"
if(SSvote)
src << browse(SSvote.interface(src), "window=vote;size=500x[300 + SSvote.choices.len * 25]")
SUBSYSTEM_DEF(vote)
name = "Vote"
wait = 10
priority = FIRE_PRIORITY_VOTE
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
flags = SS_KEEP_TIMING | SS_NO_INIT
var/list/round_voters = list()
//Current vote
var/initiator
var/started_time
var/time_remaining
var/duration
var/mode
var/question
var/list/choices = list()
var/list/gamemode_names = list()
var/list/voted = list()
var/list/current_votes = list()
var/list/additional_text = list()
/datum/controller/subsystem/vote/fire(resumed)
if(mode)
time_remaining = round((started_time + duration - world.time)/10)
if(mode == VOTE_GAMEMODE && ticker.current_state >= GAME_STATE_SETTING_UP)
to_chat(world, "<b>Gamemode vote aborted: Game has already started.</b>")
reset()
return
if(time_remaining <= 0)
result()
reset()
/datum/controller/subsystem/vote/proc/autotransfer()
initiate_vote(VOTE_CREW_TRANSFER, "the server", 1)
log_debug("The server has called a crew transfer vote.")
/datum/controller/subsystem/vote/proc/autogamemode()
initiate_vote(VOTE_GAMEMODE, "the server", 1)
log_debug("The server has called a gamemode vote.")
/datum/controller/subsystem/vote/proc/reset()
initiator = null
started_time = null
duration = null
time_remaining = null
mode = null
question = null
choices.Cut()
voted.Cut()
current_votes.Cut()
additional_text.Cut()
/datum/controller/subsystem/vote/proc/get_result() // Get the highest number of votes
var/greatest_votes = 0
var/total_votes = 0
for(var/option in choices)
var/votes = choices[option]
total_votes += votes
if(votes > greatest_votes)
greatest_votes = votes
if(!config.vote_no_default && choices.len) // Default-vote for everyone who didn't vote
var/non_voters = (GLOB.clients.len - total_votes)
if(non_voters > 0)
if(mode == VOTE_RESTART)
choices["Continue Playing"] += non_voters
if(choices["Continue Playing"] >= greatest_votes)
greatest_votes = choices["Continue Playing"]
else if(mode == VOTE_GAMEMODE)
if(master_mode in choices)
choices[master_mode] += non_voters
if(choices[master_mode] >= greatest_votes)
greatest_votes = choices[master_mode]
else if(mode == VOTE_CREW_TRANSFER)
var/factor = 0.5
switch(world.time / (10 * 60)) // minutes
if(0 to 60)
factor = 0.5
if(61 to 120)
factor = 0.8
if(121 to 240)
factor = 1
if(241 to 300)
factor = 1.2
else
factor = 1.4
choices["Initiate Crew Transfer"] = round(choices["Initiate Crew Transfer"] * factor)
world << "<font color='purple'>Crew Transfer Factor: [factor]</font>"
greatest_votes = max(choices["Initiate Crew Transfer"], choices["Continue The Round"])
. = list() // Get all options with that many votes and return them in a list
if(greatest_votes)
for(var/option in choices)
if(choices[option] == greatest_votes)
. += option
/datum/controller/subsystem/vote/proc/announce_result()
var/list/winners = get_result()
var/text
if(winners.len > 0)
if(winners.len > 1)
if(mode != VOTE_GAMEMODE || ticker.hide_mode == 0) // Here we are making sure we don't announce potential game modes
text = "<b>Vote Tied Between:</b>\n"
for(var/option in winners)
text += "\t[option]\n"
. = pick(winners)
for(var/key in current_votes)
if(choices[current_votes[key]] == .)
round_voters += key // Keep track of who voted for the winning round.
if(mode != VOTE_GAMEMODE || . == "Extended" || ticker.hide_mode == 0) // Announce Extended gamemode, but not other gamemodes
text += "<b>Vote Result: [mode == VOTE_GAMEMODE ? gamemode_names[.] : .]</b>"
else
text += "<b>The vote has ended.</b>"
else
text += "<b>Vote Result: Inconclusive - No Votes!</b>"
if(mode == VOTE_ADD_ANTAGONIST)
antag_add_failed = 1
log_vote(text)
to_chat(world, "<font color='purple'>[text]</font>")
/datum/controller/subsystem/vote/proc/result()
. = announce_result()
var/restart = 0
if(.)
switch(mode)
if(VOTE_RESTART)
if(. == "Restart Round")
restart = 1
if(VOTE_GAMEMODE)
if(master_mode != .)
world.save_mode(.)
if(ticker && ticker.mode)
restart = 1
else
master_mode = .
if(VOTE_CREW_TRANSFER)
if(. == "Initiate Crew Transfer")
init_shift_change(null, 1)
if(VOTE_ADD_ANTAGONIST)
if(isnull(.) || . == "None")
antag_add_failed = 1
else
additional_antag_types |= antag_names_to_ids[.]
if(mode == VOTE_GAMEMODE) //fire this even if the vote fails.
if(!round_progressing)
round_progressing = 1
world << "<font color='red'><b>The round will start soon.</b></font>"
if(restart)
world << "World restarting due to vote..."
feedback_set_details("end_error", "restart vote")
if(blackbox)
blackbox.save_all_data_to_sql()
sleep(50)
log_game("Rebooting due to restart vote")
world.Reboot()
/datum/controller/subsystem/vote/proc/submit_vote(ckey, newVote)
if(mode)
if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder)
return
if(current_votes[ckey])
choices[choices[current_votes[ckey]]]--
if(newVote && newVote >= 1 && newVote <= choices.len)
choices[choices[newVote]]++
current_votes[ckey] = newVote
else
current_votes[ckey] = null
/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, automatic = FALSE, time = config.vote_period)
if(!mode)
if(started_time != null && !(check_rights(R_ADMIN) || automatic))
var/next_allowed_time = (started_time + config.vote_delay)
if(next_allowed_time > world.time)
return 0
reset()
switch(vote_type)
if(VOTE_RESTART)
choices.Add("Restart Round", "Continue Playing")
if(VOTE_GAMEMODE)
if(ticker.current_state >= GAME_STATE_SETTING_UP)

View File

@@ -63,76 +63,57 @@
usr.client.debug_variables(antag)
message_admins("Admin [key_name_admin(usr)] is debugging the [antag.role_text] template.")
/client/proc/debug_controller(controller in list("Master","Ticker","Ticker Process","Air","Jobs","Sun","Radio","Supply","Shuttles","Emergency Shuttle","Configuration","pAI", "Cameras", "Transfer Controller", "Gas Data","Event","Plants","Alarm","Nano","Chemistry","Vote","Xenobio","Planets"))
/client/proc/debug_controller()
set category = "Debug"
set name = "Debug Controller"
set desc = "Debug the various periodic loop controllers for the game (be careful!)"
set desc = "Debug the various subsystems/controllers for the game (be careful!)"
if(!holder) return
switch(controller)
if("Master")
debug_variables(master_controller)
feedback_add_details("admin_verb","DMC")
if("Ticker")
debug_variables(ticker)
feedback_add_details("admin_verb","DTicker")
if("Ticker Process")
debug_variables(tickerProcess)
feedback_add_details("admin_verb","DTickerProcess")
if("Air")
debug_variables(air_master)
feedback_add_details("admin_verb","DAir")
if("Jobs")
debug_variables(job_master)
feedback_add_details("admin_verb","DJobs")
if("Sun")
debug_variables(sun)
feedback_add_details("admin_verb","DSun")
if("Radio")
debug_variables(radio_controller)
feedback_add_details("admin_verb","DRadio")
if("Supply")
debug_variables(supply_controller)
feedback_add_details("admin_verb","DSupply")
if("Shuttles")
debug_variables(shuttle_controller)
feedback_add_details("admin_verb","DShuttles")
if("Emergency Shuttle")
debug_variables(emergency_shuttle)
feedback_add_details("admin_verb","DEmergency")
if("Configuration")
debug_variables(config)
feedback_add_details("admin_verb","DConf")
if("pAI")
debug_variables(paiController)
feedback_add_details("admin_verb","DpAI")
if("Cameras")
debug_variables(cameranet)
feedback_add_details("admin_verb","DCameras")
if("Transfer Controller")
debug_variables(transfer_controller)
feedback_add_details("admin_verb","DAutovoter")
if("Gas Data")
debug_variables(gas_data)
feedback_add_details("admin_verb","DGasdata")
if("Event")
debug_variables(event_manager)
feedback_add_details("admin_verb", "DEvent")
if("Plants")
debug_variables(plant_controller)
feedback_add_details("admin_verb", "DPlants")
if("Alarm")
debug_variables(alarm_manager)
feedback_add_details("admin_verb", "DAlarm")
if("Nano")
debug_variables(GLOB.nanomanager)
feedback_add_details("admin_verb", "DNano")
if("Chemistry")
debug_variables(chemistryProcess)
feedback_add_details("admin_verb", "DChem")
message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.")
return
if(!holder)
return
var/list/options = list()
options["MC"] = Master
options["Failsafe"] = Failsafe
options["Configuration"] = config
for(var/i in Master.subsystems)
var/datum/controller/subsystem/S = i
if(!istype(S)) //Eh, we're a debug verb, let's have typechecking.
continue
var/strtype = "SS[get_end_section_of_type(S.type)]"
if(options[strtype])
var/offset = 2
while(istype(options["[strtype]_[offset] - DUPE ERROR"], /datum/controller/subsystem))
offset++
options["[strtype]_[offset] - DUPE ERROR"] = S //Something is very, very wrong.
else
options[strtype] = S
//Goon PS stuff, and other yet-to-be-subsystem things.
options["LEGACY: master_controller"] = master_controller
options["LEGACY: ticker"] = ticker
options["LEGACY: tickerProcess"] = tickerProcess
options["LEGACY: air_master"] = air_master
options["LEGACY: job_master"] = job_master
options["LEGACY: radio_controller"] = radio_controller
options["LEGACY: supply_controller"] = supply_controller
options["LEGACY: emergency_shuttle"] = emergency_shuttle
options["LEGACY: paiController"] = paiController
options["LEGACY: cameranet"] = cameranet
options["LEGACY: transfer_controller"] = transfer_controller
options["LEGACY: gas_data"] = gas_data
options["LEGACY: plant_controller"] = plant_controller
options["LEGACY: alarm_manager"] = alarm_manager
var/pick = input(mob, "Choose a controller to debug/view variables of.", "VV controller:") as null|anything in options
if(!pick)
return
var/datum/D = options[pick]
if(!istype(D))
return
feedback_add_details("admin_verb", "DebugController")
message_admins("Admin [key_name_admin(mob)] is debugging the [pick] controller.")
debug_variables(D)
//VOREStation Edit Begin
/client/proc/debug_process_scheduler()
set category = "Debug"
set name = "Debug Process Scheduler"
@@ -155,3 +136,4 @@
debug_variables(P)
feedback_add_details("admin_verb", "DProcCtrl")
message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.")
//VOREStation Edit End

View File

@@ -70,12 +70,12 @@
hidden = 1
/datum/category_item/autolathe/arms/tommymag
name = "Tommygun magazine (.45)"
name = "Tommy Gun magazine (.45)"
path =/obj/item/ammo_magazine/m45tommy
hidden = 1
/datum/category_item/autolathe/arms/tommydrum
name = "Tommygun drum magazine (.45)"
name = "Tommy Gun drum magazine (.45)"
path =/obj/item/ammo_magazine/m45tommydrum
hidden = 1
@@ -234,13 +234,13 @@
hidden = 1
/datum/category_item/autolathe/arms/tommymag
name = "Tommygun magazine (.45)"
name = "Tommy Gun magazine (.45)"
path =/obj/item/ammo_magazine/m45tommy/empty
category = "Arms and Ammunition"
hidden = 1
/datum/category_item/autolathe/arms/tommydrum
name = "Tommygun drum magazine (.45)"
name = "Tommy Gun drum magazine (.45)"
path =/obj/item/ammo_magazine/m45tommydrum/empty
category = "Arms and Ammunition"
hidden = 1

View File

@@ -0,0 +1,3 @@
/datum/category_item/autolathe/general/holocollar
name = "Holo-collar"
path =/obj/item/clothing/accessory/collar/holo

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