mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-15 10:04:30 +01:00
Merge branch 'master' of https://github.com/tgstation/-tg-station into freshfood
Conflicts: code/modules/research/designs/machine_designs.dm
This commit is contained in:
@@ -26,9 +26,14 @@ Pipelines + Other Objects -> Pipe network
|
||||
|
||||
/obj/machinery/atmospherics/New()
|
||||
..()
|
||||
|
||||
SetInitDirections()
|
||||
if(can_unwrench)
|
||||
stored = new(src, make_from=src)
|
||||
|
||||
/obj/machinery/atmospherics/proc/SetInitDirections()
|
||||
return
|
||||
|
||||
/obj/machinery/atmospherics/proc/safe_input(var/title, var/text, var/default_set)
|
||||
var/new_value = input(usr,"Enter new output pressure (0-4500kPa)","Pressure control",default_set) as num
|
||||
if(usr.canUseTopic(src))
|
||||
@@ -88,6 +93,7 @@ Pipelines + Other Objects -> Pipe network
|
||||
"[user] unfastens \the [src].", \
|
||||
"<span class='notice'>You have unfastened \the [src].</span>", \
|
||||
"You hear ratchet.")
|
||||
investigate_log("was <span class='warning'>REMOVED</span> by [key_name(usr)]", "atmos")
|
||||
Deconstruct()
|
||||
else
|
||||
return ..()
|
||||
@@ -97,11 +103,7 @@ Pipelines + Other Objects -> Pipe network
|
||||
var/turf/T = loc
|
||||
stored.loc = T
|
||||
transfer_fingerprints_to(stored)
|
||||
if(istype(src, /obj/machinery/atmospherics/pipe))
|
||||
for(var/obj/machinery/meter/meter in T)
|
||||
if(meter.target == src)
|
||||
new /obj/item/pipe_meter(T)
|
||||
qdel(meter)
|
||||
|
||||
qdel(src)
|
||||
|
||||
/obj/machinery/atmospherics/proc/nullifyPipenet(datum/pipeline/P)
|
||||
@@ -128,9 +130,15 @@ Pipelines + Other Objects -> Pipe network
|
||||
|
||||
return img
|
||||
|
||||
/obj/machinery/atmospherics/proc/construction(D, P)
|
||||
/obj/machinery/atmospherics/construction(D, P, var/pipe_type, var/obj_color)
|
||||
dir = D
|
||||
initialize_directions = P
|
||||
if(can_unwrench)
|
||||
color = obj_color
|
||||
pipe_color = obj_color
|
||||
stored.dir = D //need to define them here, because the obj directions...
|
||||
stored.pipe_type = pipe_type //... were not set at the time the stored pipe was created
|
||||
stored.color = obj_color
|
||||
var/turf/T = loc
|
||||
level = T.intact ? 2 : 1
|
||||
initialize()
|
||||
|
||||
@@ -17,6 +17,14 @@
|
||||
|
||||
/obj/machinery/atmospherics/binary/New()
|
||||
..()
|
||||
|
||||
air1 = new
|
||||
air2 = new
|
||||
|
||||
air1.volume = 200
|
||||
air2.volume = 200
|
||||
|
||||
/obj/machinery/atmospherics/binary/SetInitDirections()
|
||||
switch(dir)
|
||||
if(NORTH)
|
||||
initialize_directions = NORTH|SOUTH
|
||||
@@ -26,11 +34,6 @@
|
||||
initialize_directions = EAST|WEST
|
||||
if(WEST)
|
||||
initialize_directions = EAST|WEST
|
||||
air1 = new
|
||||
air2 = new
|
||||
|
||||
air1.volume = 200
|
||||
air2.volume = 200
|
||||
|
||||
//Separate this because we don't need to update pipe icons if we just are going to change the state
|
||||
/obj/machinery/atmospherics/binary/proc/update_icon_nopipes()
|
||||
@@ -72,7 +75,6 @@
|
||||
..()
|
||||
|
||||
/obj/machinery/atmospherics/binary/initialize()
|
||||
src.disconnect(src)
|
||||
|
||||
var/node2_connect = dir
|
||||
var/node1_connect = turn(dir, 180)
|
||||
|
||||
@@ -107,6 +107,8 @@ Passive gate is similar to the regular pump except:
|
||||
if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
|
||||
return 0
|
||||
|
||||
var/old_on = on //for logging
|
||||
|
||||
if("power" in signal.data)
|
||||
on = text2num(signal.data["power"])
|
||||
|
||||
@@ -120,6 +122,9 @@ Passive gate is similar to the regular pump except:
|
||||
ONE_ATMOSPHERE*50
|
||||
)
|
||||
|
||||
if(on != old_on)
|
||||
investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos")
|
||||
|
||||
if("status" in signal.data)
|
||||
spawn(2)
|
||||
broadcast_status()
|
||||
@@ -147,8 +152,10 @@ Passive gate is similar to the regular pump except:
|
||||
if(..()) return
|
||||
if(href_list["power"])
|
||||
on = !on
|
||||
investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
|
||||
if(href_list["set_press"])
|
||||
target_pressure = max(0, min(4500, safe_input("Pressure control", "Enter new output pressure (0-4500kPa)", target_pressure)))
|
||||
investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos")
|
||||
usr.set_machine(src)
|
||||
src.update_icon()
|
||||
src.updateUsrDialog()
|
||||
|
||||
@@ -44,7 +44,7 @@ Thus, the two variables affect pump operation are set in New():
|
||||
/obj/machinery/atmospherics/binary/pump/process()
|
||||
// ..()
|
||||
if(stat & (NOPOWER|BROKEN))
|
||||
return
|
||||
return 0
|
||||
if(!on)
|
||||
return 0
|
||||
|
||||
@@ -70,8 +70,7 @@ Thus, the two variables affect pump operation are set in New():
|
||||
|
||||
return 1
|
||||
|
||||
//Radio remote control
|
||||
|
||||
//Radio remote control
|
||||
/obj/machinery/atmospherics/binary/pump/proc/set_frequency(new_frequency)
|
||||
radio_controller.remove_object(src, frequency)
|
||||
frequency = new_frequency
|
||||
@@ -116,6 +115,8 @@ Thus, the two variables affect pump operation are set in New():
|
||||
if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
|
||||
return 0
|
||||
|
||||
var/old_on = on //for logging
|
||||
|
||||
if("power" in signal.data)
|
||||
on = text2num(signal.data["power"])
|
||||
|
||||
@@ -129,6 +130,9 @@ Thus, the two variables affect pump operation are set in New():
|
||||
ONE_ATMOSPHERE*50
|
||||
)
|
||||
|
||||
if(on != old_on)
|
||||
investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos")
|
||||
|
||||
if("status" in signal.data)
|
||||
spawn(2)
|
||||
broadcast_status()
|
||||
@@ -155,8 +159,10 @@ Thus, the two variables affect pump operation are set in New():
|
||||
if(..()) return
|
||||
if(href_list["power"])
|
||||
on = !on
|
||||
investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
|
||||
if(href_list["set_press"])
|
||||
target_pressure = max(0, min(4500, safe_input("Pressure control", "Enter new output pressure (0-4500kPa)", target_pressure)))
|
||||
investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos")
|
||||
usr.set_machine(src)
|
||||
src.update_icon()
|
||||
src.updateUsrDialog()
|
||||
|
||||
@@ -113,6 +113,8 @@ Thus, the two variables affect pump operation are set in New():
|
||||
if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command"))
|
||||
return 0
|
||||
|
||||
var/old_on = on //for logging
|
||||
|
||||
if("power" in signal.data)
|
||||
on = text2num(signal.data["power"])
|
||||
|
||||
@@ -126,6 +128,9 @@ Thus, the two variables affect pump operation are set in New():
|
||||
air1.volume
|
||||
)
|
||||
|
||||
if(on != old_on)
|
||||
investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos")
|
||||
|
||||
if("status" in signal.data)
|
||||
spawn(2)
|
||||
broadcast_status()
|
||||
@@ -151,8 +156,10 @@ Thus, the two variables affect pump operation are set in New():
|
||||
if(..()) return
|
||||
if(href_list["power"])
|
||||
on = !on
|
||||
investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos")
|
||||
if(href_list["set_transfer_rate"])
|
||||
transfer_rate = max(0, min(200, safe_input("Pressure control", "Enter new output pressure (0-4500kPa)", transfer_rate)))
|
||||
investigate_log("was set to [transfer_rate] L/s by [key_name(usr)]", "atmos")
|
||||
usr.set_machine(src)
|
||||
src.update_icon()
|
||||
src.updateUsrDialog()
|
||||
|
||||
@@ -20,6 +20,16 @@
|
||||
|
||||
/obj/machinery/atmospherics/trinary/New()
|
||||
..()
|
||||
|
||||
air1 = new
|
||||
air2 = new
|
||||
air3 = new
|
||||
|
||||
air1.volume = 200
|
||||
air2.volume = 200
|
||||
air3.volume = 200
|
||||
|
||||
/obj/machinery/atmospherics/trinary/SetInitDirections()
|
||||
switch(dir)
|
||||
if(NORTH)
|
||||
initialize_directions = EAST|NORTH|SOUTH
|
||||
@@ -29,14 +39,6 @@
|
||||
initialize_directions = EAST|WEST|SOUTH
|
||||
if(WEST)
|
||||
initialize_directions = WEST|NORTH|EAST
|
||||
air1 = new
|
||||
air2 = new
|
||||
air3 = new
|
||||
|
||||
air1.volume = 200
|
||||
air2.volume = 200
|
||||
air3.volume = 200
|
||||
|
||||
/*
|
||||
Iconnery
|
||||
*/
|
||||
|
||||
@@ -37,11 +37,11 @@
|
||||
if(!partner)
|
||||
return 0
|
||||
|
||||
if(!air_master || air_master.current_cycle <= update_cycle)
|
||||
if(SSair.times_fired <= update_cycle)
|
||||
return 0
|
||||
|
||||
update_cycle = air_master.current_cycle
|
||||
partner.update_cycle = air_master.current_cycle
|
||||
update_cycle = SSair.times_fired
|
||||
partner.update_cycle = SSair.times_fired
|
||||
|
||||
var/air_heat_capacity = air_contents.heat_capacity()
|
||||
var/other_air_heat_capacity = partner.air_contents.heat_capacity()
|
||||
|
||||
@@ -10,10 +10,12 @@
|
||||
|
||||
/obj/machinery/atmospherics/unary/New()
|
||||
..()
|
||||
initialize_directions = dir
|
||||
air_contents = new
|
||||
air_contents.volume = 200
|
||||
|
||||
|
||||
/obj/machinery/atmospherics/unary/SetInitDirections()
|
||||
initialize_directions = dir
|
||||
/*
|
||||
Iconnery
|
||||
*/
|
||||
|
||||
@@ -38,17 +38,19 @@
|
||||
update_icon_nopipes()
|
||||
parent1.update = 0
|
||||
parent2.update = 0
|
||||
investigate_log("was opened by [usr ? key_name(usr) : "a remote signal"]", "atmos")
|
||||
return
|
||||
|
||||
/obj/machinery/atmospherics/binary/valve/proc/close()
|
||||
open = 0
|
||||
update_icon_nopipes()
|
||||
investigate_log("was closed by [usr ? key_name(usr) : "a remote signal"]", "atmos")
|
||||
return
|
||||
|
||||
/obj/machinery/atmospherics/binary/valve/proc/normalize_dir()
|
||||
if(dir==3)
|
||||
if(dir==2)
|
||||
dir = 1
|
||||
else if(dir==12)
|
||||
else if(dir==8)
|
||||
dir = 4
|
||||
|
||||
/obj/machinery/atmospherics/binary/valve/attack_ai(mob/user)
|
||||
@@ -69,7 +71,7 @@
|
||||
desc = "A digitally controlled valve."
|
||||
icon_state = "dvalve_map"
|
||||
|
||||
/obj/machinery/atmospherics/valve/digital/attack_ai(mob/user)
|
||||
/obj/machinery/atmospherics/binary/valve/digital/attack_ai(mob/user)
|
||||
return src.attack_hand(user)
|
||||
|
||||
/obj/machinery/atmospherics/binary/valve/digital/update_icon_nopipes(animation)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
var/global/list/datum/pipeline/pipe_networks = list()
|
||||
|
||||
/datum/pipeline
|
||||
var/datum/gas_mixture/air
|
||||
var/list/datum/gas_mixture/other_airs = list()
|
||||
@@ -12,10 +10,10 @@ var/global/list/datum/pipeline/pipe_networks = list()
|
||||
var/alert_pressure = 0
|
||||
|
||||
/datum/pipeline/New()
|
||||
pipe_networks += src
|
||||
SSpipe.networks += src
|
||||
|
||||
/datum/pipeline/Destroy()
|
||||
pipe_networks -= src
|
||||
SSpipe.networks -= src
|
||||
if(air && air.volume)
|
||||
temporarily_store_air()
|
||||
for(var/obj/machinery/atmospherics/pipe/P in members)
|
||||
@@ -44,7 +42,6 @@ var/global/list/datum/pipeline/pipe_networks = list()
|
||||
var/pipenetwarnings = 10
|
||||
|
||||
/datum/pipeline/proc/build_pipeline(obj/machinery/atmospherics/base)
|
||||
base.setPipenet(src)
|
||||
var/volume = 0
|
||||
if(istype(base, /obj/machinery/atmospherics/pipe))
|
||||
var/obj/machinery/atmospherics/pipe/E = base
|
||||
|
||||
@@ -50,6 +50,8 @@
|
||||
|
||||
/obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction/New()
|
||||
..()
|
||||
|
||||
/obj/machinery/atmospherics/pipe/simple/heat_exchanging/junction/SetInitDirections()
|
||||
switch(dir)
|
||||
if(SOUTH)
|
||||
initialize_directions = NORTH
|
||||
|
||||
@@ -34,10 +34,23 @@
|
||||
if(istype(W, /obj/item/device/analyzer))
|
||||
atmosanalyzer_scan(parent.air, user)
|
||||
|
||||
if(istype(W,/obj/item/device/pipe_painter))
|
||||
if(istype(W,/obj/item/device/pipe_painter) || istype(W,/obj/item/weapon/pipe_dispenser))
|
||||
return
|
||||
|
||||
return ..()
|
||||
|
||||
/obj/machinery/atmospherics/pipe/setPipenet(datum/pipeline/P)
|
||||
parent = P
|
||||
parent = P
|
||||
|
||||
/obj/machinery/atmospherics/pipe/Destroy()
|
||||
var/turf/T = loc
|
||||
for(var/obj/machinery/meter/meter in T)
|
||||
if(meter.target == src)
|
||||
var/obj/item/pipe_meter/PM = new (T)
|
||||
meter.transfer_fingerprints_to(PM)
|
||||
qdel(meter)
|
||||
..()
|
||||
|
||||
/obj/machinery/atmospherics/pipe/proc/update_node_icon()
|
||||
//Used for pipe painting. Overriden in the children.
|
||||
return
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
/obj/machinery/atmospherics/pipe/manifold/New()
|
||||
color = pipe_color
|
||||
|
||||
..()
|
||||
|
||||
/obj/machinery/atmospherics/pipe/manifold/SetInitDirections()
|
||||
switch(dir)
|
||||
if(NORTH)
|
||||
initialize_directions = EAST|SOUTH|WEST
|
||||
@@ -34,7 +37,6 @@
|
||||
initialize_directions = SOUTH|WEST|NORTH
|
||||
if(WEST)
|
||||
initialize_directions = NORTH|EAST|SOUTH
|
||||
..()
|
||||
|
||||
/obj/machinery/atmospherics/pipe/manifold/initialize()
|
||||
for(var/D in cardinal)
|
||||
@@ -113,6 +115,15 @@
|
||||
/obj/machinery/atmospherics/pipe/manifold/pipeline_expansion()
|
||||
return list(node1, node2, node3)
|
||||
|
||||
/obj/machinery/atmospherics/pipe/manifold/update_node_icon()
|
||||
..()
|
||||
if(node1)
|
||||
node1.update_icon()
|
||||
if(node2)
|
||||
node2.update_icon()
|
||||
if(node3)
|
||||
node3.update_icon()
|
||||
|
||||
|
||||
//Colored pipes, use these for mapping
|
||||
/obj/machinery/atmospherics/pipe/manifold/general
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
color = pipe_color
|
||||
..()
|
||||
|
||||
/obj/machinery/atmospherics/pipe/manifold4w/SetInitDirections()
|
||||
return
|
||||
|
||||
/obj/machinery/atmospherics/pipe/manifold4w/initialize()
|
||||
for(var/D in cardinal)
|
||||
for(var/obj/machinery/atmospherics/target in get_step(src, D))
|
||||
@@ -119,6 +122,17 @@
|
||||
if(node4)
|
||||
overlays += getpipeimage('icons/obj/atmospherics/pipe_manifold.dmi', "manifold_full[invis]", WEST)
|
||||
|
||||
/obj/machinery/atmospherics/pipe/manifold4w/update_node_icon()
|
||||
..()
|
||||
if(node1)
|
||||
node1.update_icon()
|
||||
if(node2)
|
||||
node2.update_icon()
|
||||
if(node3)
|
||||
node3.update_icon()
|
||||
if(node4)
|
||||
node4.update_icon()
|
||||
|
||||
//Colored pipes, use these for mapping
|
||||
/obj/machinery/atmospherics/pipe/manifold4w/general
|
||||
name="pipe"
|
||||
|
||||
@@ -33,10 +33,16 @@ The regular pipe you see everywhere, including bent ones.
|
||||
|
||||
..()
|
||||
|
||||
|
||||
/obj/machinery/atmospherics/pipe/simple/SetInitDirections()
|
||||
switch(dir)
|
||||
if(SOUTH || NORTH)
|
||||
if(NORTH)
|
||||
initialize_directions = SOUTH|NORTH
|
||||
if(EAST || WEST)
|
||||
if(SOUTH)
|
||||
initialize_directions = SOUTH|NORTH
|
||||
if(EAST)
|
||||
initialize_directions = EAST|WEST
|
||||
if(WEST)
|
||||
initialize_directions = EAST|WEST
|
||||
if(NORTHEAST)
|
||||
initialize_directions = NORTH|EAST
|
||||
@@ -102,7 +108,7 @@ The regular pipe you see everywhere, including bent ones.
|
||||
else return 1
|
||||
|
||||
/obj/machinery/atmospherics/pipe/simple/proc/burst()
|
||||
src.visible_message("<span class='userdanger'>[src] bursts!</span>");
|
||||
visible_message("<span class='danger'>[src] bursts!</span>");
|
||||
playsound(src.loc, 'sound/effects/bang.ogg', 25, 1)
|
||||
var/datum/effect/effect/system/harmless_smoke_spread/smoke = new
|
||||
smoke.set_up(1,0, src.loc, 0)
|
||||
@@ -110,9 +116,9 @@ The regular pipe you see everywhere, including bent ones.
|
||||
qdel(src)
|
||||
|
||||
/obj/machinery/atmospherics/pipe/simple/proc/normalize_dir()
|
||||
if(dir==3)
|
||||
if(dir==2)
|
||||
dir = 1
|
||||
else if(dir==12)
|
||||
else if(dir==8)
|
||||
dir = 4
|
||||
|
||||
/obj/machinery/atmospherics/pipe/simple/update_icon()
|
||||
@@ -131,6 +137,13 @@ The regular pipe you see everywhere, including bent ones.
|
||||
/obj/machinery/atmospherics/pipe/simple/pipeline_expansion()
|
||||
return list(node1, node2)
|
||||
|
||||
/obj/machinery/atmospherics/pipe/simple/update_node_icon()
|
||||
..()
|
||||
if(node1)
|
||||
node1.update_icon()
|
||||
if(node2)
|
||||
node2.update_icon()
|
||||
|
||||
/obj/machinery/atmospherics/pipe/simple/insulated
|
||||
icon = 'icons/obj/atmospherics/red_pipe.dmi'
|
||||
icon_state = "intact"
|
||||
|
||||
@@ -34,9 +34,9 @@
|
||||
active_hotspot.temperature = exposed_temperature
|
||||
active_hotspot.volume = exposed_volume
|
||||
|
||||
active_hotspot.just_spawned = (current_cycle < air_master.current_cycle)
|
||||
active_hotspot.just_spawned = (current_cycle < SSair.times_fired)
|
||||
//remove just_spawned protection if no longer processing this cell
|
||||
air_master.add_to_active(src, 0)
|
||||
SSair.add_to_active(src, 0)
|
||||
return igniting
|
||||
|
||||
//This is the icon for fire on turfs, also helps for nurturing small fires until they are full tile
|
||||
@@ -56,7 +56,7 @@
|
||||
|
||||
/obj/effect/hotspot/New()
|
||||
..()
|
||||
air_master.hotspots += src
|
||||
SSair.hotspots += src
|
||||
perform_exposure()
|
||||
|
||||
/obj/effect/hotspot/proc/perform_exposure()
|
||||
@@ -142,7 +142,7 @@
|
||||
// Garbage collect itself by nulling reference to it
|
||||
|
||||
/obj/effect/hotspot/proc/Kill()
|
||||
air_master.hotspots -= src
|
||||
SSair.hotspots -= src
|
||||
DestroyTurf()
|
||||
qdel(src)
|
||||
|
||||
|
||||
+2
-136
@@ -1,136 +1,3 @@
|
||||
var/kill_air = 0
|
||||
|
||||
var/global/datum/controller/air_system/air_master
|
||||
|
||||
datum/controller/air_system
|
||||
var/list/excited_groups = list()
|
||||
var/list/active_turfs = list()
|
||||
var/list/hotspots = list()
|
||||
var/speed = 1
|
||||
|
||||
//Special functions lists
|
||||
var/list/turf/simulated/active_super_conductivity = list()
|
||||
var/list/turf/simulated/high_pressure_delta = list()
|
||||
|
||||
var/current_cycle = 0
|
||||
|
||||
|
||||
/datum/controller/air_system/proc/setup()
|
||||
set background = BACKGROUND_ENABLED
|
||||
world << "<span class='userdanger'>Processing Geometry...</span>"
|
||||
sleep(1)
|
||||
|
||||
var/start_time = world.timeofday
|
||||
|
||||
setup_allturfs()
|
||||
|
||||
global_activeturfs = active_turfs.len
|
||||
|
||||
world << "<span class='userdanger'>Geometry processed in [(world.timeofday-start_time)/10] seconds!</span>"
|
||||
|
||||
/datum/controller/air_system/proc/process()
|
||||
if(kill_air)
|
||||
return 1
|
||||
|
||||
if(speed > 1)
|
||||
for(var/i=0,i<speed,i++)
|
||||
spawn((10/speed)*i)
|
||||
process_air()
|
||||
else
|
||||
process_air()
|
||||
return 1
|
||||
|
||||
/datum/controller/air_system/proc/process_air()
|
||||
current_cycle++
|
||||
var/timer = world.timeofday
|
||||
process_active_turfs()
|
||||
master_controller.air_turfs = (world.timeofday - timer) / 10
|
||||
|
||||
timer = world.timeofday
|
||||
process_excited_groups()
|
||||
master_controller.air_groups = (world.timeofday - timer) / 10
|
||||
|
||||
timer = world.timeofday
|
||||
process_high_pressure_delta()
|
||||
master_controller.air_highpressure = (world.timeofday - timer) / 10
|
||||
|
||||
timer = world.timeofday
|
||||
process_hotspots()
|
||||
master_controller.air_hotspots = (world.timeofday - timer) / 10
|
||||
|
||||
timer = world.timeofday
|
||||
process_super_conductivity()
|
||||
master_controller.air_superconductivity = (world.timeofday - timer) / 10
|
||||
|
||||
/datum/controller/air_system/proc/process_hotspots()
|
||||
for(var/obj/effect/hotspot/H in hotspots)
|
||||
H.process()
|
||||
|
||||
/datum/controller/air_system/proc/process_super_conductivity()
|
||||
for(var/turf/simulated/T in active_super_conductivity)
|
||||
T.super_conduct()
|
||||
|
||||
/datum/controller/air_system/proc/process_high_pressure_delta()
|
||||
for(var/turf/T in high_pressure_delta)
|
||||
T.high_pressure_movements()
|
||||
T.pressure_difference = 0
|
||||
high_pressure_delta.len = 0
|
||||
|
||||
/datum/controller/air_system/proc/process_active_turfs()
|
||||
for(var/turf/simulated/T in active_turfs)
|
||||
T.process_cell()
|
||||
|
||||
/datum/controller/air_system/proc/remove_from_active(var/turf/simulated/T)
|
||||
if(istype(T))
|
||||
T.excited = 0
|
||||
active_turfs -= T
|
||||
if(T.excited_group)
|
||||
T.excited_group.garbage_collect()
|
||||
|
||||
/datum/controller/air_system/proc/add_to_active(var/turf/simulated/T, var/blockchanges = 1)
|
||||
if(istype(T) && T.air)
|
||||
T.excited = 1
|
||||
active_turfs |= T
|
||||
if(blockchanges && T.excited_group)
|
||||
T.excited_group.garbage_collect()
|
||||
else
|
||||
for(var/direction in cardinal)
|
||||
if(!(T.atmos_adjacent_turfs & direction))
|
||||
continue
|
||||
var/turf/simulated/S = get_step(T, direction)
|
||||
if(istype(S))
|
||||
air_master.add_to_active(S)
|
||||
|
||||
/datum/controller/air_system/proc/setup_allturfs()
|
||||
for(var/turf/simulated/T in world)
|
||||
T.CalculateAdjacentTurfs()
|
||||
if(!T.blocks_air)
|
||||
if(T.air.check_tile_graphic())
|
||||
T.update_visuals(T.air)
|
||||
for(var/direction in cardinal)
|
||||
if(!(T.atmos_adjacent_turfs & direction))
|
||||
continue
|
||||
var/turf/enemy_tile = get_step(T, direction)
|
||||
if(istype(enemy_tile,/turf/simulated/))
|
||||
var/turf/simulated/enemy_simulated = enemy_tile
|
||||
if(!T.air.compare(enemy_simulated.air))
|
||||
T.excited = 1
|
||||
active_turfs |= T
|
||||
break
|
||||
else
|
||||
if(!T.air.check_turf_total(enemy_tile))
|
||||
T.excited = 1
|
||||
active_turfs |= T
|
||||
|
||||
/datum/controller/air_system/proc/process_excited_groups()
|
||||
for(var/datum/excited_group/EG in excited_groups)
|
||||
EG.breakdown_cooldown ++
|
||||
if(EG.breakdown_cooldown == 10)
|
||||
EG.self_breakdown()
|
||||
return
|
||||
if(EG.breakdown_cooldown > 20)
|
||||
EG.dismantle()
|
||||
|
||||
/turf/proc/CanAtmosPass(var/turf/T)
|
||||
if(!istype(T)) return 0
|
||||
var/R
|
||||
@@ -221,8 +88,7 @@ turf/CanPass(atom/movable/mover, turf/target, height=1.5)
|
||||
/turf/proc/air_update_turf(var/command = 0)
|
||||
if(command)
|
||||
CalculateAdjacentTurfs()
|
||||
if(air_master)
|
||||
air_master.add_to_active(src,command)
|
||||
SSair.add_to_active(src,command)
|
||||
|
||||
/atom/movable/proc/move_update_air(var/turf/T)
|
||||
if(istype(T,/turf))
|
||||
@@ -277,4 +143,4 @@ var/const/SPAWN_AIR = 256
|
||||
G.nitrogen += MOLES_N2STANDARD * amount
|
||||
|
||||
air.merge(G)
|
||||
air_master.add_to_active(src, 0)
|
||||
SSair.add_to_active(src, 0)
|
||||
@@ -139,9 +139,9 @@ turf/simulated/proc/share_temperature_mutual_solid(turf/simulated/sharer, conduc
|
||||
|
||||
/turf/simulated/proc/process_cell()
|
||||
|
||||
if(archived_cycle < air_master.current_cycle) //archive self if not already done
|
||||
if(archived_cycle < SSair.times_fired) //archive self if not already done
|
||||
archive()
|
||||
current_cycle = air_master.current_cycle
|
||||
current_cycle = SSair.times_fired
|
||||
|
||||
var/remove = 1 //set by non simulated turfs who are sharing with this turf
|
||||
|
||||
@@ -182,7 +182,7 @@ turf/simulated/proc/share_temperature_mutual_solid(turf/simulated/sharer, conduc
|
||||
share_air(enemy_simulated) //share
|
||||
else
|
||||
if(!air.compare(enemy_simulated.air)) //compare if
|
||||
air_master.add_to_active(enemy_simulated) //excite enemy
|
||||
SSair.add_to_active(enemy_simulated) //excite enemy
|
||||
if(excited_group)
|
||||
excited_group.add_turf(enemy_simulated) //add enemy to group
|
||||
else
|
||||
@@ -221,7 +221,7 @@ turf/simulated/proc/share_temperature_mutual_solid(turf/simulated/sharer, conduc
|
||||
remove = 0
|
||||
|
||||
if(!excited_group && remove == 1)
|
||||
air_master.remove_from_active(src)
|
||||
SSair.remove_from_active(src)
|
||||
|
||||
|
||||
|
||||
@@ -229,7 +229,7 @@ turf/simulated/proc/share_temperature_mutual_solid(turf/simulated/sharer, conduc
|
||||
if(air) //For open space like floors
|
||||
air.archive()
|
||||
temperature_archived = temperature
|
||||
archived_cycle = air_master.current_cycle
|
||||
archived_cycle = SSair.times_fired
|
||||
|
||||
/turf/simulated/proc/update_visuals(datum/gas_mixture/model)
|
||||
overlays.Cut()
|
||||
@@ -238,9 +238,9 @@ turf/simulated/proc/share_temperature_mutual_solid(turf/simulated/sharer, conduc
|
||||
overlays += image('icons/turf/floors.dmi',siding_icon_state)
|
||||
switch(model.graphic)
|
||||
if("plasma")
|
||||
overlays.Add(plmaster)
|
||||
overlays.Add(SSair.plasma_overlay)
|
||||
if("sleeping_agent")
|
||||
overlays.Add(slmaster)
|
||||
overlays.Add(SSair.sleeptoxin_overlay)
|
||||
|
||||
/turf/simulated/proc/share_air(var/turf/simulated/T)
|
||||
if(T.current_cycle < current_cycle)
|
||||
@@ -254,7 +254,7 @@ turf/simulated/proc/share_temperature_mutual_solid(turf/simulated/sharer, conduc
|
||||
last_share_check()
|
||||
|
||||
/turf/proc/consider_pressure_difference(var/turf/simulated/T, var/difference)
|
||||
air_master.high_pressure_delta |= src
|
||||
SSair.high_pressure_delta |= src
|
||||
if(difference > pressure_difference)
|
||||
pressure_direction = get_dir(src, T)
|
||||
pressure_difference = difference
|
||||
@@ -274,11 +274,11 @@ atom/movable/var/pressure_resistance = 5
|
||||
atom/movable/var/last_forced_movement = 0
|
||||
|
||||
atom/movable/proc/experience_pressure_difference(pressure_difference, direction)
|
||||
if(last_forced_movement >= air_master.current_cycle)
|
||||
if(last_forced_movement >= SSair.times_fired)
|
||||
return 0
|
||||
else if(!anchored)
|
||||
if(pressure_difference > pressure_resistance)
|
||||
last_forced_movement = air_master.current_cycle
|
||||
last_forced_movement = SSair.times_fired
|
||||
spawn step(src, direction)
|
||||
return 1
|
||||
|
||||
@@ -290,8 +290,7 @@ atom/movable/proc/experience_pressure_difference(pressure_difference, direction)
|
||||
var/breakdown_cooldown = 0
|
||||
|
||||
/datum/excited_group/New()
|
||||
if(air_master)
|
||||
air_master.excited_groups += src
|
||||
SSair.excited_groups += src
|
||||
|
||||
/datum/excited_group/proc/add_turf(var/turf/simulated/T)
|
||||
turf_list += T
|
||||
@@ -301,13 +300,13 @@ atom/movable/proc/experience_pressure_difference(pressure_difference, direction)
|
||||
|
||||
/datum/excited_group/proc/merge_groups(var/datum/excited_group/E)
|
||||
if(turf_list.len > E.turf_list.len)
|
||||
air_master.excited_groups -= E
|
||||
SSair.excited_groups -= E
|
||||
for(var/turf/simulated/T in E.turf_list)
|
||||
T.excited_group = src
|
||||
turf_list += T
|
||||
reset_cooldowns()
|
||||
else
|
||||
air_master.excited_groups -= src
|
||||
SSair.excited_groups -= src
|
||||
for(var/turf/simulated/T in turf_list)
|
||||
T.excited_group = E
|
||||
E.turf_list += T
|
||||
@@ -354,14 +353,14 @@ atom/movable/proc/experience_pressure_difference(pressure_difference, direction)
|
||||
T.excited = 0
|
||||
T.recently_active = 0
|
||||
T.excited_group = null
|
||||
air_master.active_turfs -= T
|
||||
SSair.active_turfs -= T
|
||||
garbage_collect()
|
||||
|
||||
/datum/excited_group/proc/garbage_collect()
|
||||
for(var/turf/simulated/T in turf_list)
|
||||
T.excited_group = null
|
||||
turf_list.Cut()
|
||||
air_master.excited_groups -= src
|
||||
SSair.excited_groups -= src
|
||||
|
||||
|
||||
|
||||
@@ -378,7 +377,7 @@ turf/simulated/proc/super_conduct()
|
||||
//Does not participate in air exchange, so will conduct heat across all four borders at this time
|
||||
conductivity_directions = NORTH|SOUTH|EAST|WEST
|
||||
|
||||
if(archived_cycle < air_master.current_cycle)
|
||||
if(archived_cycle < SSair.times_fired)
|
||||
archive()
|
||||
else
|
||||
//Does particate in air exchange so only consider directions not considered during process_cell()
|
||||
@@ -398,7 +397,7 @@ turf/simulated/proc/super_conduct()
|
||||
if(istype(neighbor, /turf/simulated)) //anything under this subtype will share in the exchange
|
||||
var/turf/simulated/T = neighbor
|
||||
|
||||
if(T.archived_cycle < air_master.current_cycle)
|
||||
if(T.archived_cycle < SSair.times_fired)
|
||||
T.archive()
|
||||
|
||||
if(T.air)
|
||||
@@ -406,7 +405,7 @@ turf/simulated/proc/super_conduct()
|
||||
air.temperature_share(T.air, WINDOW_HEAT_TRANSFER_COEFFICIENT)
|
||||
else //Solid but neighbor is open
|
||||
T.air.temperature_turf_share(src, T.thermal_conductivity)
|
||||
air_master.add_to_active(T, 0)
|
||||
SSair.add_to_active(T, 0)
|
||||
else
|
||||
if(air) //Open but neighbor is solid
|
||||
air.temperature_turf_share(T, T.thermal_conductivity)
|
||||
@@ -430,12 +429,12 @@ turf/simulated/proc/super_conduct()
|
||||
|
||||
//Make sure still hot enough to continue conducting heat
|
||||
if(air.temperature < MINIMUM_TEMPERATURE_FOR_SUPERCONDUCTION)
|
||||
air_master.active_super_conductivity -= src
|
||||
SSair.active_super_conductivity -= src
|
||||
return 0
|
||||
|
||||
else
|
||||
if(temperature < MINIMUM_TEMPERATURE_FOR_SUPERCONDUCTION)
|
||||
air_master.active_super_conductivity -= src
|
||||
SSair.active_super_conductivity -= src
|
||||
return 0
|
||||
|
||||
turf/simulated/proc/consider_superconductivity(starting)
|
||||
@@ -451,7 +450,7 @@ turf/simulated/proc/consider_superconductivity(starting)
|
||||
if(temperature < (starting?MINIMUM_TEMPERATURE_START_SUPERCONDUCTION:MINIMUM_TEMPERATURE_FOR_SUPERCONDUCTION))
|
||||
return 0
|
||||
|
||||
air_master.active_super_conductivity |= src
|
||||
SSair.active_super_conductivity |= src
|
||||
return 1
|
||||
|
||||
turf/simulated/proc/radiate_to_spess() //Radiate excess tile heat to space
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#define STUTTER "stutter"
|
||||
#define EYE_BLUR "eye_blur"
|
||||
#define DROWSY "drowsy"
|
||||
#define JITTER "jitter"
|
||||
|
||||
//I hate adding defines like this but I'd much rather deal with bitflags than lists and string searches
|
||||
#define BRUTELOSS 1
|
||||
|
||||
+23
-35
@@ -1,31 +1,31 @@
|
||||
|
||||
//bitflags for mutations
|
||||
// Extra powers:
|
||||
#define SHADOW (1<<10) // shadow teleportation (create in/out portals anywhere) (25%)
|
||||
#define SCREAM (1<<11) // supersonic screaming (25%)
|
||||
#define EXPLOSIVE (1<<12) // exploding on-demand (15%)
|
||||
#define REGENERATION (1<<13) // superhuman regeneration (30%)
|
||||
#define REPROCESSOR (1<<14) // eat anything (50%)
|
||||
#define SHAPESHIFTING (1<<15) // take on the appearance of anything (40%)
|
||||
#define PHASING (1<<16) // ability to phase through walls (40%)
|
||||
#define SHIELD (1<<17) // shielding from all projectile attacks (30%)
|
||||
#define SHOCKWAVE (1<<18) // attack a nearby tile and cause a massive shockwave, knocking most people on their asses (25%)
|
||||
#define ELECTRICITY (1<<19) // ability to shoot electric attacks (15%)
|
||||
//Defines copying names of mutations in all cases, make sure to change this if you change mutation's name
|
||||
#define HULK "Hulk"
|
||||
#define XRAY "X Ray Vision"
|
||||
#define COLDRES "Cold Resistance"
|
||||
#define TK "Telekinesis"
|
||||
#define NERVOUS "Nervousness"
|
||||
#define EPILEPSY "Epilepsy"
|
||||
#define MUTATE "Unstable DNA"
|
||||
#define COUGH "Cough"
|
||||
#define CLOWNMUT "Clumsiness"
|
||||
#define TOURETTES "Tourettes Syndrome"
|
||||
#define DEAFMUT "Deafness"
|
||||
#define BLINDMUT "Blindness"
|
||||
#define RACEMUT "Monkified"
|
||||
#define BADSIGHT "Near Sightness"
|
||||
#define LASEREYES "Laser Eyes"
|
||||
|
||||
|
||||
// String identifiers for associative list lookup
|
||||
// mob/var/list/mutations
|
||||
|
||||
// Generic mutations:
|
||||
#define TK 1
|
||||
#define COLD_RESISTANCE 2
|
||||
#define XRAY 3
|
||||
#define HULK 4
|
||||
#define CLUMSY 5
|
||||
#define FAT 6
|
||||
#define HUSK 7
|
||||
#define NOCLONE 8
|
||||
//Types of usual mutations
|
||||
#define POSITIVE 1
|
||||
#define NEGATIVE 2
|
||||
#define MINOR_NEGATIVE 3
|
||||
|
||||
//Mutations that cant be taken from genetics and are not in SE
|
||||
#define NON_SCANNABLE -1
|
||||
|
||||
// Extra powers:
|
||||
#define LASER 9 // harm intent - click anywhere to shoot lasers from eyes
|
||||
@@ -41,18 +41,6 @@
|
||||
#define SHOCKWAVE 19 // attack a nearby tile and cause a massive shockwave, knocking most people on their asses (25%)
|
||||
#define ELECTRICITY 20 // ability to shoot electric attacks (15%)
|
||||
|
||||
//disabilities
|
||||
#define NEARSIGHTED 1
|
||||
#define EPILEPSY 2
|
||||
#define COUGHING 4
|
||||
#define TOURETTES 8
|
||||
#define NERVOUS 16
|
||||
|
||||
//sdisabilities
|
||||
#define BLIND 1
|
||||
#define MUTE 2
|
||||
#define DEAF 4
|
||||
|
||||
//DNA - Because fuck you and your magic numbers being all over the codebase.
|
||||
#define DNA_BLOCK_SIZE 3
|
||||
|
||||
@@ -88,4 +76,4 @@
|
||||
#define NUTRITION_LEVEL_WELL_FED 450
|
||||
#define NUTRITION_LEVEL_FED 350
|
||||
#define NUTRITION_LEVEL_HUNGRY 250
|
||||
#define NUTRITION_LEVEL_STARVING 150
|
||||
#define NUTRITION_LEVEL_STARVING 150
|
||||
|
||||
+39
-1
@@ -1,5 +1,32 @@
|
||||
#define MIDNIGHT_ROLLOVER 864000 //number of deciseconds in a day
|
||||
|
||||
|
||||
//Human Overlays Indexes/////////
|
||||
#define SPECIES_LAYER 23 // mutantrace colors... these are on a seperate layer in order to prvent
|
||||
#define BODY_LAYER 22 //underwear, undershirts, eyes, lips(makeup)
|
||||
#define MUTATIONS_LAYER 21 //Tk headglows etc.
|
||||
#define AUGMENTS_LAYER 20
|
||||
#define DAMAGE_LAYER 19 //damage indicators (cuts and burns)
|
||||
#define UNIFORM_LAYER 18
|
||||
#define ID_LAYER 17
|
||||
#define SHOES_LAYER 16
|
||||
#define GLOVES_LAYER 15
|
||||
#define EARS_LAYER 14
|
||||
#define SUIT_LAYER 13
|
||||
#define GLASSES_LAYER 12
|
||||
#define BELT_LAYER 11 //Possible make this an overlay of somethign required to wear a belt?
|
||||
#define SUIT_STORE_LAYER 10
|
||||
#define BACK_LAYER 9
|
||||
#define HAIR_LAYER 8 //TODO: make part of head layer?
|
||||
#define FACEMASK_LAYER 7
|
||||
#define HEAD_LAYER 6
|
||||
#define HANDCUFF_LAYER 5
|
||||
#define LEGCUFF_LAYER 4
|
||||
#define L_HAND_LAYER 3
|
||||
#define R_HAND_LAYER 2 //Having the two hands seperate seems rather silly, merge them together? It'll allow for code to be reused on mobs with arbitarily many hands
|
||||
#define FIRE_LAYER 1 //If you're on fire
|
||||
#define TOTAL_LAYERS 23 //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_;
|
||||
|
||||
//Security levels
|
||||
#define SEC_LEVEL_GREEN 0
|
||||
#define SEC_LEVEL_BLUE 1
|
||||
@@ -52,8 +79,19 @@
|
||||
#define STAGE_FOUR 7
|
||||
#define STAGE_FIVE 9
|
||||
|
||||
//zlevel defines, can be overridden for different maps in the appropriate _maps file.
|
||||
#define ZLEVEL_SPACEMAX 7
|
||||
#define ZLEVEL_SPACEMIN 3
|
||||
#define ZLEVEL_CENTCOM 2
|
||||
#define ZLEVEL_STATION 1
|
||||
|
||||
//ticker.current_state values
|
||||
#define GAME_STATE_STARTUP 0
|
||||
#define GAME_STATE_PREGAME 1
|
||||
#define GAME_STATE_SETTING_UP 2
|
||||
#define GAME_STATE_PLAYING 3
|
||||
#define GAME_STATE_FINISHED 4
|
||||
//SOUND:
|
||||
#define SOUND_MINIMUM_PRESSURE 10
|
||||
#define FALLOFF_SOUNDS 1
|
||||
#define SURROUND_CAP 7
|
||||
#define SURROUND_CAP 7
|
||||
|
||||
@@ -7,9 +7,28 @@
|
||||
#define UNCONSCIOUS 1
|
||||
#define DEAD 2
|
||||
|
||||
//mob disabilities stat
|
||||
|
||||
#define BLIND 1
|
||||
#define MUTE 2
|
||||
#define DEAF 4
|
||||
#define NEARSIGHT 8
|
||||
#define FAT 32
|
||||
#define HUSK 64
|
||||
#define NOCLONE 128
|
||||
#define CLUMSY 256
|
||||
|
||||
// bitflags for machine stat variable
|
||||
#define BROKEN 1
|
||||
#define NOPOWER 2
|
||||
#define POWEROFF 4 // tbd
|
||||
#define MAINT 8 // under maintaince
|
||||
#define EMPED 16 // temporary broken by EMP pulse
|
||||
|
||||
//shuttle mode defines
|
||||
#define SHUTTLE_IDLE 0
|
||||
#define SHUTTLE_RECALL 1
|
||||
#define SHUTTLE_CALL 2
|
||||
#define SHUTTLE_DOCKED 3
|
||||
#define SHUTTLE_ESCAPE 4
|
||||
#define SHUTTLE_ENDGAME 5
|
||||
|
||||
@@ -27,4 +27,7 @@ var/cmp_field = "name"
|
||||
return sorttext(b.ckey, a.ckey)
|
||||
|
||||
/proc/cmp_ckey_dsc(client/a, client/b)
|
||||
return sorttext(a.ckey, b.ckey)
|
||||
return sorttext(a.ckey, b.ckey)
|
||||
|
||||
/proc/cmp_subsystem_priority(datum/subsystem/a, datum/subsystem/b)
|
||||
return b.priority - a.priority
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear, underwear_list, underwear_m, underwear_f)
|
||||
//undershirt
|
||||
init_sprite_accessory_subtypes(/datum/sprite_accessory/undershirt, undershirt_list, undershirt_m, undershirt_f)
|
||||
//socks
|
||||
init_sprite_accessory_subtypes(/datum/sprite_accessory/socks, socks_list, socks_m, socks_f)
|
||||
|
||||
//Species
|
||||
for(var/spath in typesof(/datum/species))
|
||||
|
||||
+20
-10
@@ -70,11 +70,14 @@
|
||||
return
|
||||
|
||||
//Removes any null entries from the list
|
||||
/proc/listclearnulls(list/list)
|
||||
if(istype(list))
|
||||
while(null in list)
|
||||
list -= null
|
||||
return
|
||||
/proc/listclearnulls(list/L)
|
||||
if(istype(L))
|
||||
var/i=1
|
||||
for(var/thing in L)
|
||||
if(thing != null)
|
||||
++i
|
||||
continue
|
||||
L.Cut(i,i+1)
|
||||
|
||||
/*
|
||||
* Returns list containing all the entries from first list that are not present in second.
|
||||
@@ -257,8 +260,6 @@
|
||||
/proc/moveElement(list/L, fromIndex, toIndex)
|
||||
if(fromIndex > toIndex)
|
||||
++fromIndex
|
||||
else
|
||||
++toIndex
|
||||
|
||||
L.Insert(toIndex, null)
|
||||
L.Swap(fromIndex, toIndex)
|
||||
@@ -283,8 +284,6 @@
|
||||
else
|
||||
if(fromIndex > toIndex)
|
||||
fromIndex += len
|
||||
else
|
||||
toIndex += len //?
|
||||
|
||||
for(var/i=0, i<len, ++i)
|
||||
L.Insert(toIndex, null)
|
||||
@@ -329,4 +328,15 @@
|
||||
while(start < end)
|
||||
L.Swap(start++,end--)
|
||||
|
||||
return L
|
||||
return L
|
||||
|
||||
|
||||
//return first thing in L which has var/varname == value
|
||||
//this is typecaste as list/L, but you could actually feed it an atom instead.
|
||||
//completely safe to use
|
||||
/proc/getElementByVar(list/L, varname, value)
|
||||
varname = "[varname]"
|
||||
for(var/datum/D in L)
|
||||
if(D.vars.Find(varname))
|
||||
if(D.vars[varname] == value)
|
||||
return D
|
||||
@@ -28,6 +28,14 @@
|
||||
if(FEMALE) return pick(undershirt_f)
|
||||
else return pick(undershirt_list)
|
||||
|
||||
/proc/random_socks(gender)
|
||||
if(!socks_list.len)
|
||||
init_sprite_accessory_subtypes(/datum/sprite_accessory/socks, socks_list, socks_m, socks_f)
|
||||
switch(gender)
|
||||
if(MALE) return pick(socks_m)
|
||||
if(FEMALE) return pick(socks_f)
|
||||
else return pick(socks_list)
|
||||
|
||||
/proc/random_hair_style(gender)
|
||||
switch(gender)
|
||||
if(MALE) return pick(hair_styles_male_list)
|
||||
|
||||
+12
-13
@@ -47,18 +47,17 @@ var/religion_name = null
|
||||
if (station_name)
|
||||
return station_name
|
||||
|
||||
if(events)
|
||||
if (config && config.station_name)
|
||||
station_name = config.station_name
|
||||
else
|
||||
station_name = new_station_name()
|
||||
if(config && config.station_name)
|
||||
station_name = config.station_name
|
||||
else
|
||||
station_name = new_station_name()
|
||||
|
||||
if (config && config.server_name)
|
||||
world.name = "[config.server_name][config.server_name==station_name ? "" : ": [station_name]"]"
|
||||
else
|
||||
world.name = station_name
|
||||
if (config && config.server_name)
|
||||
world.name = "[config.server_name][config.server_name==station_name ? "" : ": [station_name]"]"
|
||||
else
|
||||
world.name = station_name
|
||||
|
||||
return station_name
|
||||
return station_name
|
||||
|
||||
/proc/new_station_name()
|
||||
var/random = rand(1,5)
|
||||
@@ -71,7 +70,7 @@ var/religion_name = null
|
||||
new_station_name = name + " "
|
||||
|
||||
// Prefix
|
||||
switch(events.holiday)
|
||||
switch(SSevent.holiday)
|
||||
//get normal name
|
||||
if(null,"",0)
|
||||
name = pick("", "Stanford", "Dorf", "Alium", "Prefix", "Clowning", "Aegis", "Ishimura", "Scaredy", "Death-World", "Mime", "Honk", "Rogue", "MacRagge", "Ultrameens", "Safety", "Paranoia", "Explosive", "Neckbear", "Donk", "Muppet", "North", "West", "East", "South", "Slant-ways", "Widdershins", "Rimward", "Expensive", "Procreatory", "Imperial", "Unidentified", "Immoral", "Carp", "Ork", "Pete", "Control", "Nettle", "Aspie", "Class", "Crab", "Fist","Corrogated","Skeleton","Race", "Fatguy", "Gentleman", "Capitalist", "Communist", "Bear", "Beard", "Derp", "Space", "Spess", "Star", "Moon", "System", "Mining", "Neckbeard", "Research", "Supply", "Military", "Orbital", "Battle", "Science", "Asteroid", "Home", "Production", "Transport", "Delivery", "Extraplanetary", "Orbital", "Correctional", "Robot", "Hats", "Pizza")
|
||||
@@ -85,8 +84,8 @@ var/religion_name = null
|
||||
random = 13
|
||||
else
|
||||
//get the first word of the Holiday and use that
|
||||
var/i = findtext(events.holiday," ",1,0)
|
||||
name = copytext(events.holiday,1,i)
|
||||
var/i = findtext(SSevent.holiday," ",1,0)
|
||||
name = copytext(SSevent.holiday,1,i)
|
||||
new_station_name += name + " "
|
||||
|
||||
// Suffix
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
. = "[ls[++i]]" // Make sure the initial element is converted to text.
|
||||
|
||||
if(l-1 & 0x01) // 'i' will always be 1 here.
|
||||
. += S1 // Append 1 element if the remaining elements are not a multiple of 2.
|
||||
. += "[S1]" // Append 1 element if the remaining elements are not a multiple of 2.
|
||||
if(l-i & 0x02)
|
||||
. = text("[][][]", ., S1, S1) // Append 2 elements if the remaining elements are not a multiple of 4.
|
||||
if(l-i & 0x04)
|
||||
|
||||
+59
-25
@@ -1010,15 +1010,15 @@ Turf and target are seperate in case you want to teleport some distance from a t
|
||||
|
||||
if(toupdate.len)
|
||||
for(var/turf/simulated/T1 in toupdate)
|
||||
air_master.remove_from_active(T1)
|
||||
SSair.remove_from_active(T1)
|
||||
T1.CalculateAdjacentTurfs()
|
||||
air_master.add_to_active(T1,1)
|
||||
SSair.add_to_active(T1,1)
|
||||
|
||||
if(fromupdate.len)
|
||||
for(var/turf/simulated/T2 in fromupdate)
|
||||
air_master.remove_from_active(T2)
|
||||
SSair.remove_from_active(T2)
|
||||
T2.CalculateAdjacentTurfs()
|
||||
air_master.add_to_active(T2,1)
|
||||
SSair.add_to_active(T2,1)
|
||||
|
||||
|
||||
|
||||
@@ -1163,7 +1163,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
|
||||
if(toupdate.len)
|
||||
for(var/turf/simulated/T1 in toupdate)
|
||||
T1.CalculateAdjacentTurfs()
|
||||
air_master.add_to_active(T1,1)
|
||||
SSair.add_to_active(T1,1)
|
||||
|
||||
|
||||
return copiedobjs
|
||||
@@ -1207,12 +1207,64 @@ Turf and target are seperate in case you want to teleport some distance from a t
|
||||
else return zone
|
||||
|
||||
|
||||
//Gets the turf this atom inhabits
|
||||
/proc/get_turf(atom/movable/AM)
|
||||
if(istype(AM))
|
||||
return locate(/turf) in AM.locs
|
||||
else if(isturf(AM))
|
||||
return AM
|
||||
|
||||
//Gets the turf this atom's *ICON* appears to inhabit
|
||||
//Uses half the width/height respectively to work out
|
||||
//A minimum pixel amt this icon needs to be pixel'd by
|
||||
//to be considered to be in another turf
|
||||
|
||||
//division = world.icon_size - icon-width/2; DX = pixel_x/division
|
||||
//division = world.icon_size - icon-height/2; DY = pixel_y/division
|
||||
|
||||
//Eg: Humans
|
||||
//32 - 16; 16/16 = 1, DX = 1
|
||||
//32 - 16; 15/16 = 0.9375 = 0 when round()'d, DX = 0
|
||||
|
||||
//NOTE: if your atom has non-standard bounds then this proc
|
||||
//will handle it, but it'll be a bit slower.
|
||||
|
||||
/proc/get_turf_pixel(atom/movable/AM)
|
||||
if(istype(AM))
|
||||
var/rough_x = 0
|
||||
var/rough_y = 0
|
||||
var/final_x = 0
|
||||
var/final_y = 0
|
||||
|
||||
//Assume standards
|
||||
var/i_width = world.icon_size
|
||||
var/i_height = world.icon_size
|
||||
|
||||
//Handle snowflake objects only if necessary
|
||||
if(AM.bound_height != world.icon_size || AM.bound_width != world.icon_size)
|
||||
var/icon/AMicon = icon(AM.icon, AM.icon_state)
|
||||
i_width = AMicon.Width()
|
||||
i_height = AMicon.Height()
|
||||
qdel(AMicon)
|
||||
|
||||
//Find a value to divide pixel_ by
|
||||
var/n_width = (world.icon_size - (i_width/2))
|
||||
var/n_height = (world.icon_size - (i_height/2))
|
||||
|
||||
//DY and DX
|
||||
rough_x = round(AM.pixel_x/n_width)
|
||||
rough_y = round(AM.pixel_y/n_height)
|
||||
|
||||
//Find coordinates
|
||||
final_x = AM.x + rough_x
|
||||
final_y = AM.y + rough_y
|
||||
|
||||
if(final_x || final_y)
|
||||
var/turf/T = locate(final_x, final_y, AM.z)
|
||||
if(T)
|
||||
return T
|
||||
|
||||
|
||||
/proc/get(atom/loc, type)
|
||||
while(loc)
|
||||
if(istype(loc, type))
|
||||
@@ -1300,7 +1352,6 @@ var/global/list/common_tools = list(
|
||||
istype(W, /obj/item/weapon/melee/energy/blade) || \
|
||||
istype(W, /obj/item/weapon/shovel) || \
|
||||
istype(W, /obj/item/weapon/kitchenknife) || \
|
||||
istype(W, /obj/item/weapon/butch) || \
|
||||
istype(W, /obj/item/weapon/scalpel) || \
|
||||
istype(W, /obj/item/weapon/kitchen/utensil/knife) || \
|
||||
istype(W, /obj/item/weapon/shard) || \
|
||||
@@ -1331,8 +1382,8 @@ var/list/WALLITEMS = list(
|
||||
return 1
|
||||
|
||||
//Some stuff doesn't use dir properly, so we need to check pixel instead
|
||||
//That's exactly what get_wall_mounted_turf() does
|
||||
if(get_wall_mounted_turf(O) == locdir)
|
||||
//That's exactly what get_turf_pixel() does
|
||||
if(get_turf_pixel(O) == locdir)
|
||||
return 1
|
||||
|
||||
//Some stuff is placed directly on the wallturf (signs)
|
||||
@@ -1394,20 +1445,3 @@ var/list/WALLITEMS = list(
|
||||
chance = max(chance - (initial_chance / steps), 0)
|
||||
steps--
|
||||
|
||||
/proc/get_wall_mounted_turf(atom/A)
|
||||
/* This proc uses the pixel_x/y vars to guess the fake turf of a wall mounted atom.
|
||||
Needless to say, this is an error prone method and possibly open to exploits.
|
||||
It is however faster than using icon procs to get a more accurate reading.
|
||||
I've only tested this proc on APCs. Use it with care.*/
|
||||
if(is_type_in_list(A, WALLITEMS))
|
||||
var/stepdir = 0
|
||||
|
||||
if(A.pixel_x > 10) stepdir |= EAST
|
||||
if(A.pixel_x < -10) stepdir |= WEST
|
||||
if(A.pixel_y > 10) stepdir |= NORTH
|
||||
if(A.pixel_y < -10) stepdir |= SOUTH
|
||||
|
||||
if(stepdir)
|
||||
return get_step(A, stepdir)
|
||||
return get_turf(A)
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#define BACKGROUND_ENABLED 0 // The default value for all uses of set background. Set background can cause gradual lag and is recommended you only turn this on if necessary.
|
||||
// 1 will enable set background. 0 will disable set background.
|
||||
|
||||
#define INACTIVITY_KICK 6000 //10 minutes in ticks (approx.)
|
||||
|
||||
//ADMIN STUFF
|
||||
#define ROUNDSTART_LOGOUT_REPORT_TIME 6000 //Amount of time (in deciseconds) after the rounds starts, that the player disconnect report is issued.
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
var/going = 1.0 // ticker pregame countdown
|
||||
var/master_mode = "traitor"//"extended"
|
||||
var/secret_force_mode = "secret" // if this is anything but "secret", the secret rotation will forceably choose this mode
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//////////////
|
||||
var/NEARSIGHTEDBLOCK = 0
|
||||
var/NEARSIGHTBLOCK = 0
|
||||
var/EPILEPSYBLOCK = 0
|
||||
var/COUGHBLOCK = 0
|
||||
var/TOURETTESBLOCK = 0
|
||||
@@ -23,3 +23,6 @@ var/NULLED_UI
|
||||
|
||||
var/list/global_mutations = list() // list of hidden mutation things
|
||||
|
||||
var/list/bad_mutations = list()
|
||||
var/list/good_mutations = list()
|
||||
var/list/not_good_mutations = list()
|
||||
@@ -14,6 +14,10 @@ var/global/list/underwear_f = list() //stores only underwear name
|
||||
var/global/list/undershirt_list = list() //stores /datum/sprite_accessory/undershirt indexed by name
|
||||
var/global/list/undershirt_m = list() //stores only undershirt name
|
||||
var/global/list/undershirt_f = list() //stores only undershirt name
|
||||
//Socks
|
||||
var/global/list/socks_list = list() //stores /datum/sprite_accessory/socks indexed by name
|
||||
var/global/list/socks_m = list() //stores only socks name
|
||||
var/global/list/socks_f = list() //stores only socks name
|
||||
//Backpacks
|
||||
var/global/list/backbaglist = list("Nothing", "Backpack", "Satchel")
|
||||
//Female Uniforms
|
||||
|
||||
@@ -43,7 +43,6 @@ var/list/prisonwarped = list() //list of players already warped
|
||||
var/list/blobstart = list()
|
||||
var/list/secequipment = list()
|
||||
var/list/deathsquadspawn = list()
|
||||
var/list/global_activeturfs
|
||||
|
||||
//away missions
|
||||
var/list/awaydestinations = list() //a list of landmarks that the warpgate can take you to
|
||||
|
||||
@@ -3,12 +3,7 @@ var/global/list/portals = list() //list of all /obj/effect/portal
|
||||
var/global/list/airlocks = list() //list of all airlocks
|
||||
var/global/list/mechas_list = list() //list of all mechs. Used by hostile mobs target tracking.
|
||||
var/global/list/shuttle_caller_list = list() //list of all communication consoles and AIs, for automatic shuttle calls when there are none.
|
||||
|
||||
//items that ask to be called every cycle
|
||||
var/global/list/machines = list()
|
||||
var/global/list/processing_objects = list()
|
||||
var/global/list/active_diseases = list()
|
||||
var/global/list/aibots = list()
|
||||
var/global/list/machines = list() //NOTE: this is a list of ALL machines now. The processing machines list is SSmachine.processing !
|
||||
|
||||
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
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
var/global/obj/effect/overlay/plmaster = null // atmospheric overlay for plasma
|
||||
var/global/obj/effect/overlay/slmaster = null // atmospheric overlay for sleeping gas
|
||||
var/admin_notice = "" // Admin notice that all clients see when joining the server
|
||||
|
||||
|
||||
// nanomanager, the manager for Nano UIs
|
||||
var/datum/nanomanager/nanomanager = new()
|
||||
|
||||
var/timezoneOffset = 0 // The difference betwen midnight (of the host computer) and 0 world.ticks.
|
||||
|
||||
// For FTP requests. (i.e. downloading runtime logs.)
|
||||
// However it'd be ok to use for accessing attack logs and such too, which are even laggier.
|
||||
var/fileaccess_timer = 0
|
||||
|
||||
var/TAB = " "
|
||||
var/TAB = " "
|
||||
|
||||
|
||||
|
||||
var/map_ready = 0
|
||||
/*
|
||||
basically, this will be used to avoid initialize() being called twice for objects
|
||||
initialize() is necessary because the map is instanced on a turf-by-turf basis
|
||||
i.e. all obj on a turf are instanced, then all mobs on that turf, before moving to the next turf (starting bottom-left)
|
||||
This means if we want to say, get any neighbouring objects in New(), only objects to the south and west will exist yet.
|
||||
Therefore, we'd need to use spawn() inside New() to wait for the surrounding turf contents to be instanced
|
||||
However, using lots of spawn() has a severe performance impact, and often results in spaghetti-code
|
||||
map_ready will be set to 1 when world/New() is called (which happens just after the map is instanced)
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ var/global/obj/effect/datacore/data_core = null
|
||||
|
||||
var/CELLRATE = 0.002 // multiplier for watts per tick <> cell storage (eg: .002 means if there is a load of 1000 watts, 20 units will be taken from a cell per second)
|
||||
var/CHARGELEVEL = 0.001 // Cap for how fast cells charge, as a percentage-per-tick (.001 means cellcharge is capped to 1% per second)
|
||||
var/datum/sun/sun = null
|
||||
|
||||
var/list/powernets = list()
|
||||
|
||||
// this is not strictly unused although the whole modules datum thing is unused
|
||||
|
||||
+38
-40
@@ -104,34 +104,20 @@
|
||||
The following criminally helpful code is just the previous code cleaned up;
|
||||
I have no idea why it was in atoms.dm instead of respective files.
|
||||
*/
|
||||
/atom/proc/AICtrlShiftClick()
|
||||
return
|
||||
|
||||
/obj/machinery/door/airlock/AICtrlShiftClick() // Sets/Unsets Emergency Access Override
|
||||
if(emagged)
|
||||
return
|
||||
if(!emergency)
|
||||
Topic("aiEnable=11", list("aiEnable"="11"), 1) // 1 meaning no window (consistency!)
|
||||
else
|
||||
Topic("aiDisable=11", list("aiDisable"="11"), 1)
|
||||
return
|
||||
|
||||
/atom/proc/AIShiftClick()
|
||||
return
|
||||
|
||||
/obj/machinery/door/airlock/AIShiftClick() // Opens and closes doors!
|
||||
if(emagged)
|
||||
return
|
||||
if(density)
|
||||
Topic("aiEnable=7", list("aiEnable"="7"), 1) // 1 meaning no window (consistency!)
|
||||
else
|
||||
Topic("aiDisable=7", list("aiDisable"="7"), 1)
|
||||
return
|
||||
|
||||
/* Questions: Instead of an Emag check on every function, can we not add to airlocks onclick if emag return? */
|
||||
|
||||
/* Atom Procs */
|
||||
/atom/proc/AICtrlClick()
|
||||
return
|
||||
|
||||
/atom/proc/AIAltClick(var/mob/living/silicon/ai/user)
|
||||
AltClick(user)
|
||||
return
|
||||
/atom/proc/AIShiftClick()
|
||||
return
|
||||
/atom/proc/AICtrlShiftClick()
|
||||
return
|
||||
|
||||
/* Airlocks */
|
||||
/obj/machinery/door/airlock/AICtrlClick() // Bolts doors
|
||||
if(emagged)
|
||||
return
|
||||
@@ -139,21 +125,7 @@
|
||||
Topic("aiEnable=4", list("aiEnable"="4"), 1)// 1 meaning no window (consistency!)
|
||||
else
|
||||
Topic("aiDisable=4", list("aiDisable"="4"), 1)
|
||||
|
||||
/obj/machinery/power/apc/AICtrlClick() // turns off/on APCs.
|
||||
toggle_breaker()
|
||||
add_fingerprint(usr)
|
||||
|
||||
/obj/machinery/turretid/AICtrlClick() //turns off/on Turrets
|
||||
if(can_be_used_by(usr))
|
||||
toggle_on()
|
||||
add_fingerprint(usr)
|
||||
|
||||
|
||||
/atom/proc/AIAltClick(var/mob/living/silicon/ai/user)
|
||||
AltClick(user)
|
||||
return
|
||||
|
||||
/obj/machinery/door/airlock/AIAltClick() // Eletrifies doors.
|
||||
if(emagged)
|
||||
return
|
||||
@@ -164,15 +136,41 @@
|
||||
// disable/6 is not in Topic; disable/5 disables both temporary and permenant shock
|
||||
Topic("aiDisable=5", list("aiDisable"="5"), 1)
|
||||
return
|
||||
/obj/machinery/door/airlock/AIShiftClick() // Opens and closes doors!
|
||||
if(emagged)
|
||||
return
|
||||
if(density)
|
||||
Topic("aiEnable=7", list("aiEnable"="7"), 1) // 1 meaning no window (consistency!)
|
||||
else
|
||||
Topic("aiDisable=7", list("aiDisable"="7"), 1)
|
||||
return
|
||||
/obj/machinery/door/airlock/AICtrlShiftClick() // Sets/Unsets Emergency Access Override
|
||||
if(emagged)
|
||||
return
|
||||
if(!emergency)
|
||||
Topic("aiEnable=11", list("aiEnable"="11"), 1) // 1 meaning no window (consistency!)
|
||||
else
|
||||
Topic("aiDisable=11", list("aiDisable"="11"), 1)
|
||||
return
|
||||
|
||||
/* APC */
|
||||
/obj/machinery/power/apc/AICtrlClick() // turns off/on APCs.
|
||||
toggle_breaker()
|
||||
add_fingerprint(usr)
|
||||
|
||||
/* AI Turrets */
|
||||
/obj/machinery/turretid/AIAltClick() //toggles lethal on turrets
|
||||
if(can_be_used_by(usr))
|
||||
toggle_lethal()
|
||||
add_fingerprint(usr)
|
||||
/obj/machinery/turretid/AICtrlClick() //turns off/on Turrets
|
||||
if(can_be_used_by(usr))
|
||||
toggle_on()
|
||||
add_fingerprint(usr)
|
||||
|
||||
//
|
||||
// Override TurfAdjacent for AltClicking
|
||||
//
|
||||
|
||||
/mob/living/silicon/ai/TurfAdjacent(var/turf/T)
|
||||
return (cameranet && cameranet.checkTurfVis(T))
|
||||
return (cameranet && cameranet.checkTurfVis(T))
|
||||
|
||||
@@ -161,12 +161,6 @@
|
||||
animals lunging, etc.
|
||||
*/
|
||||
/mob/proc/RangedAttack(var/atom/A, var/params)
|
||||
if(!mutations.len) return
|
||||
if((LASER in mutations) && a_intent == "harm")
|
||||
LaserEyes(A) // moved into a proc below
|
||||
else
|
||||
if(TK in mutations)
|
||||
A.attack_tk(src)
|
||||
/*
|
||||
Restrained ClickOn
|
||||
|
||||
|
||||
+19
-10
@@ -13,7 +13,15 @@
|
||||
if(proximity && istype(G) && G.Touch(A,1))
|
||||
return
|
||||
|
||||
var/override = 0
|
||||
|
||||
for(var/datum/mutation/human/HM in dna.mutations)
|
||||
override += HM.on_attack_hand(src, A)
|
||||
|
||||
if(override) return
|
||||
|
||||
A.attack_hand(src)
|
||||
|
||||
/atom/proc/attack_hand(mob/user as mob)
|
||||
return
|
||||
|
||||
@@ -26,28 +34,29 @@
|
||||
return 0
|
||||
|
||||
/mob/living/carbon/human/RangedAttack(var/atom/A)
|
||||
if(!gloves && !mutations.len) return
|
||||
var/obj/item/clothing/gloves/G = gloves
|
||||
if((LASER in mutations) && a_intent == "harm")
|
||||
LaserEyes(A) // moved into a proc below
|
||||
if(gloves)
|
||||
var/obj/item/clothing/gloves/G = gloves
|
||||
if(istype(G) && G.Touch(A,0)) // for magic gloves
|
||||
return
|
||||
|
||||
else if(istype(G) && G.Touch(A,0)) // for magic gloves
|
||||
return
|
||||
|
||||
else if(TK in mutations)
|
||||
A.attack_tk(src)
|
||||
for(var/datum/mutation/human/HM in dna.mutations)
|
||||
HM.on_ranged_attack(src, A)
|
||||
|
||||
/*
|
||||
Animals & All Unspecified
|
||||
*/
|
||||
/mob/living/UnarmedAttack(var/atom/A)
|
||||
A.attack_animal(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/UnarmedAttack(var/atom/A)
|
||||
target = A
|
||||
AttackingTarget()
|
||||
|
||||
/atom/proc/attack_animal(mob/user as mob)
|
||||
return
|
||||
/mob/living/RestrainedClickOn(var/atom/A)
|
||||
return
|
||||
|
||||
|
||||
/*
|
||||
Monkeys
|
||||
*/
|
||||
|
||||
@@ -38,14 +38,11 @@ var/const/tk_maxrange = 15
|
||||
return
|
||||
|
||||
/obj/item/attack_tk(mob/user)
|
||||
if(user.stat || !isturf(loc)) return
|
||||
if((TK in user.mutations) && !user.get_active_hand()) // both should already be true to get here
|
||||
var/obj/item/tk_grab/O = new(src)
|
||||
user.put_in_active_hand(O)
|
||||
O.host = user
|
||||
O.focus_object(src)
|
||||
else
|
||||
WARNING("Strange attack_tk(): TK([TK in user.mutations]) empty hand([!user.get_active_hand()])")
|
||||
if(user.stat) return
|
||||
var/obj/item/tk_grab/O = new(src)
|
||||
user.put_in_active_hand(O)
|
||||
O.host = user
|
||||
O.focus_object(src)
|
||||
return
|
||||
|
||||
|
||||
@@ -95,13 +92,13 @@ var/const/tk_maxrange = 15
|
||||
if(focus)
|
||||
focus.attack_self_tk(user)
|
||||
|
||||
/obj/item/tk_grab/afterattack(atom/target, mob/living/user, proximity)//TODO: go over this
|
||||
/obj/item/tk_grab/afterattack(atom/target, mob/living/carbon/user, proximity)//TODO: go over this
|
||||
if(!target || !user) return
|
||||
if(last_throw+3 > world.time) return
|
||||
if(!host || host != user)
|
||||
qdel(src)
|
||||
return
|
||||
if(!(TK in host.mutations))
|
||||
if(!(user.dna.check_mutation(TK)))
|
||||
qdel(src)
|
||||
return
|
||||
if(isobj(target) && !isturf(target.loc))
|
||||
|
||||
@@ -10,11 +10,12 @@
|
||||
|
||||
Unlike sd_DAL however it uses a queueing system. Everytime we call a change to opacity or luminosity
|
||||
(through SetOpacity() or SetLuminosity()) we are simply updating variables and scheduling certain lights/turfs for an
|
||||
update. Actual updates are handled periodically by the lighting_controller. This carries additional overheads, however it
|
||||
means that each thing is changed only once per lighting_controller.processing_interval ticks. Allowing for greater control
|
||||
over how much priority we'd like lighting updates to have. It also makes it possible for us to simply delay updates by
|
||||
setting lighting_controller.processing = 0 at say, the start of a large explosion, waiting for it to finish, and then
|
||||
turning it back on with lighting_controller.processing = 1.
|
||||
update. Actual updates are handled periodically by the SSlighting subsystem. This carries additional overheads, however it
|
||||
means that each thing is changed only once per SSlighting.wait deciseconds. Allowing for greater control
|
||||
over how much priority we'd like lighting updates to have.
|
||||
|
||||
UPDATE: we no longer postpone lighting updates by editting variables, there is now a subsystem/proc/postpone() procedure.
|
||||
So you would do SSlighting.postpone()
|
||||
|
||||
Unlike our old system there are hardcoded maximum luminositys (different for certain atoms).
|
||||
This is to cap the cost of creating lighting effects.
|
||||
@@ -29,9 +30,7 @@
|
||||
No directional lighting support. (prototype looked ugly)
|
||||
*/
|
||||
|
||||
#define LIGHTING_CIRCULAR 1 //comment this out to use old square lighting effects.
|
||||
#define LIGHTING_LAYER 10 //Drawing layer for lighting overlays
|
||||
#define LIGHTING_ICON 'icons/effects/ss13_dark_alpha6.dmi' //Icon used for lighting shading effects
|
||||
#define USE_CIRCULAR_LIGHTING //comment this out to use old square lighting effects.
|
||||
|
||||
/datum/light_source
|
||||
var/atom/owner
|
||||
@@ -49,7 +48,7 @@
|
||||
__x = owner.x
|
||||
__y = owner.y
|
||||
// the lighting object maintains a list of all light sources
|
||||
lighting_controller.lights += src
|
||||
SSlighting.lights += src
|
||||
|
||||
|
||||
//Check a light to see if its effect needs reprocessing. If it does, remove any old effect and create a new one
|
||||
@@ -75,17 +74,30 @@
|
||||
// before we apply the effect we remove the light's current effect.
|
||||
for(var/turf/T in effect) // negate the effect of this light source
|
||||
T.update_lumcount(-effect[T])
|
||||
|
||||
if(T.affecting_lights && T.affecting_lights.len)
|
||||
T.affecting_lights -= src
|
||||
else
|
||||
T.affecting_lights = null
|
||||
|
||||
effect.Cut() // clear the effect list
|
||||
|
||||
/datum/light_source/proc/add_effect()
|
||||
// only do this if the light is turned on and is on the map
|
||||
if(owner.loc && owner.luminosity > 0)
|
||||
effect = list()
|
||||
for(var/turf/T in view(owner.get_light_range(),get_turf(owner)))
|
||||
var/delta_lumen = lum(T)
|
||||
if(delta_lumen > 0)
|
||||
effect[T] = delta_lumen
|
||||
T.update_lumcount(delta_lumen)
|
||||
var/turf/To = get_turf(owner)
|
||||
var/range = owner.get_light_range()
|
||||
|
||||
for(var/turf/T in view(range, To))
|
||||
var/delta_lumcount = T.lumen(src)
|
||||
if(delta_lumcount > 0)
|
||||
effect[T] = delta_lumcount
|
||||
T.update_lumcount(delta_lumcount)
|
||||
|
||||
if(!T.affecting_lights)
|
||||
T.affecting_lights = list()
|
||||
T.affecting_lights += src
|
||||
|
||||
return 0
|
||||
else
|
||||
@@ -93,27 +105,21 @@
|
||||
return 1 //cause the light to be removed from the lights list and garbage collected once it's no
|
||||
//longer referenced by the queue
|
||||
|
||||
/datum/light_source/proc/lum(turf/A)
|
||||
if (owner.trueLuminosity < 1)
|
||||
return 0
|
||||
var/dist
|
||||
if(!A)
|
||||
dist = 0
|
||||
else
|
||||
#ifdef LIGHTING_CIRCULAR
|
||||
dist = cheap_hypotenuse(A.x, A.y, __x, __y)
|
||||
/turf/proc/lumen(datum/light_source/L)
|
||||
. = L.owner.luminosity
|
||||
#ifdef USE_CIRCULAR_LIGHTING
|
||||
. -= cheap_hypotenuse(x, y, L.__x, L.__y)
|
||||
#else
|
||||
dist = max(abs(A.x - __x), abs(A.y - __y))
|
||||
. -= max(abs(x - L.__x), abs(y - L.__y))
|
||||
#endif
|
||||
if (owner.trueLuminosity > 100) // This will never happen... right?
|
||||
return sqrt(owner.trueLuminosity) - dist
|
||||
else
|
||||
return sqrtTable[owner.trueLuminosity] - dist
|
||||
return .
|
||||
|
||||
/turf/space/lumen()
|
||||
return 0
|
||||
|
||||
|
||||
/atom
|
||||
var/datum/light_source/light
|
||||
var/trueLuminosity = 0 // Typically 'luminosity' squared. The builtin luminosity must remain linear.
|
||||
// We may read it, but NEVER set it directly.
|
||||
|
||||
|
||||
//Turfs with opacity when they are constructed will trigger nearby lights to update
|
||||
@@ -122,7 +128,6 @@
|
||||
..()
|
||||
if(luminosity)
|
||||
if(light) WARNING("[type] - Don't set lights up manually during New(), We do it automatically.")
|
||||
trueLuminosity = luminosity * luminosity
|
||||
light = new(src)
|
||||
|
||||
//Movable atoms with opacity when they are constructed will trigger nearby lights to update
|
||||
@@ -130,20 +135,15 @@
|
||||
/atom/movable/New()
|
||||
..()
|
||||
if(opacity)
|
||||
if(isturf(loc))
|
||||
if(loc:lighting_lumcount > 1)
|
||||
UpdateAffectingLights()
|
||||
UpdateAffectingLights()
|
||||
if(luminosity)
|
||||
if(light) WARNING("[type] - Don't set lights up manually during New(), We do it automatically.")
|
||||
trueLuminosity = luminosity * luminosity
|
||||
light = new(src)
|
||||
|
||||
//Objects with opacity will trigger nearby lights to update at next lighting process.
|
||||
/atom/movable/Destroy()
|
||||
if(opacity)
|
||||
if(isturf(loc))
|
||||
if(loc:lighting_lumcount > 1)
|
||||
UpdateAffectingLights()
|
||||
UpdateAffectingLights()
|
||||
return ..()
|
||||
|
||||
//Sets our luminosity.
|
||||
@@ -151,34 +151,22 @@
|
||||
//If we are setting luminosity to 0 the light will be cleaned up by the controller and garbage collected once all its
|
||||
//queues are complete.
|
||||
//if we have a light already it is merely updated, rather than making a new one.
|
||||
/atom/proc/SetLuminosity(new_luminosity, trueLum = FALSE)
|
||||
/atom/proc/SetLuminosity(new_luminosity)
|
||||
if(new_luminosity < 0)
|
||||
new_luminosity = 0
|
||||
if(!trueLum)
|
||||
new_luminosity *= new_luminosity
|
||||
if(light)
|
||||
if(trueLuminosity != new_luminosity) //non-luminous lights are removed from the lights list in add_effect()
|
||||
if(luminosity != new_luminosity) //non-luminous lights are removed from the lights list in add_effect()
|
||||
light.changed = 1
|
||||
else
|
||||
if(new_luminosity)
|
||||
light = new(src)
|
||||
trueLuminosity = new_luminosity
|
||||
if (trueLuminosity < 1)
|
||||
luminosity = 0
|
||||
else if (trueLuminosity <= 100)
|
||||
luminosity = sqrtTable[trueLuminosity]
|
||||
else
|
||||
luminosity = sqrt(trueLuminosity)
|
||||
luminosity = new_luminosity
|
||||
|
||||
/atom/proc/AddLuminosity(delta_luminosity)
|
||||
if(delta_luminosity > 0)
|
||||
SetLuminosity(trueLuminosity + delta_luminosity*delta_luminosity, TRUE)
|
||||
else if(delta_luminosity < 0)
|
||||
SetLuminosity(trueLuminosity - delta_luminosity*delta_luminosity, TRUE)
|
||||
SetLuminosity(luminosity + delta_luminosity)
|
||||
|
||||
/area/SetLuminosity(new_luminosity) //we don't want dynamic lighting for areas
|
||||
luminosity = !!new_luminosity
|
||||
trueLuminosity = luminosity
|
||||
|
||||
|
||||
//change our opacity (defaults to toggle), and then update all lights that affect us.
|
||||
@@ -188,24 +176,13 @@
|
||||
else if(opacity == new_opacity)
|
||||
return 0 //opacity hasn't changed! don't bother doing anything
|
||||
opacity = new_opacity //update opacity, the below procs now call light updates.
|
||||
return 1
|
||||
|
||||
/turf/SetOpacity(new_opacity)
|
||||
if(..()==1) //only bother if opacity changed
|
||||
if(lighting_lumcount) //only bother with an update if our turf is currently affected by a light
|
||||
UpdateAffectingLights()
|
||||
|
||||
/atom/movable/SetOpacity(new_opacity)
|
||||
if(..()==1) //only bother if opacity changed
|
||||
if(isturf(loc)) //only bother with an update if we're on a turf
|
||||
var/turf/T = loc
|
||||
if(T.lighting_lumcount) //only bother with an update if our turf is currently affected by a light
|
||||
UpdateAffectingLights()
|
||||
UpdateAffectingLights()
|
||||
|
||||
|
||||
/turf
|
||||
var/lighting_lumcount = 0
|
||||
var/lighting_changed = 0
|
||||
var/list/affecting_lights //not initialised until used (even empty lists reserve a fair bit of memory)
|
||||
|
||||
/turf/space
|
||||
lighting_lumcount = 4 //starlight
|
||||
@@ -213,29 +190,30 @@
|
||||
/turf/proc/update_lumcount(amount)
|
||||
lighting_lumcount += amount
|
||||
if(!lighting_changed)
|
||||
lighting_controller.changed_turfs += src
|
||||
SSlighting.changed_turfs += src
|
||||
lighting_changed = 1
|
||||
|
||||
/turf/proc/lighting_tag(var/level)
|
||||
var/area/A = loc
|
||||
return A.tagbase + "sd_L[level]"
|
||||
/area/proc/lighting_tag(level)
|
||||
return tagbase + "sd_L[level]"
|
||||
|
||||
/turf/proc/build_lighting_area(var/tag, var/level)
|
||||
var/area/Area = loc
|
||||
var/area/A = new Area.type() // create area if it wasn't found
|
||||
/area/proc/build_lighting_area(tag, level)
|
||||
var/area/A = locate(tag) // find an appropriate area
|
||||
if(A)
|
||||
return A
|
||||
A = new type() // create area if it wasn't found
|
||||
// replicate vars
|
||||
for(var/V in Area.vars)
|
||||
for(var/V in vars)
|
||||
switch(V)
|
||||
if("contents","lighting_overlay","overlays") continue
|
||||
if("contents","last_light","overlays") continue
|
||||
else
|
||||
if(issaved(Area.vars[V])) A.vars[V] = Area.vars[V]
|
||||
if(issaved(vars[V])) A.vars[V] = vars[V]
|
||||
|
||||
A.tag = tag
|
||||
A.lighting_subarea = 1
|
||||
A.lighting_space = 0 // in case it was copied from a space subarea
|
||||
A.SetLightLevel(level)
|
||||
|
||||
Area.related += A
|
||||
related += A
|
||||
return A
|
||||
|
||||
/turf/proc/shift_to_subarea()
|
||||
@@ -244,58 +222,38 @@
|
||||
|
||||
if(!istype(Area) || !Area.lighting_use_dynamic) return
|
||||
|
||||
var/level = min(max(round(lighting_lumcount,1),0),lighting_controller.lighting_states)
|
||||
var/new_tag = lighting_tag(level)
|
||||
var/level = min(max(round(lighting_lumcount,1),0),SSlighting.lighting_images.len)
|
||||
var/new_tag = Area.lighting_tag(level)
|
||||
if(Area.tag!=new_tag) //skip if already in this area
|
||||
var/area/A = locate(new_tag) // find an appropriate area
|
||||
|
||||
if(!A)
|
||||
A = build_lighting_area(new_tag,level)
|
||||
|
||||
var/area/A = Area.build_lighting_area(new_tag,level)
|
||||
A.contents += src // move the turf into the area
|
||||
|
||||
// Dedicated lighting sublevel for space turfs
|
||||
// helps us depower things in space, remove space fire alarms,
|
||||
// and evens out space lighting
|
||||
/turf/space/lighting_tag(var/level)
|
||||
var/area/A = loc
|
||||
return A.tagbase + "sd_L_space"
|
||||
/turf/space/build_lighting_area(var/tag,var/level)
|
||||
var/area/A = ..(tag,4)
|
||||
A.lighting_space = 1
|
||||
A.SetLightLevel(4)
|
||||
A.icon_state = null
|
||||
return A
|
||||
|
||||
/area
|
||||
var/lighting_use_dynamic = 1 //Turn this flag off to prevent sd_DynamicAreaLighting from affecting this area
|
||||
var/image/lighting_overlay //tracks the darkness image of the area for easy removal
|
||||
var/last_light //tracks the last light level set for this area (used for removing previously applied lighting overlays)
|
||||
var/lighting_subarea = 0 //tracks whether we're a lighting sub-area
|
||||
var/lighting_space = 0 // true for space-only lighting subareas
|
||||
var/tagbase
|
||||
|
||||
/area/proc/SetLightLevel(light)
|
||||
if(!src) return
|
||||
if(light <= 0)
|
||||
light = 0
|
||||
if(light <= 1)
|
||||
light = 1
|
||||
luminosity = 0
|
||||
else
|
||||
if(light > lighting_controller.lighting_states)
|
||||
light = lighting_controller.lighting_states
|
||||
if(light > SSlighting.lighting_images.len)
|
||||
light = SSlighting.lighting_images.len
|
||||
luminosity = 1
|
||||
|
||||
if(lighting_overlay)
|
||||
overlays -= lighting_overlay
|
||||
lighting_overlay.icon_state = "[light]"
|
||||
else
|
||||
lighting_overlay = image(LIGHTING_ICON,,num2text(light),LIGHTING_LAYER)
|
||||
|
||||
overlays += lighting_overlay
|
||||
if(last_light != light)
|
||||
if(last_light)
|
||||
overlays -= SSlighting.lighting_images[last_light]
|
||||
overlays += SSlighting.lighting_images[light]
|
||||
last_light = light
|
||||
|
||||
/area/proc/SetDynamicLighting()
|
||||
|
||||
src.lighting_use_dynamic = 1
|
||||
for(var/turf/T in src.contents)
|
||||
lighting_use_dynamic = 1
|
||||
for(var/turf/T in contents)
|
||||
T.update_lumcount(0)
|
||||
|
||||
/area/proc/InitializeLighting() //TODO: could probably improve this bit ~Carn
|
||||
@@ -306,20 +264,27 @@
|
||||
//show the dark overlay so areas, not yet in a lighting subarea, won't be bright as day and look silly.
|
||||
SetLightLevel(4)
|
||||
|
||||
#undef LIGHTING_LAYER
|
||||
#undef LIGHTING_CIRCULAR
|
||||
//#undef LIGHTING_ICON
|
||||
#define LIGHTING_MAX_LUMINOSITY_STATIC 8 //Maximum luminosity to reduce lag.
|
||||
#define LIGHTING_MAX_LUMINOSITY_MOBILE 5 //Moving objects have a lower max luminosity since these update more often. (lag reduction)
|
||||
#define LIGHTING_MAX_LUMINOSITY_TURF 1 //turfs have a severely shortened range to protect from inevitable floor-lighttile spam.
|
||||
#undef USE_CIRCULAR_LIGHTING
|
||||
|
||||
//set the changed status of all lights which could have possibly lit this atom.
|
||||
//We don't need to worry about lights which lit us but moved away, since they will have change status set already
|
||||
//This proc can cause lots of lights to be updated. :(
|
||||
/atom/proc/UpdateAffectingLights()
|
||||
for(var/atom/A in oview(LIGHTING_MAX_LUMINOSITY_STATIC-1,src))
|
||||
if(A.light)
|
||||
A.light.changed = 1 //force it to update at next process()
|
||||
|
||||
/atom/movable/UpdateAffectingLights()
|
||||
if(isturf(loc))
|
||||
loc.UpdateAffectingLights()
|
||||
|
||||
/turf/UpdateAffectingLights()
|
||||
if(affecting_lights)
|
||||
for(var/thing in affecting_lights)
|
||||
thing:changed = 1 //force it to update at next process()
|
||||
|
||||
|
||||
#define LIGHTING_MAX_LUMINOSITY_STATIC 8 //Maximum luminosity to reduce lag.
|
||||
#define LIGHTING_MAX_LUMINOSITY_MOBILE 5 //Moving objects have a lower max luminosity since these update more often. (lag reduction)
|
||||
#define LIGHTING_MAX_LUMINOSITY_MOB 5
|
||||
#define LIGHTING_MAX_LUMINOSITY_TURF 1 //turfs have a severely shortened range to protect from inevitable floor-lighttile spam.
|
||||
|
||||
//caps luminosity effects max-range based on what type the light's owner is.
|
||||
/atom/proc/get_light_range()
|
||||
@@ -328,6 +293,9 @@
|
||||
/atom/movable/get_light_range()
|
||||
return min(luminosity, LIGHTING_MAX_LUMINOSITY_MOBILE)
|
||||
|
||||
/mob/get_light_range()
|
||||
return min(luminosity, LIGHTING_MAX_LUMINOSITY_MOB)
|
||||
|
||||
/obj/machinery/light/get_light_range()
|
||||
return min(luminosity, LIGHTING_MAX_LUMINOSITY_STATIC)
|
||||
|
||||
@@ -336,4 +304,5 @@
|
||||
|
||||
#undef LIGHTING_MAX_LUMINOSITY_STATIC
|
||||
#undef LIGHTING_MAX_LUMINOSITY_MOBILE
|
||||
#undef LIGHTING_MAX_LUMINOSITY_MOB
|
||||
#undef LIGHTING_MAX_LUMINOSITY_TURF
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
var/del_new_on_log = 1 // del's new players if they log before they spawn in
|
||||
var/allow_Metadata = 0 // Metadata is supported.
|
||||
var/popup_admin_pm = 0 //adminPMs to non-admins show in a pop-up 'reply' window when set to 1.
|
||||
var/Ticklag = 0.9
|
||||
var/fps = 10
|
||||
var/Tickcomp = 0
|
||||
var/allow_holidays = 0 //toggles whether holiday-specific content should be used
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
var/admin_legacy_system = 0 //Defines whether the server uses the legacy admin system with admins.txt or the SQL system. Config option in config.txt
|
||||
var/ban_legacy_system = 0 //Defines whether the server uses the legacy banning system with the files in /data or the SQL system. Config option in config.txt
|
||||
var/use_age_restriction_for_jobs = 0 //Do jobs use account age restrictions? --requires database
|
||||
var/see_own_notes = 0 //Can players see their own admin notes (read-only)? Config option in config.txt
|
||||
|
||||
//game_options.txt configs
|
||||
var/force_random_names = 0
|
||||
@@ -109,6 +110,7 @@
|
||||
|
||||
var/rename_cyborg = 0
|
||||
var/ooc_during_round = 0
|
||||
var/emojis = 0
|
||||
|
||||
//Used for modifying movement speed for mobs.
|
||||
//Unversal modifiers
|
||||
@@ -260,7 +262,9 @@
|
||||
if("allow_metadata")
|
||||
config.allow_Metadata = 1
|
||||
if("kick_inactive")
|
||||
config.kick_inactive = 1
|
||||
if(value < 1)
|
||||
value = INACTIVITY_KICK
|
||||
config.kick_inactive = value
|
||||
if("load_jobs_from_txt")
|
||||
load_jobs_from_txt = 1
|
||||
if("forbid_singulo_possession")
|
||||
@@ -272,7 +276,11 @@
|
||||
if("useircbot")
|
||||
useircbot = 1
|
||||
if("ticklag")
|
||||
Ticklag = text2num(value)
|
||||
var/ticklag = text2num(value)
|
||||
if(ticklag > 0)
|
||||
fps = 10 / ticklag
|
||||
if("fps")
|
||||
fps = text2num(value)
|
||||
if("tickcomp")
|
||||
Tickcomp = 1
|
||||
if("automute_on")
|
||||
@@ -281,6 +289,8 @@
|
||||
global.comms_key = value
|
||||
if(value != "default_pwd" && length(value) > 6) //It's the default value or less than 6 characters long, warn badmins
|
||||
global.comms_allowed = 1
|
||||
if("see_own_notes")
|
||||
config.see_own_notes = 1
|
||||
else
|
||||
diary << "Unknown setting in configuration: '[name]'"
|
||||
|
||||
@@ -300,6 +310,8 @@
|
||||
config.rename_cyborg = 1
|
||||
if("ooc_during_round")
|
||||
config.ooc_during_round = 1
|
||||
if("emojis")
|
||||
config.emojis = 1
|
||||
if("run_delay")
|
||||
config.run_speed = text2num(value)
|
||||
if("walk_delay")
|
||||
@@ -408,6 +420,10 @@
|
||||
else
|
||||
diary << "Unknown setting in configuration: '[name]'"
|
||||
|
||||
fps = round(fps)
|
||||
if(fps <= 0)
|
||||
fps = initial(fps)
|
||||
|
||||
/datum/configuration/proc/loadsql(filename)
|
||||
var/list/Lines = file2list(filename)
|
||||
for(var/t in Lines)
|
||||
|
||||
@@ -1,38 +1,31 @@
|
||||
var/datum/controller/failsafe/Failsafe
|
||||
|
||||
/datum/controller/failsafe // This thing pretty much just keeps poking the master controller
|
||||
var/processing = 0
|
||||
var/processing_interval = 100 //poke the MC every 10 seconds
|
||||
var/processing_interval = 100 //poke the MC every 10 seconds - set to 0 to disable
|
||||
|
||||
var/MC_iteration = 0
|
||||
var/MC_defcon = 0 //alert level. For every poke that fails this is raised by 1. When it reaches 5 the MC is replaced with a new one. (effectively killing any master_controller.process() and starting a new one)
|
||||
|
||||
var/lighting_iteration = 0
|
||||
var/lighting_defcon = 0 //alert level for lighting controller.
|
||||
|
||||
/datum/controller/failsafe/New()
|
||||
//There can be only one failsafe. Out with the old in with the new (that way we can restart the Failsafe by spawning a new one)
|
||||
if(Failsafe != src)
|
||||
if(istype(Failsafe))
|
||||
del(Failsafe)
|
||||
qdel(Failsafe)
|
||||
Failsafe = src
|
||||
Failsafe.process()
|
||||
|
||||
|
||||
/datum/controller/failsafe/proc/process()
|
||||
processing = 1
|
||||
spawn(0)
|
||||
set background = BACKGROUND_ENABLED
|
||||
while(1) //more efficient than recursivly calling ourself over and over. background = 1 ensures we do not trigger an infinite loop
|
||||
if(!master_controller) new /datum/controller/game_controller() //replace the missing master_controller! This should never happen.
|
||||
if(!lighting_controller) new /datum/controller/lighting() //replace the missing lighting_controller
|
||||
|
||||
if(processing)
|
||||
if(master_controller.processing) //only poke if these overrides aren't in effect
|
||||
if(MC_iteration == controller_iteration) //master_controller hasn't finished processing in the defined interval
|
||||
if(processing_interval > 0)
|
||||
if(master_controller.processing_interval > 0) //only poke if these overrides aren't in effect
|
||||
if(MC_iteration == master_controller.iteration) //master_controller hasn't finished processing in the defined interval
|
||||
switch(MC_defcon)
|
||||
if(0 to 3)
|
||||
MC_defcon++
|
||||
++MC_defcon
|
||||
if(4)
|
||||
admins << "<font color='red' size='2'><b>Warning. The Master Controller has not fired in the last [MC_defcon*processing_interval] ticks. Automatic restart in [processing_interval] ticks.</b></font>"
|
||||
MC_defcon = 5
|
||||
@@ -43,26 +36,8 @@ var/datum/controller/failsafe/Failsafe
|
||||
MC_defcon = 0
|
||||
else
|
||||
MC_defcon = 0
|
||||
MC_iteration = controller_iteration
|
||||
|
||||
if(lighting_controller.processing)
|
||||
if(lighting_iteration == lighting_controller.iteration) //master_controller hasn't finished processing in the defined interval
|
||||
switch(lighting_defcon)
|
||||
if(0 to 3)
|
||||
lighting_defcon++
|
||||
if(4)
|
||||
admins << "<font color='red' size='2'><b>Warning. The Lighting Controller has not fired in the last [lighting_defcon*processing_interval] ticks. Automatic restart in [processing_interval] ticks.</b></font>"
|
||||
lighting_defcon = 5
|
||||
if(5)
|
||||
admins << "<font color='red' size='2'><b>Warning. The Lighting Controller has still not fired within the last [lighting_defcon*processing_interval] ticks. Killing and restarting...</b></font>"
|
||||
new /datum/controller/lighting() //replace the old lighting_controller (hence killing the old one's process)
|
||||
lighting_controller.process() //Start it rolling again
|
||||
lighting_defcon = 0
|
||||
else
|
||||
lighting_defcon = 0
|
||||
lighting_iteration = lighting_controller.iteration
|
||||
MC_iteration = master_controller.iteration
|
||||
sleep(processing_interval)
|
||||
else
|
||||
MC_defcon = 0
|
||||
lighting_defcon = 0
|
||||
|
||||
sleep(processing_interval)
|
||||
sleep(100)
|
||||
@@ -1,130 +0,0 @@
|
||||
var/datum/controller/lighting/lighting_controller = new ()
|
||||
|
||||
/datum/controller/lighting
|
||||
var/processing = 0
|
||||
var/processing_interval = 5 //setting this too low will probably kill the server. Don't be silly with it!
|
||||
var/process_cost = 0
|
||||
var/iteration = 0
|
||||
var/max_cpu_use = 98 //this is just to prevent it queueing up when the server is dying. Not a solution, just damage control while I rethink a lot of this and try out ideas.
|
||||
|
||||
var/lighting_states = 6
|
||||
|
||||
var/list/lights = list()
|
||||
var/lights_workload_max = 0
|
||||
|
||||
// var/list/changed_lights() //TODO: possibly implement this to reduce on overheads? Also, Look into static-lights idea.
|
||||
|
||||
var/list/changed_turfs = list()
|
||||
var/changed_turfs_workload_max = 0
|
||||
|
||||
/datum/controller/lighting/New()
|
||||
lighting_states = max( 0, length(icon_states(LIGHTING_ICON))-1 )
|
||||
if(lighting_controller != src)
|
||||
if(istype(lighting_controller,/datum/controller/lighting))
|
||||
Recover() //if we are replacing an existing lighting_controller (due to a crash) we attempt to preserve as much as we can
|
||||
del(lighting_controller)
|
||||
lighting_controller = src
|
||||
|
||||
|
||||
//Workhorse of lighting. It cycles through each light to see which ones need their effects updating. It updates their
|
||||
//effects and then processes every turf in the queue, moving the turfs to the corresponing lighting sub-area.
|
||||
//All queue lists prune themselves, which will cause lights with no luminosity to be garbage collected (cheaper and safer
|
||||
//than deleting them). Processing interval should be roughly half a second for best results.
|
||||
//By using queues we are ensuring we don't perform more updates than are necessary
|
||||
/datum/controller/lighting/proc/process()
|
||||
processing = 1
|
||||
spawn(0)
|
||||
set background = BACKGROUND_ENABLED
|
||||
while(1)
|
||||
if(processing && (world.cpu <= max_cpu_use))
|
||||
iteration++
|
||||
var/started = world.timeofday
|
||||
|
||||
lights_workload_max = max(lights_workload_max,lights.len)
|
||||
for(var/i=1, i<=lights.len, i++)
|
||||
var/datum/light_source/L = lights[i]
|
||||
if(L && !L.check())
|
||||
continue
|
||||
lights.Cut(i,i+1)
|
||||
i--
|
||||
|
||||
sleep(-1)
|
||||
|
||||
changed_turfs_workload_max = max(changed_turfs_workload_max,changed_turfs.len)
|
||||
for(var/i=1, i<=changed_turfs.len, i++)
|
||||
var/turf/T = changed_turfs[i]
|
||||
if(T && T.lighting_changed)
|
||||
T.shift_to_subarea()
|
||||
changed_turfs.Cut() // reset the changed list
|
||||
|
||||
process_cost = (world.timeofday - started)
|
||||
|
||||
sleep(processing_interval)
|
||||
|
||||
//same as above except it attempts to shift ALL turfs in the world regardless of lighting_changed status
|
||||
//Does not loop. Should be run prior to process() being called for the first time.
|
||||
//Note: if we get additional z-levels at runtime (e.g. if the gateway thin ever gets finished) we can initialize specific
|
||||
//z-levels with the z_level argument
|
||||
/datum/controller/lighting/proc/initializeLighting(var/z_level)
|
||||
processing = 0
|
||||
spawn(-1)
|
||||
set background = BACKGROUND_ENABLED
|
||||
for(var/i=1, i<=lights.len, i++)
|
||||
var/datum/light_source/L = lights[i]
|
||||
if(L.check())
|
||||
lights.Cut(i,i+1)
|
||||
i--
|
||||
|
||||
var/z_start = 1
|
||||
var/z_finish = world.maxz
|
||||
if(z_level)
|
||||
z_level = round(z_level,1)
|
||||
if(z_level > 0 && z_level <= world.maxz)
|
||||
z_start = z_level
|
||||
z_finish = z_level
|
||||
|
||||
for(var/k=z_start,k<=z_finish,k++)
|
||||
for(var/i=1,i<=world.maxx,i++)
|
||||
for(var/j=1,j<=world.maxy,j++)
|
||||
var/turf/T = locate(i,j,k)
|
||||
if(T) T.shift_to_subarea()
|
||||
|
||||
changed_turfs.Cut() // reset the changed list
|
||||
|
||||
|
||||
//Used to strip valid information from an existing controller and transfer it to a replacement
|
||||
//It works by using spawn(-1) to transfer the data, if there is a runtime the data does not get transfered but the loop
|
||||
//does not crash
|
||||
/datum/controller/lighting/proc/Recover()
|
||||
if(!istype(lighting_controller.changed_turfs,/list))
|
||||
lighting_controller.changed_turfs = list()
|
||||
if(!istype(lighting_controller.lights,/list))
|
||||
lighting_controller.lights = list()
|
||||
|
||||
for(var/i=1, i<=lighting_controller.lights.len, i++)
|
||||
var/datum/light_source/L = lighting_controller.lights[i]
|
||||
if(istype(L))
|
||||
spawn(-1) //so we don't crash the loop (inefficient)
|
||||
L.check()
|
||||
lights += L //If we didn't runtime then this will get transferred over
|
||||
|
||||
for(var/i=1, i<=lighting_controller.changed_turfs.len, i++)
|
||||
var/turf/T = lighting_controller.changed_turfs[i]
|
||||
if(istype(T) && T.lighting_changed)
|
||||
spawn(-1)
|
||||
T.shift_to_subarea()
|
||||
|
||||
var/msg = "## DEBUG: [time2text(world.timeofday)] lighting_controller restarted. Reports:\n"
|
||||
for(var/varname in lighting_controller.vars)
|
||||
switch(varname)
|
||||
if("tag","type","parent_type","vars") continue
|
||||
else
|
||||
var/varval1 = lighting_controller.vars[varname]
|
||||
var/varval2 = vars[varname]
|
||||
if(istype(varval1,/list))
|
||||
varval1 = "/list([length(varval1)])"
|
||||
varval2 = "/list([length(varval2)])"
|
||||
msg += "\t [varname] = [varval1] -> [varval2]\n"
|
||||
world.log << msg
|
||||
|
||||
#undef LIGHTING_ICON
|
||||
@@ -1,310 +1,118 @@
|
||||
//simplified MC that is designed to fail when procs 'break'. When it fails it's just replaced with a new one.
|
||||
//It ensures master_controller.process() is never doubled up by killing the MC (hence terminating any of its sleeping procs)
|
||||
//WIP, needs lots of work still
|
||||
|
||||
var/global/datum/controller/game_controller/master_controller //Set in world.New()
|
||||
//Update: all core-systems are now placed inside subsystem datums. This makes them highly configurable and easy to work with.
|
||||
|
||||
var/global/controller_iteration = 0
|
||||
var/global/last_tick_timeofday = world.timeofday
|
||||
var/global/last_tick_duration = 0
|
||||
|
||||
var/global/air_processing_killed = 0
|
||||
var/global/pipe_processing_killed = 0
|
||||
var/global/datum/controller/game_controller/master_controller = new()
|
||||
|
||||
/datum/controller/game_controller
|
||||
var/processing = 0
|
||||
var/breather_ticks = 2 //a somewhat crude attempt to iron over the 'bumps' caused by high-cpu use by letting the MC have a breather for this many ticks after every loop
|
||||
var/minimum_ticks = 20 //The minimum length of time between MC ticks
|
||||
|
||||
var/air_cost = 0
|
||||
var/air_turfs = 0
|
||||
var/air_groups = 0
|
||||
var/air_highpressure= 0
|
||||
var/air_hotspots = 0
|
||||
var/air_superconductivity = 0
|
||||
var/sun_cost = 0
|
||||
var/mobs_cost = 0
|
||||
var/diseases_cost = 0
|
||||
var/machines_cost = 0
|
||||
var/aibots_cost = 0
|
||||
var/objects_cost = 0
|
||||
var/networks_cost = 0
|
||||
var/powernets_cost = 0
|
||||
var/nano_cost = 0
|
||||
var/events_cost = 0
|
||||
var/ticker_cost = 0
|
||||
var/gc_cost = 0
|
||||
var/total_cost = 0
|
||||
|
||||
var/processing_interval = 1 //The minimum length of time between MC ticks (in deciseconds). The highest this can be without affecting schedules, is the GCD of all subsystem var/wait. Set to 0 to disable all processing.
|
||||
var/iteration = 0
|
||||
var/cost = 0
|
||||
var/last_thing_processed
|
||||
|
||||
var/list/subsystems = list()
|
||||
|
||||
/datum/controller/game_controller/New()
|
||||
//There can be only one master_controller. Out with the old and in with the new.
|
||||
if(master_controller != src)
|
||||
if(istype(master_controller))
|
||||
Recover()
|
||||
del(master_controller)
|
||||
master_controller.Del()
|
||||
else
|
||||
init_subtypes(/datum/subsystem, subsystems)
|
||||
|
||||
master_controller = src
|
||||
|
||||
if(!events)
|
||||
new /datum/controller/event()
|
||||
|
||||
if(!air_master)
|
||||
air_master = new /datum/controller/air_system()
|
||||
air_master.setup()
|
||||
|
||||
if(!job_master)
|
||||
job_master = new /datum/controller/occupations()
|
||||
job_master.SetupOccupations()
|
||||
job_master.LoadJobs("config/jobs.txt")
|
||||
world << "<span class='userdanger'>Job setup complete</span>"
|
||||
|
||||
if(!syndicate_code_phrase) syndicate_code_phrase = generate_code_phrase()
|
||||
if(!syndicate_code_response) syndicate_code_response = generate_code_phrase()
|
||||
if(!ticker) ticker = new /datum/controller/gameticker()
|
||||
if(!emergency_shuttle) emergency_shuttle = new /datum/shuttle_controller/emergency_shuttle()
|
||||
if(!supply_shuttle) supply_shuttle = new /datum/controller/supply_shuttle()
|
||||
/*
|
||||
calculate the longest number of ticks the MC can wait between each cycle without causing subsystems to not fire on schedule
|
||||
Note: you can set the datum's defined processing_interval to some integer to set an -absolute- minimum wait duration.
|
||||
*/
|
||||
var/GCD
|
||||
for(var/datum/subsystem/SS in subsystems)
|
||||
if(SS.wait)
|
||||
GCD = Gcd(SS.wait, GCD)
|
||||
GCD = round(GCD)
|
||||
if(GCD > processing_interval)
|
||||
processing_interval = GCD
|
||||
|
||||
/datum/controller/game_controller/proc/setup()
|
||||
world.tick_lag = config.Ticklag
|
||||
world << "<span class='userdanger'>Initializing Subsystems...</span>"
|
||||
|
||||
setup_objects()
|
||||
setupgenetics()
|
||||
setupfactions()
|
||||
//sort subsystems by priority, so they initialize in the correct order
|
||||
sortTim(subsystems, /proc/cmp_subsystem_priority)
|
||||
|
||||
spawn(0)
|
||||
if(ticker)
|
||||
ticker.pregame()
|
||||
createRandomZlevel() //gate system
|
||||
setup_map_transitions()
|
||||
for(var/i=0, i<max_secret_rooms, i++)
|
||||
make_mining_asteroid_secret()
|
||||
|
||||
/datum/controller/game_controller/proc/setup_objects()
|
||||
world << "<span class='userdanger'>Initializing objects...</span>"
|
||||
sleep(-1)
|
||||
for(var/atom/movable/object in world)
|
||||
object.initialize()
|
||||
//Eventually all this other setup stuff should be contained in subsystems and done in subsystem.Initialize()
|
||||
for(var/datum/subsystem/S in subsystems)
|
||||
S.Initialize(world.timeofday)
|
||||
sleep(-1)
|
||||
|
||||
world << "<span class='userdanger'>Initializing pipe networks...</span>"
|
||||
sleep(-1)
|
||||
for(var/obj/machinery/atmospherics/machine in world)
|
||||
machine.build_network()
|
||||
world << "<span class='userdanger'>Initializations complete</span>"
|
||||
world.log << "Initializations complete"
|
||||
|
||||
world << "<span class='userdanger'>Initializing atmos machinery...</span>"
|
||||
sleep(-1)
|
||||
for(var/obj/machinery/atmospherics/unary/U in world)
|
||||
if(istype(U, /obj/machinery/atmospherics/unary/vent_pump))
|
||||
var/obj/machinery/atmospherics/unary/vent_pump/T = U
|
||||
T.broadcast_status()
|
||||
else if(istype(U, /obj/machinery/atmospherics/unary/vent_scrubber))
|
||||
var/obj/machinery/atmospherics/unary/vent_scrubber/T = U
|
||||
T.broadcast_status()
|
||||
world.sleep_offline = 1
|
||||
world.fps = config.fps
|
||||
|
||||
world << "<span class='userdanger'>Making a mess...</span>"
|
||||
sleep(-1)
|
||||
for(var/turf/simulated/floor/F in world)
|
||||
F.MakeDirty()
|
||||
|
||||
world << "<span class='userdanger'>Initializations complete.</span>"
|
||||
sleep(-1)
|
||||
|
||||
process()
|
||||
|
||||
//used for smoothing out the cost values so they don't fluctuate wildly
|
||||
#define MC_AVERAGE(average, current) (0.8*(average) + 0.2*(current))
|
||||
|
||||
/datum/controller/game_controller/proc/process()
|
||||
processing = 1
|
||||
if(!Failsafe) new /datum/controller/failsafe()
|
||||
spawn(0)
|
||||
set background = BACKGROUND_ENABLED
|
||||
var/timer = world.time
|
||||
for(var/datum/subsystem/SS in subsystems)
|
||||
timer += processing_interval
|
||||
SS.next_fire = timer
|
||||
|
||||
var/start_time
|
||||
var/cpu
|
||||
|
||||
while(1) //far more efficient than recursively calling ourself
|
||||
if(!Failsafe) new /datum/controller/failsafe()
|
||||
if(processing_interval > 0)
|
||||
++iteration
|
||||
|
||||
var/currenttime = world.timeofday
|
||||
start_time = world.timeofday
|
||||
|
||||
if((last_tick_timeofday - currenttime) > 1e5) //midnight rollover protection
|
||||
last_tick_timeofday -= MIDNIGHT_ROLLOVER
|
||||
for(var/datum/subsystem/SS in subsystems)
|
||||
if(SS.next_fire <= world.time)
|
||||
SS.next_fire += SS.wait
|
||||
if(SS.can_fire > 0)
|
||||
timer = world.timeofday
|
||||
cpu = world.cpu
|
||||
last_thing_processed = SS.type
|
||||
SS.last_fire = world.time
|
||||
SS.fire()
|
||||
SS.cpu = MC_AVERAGE(SS.cpu, world.cpu - cpu)
|
||||
SS.cost = MC_AVERAGE(SS.cost, world.timeofday - timer)
|
||||
++SS.times_fired
|
||||
sleep(-1)
|
||||
|
||||
last_tick_duration = (currenttime - last_tick_timeofday) / 10
|
||||
last_tick_timeofday = currenttime
|
||||
cost = MC_AVERAGE(cost, world.timeofday - start_time)
|
||||
|
||||
if(processing)
|
||||
var/timer
|
||||
var/start_time = world.timeofday
|
||||
controller_iteration++
|
||||
|
||||
vote.process()
|
||||
|
||||
//AIR
|
||||
if(!air_processing_killed)
|
||||
timer = world.timeofday
|
||||
last_thing_processed = air_master.type
|
||||
air_master.process()
|
||||
air_cost = (world.timeofday - timer) / 10
|
||||
global_activeturfs = air_master.active_turfs.len
|
||||
|
||||
sleep(breather_ticks)
|
||||
|
||||
//SUN
|
||||
timer = world.timeofday
|
||||
last_thing_processed = sun.type
|
||||
sun.calc_position()
|
||||
sun_cost = (world.timeofday - timer) / 10
|
||||
|
||||
sleep(breather_ticks)
|
||||
|
||||
//MOBS
|
||||
timer = world.timeofday
|
||||
process_mobs()
|
||||
mobs_cost = (world.timeofday - timer) / 10
|
||||
|
||||
sleep(breather_ticks)
|
||||
|
||||
//DISEASES
|
||||
timer = world.timeofday
|
||||
process_diseases()
|
||||
diseases_cost = (world.timeofday - timer) / 10
|
||||
|
||||
sleep(breather_ticks)
|
||||
|
||||
//MACHINES
|
||||
timer = world.timeofday
|
||||
process_machines()
|
||||
machines_cost = (world.timeofday - timer) / 10
|
||||
|
||||
sleep(breather_ticks)
|
||||
|
||||
//BOTS
|
||||
timer = world.timeofday
|
||||
process_bots()
|
||||
aibots_cost = (world.timeofday - timer) / 10
|
||||
|
||||
sleep(breather_ticks)
|
||||
|
||||
//OBJECTS
|
||||
timer = world.timeofday
|
||||
process_objects()
|
||||
objects_cost = (world.timeofday - timer) / 10
|
||||
|
||||
sleep(breather_ticks)
|
||||
|
||||
//PIPENETS
|
||||
if(!pipe_processing_killed)
|
||||
timer = world.timeofday
|
||||
process_pipenets()
|
||||
networks_cost = (world.timeofday - timer) / 10
|
||||
|
||||
sleep(breather_ticks)
|
||||
|
||||
//POWERNETS
|
||||
timer = world.timeofday
|
||||
process_powernets()
|
||||
powernets_cost = (world.timeofday - timer) / 10
|
||||
|
||||
sleep(breather_ticks)
|
||||
|
||||
//NANO UIS
|
||||
timer = world.timeofday
|
||||
process_nano()
|
||||
nano_cost = (world.timeofday - timer) / 10
|
||||
|
||||
//EVENTS
|
||||
timer = world.timeofday
|
||||
last_thing_processed = /datum/round_event
|
||||
events.process()
|
||||
events_cost = (world.timeofday - timer) / 10
|
||||
|
||||
//TICKER
|
||||
timer = world.timeofday
|
||||
last_thing_processed = ticker.type
|
||||
ticker.process()
|
||||
ticker_cost = (world.timeofday - timer) / 10
|
||||
|
||||
// GC
|
||||
timer = world.timeofday
|
||||
last_thing_processed = garbage.type
|
||||
garbage.process()
|
||||
gc_cost = (world.timeofday - timer) / 10
|
||||
|
||||
//TIMING
|
||||
total_cost = air_cost + sun_cost + mobs_cost + diseases_cost + machines_cost + aibots_cost + objects_cost + networks_cost + powernets_cost + nano_cost + events_cost + ticker_cost + gc_cost
|
||||
|
||||
var/end_time = world.timeofday
|
||||
if(end_time < start_time)
|
||||
start_time -= MIDNIGHT_ROLLOVER //deciseconds in a day
|
||||
sleep( round(minimum_ticks - (end_time - start_time),1) )
|
||||
sleep(processing_interval)
|
||||
else
|
||||
sleep(10)
|
||||
sleep(50)
|
||||
|
||||
/*
|
||||
/datum/controller/game_controller/proc/process_liquid()
|
||||
last_thing_processed = /datum/puddle
|
||||
var/i = 1
|
||||
while(i<=puddles.len)
|
||||
var/datum/puddle/Puddle = puddles[i]
|
||||
if(Puddle)
|
||||
Puddle.process()
|
||||
i++
|
||||
continue
|
||||
puddles.Cut(i,i+1)
|
||||
*/
|
||||
#undef MC_AVERAGE
|
||||
|
||||
/datum/controller/game_controller/proc/process_mobs()
|
||||
for(var/mob/M in mob_list)
|
||||
if(!M.gc_destroyed)
|
||||
last_thing_processed = M.type
|
||||
M.Life()
|
||||
continue
|
||||
mob_list -= M
|
||||
/datum/controller/game_controller/proc/roundHasStarted()
|
||||
for(var/datum/subsystem/SS in subsystems)
|
||||
SS.can_fire = 1
|
||||
|
||||
/datum/controller/game_controller/proc/process_diseases()
|
||||
for(var/datum/disease/Disease in active_diseases)
|
||||
last_thing_processed = Disease.type
|
||||
Disease.process()
|
||||
|
||||
/datum/controller/game_controller/proc/process_machines()
|
||||
for(var/obj/machinery/Machine in machines)
|
||||
if(!Machine.gc_destroyed)
|
||||
last_thing_processed = Machine.type
|
||||
if(Machine.process() != PROCESS_KILL)
|
||||
if(Machine)
|
||||
if(Machine.use_power)
|
||||
Machine.auto_use_power()
|
||||
continue
|
||||
machines -= Machine
|
||||
|
||||
/datum/controller/game_controller/proc/process_bots()
|
||||
for(var/obj/machinery/bot/Bot in aibots)
|
||||
if(!Bot.gc_destroyed)
|
||||
last_thing_processed = Bot.type
|
||||
spawn(0)
|
||||
Bot.bot_process()
|
||||
continue
|
||||
aibots -= Bot
|
||||
|
||||
/datum/controller/game_controller/proc/process_objects()
|
||||
for(var/obj/Object in processing_objects)
|
||||
if(!Object.gc_destroyed)
|
||||
last_thing_processed = Object.type
|
||||
Object.process()
|
||||
continue
|
||||
processing_objects -= Object
|
||||
|
||||
/datum/controller/game_controller/proc/process_pipenets()
|
||||
last_thing_processed = /datum/pipeline
|
||||
for(var/datum/pipeline/P in pipe_networks)
|
||||
P.process()
|
||||
|
||||
/datum/controller/game_controller/proc/process_powernets()
|
||||
last_thing_processed = /datum/powernet
|
||||
for(var/datum/powernet/Powernet in powernets)
|
||||
Powernet.reset()
|
||||
|
||||
/datum/controller/game_controller/proc/process_nano()
|
||||
for(var/datum/nanoui/ui in nanomanager.processing_uis)
|
||||
if(ui.src_object && ui.user)
|
||||
ui.process()
|
||||
continue
|
||||
nanomanager.processing_uis -= ui
|
||||
|
||||
/datum/controller/game_controller/proc/Recover() //Mostly a placeholder for now.
|
||||
/datum/controller/game_controller/proc/Recover()
|
||||
var/msg = "## DEBUG: [time2text(world.timeofday)] MC restarted. Reports:\n"
|
||||
for(var/varname in master_controller.vars)
|
||||
switch(varname)
|
||||
if("tag","type","parent_type","vars") continue
|
||||
if("tag","bestF","type","parent_type","vars") continue
|
||||
else
|
||||
var/varval = master_controller.vars[varname]
|
||||
if(istype(varval,/datum))
|
||||
@@ -313,3 +121,5 @@ var/global/pipe_processing_killed = 0
|
||||
else
|
||||
msg += "\t [varname] = [varval]\n"
|
||||
world.log << msg
|
||||
|
||||
subsystems = master_controller.subsystems
|
||||
@@ -1,247 +0,0 @@
|
||||
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
|
||||
|
||||
// Controls the emergency shuttle
|
||||
|
||||
|
||||
// these define the time taken for the shuttle to get to SS13
|
||||
// and the time before it leaves again
|
||||
|
||||
#define SHUTTLEARRIVETIME 600 // 10 minutes = 600 seconds
|
||||
#define SHUTTLELEAVETIME 180 // 3 minutes = 180 seconds
|
||||
#define SHUTTLETRANSITTIME 120 // 2 minutes = 120 seconds
|
||||
#define SHUTTLEAUTOCALLTIMER 2.5 // 25 minutes
|
||||
|
||||
#define UNDOCKED 0 //Shuttle is always this until the shuttle has reached the station.
|
||||
#define DOCKED -1 //Shuttle is at the station
|
||||
#define TRANSIT 1 //Shuttle is coming to centcom from the station
|
||||
#define ENDGAME 2 //It's what game tickers check for for the purposes of round completion, I'm not touching it.
|
||||
|
||||
var/global/datum/shuttle_controller/emergency_shuttle/emergency_shuttle
|
||||
|
||||
/datum/shuttle_controller
|
||||
var/location = UNDOCKED //
|
||||
var/online = 0
|
||||
var/direction = 1 //-1 = going back to central command, 1 = going to SS13. Only important for recalling
|
||||
var/recall_count = 0
|
||||
var/area/last_call_loc = null // Stores where the last shuttle call/recall was made from
|
||||
|
||||
var/endtime // timeofday that shuttle arrives
|
||||
var/timelimit //important when the shuttle gets called for more than shuttlearrivetime
|
||||
//timeleft = 360 //600
|
||||
var/fake_recall = 0 //Used in rounds to prevent "ON NOES, IT MUST [INSERT ROUND] BECAUSE SHUTTLE CAN'T BE CALLED"
|
||||
var/always_fake_recall = 0
|
||||
|
||||
var/pods = list("escape", "pod1", "pod2", "pod3", "pod4")
|
||||
|
||||
|
||||
// call the shuttle
|
||||
// if not called before, set the endtime to T+600 seconds
|
||||
// otherwise if outgoing, switch to incoming
|
||||
/datum/shuttle_controller/proc/incall(coeff = 1, var/signal_origin, var/emergency_reason, var/red_alert)
|
||||
|
||||
if(endtime)
|
||||
if(direction == -1)
|
||||
setdirection(1)
|
||||
else
|
||||
if(recall_count > 2 && signal_origin && prob(70)) //30% chance the signal tracing will fail
|
||||
last_call_loc = signal_origin
|
||||
else
|
||||
last_call_loc = null
|
||||
|
||||
settimeleft(SHUTTLEARRIVETIME*coeff)
|
||||
online = 1
|
||||
|
||||
priority_announce("The emergency shuttle has been called. [red_alert ? "Red Alert state confirmed: Dispatching priority shuttle. " : "" ]It will arrive in [round(emergency_shuttle.timeleft()/60)] minutes.[emergency_reason][emergency_shuttle.last_call_loc ? "\n\nCall signal traced. Results can be viewed on any communcations console." : "" ]", null, 'sound/AI/shuttlecalled.ogg', "Priority")
|
||||
|
||||
if(always_fake_recall)
|
||||
|
||||
if ((seclevel2num(get_security_level()) == SEC_LEVEL_RED))
|
||||
fake_recall = rand(SHUTTLEARRIVETIME / 4, SHUTTLEARRIVETIME - 100 / 2)
|
||||
else
|
||||
fake_recall = rand(SHUTTLEARRIVETIME / 2, SHUTTLEARRIVETIME - 100)
|
||||
|
||||
/datum/shuttle_controller/proc/recall(var/signal_origin)
|
||||
if(direction == 1)
|
||||
var/timeleft = timeleft()
|
||||
if(timeleft >= SHUTTLEARRIVETIME)
|
||||
online = 0
|
||||
direction = 1
|
||||
endtime = null
|
||||
return
|
||||
|
||||
recall_count ++
|
||||
|
||||
if(recall_count > 2 && signal_origin && prob(70)) //30% chance the signal tracing will fail
|
||||
last_call_loc = signal_origin
|
||||
else
|
||||
last_call_loc = null
|
||||
|
||||
if(recall_count == 2)
|
||||
priority_announce("The emergency shuttle has been recalled.\n\nExcessive number of emergency shuttle calls detected. We will attempt to trace all future calls and recalls to their source. Tracing results may be viewed on any communications console.", null, 'sound/AI/shuttlerecalled.ogg')
|
||||
else
|
||||
priority_announce("The emergency shuttle has been recalled.[last_call_loc ? " Recall signal traced. Results can be viewed on any communcations console." : "" ]", null, 'sound/AI/shuttlerecalled.ogg', "Priority")
|
||||
setdirection(-1)
|
||||
online = 1
|
||||
|
||||
|
||||
// returns the time (in seconds) before shuttle arrival
|
||||
// note if direction = -1, gives a count-up to SHUTTLEARRIVETIME
|
||||
/datum/shuttle_controller/proc/timeleft()
|
||||
if(online)
|
||||
var/timeleft = round((endtime - world.timeofday)/10 ,1)
|
||||
if(timeleft > (MIDNIGHT_ROLLOVER/10)) // midnight rollover protection
|
||||
endtime -= MIDNIGHT_ROLLOVER // subtract 24 hours from endtime
|
||||
timeleft = round((endtime - world.timeofday)/10 ,1) // recalculate timeleft
|
||||
if(direction == 1)
|
||||
return timeleft
|
||||
else
|
||||
return SHUTTLEARRIVETIME-timeleft
|
||||
else
|
||||
return SHUTTLEARRIVETIME
|
||||
|
||||
// sets the time left to a given delay (in seconds)
|
||||
/datum/shuttle_controller/proc/settimeleft(var/delay)
|
||||
endtime = world.timeofday + delay * 10
|
||||
timelimit = delay
|
||||
|
||||
// sets the shuttle direction
|
||||
// 1 = towards SS13, -1 = back to centcom
|
||||
/datum/shuttle_controller/proc/setdirection(var/dirn)
|
||||
if(direction == dirn)
|
||||
return
|
||||
direction = dirn
|
||||
// if changing direction, flip the timeleft by SHUTTLEARRIVETIME
|
||||
var/ticksleft = endtime - world.timeofday
|
||||
endtime = world.timeofday + (SHUTTLEARRIVETIME*10 - ticksleft)
|
||||
return
|
||||
|
||||
//calls the shuttle if there's no live active AI or powered non broken comms console,
|
||||
/datum/shuttle_controller/proc/autoshuttlecall()
|
||||
var/callshuttle = 1
|
||||
|
||||
for(var/SC in shuttle_caller_list)
|
||||
if(istype(SC,/mob/living/silicon/ai))
|
||||
var/mob/living/silicon/ai/AI = SC
|
||||
if(AI.stat || !AI.client)
|
||||
continue
|
||||
if(istype(SC,/obj/machinery/computer/communications))
|
||||
var/obj/machinery/computer/communications/C = SC
|
||||
if(C.stat & BROKEN)
|
||||
continue
|
||||
var/turf/T = get_turf(SC)
|
||||
if(T && T.z == 1)
|
||||
callshuttle = 0 //if there's an alive AI or a powered non broken communication console on the station z level, we don't call the shuttle
|
||||
break
|
||||
|
||||
if(callshuttle)
|
||||
if(!online && direction == 1) //we don't call the shuttle if it's already coming
|
||||
incall(SHUTTLEAUTOCALLTIMER) //X minutes! If they want to recall, they have X-(X-5) minutes to do so
|
||||
log_game("All the communications consoles were destroyed and all AIs are inactive. Shuttle called.")
|
||||
message_admins("All the communications consoles were destroyed and all AIs are inactive. Shuttle called.")
|
||||
|
||||
/datum/shuttle_controller/proc/move_shuttles()
|
||||
var/datum/shuttle_manager/s
|
||||
for(var/t in pods)
|
||||
s = shuttles[t]
|
||||
s.move_shuttle()
|
||||
|
||||
/datum/shuttle_controller/proc/process()
|
||||
|
||||
/datum/shuttle_controller/emergency_shuttle/process()
|
||||
if(!online)
|
||||
return
|
||||
var/timeleft = timeleft()
|
||||
if(location == UNDOCKED)
|
||||
if(direction == -1)
|
||||
if(timeleft >= timelimit) // Shuttle reaches CentCom after being recalled.
|
||||
online = 0
|
||||
direction = 1
|
||||
endtime = null
|
||||
return 0
|
||||
else if(fake_recall && (timeleft <= fake_recall))
|
||||
recall()
|
||||
fake_recall = 0
|
||||
return 0
|
||||
else if(timeleft <= 0)
|
||||
var/datum/shuttle_manager/s = shuttles["escape"]
|
||||
s.move_shuttle()
|
||||
location = DOCKED
|
||||
settimeleft(SHUTTLELEAVETIME)
|
||||
send2irc("Server", "The Emergency Shuttle has docked with the station.")
|
||||
priority_announce("The Emergency Shuttle has docked with the station. You have [round(timeleft()/60,1)] minutes to board the Emergency Shuttle.", null, 'sound/AI/shuttledock.ogg', "Priority")
|
||||
else if(timeleft <= 0) //Nothing happens if time's not up and the ship's docked or later
|
||||
if(location == DOCKED)
|
||||
move_shuttles()
|
||||
location = TRANSIT
|
||||
settimeleft(SHUTTLETRANSITTIME)
|
||||
priority_announce("The Emergency Shuttle has left the station. Estimate [round(timeleft()/60,1)] minutes until the shuttle docks at Central Command.", null, null, "Priority")
|
||||
else if(location == TRANSIT)
|
||||
move_shuttles()
|
||||
//message_admins("Shuttles have attempted to move to Centcom")
|
||||
location = ENDGAME
|
||||
online = 0
|
||||
endtime = null
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/*
|
||||
Some slapped-together star effects for maximum spess immershuns. Basically consists of a
|
||||
spawner, an ender, and bgstar. Spawners create bgstars, bgstars shoot off into a direction
|
||||
until they reach a starender.
|
||||
*/
|
||||
|
||||
/obj/effect/bgstar
|
||||
name = "star"
|
||||
var/speed = 10
|
||||
var/direction = SOUTH
|
||||
layer = 2 // TURF_LAYER
|
||||
|
||||
/obj/effect/bgstar/New()
|
||||
..()
|
||||
pixel_x += rand(-2,30)
|
||||
pixel_y += rand(-2,30)
|
||||
var/starnum = pick("1", "1", "1", "2", "3", "4")
|
||||
|
||||
icon_state = "star"+starnum
|
||||
|
||||
speed = rand(2, 5)
|
||||
|
||||
/obj/effect/bgstar/proc/startmove()
|
||||
|
||||
while(src)
|
||||
sleep(speed)
|
||||
step(src, direction)
|
||||
for(var/obj/effect/starender/E in loc)
|
||||
qdel(src)
|
||||
|
||||
|
||||
/obj/effect/starender
|
||||
invisibility = 101
|
||||
|
||||
/obj/effect/starspawner
|
||||
invisibility = 101
|
||||
var/spawndir = SOUTH
|
||||
var/spawning = 0
|
||||
|
||||
/obj/effect/starspawner/West
|
||||
spawndir = WEST
|
||||
|
||||
/obj/effect/starspawner/proc/startspawn()
|
||||
spawning = 1
|
||||
while(spawning)
|
||||
sleep(rand(2, 30))
|
||||
var/obj/effect/bgstar/S = new/obj/effect/bgstar(locate(x,y,z))
|
||||
S.direction = spawndir
|
||||
spawn()
|
||||
S.startmove()
|
||||
|
||||
|
||||
/proc/push_mob_back(var/mob/living/L, var/dir)
|
||||
if(iscarbon(L) && isturf(L.loc))
|
||||
if(prob(88))
|
||||
var/turf/T = get_step(L, dir)
|
||||
if(T)
|
||||
for(var/obj/O in T) // For doors and such (kinda ugly but we can't have people opening doors)
|
||||
if(!O.CanPass(L, L.loc, 1))
|
||||
return
|
||||
L.Move(get_step(L, dir), dir)
|
||||
@@ -0,0 +1,134 @@
|
||||
var/datum/subsystem/air/SSair
|
||||
|
||||
/datum/subsystem/air
|
||||
name = "Air"
|
||||
priority = 20
|
||||
|
||||
var/cost_turfs = 0
|
||||
var/cost_groups = 0
|
||||
var/cost_highpressure = 0
|
||||
var/cost_hotspots = 0
|
||||
var/cost_superconductivity = 0
|
||||
|
||||
var/obj/effect/overlay/plasma_overlay //overlay for plasma
|
||||
var/obj/effect/overlay/sleeptoxin_overlay //overlay for sleeptoxin
|
||||
|
||||
var/list/excited_groups = list()
|
||||
var/list/active_turfs = list()
|
||||
var/list/hotspots = list()
|
||||
|
||||
//Special functions lists
|
||||
var/list/turf/simulated/active_super_conductivity = list()
|
||||
var/list/turf/simulated/high_pressure_delta = list()
|
||||
|
||||
|
||||
/datum/subsystem/air/New()
|
||||
NEW_SS_GLOBAL(SSair)
|
||||
|
||||
plasma_overlay = new /obj/effect/overlay{icon='icons/effects/tile_effects.dmi';mouse_opacity=0;layer=5;icon_state="plasma"}()
|
||||
sleeptoxin_overlay = new /obj/effect/overlay{icon='icons/effects/tile_effects.dmi';mouse_opacity=0;layer=5;icon_state="sleeping_agent"}()
|
||||
|
||||
|
||||
/datum/subsystem/air/Initialize()
|
||||
setup_allturfs()
|
||||
..()
|
||||
|
||||
#define MC_AVERAGE(average, current) (0.8*(average) + 0.2*(current))
|
||||
/datum/subsystem/air/fire()
|
||||
var/timer = world.timeofday
|
||||
process_active_turfs()
|
||||
cost_turfs = MC_AVERAGE(cost_turfs, (world.timeofday - timer))
|
||||
|
||||
timer = world.timeofday
|
||||
process_excited_groups()
|
||||
cost_groups = MC_AVERAGE(cost_groups, (world.timeofday - timer))
|
||||
|
||||
timer = world.timeofday
|
||||
process_high_pressure_delta()
|
||||
cost_highpressure = MC_AVERAGE(cost_highpressure, (world.timeofday - timer))
|
||||
|
||||
timer = world.timeofday
|
||||
process_hotspots()
|
||||
cost_hotspots = MC_AVERAGE(cost_hotspots, (world.timeofday - timer))
|
||||
|
||||
timer = world.timeofday
|
||||
process_super_conductivity()
|
||||
cost_superconductivity = MC_AVERAGE(cost_superconductivity, (world.timeofday - timer))
|
||||
|
||||
#undef MC_AVERAGE
|
||||
|
||||
|
||||
/datum/subsystem/air/proc/process_super_conductivity()
|
||||
for(var/turf/simulated/T in active_super_conductivity)
|
||||
T.super_conduct()
|
||||
|
||||
|
||||
/datum/subsystem/air/proc/process_hotspots()
|
||||
for(var/obj/effect/hotspot/H in hotspots)
|
||||
H.process()
|
||||
|
||||
|
||||
/datum/subsystem/air/proc/process_high_pressure_delta()
|
||||
for(var/turf/T in high_pressure_delta)
|
||||
T.high_pressure_movements()
|
||||
T.pressure_difference = 0
|
||||
high_pressure_delta.len = 0
|
||||
|
||||
|
||||
/datum/subsystem/air/proc/process_active_turfs()
|
||||
for(var/turf/simulated/T in active_turfs)
|
||||
T.process_cell()
|
||||
|
||||
|
||||
/datum/subsystem/air/proc/remove_from_active(var/turf/simulated/T)
|
||||
if(istype(T))
|
||||
T.excited = 0
|
||||
active_turfs -= T
|
||||
if(T.excited_group)
|
||||
T.excited_group.garbage_collect()
|
||||
|
||||
|
||||
/datum/subsystem/air/proc/add_to_active(var/turf/simulated/T, var/blockchanges = 1)
|
||||
if(istype(T) && T.air)
|
||||
T.excited = 1
|
||||
active_turfs |= T
|
||||
if(blockchanges && T.excited_group)
|
||||
T.excited_group.garbage_collect()
|
||||
else
|
||||
for(var/direction in cardinal)
|
||||
if(!(T.atmos_adjacent_turfs & direction))
|
||||
continue
|
||||
var/turf/simulated/S = get_step(T, direction)
|
||||
if(istype(S))
|
||||
add_to_active(S)
|
||||
|
||||
|
||||
/datum/subsystem/air/proc/setup_allturfs()
|
||||
for(var/turf/simulated/T in world)
|
||||
T.CalculateAdjacentTurfs()
|
||||
if(!T.blocks_air)
|
||||
if(T.air.check_tile_graphic())
|
||||
T.update_visuals(T.air)
|
||||
for(var/direction in cardinal)
|
||||
if(!(T.atmos_adjacent_turfs & direction))
|
||||
continue
|
||||
var/turf/enemy_tile = get_step(T, direction)
|
||||
if(istype(enemy_tile,/turf/simulated/))
|
||||
var/turf/simulated/enemy_simulated = enemy_tile
|
||||
if(!T.air.compare(enemy_simulated.air))
|
||||
T.excited = 1
|
||||
active_turfs |= T
|
||||
break
|
||||
else
|
||||
if(!T.air.check_turf_total(enemy_tile))
|
||||
T.excited = 1
|
||||
active_turfs |= T
|
||||
|
||||
/datum/subsystem/air/proc/process_excited_groups()
|
||||
for(var/datum/excited_group/EG in excited_groups)
|
||||
EG.breakdown_cooldown ++
|
||||
if(EG.breakdown_cooldown == 10)
|
||||
EG.self_breakdown()
|
||||
return
|
||||
if(EG.breakdown_cooldown > 20)
|
||||
EG.dismantle()
|
||||
@@ -0,0 +1,21 @@
|
||||
var/datum/subsystem/bots/SSbot
|
||||
|
||||
/datum/subsystem/bots
|
||||
name = "Bots"
|
||||
priority = 8
|
||||
|
||||
var/list/processing = list()
|
||||
|
||||
/datum/subsystem/bots/New()
|
||||
NEW_SS_GLOBAL(SSbot)
|
||||
|
||||
/datum/subsystem/bots/fire()
|
||||
var/seconds = wait * 0.1
|
||||
var/i=1
|
||||
for(var/thing in processing)
|
||||
if(thing && !thing:gc_destroyed)
|
||||
spawn(-1)
|
||||
thing:bot_process(seconds)
|
||||
++i
|
||||
continue
|
||||
processing.Cut(i, i+1)
|
||||
@@ -0,0 +1,20 @@
|
||||
var/datum/subsystem/diseases/SSdisease
|
||||
|
||||
/datum/subsystem/diseases
|
||||
name = "Diseases"
|
||||
priority = 7
|
||||
|
||||
var/list/processing = list()
|
||||
|
||||
/datum/subsystem/diseases/New()
|
||||
NEW_SS_GLOBAL(SSdisease)
|
||||
|
||||
|
||||
/datum/subsystem/diseases/fire()
|
||||
var/i=1
|
||||
for(var/thing in processing)
|
||||
if(thing)
|
||||
thing:process()
|
||||
++i
|
||||
continue
|
||||
processing.Cut(i,i+1)
|
||||
@@ -1,282 +1,280 @@
|
||||
var/datum/controller/event/events
|
||||
|
||||
/datum/controller/event
|
||||
var/list/control = list() //list of all datum/round_event_control. Used for selecting events based on weight and occurrences.
|
||||
var/list/running = list() //list of all existing /datum/round_event
|
||||
|
||||
var/scheduled = 0 //The next world.time that a naturally occuring random event can be selected.
|
||||
var/frequency_lower = 3000 //5 minutes lower bound.
|
||||
var/frequency_upper = 9000 //15 minutes upper bound. Basically an event will happen every 15 to 30 minutes.
|
||||
|
||||
var/holiday //This will be a string of the name of any realworld holiday which occurs today (GMT time)
|
||||
var/wizardmode = 0 //If the summon events spell is in effect this is true
|
||||
|
||||
//Initial controller setup.
|
||||
/datum/controller/event/New()
|
||||
//There can be only one events manager. Out with the old and in with the new.
|
||||
if(events != src)
|
||||
if(istype(events))
|
||||
del(events)
|
||||
events = src
|
||||
|
||||
for(var/type in typesof(/datum/round_event_control))
|
||||
var/datum/round_event_control/E = new type()
|
||||
if(!E.typepath)
|
||||
continue //don't want this one! leave it for the garbage collector
|
||||
if(E.wizardevent && !wizardmode)
|
||||
E.weight = 0
|
||||
control += E //add it to the list of all events (controls)
|
||||
reschedule()
|
||||
getHoliday()
|
||||
|
||||
//This is called by the MC every MC-tick (*neatfreak*).
|
||||
/datum/controller/event/proc/process()
|
||||
checkEvent()
|
||||
var/i = 1
|
||||
while(i<=running.len)
|
||||
var/datum/round_event/Event = running[i]
|
||||
if(Event)
|
||||
Event.process()
|
||||
i++
|
||||
continue
|
||||
running.Cut(i,i+1)
|
||||
|
||||
//checks if we should select a random event yet, and reschedules if necessary
|
||||
/datum/controller/event/proc/checkEvent()
|
||||
if(scheduled <= world.time)
|
||||
spawnEvent()
|
||||
reschedule()
|
||||
|
||||
//decides which world.time we should select another random event at.
|
||||
/datum/controller/event/proc/reschedule()
|
||||
scheduled = world.time + rand(frequency_lower, max(frequency_lower,frequency_upper))
|
||||
|
||||
//selects a random event based on whether it can occur and it's 'weight'(probability)
|
||||
/datum/controller/event/proc/spawnEvent()
|
||||
if(!config.allow_random_events)
|
||||
return
|
||||
|
||||
var/sum_of_weights = 0
|
||||
for(var/datum/round_event_control/E in control)
|
||||
if(E.occurrences >= E.max_occurrences) continue
|
||||
if(E.earliest_start >= world.time) continue
|
||||
if(E.holidayID)
|
||||
if(E.holidayID != holiday) continue
|
||||
if(E.weight < 0) //for round-start events etc.
|
||||
if(E.runEvent() == PROCESS_KILL)
|
||||
E.max_occurrences = 0
|
||||
continue
|
||||
return
|
||||
sum_of_weights += E.weight
|
||||
|
||||
sum_of_weights = rand(0,sum_of_weights) //reusing this variable. It now represents the 'weight' we want to select
|
||||
|
||||
for(var/datum/round_event_control/E in control)
|
||||
if(E.occurrences >= E.max_occurrences) continue
|
||||
if(E.earliest_start >= world.time) continue
|
||||
if(E.holidayID)
|
||||
if(E.holidayID != holiday) continue
|
||||
sum_of_weights -= E.weight
|
||||
|
||||
if(sum_of_weights <= 0) //we've hit our goal
|
||||
if(E.runEvent() == PROCESS_KILL)//we couldn't run this event for some reason, set its max_occurrences to 0
|
||||
E.max_occurrences = 0
|
||||
continue
|
||||
return
|
||||
|
||||
|
||||
/datum/round_event/proc/findEventArea() //Here's a nice proc to use to find an area for your event to land in!
|
||||
var/list/safe_areas = list(
|
||||
/area/turret_protected/ai,
|
||||
/area/turret_protected/ai_upload,
|
||||
/area/engine,
|
||||
/area/solar,
|
||||
/area/holodeck,
|
||||
/area/shuttle/arrival,
|
||||
/area/shuttle/escape/station,
|
||||
/area/shuttle/escape_pod1/station,
|
||||
/area/shuttle/escape_pod2/station,
|
||||
/area/shuttle/escape_pod3/station,
|
||||
/area/shuttle/escape_pod4/station,
|
||||
/area/shuttle/mining/station,
|
||||
/area/shuttle/transport1/station,
|
||||
/area/shuttle/specops/station)
|
||||
|
||||
//These are needed because /area/engine has to be removed from the list, but we still want these areas to get fucked up.
|
||||
var/list/danger_areas = list(
|
||||
/area/engine/break_room,
|
||||
/area/engine/chiefs_office)
|
||||
|
||||
//Need to locate() as it's just a list of paths.
|
||||
return locate(pick((the_station_areas - safe_areas) + danger_areas))
|
||||
|
||||
|
||||
|
||||
//allows a client to trigger an event
|
||||
//aka Badmin Central
|
||||
/client/proc/forceEvent()
|
||||
set name = "Trigger Event"
|
||||
set category = "Fun"
|
||||
|
||||
if(!holder ||!check_rights(R_FUN))
|
||||
return
|
||||
|
||||
holder.forceEvent()
|
||||
|
||||
/datum/admins/proc/forceEvent()
|
||||
var/dat = ""
|
||||
var/normal = ""
|
||||
var/magic = ""
|
||||
var/holiday = ""
|
||||
for(var/datum/round_event_control/E in events.control)
|
||||
dat = "<BR><A href='?src=\ref[src];forceevent=\ref[E]'>[E]</A>"
|
||||
if(E.holidayID)
|
||||
holiday += dat
|
||||
else if(E.wizardevent)
|
||||
magic += dat
|
||||
else
|
||||
normal += dat
|
||||
|
||||
dat = normal + "<BR>" + magic + "<BR>" + holiday
|
||||
|
||||
var/datum/browser/popup = new(usr, "forceevent", "Force Random Event", 300, 750)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
|
||||
|
||||
/*
|
||||
//////////////
|
||||
// HOLIDAYS //
|
||||
//////////////
|
||||
//Uncommenting ALLOW_HOLIDAYS in config.txt will enable holidays
|
||||
|
||||
//It's easy to add stuff. Just modify getHoliday to set holiday to something using the switch for DD(#day) MM(#month) YY(#year).
|
||||
//You can then check if it's a special day in any code in the game by doing if(events.holiday == "MyHolidayID")
|
||||
|
||||
//You can also make holiday random events easily thanks to Pete/Gia's system.
|
||||
//simply make a random event normally, then assign it a holidayID string which matches the one you gave it in getHolday.
|
||||
//Anything with a holidayID, which does not match the holiday string, will never occur.
|
||||
|
||||
//Please, Don't spam stuff up with stupid stuff (key example being april-fools Pooh/ERP/etc),
|
||||
//And don't forget: CHECK YOUR CODE!!!! We don't want any zero-day bugs which happen only on holidays and never get found/fixed!
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//ALSO, MOST IMPORTANTLY: Don't add stupid stuff! Discuss bonus content with Project-Heads first please!//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
~Carn */
|
||||
|
||||
//sets up the holiday string in the events manager.
|
||||
/datum/controller/event/proc/getHoliday()
|
||||
if(!config.allow_holidays) return // Holiday stuff was not enabled in the config!
|
||||
holiday = null
|
||||
|
||||
var/YY = text2num(time2text(world.timeofday, "YY")) // get the current year
|
||||
var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month
|
||||
var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day
|
||||
|
||||
//Main switch. If any of these are too dumb/inappropriate, or you have better ones, feel free to change whatever
|
||||
switch(MM)
|
||||
if(1) //Jan
|
||||
switch(DD)
|
||||
if(1) holiday = "New Year"
|
||||
|
||||
if(2) //Feb
|
||||
switch(DD)
|
||||
if(2) holiday = "Groundhog Day"
|
||||
if(14) holiday = "Valentine's Day"
|
||||
if(17) holiday = "Random Acts of Kindness Day"
|
||||
|
||||
if(3) //Mar
|
||||
switch(DD)
|
||||
if(14) holiday = "Pi Day"
|
||||
if(17) holiday = "St. Patrick's Day"
|
||||
if(27)
|
||||
if(YY == 16)
|
||||
holiday = "Easter"
|
||||
if(31)
|
||||
if(YY == 13)
|
||||
holiday = "Easter"
|
||||
|
||||
if(4) //Apr
|
||||
switch(DD)
|
||||
if(1)
|
||||
holiday = "April Fool's Day"
|
||||
if(YY == 18 && prob(50)) holiday = "Easter"
|
||||
if(5)
|
||||
if(YY == 15) holiday = "Easter"
|
||||
if(16)
|
||||
if(YY == 17) holiday = "Easter"
|
||||
if(20)
|
||||
holiday = "Four-Twenty"
|
||||
if(YY == 14 && prob(50)) holiday = "Easter"
|
||||
if(22) holiday = "Earth Day"
|
||||
|
||||
if(5) //May
|
||||
switch(DD)
|
||||
if(1) holiday = "Labour Day"
|
||||
if(4) holiday = "FireFighter's Day"
|
||||
if(12) holiday = "Owl and Pussycat Day" //what a dumb day of observence...but we -do- have costumes already :3
|
||||
|
||||
if(6) //Jun
|
||||
|
||||
if(7) //Jul
|
||||
switch(DD)
|
||||
if(1) holiday = "Doctor's Day"
|
||||
if(2) holiday = "UFO Day"
|
||||
if(8) holiday = "Writer's Day"
|
||||
if(30) holiday = "Friendship Day"
|
||||
|
||||
if(8) //Aug
|
||||
switch(DD)
|
||||
if(5) holiday = "Beer Day"
|
||||
|
||||
if(9) //Sep
|
||||
switch(DD)
|
||||
if(19) holiday = "Talk-Like-a-Pirate Day"
|
||||
if(28) holiday = "Stupid-Questions Day"
|
||||
|
||||
if(10) //Oct
|
||||
switch(DD)
|
||||
if(4) holiday = "Animal's Day"
|
||||
if(7) holiday = "Smiling Day"
|
||||
if(16) holiday = "Boss' Day"
|
||||
if(31) holiday = "Halloween"
|
||||
|
||||
if(11) //Nov
|
||||
switch(DD)
|
||||
if(1) holiday = "Vegan Day"
|
||||
if(13) holiday = "Kindness Day"
|
||||
if(19) holiday = "Flowers Day"
|
||||
if(21) holiday = "Saying-'Hello' Day"
|
||||
|
||||
if(12) //Dec
|
||||
switch(DD)
|
||||
if(10) holiday = "Human-Rights Day"
|
||||
if(14) holiday = "Monkey Day"
|
||||
if(21) holiday = "Mayan Doomsday Anniversary"
|
||||
if(22) holiday = "Orgasming Day" //lol. These all actually exist
|
||||
if(24) holiday = "Xmas"
|
||||
if(25) holiday = "Xmas"
|
||||
if(26) holiday = "Boxing Day"
|
||||
if(31) holiday = "New Year"
|
||||
|
||||
if(!holiday)
|
||||
//Friday the 13th
|
||||
if(DD == 13)
|
||||
if(time2text(world.timeofday, "DDD") == "Fri")
|
||||
holiday = "Friday the 13th"
|
||||
|
||||
world.update_status()
|
||||
|
||||
/datum/controller/event/proc/toggleWizardmode()
|
||||
wizardmode = !wizardmode
|
||||
for(var/datum/round_event_control/E in control)
|
||||
E.weight = initial(E.weight)
|
||||
if((E.wizardevent && !wizardmode) || (!E.wizardevent && wizardmode))
|
||||
E.weight = 0
|
||||
message_admins("Summon Events has been [wizardmode ? "enabled, events will occur every [events.frequency_lower / 600] to [events.frequency_upper / 600] minutes" : "disabled"]!")
|
||||
log_game("Summon Events was [wizardmode ? "enabled" : "disabled"]!")
|
||||
|
||||
/datum/controller/event/proc/resetFrequency()
|
||||
frequency_lower = initial(frequency_lower)
|
||||
frequency_upper = initial(frequency_upper)
|
||||
var/datum/subsystem/events/SSevent
|
||||
|
||||
/datum/subsystem/events
|
||||
name = "Events"
|
||||
priority = 6
|
||||
|
||||
var/list/control = list() //list of all datum/round_event_control. Used for selecting events based on weight and occurrences.
|
||||
var/list/running = list() //list of all existing /datum/round_event
|
||||
|
||||
var/scheduled = 0 //The next world.time that a naturally occuring random event can be selected.
|
||||
var/frequency_lower = 3000 //5 minutes lower bound.
|
||||
var/frequency_upper = 9000 //15 minutes upper bound. Basically an event will happen every 5 to 15 minutes.
|
||||
|
||||
var/holiday //This will be a string of the name of any realworld holiday which occurs today (GMT time)
|
||||
var/wizardmode = 0
|
||||
|
||||
|
||||
/datum/subsystem/events/New()
|
||||
NEW_SS_GLOBAL(SSevent)
|
||||
|
||||
|
||||
/datum/subsystem/events/Initialize()
|
||||
for(var/type in typesof(/datum/round_event_control))
|
||||
var/datum/round_event_control/E = new type()
|
||||
if(!E.typepath)
|
||||
continue //don't want this one! leave it for the garbage collector
|
||||
if(E.wizardevent && !wizardmode)
|
||||
E.weight = 0
|
||||
control += E //add it to the list of all events (controls)
|
||||
reschedule()
|
||||
getHoliday()
|
||||
..()
|
||||
|
||||
|
||||
/datum/subsystem/events/fire()
|
||||
checkEvent()
|
||||
var/i=1
|
||||
for(var/thing in running)
|
||||
if(thing)
|
||||
thing:process()
|
||||
++i
|
||||
continue
|
||||
running.Cut(i,i+1)
|
||||
|
||||
|
||||
//checks if we should select a random event yet, and reschedules if necessary
|
||||
/datum/subsystem/events/proc/checkEvent()
|
||||
if(scheduled <= world.time)
|
||||
spawnEvent()
|
||||
reschedule()
|
||||
|
||||
//decides which world.time we should select another random event at.
|
||||
/datum/subsystem/events/proc/reschedule()
|
||||
scheduled = world.time + rand(frequency_lower, max(frequency_lower,frequency_upper))
|
||||
|
||||
//selects a random event based on whether it can occur and it's 'weight'(probability)
|
||||
/datum/subsystem/events/proc/spawnEvent()
|
||||
if(!config.allow_random_events)
|
||||
// var/datum/round_event_control/E = locate(/datum/round_event_control/dust) in control
|
||||
// if(E) E.runEvent()
|
||||
return
|
||||
|
||||
var/sum_of_weights = 0
|
||||
for(var/datum/round_event_control/E in control)
|
||||
if(E.occurrences >= E.max_occurrences) continue
|
||||
if(E.earliest_start >= world.time) continue
|
||||
if(E.holidayID)
|
||||
if(E.holidayID != holiday) continue
|
||||
if(E.weight < 0) //for round-start events etc.
|
||||
if(E.runEvent() == PROCESS_KILL)
|
||||
E.max_occurrences = 0
|
||||
continue
|
||||
return
|
||||
sum_of_weights += E.weight
|
||||
|
||||
sum_of_weights = rand(0,sum_of_weights) //reusing this variable. It now represents the 'weight' we want to select
|
||||
|
||||
for(var/datum/round_event_control/E in control)
|
||||
if(E.occurrences >= E.max_occurrences) continue
|
||||
if(E.earliest_start >= world.time) continue
|
||||
if(E.holidayID)
|
||||
if(E.holidayID != holiday) continue
|
||||
sum_of_weights -= E.weight
|
||||
|
||||
if(sum_of_weights <= 0) //we've hit our goal
|
||||
if(E.runEvent() == PROCESS_KILL)//we couldn't run this event for some reason, set its max_occurrences to 0
|
||||
E.max_occurrences = 0
|
||||
continue
|
||||
return
|
||||
|
||||
|
||||
/datum/round_event/proc/findEventArea() //Here's a nice proc to use to find an area for your event to land in!
|
||||
var/list/safe_areas = list(
|
||||
/area/turret_protected/ai,
|
||||
/area/turret_protected/ai_upload,
|
||||
/area/engine,
|
||||
/area/solar,
|
||||
/area/holodeck,
|
||||
/area/shuttle
|
||||
)
|
||||
|
||||
//These are needed because /area/engine has to be removed from the list, but we still want these areas to get fucked up.
|
||||
var/list/danger_areas = list(
|
||||
/area/engine/break_room,
|
||||
/area/engine/chiefs_office)
|
||||
|
||||
//Need to locate() as it's just a list of paths.
|
||||
return locate(pick((the_station_areas - safe_areas) + danger_areas))
|
||||
|
||||
|
||||
//allows a client to trigger an event
|
||||
//aka Badmin Central
|
||||
/client/proc/forceEvent()
|
||||
set name = "Trigger Event"
|
||||
set category = "Fun"
|
||||
|
||||
if(!holder ||!check_rights(R_FUN))
|
||||
return
|
||||
|
||||
holder.forceEvent()
|
||||
|
||||
/datum/admins/proc/forceEvent()
|
||||
var/dat = ""
|
||||
var/normal = ""
|
||||
var/magic = ""
|
||||
var/holiday = ""
|
||||
for(var/datum/round_event_control/E in SSevent.control)
|
||||
dat = "<BR><A href='?src=\ref[src];forceevent=\ref[E]'>[E]</A>"
|
||||
if(E.holidayID)
|
||||
holiday += dat
|
||||
else if(E.wizardevent)
|
||||
magic += dat
|
||||
else
|
||||
normal += dat
|
||||
|
||||
dat = normal + "<BR>" + magic + "<BR>" + holiday
|
||||
|
||||
var/datum/browser/popup = new(usr, "forceevent", "Force Random Event", 300, 750)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
|
||||
|
||||
/*
|
||||
//////////////
|
||||
// HOLIDAYS //
|
||||
//////////////
|
||||
//Uncommenting ALLOW_HOLIDAYS in config.txt will enable holidays
|
||||
|
||||
//It's easy to add stuff. Just modify getHoliday to set holiday to something using the switch for DD(#day) MM(#month) YY(#year).
|
||||
//You can then check if it's a special day in any code in the game by doing if(events.holiday == "MyHolidayID")
|
||||
|
||||
//You can also make holiday random events easily thanks to Pete/Gia's system.
|
||||
//simply make a random event normally, then assign it a holidayID string which matches the one you gave it in getHolday.
|
||||
//Anything with a holidayID, which does not match the holiday string, will never occur.
|
||||
|
||||
//Please, Don't spam stuff up with stupid stuff (key example being april-fools Pooh/ERP/etc),
|
||||
//And don't forget: CHECK YOUR CODE!!!! We don't want any zero-day bugs which happen only on holidays and never get found/fixed!
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//ALSO, MOST IMPORTANTLY: Don't add stupid stuff! Discuss bonus content with Project-Heads first please!//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
~Carn */
|
||||
|
||||
//sets up the holiday string in the events manager.
|
||||
/datum/subsystem/events/proc/getHoliday()
|
||||
if(!config.allow_holidays) return // Holiday stuff was not enabled in the config!
|
||||
holiday = null
|
||||
|
||||
var/YY = text2num(time2text(world.timeofday, "YY")) // get the current year
|
||||
var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month
|
||||
var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day
|
||||
|
||||
//Main switch. If any of these are too dumb/inappropriate, or you have better ones, feel free to change whatever
|
||||
switch(MM)
|
||||
if(1) //Jan
|
||||
switch(DD)
|
||||
if(1) holiday = "New Year"
|
||||
|
||||
if(2) //Feb
|
||||
switch(DD)
|
||||
if(2) holiday = "Groundhog Day"
|
||||
if(14) holiday = "Valentine's Day"
|
||||
if(17) holiday = "Random Acts of Kindness Day"
|
||||
|
||||
if(3) //Mar
|
||||
switch(DD)
|
||||
if(14) holiday = "Pi Day"
|
||||
if(17) holiday = "St. Patrick's Day"
|
||||
if(27)
|
||||
if(YY == 16)
|
||||
holiday = "Easter"
|
||||
if(31)
|
||||
if(YY == 13)
|
||||
holiday = "Easter"
|
||||
|
||||
if(4) //Apr
|
||||
switch(DD)
|
||||
if(1)
|
||||
holiday = "April Fool's Day"
|
||||
if(YY == 18 && prob(50)) holiday = "Easter"
|
||||
if(5)
|
||||
if(YY == 15) holiday = "Easter"
|
||||
if(16)
|
||||
if(YY == 17) holiday = "Easter"
|
||||
if(20)
|
||||
holiday = "Four-Twenty"
|
||||
if(YY == 14 && prob(50)) holiday = "Easter"
|
||||
if(22) holiday = "Earth Day"
|
||||
|
||||
if(5) //May
|
||||
switch(DD)
|
||||
if(1) holiday = "Labour Day"
|
||||
if(4) holiday = "FireFighter's Day"
|
||||
if(12) holiday = "Owl and Pussycat Day" //what a dumb day of observence...but we -do- have costumes already :3
|
||||
|
||||
if(6) //Jun
|
||||
|
||||
if(7) //Jul
|
||||
switch(DD)
|
||||
if(1) holiday = "Doctor's Day"
|
||||
if(2) holiday = "UFO Day"
|
||||
if(8) holiday = "Writer's Day"
|
||||
if(30) holiday = "Friendship Day"
|
||||
|
||||
if(8) //Aug
|
||||
switch(DD)
|
||||
if(5) holiday = "Beer Day"
|
||||
|
||||
if(9) //Sep
|
||||
switch(DD)
|
||||
if(19) holiday = "Talk-Like-a-Pirate Day"
|
||||
if(28) holiday = "Stupid-Questions Day"
|
||||
|
||||
if(10) //Oct
|
||||
switch(DD)
|
||||
if(4) holiday = "Animal's Day"
|
||||
if(7) holiday = "Smiling Day"
|
||||
if(16) holiday = "Boss' Day"
|
||||
if(31) holiday = "Halloween"
|
||||
|
||||
if(11) //Nov
|
||||
switch(DD)
|
||||
if(1) holiday = "Vegan Day"
|
||||
if(13) holiday = "Kindness Day"
|
||||
if(19) holiday = "Flowers Day"
|
||||
if(21) holiday = "Saying-'Hello' Day"
|
||||
|
||||
if(12) //Dec
|
||||
switch(DD)
|
||||
if(10) holiday = "Human-Rights Day"
|
||||
if(14) holiday = "Monkey Day"
|
||||
if(21) holiday = "Mayan Doomsday Anniversary"
|
||||
if(22) holiday = "Orgasming Day" //lol. These all actually exist
|
||||
if(24) holiday = "Xmas"
|
||||
if(25) holiday = "Xmas"
|
||||
if(26) holiday = "Boxing Day"
|
||||
if(31) holiday = "New Year"
|
||||
|
||||
if(!holiday)
|
||||
//Friday the 13th
|
||||
if(DD == 13)
|
||||
if(time2text(world.timeofday, "DDD") == "Fri")
|
||||
holiday = "Friday the 13th"
|
||||
|
||||
world.update_status()
|
||||
|
||||
|
||||
/datum/subsystem/events/proc/toggleWizardmode()
|
||||
wizardmode = !wizardmode
|
||||
for(var/datum/round_event_control/E in SSevent.control)
|
||||
E.weight = initial(E.weight)
|
||||
if((E.wizardevent && !wizardmode) || (!E.wizardevent && wizardmode))
|
||||
E.weight = 0
|
||||
message_admins("Summon Events has been [wizardmode ? "enabled, events will occur every [SSevent.frequency_lower / 600] to [SSevent.frequency_upper / 600] minutes" : "disabled"]!")
|
||||
log_game("Summon Events was [wizardmode ? "enabled" : "disabled"]!")
|
||||
|
||||
|
||||
/datum/subsystem/events/proc/resetFrequency()
|
||||
frequency_lower = initial(frequency_lower)
|
||||
frequency_upper = initial(frequency_upper)
|
||||
@@ -1,133 +1,144 @@
|
||||
#define GC_COLLECTION_TIMEOUT 300 // deciseconds to wait to let running procs finish before we just say fuck it and force del() the object
|
||||
#define GC_DEL_CHECK_PER_TICK 100 // number of tests per master controller tick to make sure our GC'd objects are actually GC'd
|
||||
#define GC_FORCE_DEL_PER_TICK 20 // max force del() calls per master controller tick
|
||||
|
||||
var/datum/controller/garbage_collector/garbage = new()
|
||||
|
||||
|
||||
/datum/controller/garbage_collector
|
||||
var/dels = 0 // number of del()'s we've done this tick
|
||||
var/list/destroyed = list() // list of refID's of things that should be garbage collected
|
||||
// refID's are associated with the time at which they time out and need to be manually del()
|
||||
// we do this so we aren't constantly locating them and preventing them from being gc'd
|
||||
|
||||
var/list/logging = list() // list of all types that have failed to GC associated with the number of times that's happened.
|
||||
// the types are stored as strings
|
||||
|
||||
/datum/controller/garbage_collector/proc/AddTrash(var/datum/A)
|
||||
if(!istype(A) || !isnull(A.gc_destroyed))
|
||||
return
|
||||
// testing("GC: AddTrash([A.type])")
|
||||
A.gc_destroyed = world.time
|
||||
destroyed -= "\ref[A]" // Removing any previous references that were GC'd so that the current object will be at the end of the list.
|
||||
destroyed["\ref[A]"] = world.time
|
||||
|
||||
/datum/controller/garbage_collector/proc/process()
|
||||
dels = 0
|
||||
var/time_to_kill = world.time - GC_COLLECTION_TIMEOUT // Anything qdel() but not GC'd BEFORE this time needs to be manually del()
|
||||
var/checkRemain = GC_DEL_CHECK_PER_TICK
|
||||
while(destroyed.len && --checkRemain >= 0)
|
||||
if(dels > GC_FORCE_DEL_PER_TICK)
|
||||
// testing("GC: Reached max force dels per tick [dels] vs [GC_FORCE_DEL_PER_TICK]")
|
||||
break // Server's already pretty pounded, everything else can wait 2 seconds
|
||||
var/refID = destroyed[1]
|
||||
var/GCd_at_time = destroyed[refID]
|
||||
if(GCd_at_time > time_to_kill)
|
||||
// testing("GC: [refID] not old enough, breaking at [world.time] for [GCd_at_time - time_to_kill] deciseconds until [GCd_at_time + GC_COLLECTION_TIMEOUT]")
|
||||
break // Everything else is newer, skip them
|
||||
var/atom/A = locate(refID)
|
||||
// testing("GC: [refID] old enough to test: GCd_at_time: [GCd_at_time] time_to_kill: [time_to_kill] current: [world.time]")
|
||||
if(A && A.gc_destroyed == GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake
|
||||
// Something's still referring to the qdel'd object. Kill it.
|
||||
testing("GC: -- \ref[A] | [A.type] was unable to be GC'd and was deleted --")
|
||||
logging["[A.type]"]++
|
||||
del(A)
|
||||
dels++
|
||||
// else
|
||||
// testing("GC: [refID] properly GC'd at [world.time] with timeout [GCd_at_time]")
|
||||
destroyed.Cut(1, 2)
|
||||
|
||||
// Should be treated as a replacement for the 'del' keyword.
|
||||
// Datums passed to this will be given a chance to clean up references to allow the GC to collect them.
|
||||
/proc/qdel(var/datum/A)
|
||||
if(!A)
|
||||
return
|
||||
if(!istype(A))
|
||||
//warning("qdel() passed object of type [A.type]. qdel() can only handle /datum types.")
|
||||
del(A)
|
||||
if(garbage)
|
||||
garbage.dels++
|
||||
else if(isnull(A.gc_destroyed))
|
||||
// Let our friend know they're about to get fucked up.
|
||||
. = !A.Destroy()
|
||||
if(. && A)
|
||||
if(garbage && !isturf(A))
|
||||
garbage.AddTrash(A)
|
||||
else
|
||||
del(A)
|
||||
|
||||
// Default implementation of clean-up code.
|
||||
// This should be overridden to remove all references pointing to the object being destroyed.
|
||||
// Return true if the the GC controller should allow the object to continue existing. (Useful if pooling objects.)
|
||||
/datum/proc/Destroy()
|
||||
//del(src)
|
||||
return
|
||||
|
||||
/datum/var/gc_destroyed //Time when this object was destroyed.
|
||||
|
||||
#ifdef TESTING
|
||||
/client/var/running_find_references
|
||||
|
||||
/atom/verb/find_references()
|
||||
set category = "Debug"
|
||||
set name = "Find References"
|
||||
set background = 1
|
||||
set src in world
|
||||
|
||||
if(!usr || !usr.client)
|
||||
return
|
||||
|
||||
if(usr.client.running_find_references)
|
||||
testing("CANCELLED search for references to a [usr.client.running_find_references].")
|
||||
usr.client.running_find_references = null
|
||||
return
|
||||
|
||||
if(alert("Running this will create a lot of lag until it finishes. You can cancel it by running it again. Would you like to begin the search?", "Find References", "Yes", "No") == "No")
|
||||
return
|
||||
|
||||
// Remove this object from the list of things to be auto-deleted.
|
||||
if(garbage)
|
||||
garbage.destroyed -= "\ref[src]"
|
||||
|
||||
usr.client.running_find_references = type
|
||||
testing("Beginning search for references to a [type].")
|
||||
var/list/things = list()
|
||||
for(var/client/thing)
|
||||
things += thing
|
||||
for(var/datum/thing)
|
||||
things += thing
|
||||
for(var/atom/thing)
|
||||
things += thing
|
||||
testing("Collected list of things in search for references to a [type]. ([things.len] Thing\s)")
|
||||
for(var/datum/thing in things)
|
||||
if(!usr.client.running_find_references) return
|
||||
for(var/varname in thing.vars)
|
||||
var/variable = thing.vars[varname]
|
||||
if(variable == src)
|
||||
testing("Found [src.type] \ref[src] in [thing.type]'s [varname] var.")
|
||||
else if(islist(variable))
|
||||
if(src in variable)
|
||||
testing("Found [src.type] \ref[src] in [thing.type]'s [varname] list var.")
|
||||
testing("Completed search for references to a [type].")
|
||||
usr.client.running_find_references = null
|
||||
|
||||
/client/verb/purge_all_destroyed_objects()
|
||||
set category = "Debug"
|
||||
if(garbage)
|
||||
while(garbage.destroyed.len)
|
||||
var/datum/o = locate(garbage.destroyed[1])
|
||||
if(istype(o) && o.gc_destroyed)
|
||||
del(o)
|
||||
garbage.dels++
|
||||
garbage.destroyed.Cut(1, 2)
|
||||
#endif
|
||||
var/datum/subsystem/garbage_collector/SSgarbage
|
||||
|
||||
/datum/subsystem/garbage_collector
|
||||
name = "Garbage"
|
||||
can_fire = 1
|
||||
wait = 60
|
||||
priority = 21
|
||||
|
||||
var/collection_timeout = 300 //deciseconds to wait to let running procs finish before we just say fuck it and force del() the object
|
||||
var/max_checks_multiplier = 5 //multiplier (per-decisecond) for calculating max number of tests per SS tick. These tests check if our GC'd objects are actually GC'd
|
||||
var/max_forcedel_multiplier = 1 //multiplier (per-decisecond) for calculating max number of force del() calls per SS tick.
|
||||
|
||||
var/dels = 0 // number of del()'s we've done this tick
|
||||
var/list/destroyed = list() // list of refID's of things that should be garbage collected
|
||||
// refID's are associated with the time at which they time out and need to be manually del()
|
||||
// we do this so we aren't constantly locating them and preventing them from being gc'd
|
||||
|
||||
var/list/logging = list() // list of all types that have failed to GC associated with the number of times that's happened.
|
||||
// the types are stored as strings
|
||||
|
||||
/datum/subsystem/garbage_collector/New()
|
||||
NEW_SS_GLOBAL(SSgarbage)
|
||||
|
||||
/datum/subsystem/garbage_collector/fire()
|
||||
dels = 0
|
||||
|
||||
var/time_to_kill = world.time - collection_timeout // Anything qdel() but not GC'd BEFORE this time needs to be manually del()
|
||||
var/checkRemain = max_checks_multiplier * wait
|
||||
var/maxDels = max_forcedel_multiplier * wait
|
||||
|
||||
while(destroyed.len && --checkRemain >= 0)
|
||||
if(dels >= maxDels)
|
||||
// testing("GC: Reached max force dels per tick [dels] vs [GC_FORCE_DEL_PER_TICK]")
|
||||
break // Server's already pretty pounded, everything else can wait 2 seconds
|
||||
var/refID = destroyed[1]
|
||||
var/GCd_at_time = destroyed[refID]
|
||||
if(GCd_at_time > time_to_kill)
|
||||
// testing("GC: [refID] not old enough, breaking at [world.time] for [GCd_at_time - time_to_kill] deciseconds until [GCd_at_time + GC_COLLECTION_TIMEOUT]")
|
||||
break // Everything else is newer, skip them
|
||||
var/atom/A = locate(refID)
|
||||
// testing("GC: [refID] old enough to test: GCd_at_time: [GCd_at_time] time_to_kill: [time_to_kill] current: [world.time]")
|
||||
if(A && A.gc_destroyed == GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake
|
||||
// Something's still referring to the qdel'd object. Kill it.
|
||||
testing("GC: -- \ref[A] | [A.type] was unable to be GC'd and was deleted --")
|
||||
logging["[A.type]"]++
|
||||
del(A)
|
||||
++dels
|
||||
// else
|
||||
// testing("GC: [refID] properly GC'd at [world.time] with timeout [GCd_at_time]")
|
||||
destroyed.Cut(1, 2)
|
||||
|
||||
|
||||
/datum/subsystem/garbage_collector/proc/AddTrash(datum/A)
|
||||
if(!istype(A) || !isnull(A.gc_destroyed))
|
||||
return
|
||||
// testing("GC: AddTrash([A.type])")
|
||||
A.gc_destroyed = world.time
|
||||
destroyed -= "\ref[A]" // Removing any previous references that were GC'd so that the current object will be at the end of the list.
|
||||
destroyed["\ref[A]"] = world.time
|
||||
|
||||
|
||||
// Should be treated as a replacement for the 'del' keyword.
|
||||
// Datums passed to this will be given a chance to clean up references to allow the GC to collect them.
|
||||
/proc/qdel(var/datum/A)
|
||||
if(!A)
|
||||
return
|
||||
if(!istype(A))
|
||||
//warning("qdel() passed object of type [A.type]. qdel() can only handle /datum types.")
|
||||
del(A)
|
||||
SSgarbage.dels++
|
||||
else if(isnull(A.gc_destroyed))
|
||||
// Let our friend know they're about to get fucked up.
|
||||
. = !A.Destroy()
|
||||
if(. && A)
|
||||
if(!isturf(A))
|
||||
SSgarbage.AddTrash(A)
|
||||
else
|
||||
del(A)
|
||||
|
||||
// Default implementation of clean-up code.
|
||||
// This should be overridden to remove all references pointing to the object being destroyed.
|
||||
// Return true if the the GC controller should allow the object to continue existing. (Useful if pooling objects.)
|
||||
/datum/proc/Destroy()
|
||||
//del(src)
|
||||
return
|
||||
|
||||
/datum/var/gc_destroyed //Time when this object was destroyed.
|
||||
|
||||
#ifdef TESTING
|
||||
/client/var/running_find_references
|
||||
|
||||
/atom/verb/find_references()
|
||||
set category = "Debug"
|
||||
set name = "Find References"
|
||||
set background = 1
|
||||
set src in world
|
||||
|
||||
if(!usr || !usr.client)
|
||||
return
|
||||
|
||||
if(usr.client.running_find_references)
|
||||
testing("CANCELLED search for references to a [usr.client.running_find_references].")
|
||||
usr.client.running_find_references = null
|
||||
return
|
||||
|
||||
if(alert("Running this will create a lot of lag until it finishes. You can cancel it by running it again. Would you like to begin the search?", "Find References", "Yes", "No") == "No")
|
||||
return
|
||||
|
||||
// Remove this object from the list of things to be auto-deleted.
|
||||
if(garbage)
|
||||
garbage.destroyed -= "\ref[src]"
|
||||
|
||||
usr.client.running_find_references = type
|
||||
testing("Beginning search for references to a [type].")
|
||||
var/list/things = list()
|
||||
for(var/client/thing)
|
||||
things += thing
|
||||
for(var/datum/thing)
|
||||
things += thing
|
||||
for(var/atom/thing)
|
||||
things += thing
|
||||
testing("Collected list of things in search for references to a [type]. ([things.len] Thing\s)")
|
||||
for(var/datum/thing in things)
|
||||
if(!usr.client.running_find_references) return
|
||||
for(var/varname in thing.vars)
|
||||
var/variable = thing.vars[varname]
|
||||
if(variable == src)
|
||||
testing("Found [src.type] \ref[src] in [thing.type]'s [varname] var.")
|
||||
else if(islist(variable))
|
||||
if(src in variable)
|
||||
testing("Found [src.type] \ref[src] in [thing.type]'s [varname] list var.")
|
||||
testing("Completed search for references to a [type].")
|
||||
usr.client.running_find_references = null
|
||||
|
||||
/client/verb/purge_all_destroyed_objects()
|
||||
set category = "Debug"
|
||||
if(garbage)
|
||||
while(garbage.destroyed.len)
|
||||
var/datum/o = locate(garbage.destroyed[1])
|
||||
if(istype(o) && o.gc_destroyed)
|
||||
del(o)
|
||||
garbage.dels++
|
||||
garbage.destroyed.Cut(1, 2)
|
||||
#endif
|
||||
@@ -1,416 +1,424 @@
|
||||
var/global/datum/controller/occupations/job_master
|
||||
|
||||
/datum/controller/occupations
|
||||
//List of all jobs
|
||||
var/list/occupations = list()
|
||||
//Players who need jobs
|
||||
var/list/unassigned = list()
|
||||
//Debug info
|
||||
var/list/job_debug = list()
|
||||
|
||||
|
||||
/datum/controller/occupations/proc/SetupOccupations(var/faction = "Station")
|
||||
occupations = list()
|
||||
var/list/all_jobs = typesof(/datum/job)
|
||||
if(!all_jobs.len)
|
||||
world << "<span class='userdanger'>Error setting up jobs, no job datums found</span>"
|
||||
return 0
|
||||
|
||||
for(var/J in all_jobs)
|
||||
var/datum/job/job = new J()
|
||||
if(!job) continue
|
||||
if(job.faction != faction) continue
|
||||
if(!job.config_check()) continue
|
||||
occupations += job
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
/datum/controller/occupations/proc/Debug(var/text)
|
||||
if(!Debug2) return 0
|
||||
job_debug.Add(text)
|
||||
return 1
|
||||
|
||||
|
||||
/datum/controller/occupations/proc/GetJob(var/rank)
|
||||
if(!rank) return null
|
||||
for(var/datum/job/J in occupations)
|
||||
if(!J) continue
|
||||
if(J.title == rank) return J
|
||||
return null
|
||||
|
||||
/datum/controller/occupations/proc/AssignRole(var/mob/new_player/player, var/rank, var/latejoin = 0)
|
||||
Debug("Running AR, Player: [player], Rank: [rank], LJ: [latejoin]")
|
||||
if(player && player.mind && rank)
|
||||
var/datum/job/job = GetJob(rank)
|
||||
if(!job) return 0
|
||||
if(jobban_isbanned(player, rank)) return 0
|
||||
if(!job.player_old_enough(player.client)) return 0
|
||||
var/position_limit = job.total_positions
|
||||
if(!latejoin)
|
||||
position_limit = job.spawn_positions
|
||||
if((job.current_positions < position_limit) || position_limit == -1)
|
||||
Debug("Player: [player] is now Rank: [rank], JCP:[job.current_positions], JPL:[position_limit]")
|
||||
player.mind.assigned_role = rank
|
||||
unassigned -= player
|
||||
job.current_positions++
|
||||
return 1
|
||||
Debug("AR has failed, Player: [player], Rank: [rank]")
|
||||
return 0
|
||||
|
||||
|
||||
/datum/controller/occupations/proc/FindOccupationCandidates(datum/job/job, level, flag)
|
||||
Debug("Running FOC, Job: [job], Level: [level], Flag: [flag]")
|
||||
var/list/candidates = list()
|
||||
for(var/mob/new_player/player in unassigned)
|
||||
if(jobban_isbanned(player, job.title))
|
||||
Debug("FOC isbanned failed, Player: [player]")
|
||||
continue
|
||||
if(!job.player_old_enough(player.client))
|
||||
Debug("FOC player not old enough, Player: [player]")
|
||||
continue
|
||||
if(flag && (!player.client.prefs.be_special & flag))
|
||||
Debug("FOC flag failed, Player: [player], Flag: [flag], ")
|
||||
continue
|
||||
|
||||
if(config.enforce_human_authority && (job.title in command_positions) && player.client.prefs.pref_species.id != "human")
|
||||
Debug("FOC non-human failed, Player: [player]")
|
||||
continue
|
||||
|
||||
if(player.client.prefs.GetJobDepartment(job, level) & job.flag)
|
||||
Debug("FOC pass, Player: [player], Level:[level]")
|
||||
candidates += player
|
||||
return candidates
|
||||
|
||||
/datum/controller/occupations/proc/GiveRandomJob(var/mob/new_player/player)
|
||||
Debug("GRJ Giving random job, Player: [player]")
|
||||
for(var/datum/job/job in shuffle(occupations))
|
||||
if(!job)
|
||||
continue
|
||||
|
||||
if(istype(job, GetJob("Assistant"))) // We don't want to give him assistant, that's boring!
|
||||
continue
|
||||
|
||||
if(job in command_positions) //If you want a command position, select it!
|
||||
continue
|
||||
|
||||
if(jobban_isbanned(player, job.title))
|
||||
Debug("GRJ isbanned failed, Player: [player], Job: [job.title]")
|
||||
continue
|
||||
|
||||
if(!job.player_old_enough(player.client))
|
||||
Debug("GRJ player not old enough, Player: [player]")
|
||||
continue
|
||||
|
||||
if(config.enforce_human_authority && (job.title in command_positions) && player.client.prefs.pref_species.id != "human")
|
||||
Debug("GRJ non-human failed, Player: [player]")
|
||||
continue
|
||||
|
||||
|
||||
if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1)
|
||||
Debug("GRJ Random job given, Player: [player], Job: [job]")
|
||||
AssignRole(player, job.title)
|
||||
unassigned -= player
|
||||
break
|
||||
|
||||
/datum/controller/occupations/proc/ResetOccupations()
|
||||
for(var/mob/new_player/player in player_list)
|
||||
if((player) && (player.mind))
|
||||
player.mind.assigned_role = null
|
||||
player.mind.special_role = null
|
||||
SetupOccupations()
|
||||
unassigned = list()
|
||||
return
|
||||
|
||||
|
||||
//This proc is called before the level loop of DivideOccupations() and will try to select a head, ignoring ALL non-head preferences for every level until
|
||||
//it locates a head or runs out of levels to check
|
||||
//This is basically to ensure that there's atleast a few heads in the round
|
||||
/datum/controller/occupations/proc/FillHeadPosition()
|
||||
for(var/level = 1 to 3)
|
||||
for(var/command_position in command_positions)
|
||||
var/datum/job/job = GetJob(command_position)
|
||||
if(!job) continue
|
||||
var/list/candidates = FindOccupationCandidates(job, level)
|
||||
if(!candidates.len) continue
|
||||
var/mob/new_player/candidate = pick(candidates)
|
||||
if(AssignRole(candidate, command_position))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
//This proc is called at the start of the level loop of DivideOccupations() and will cause head jobs to be checked before any other jobs of the same level
|
||||
//This is also to ensure we get as many heads as possible
|
||||
/datum/controller/occupations/proc/CheckHeadPositions(var/level)
|
||||
for(var/command_position in command_positions)
|
||||
var/datum/job/job = GetJob(command_position)
|
||||
if(!job) continue
|
||||
var/list/candidates = FindOccupationCandidates(job, level)
|
||||
if(!candidates.len) continue
|
||||
var/mob/new_player/candidate = pick(candidates)
|
||||
AssignRole(candidate, command_position)
|
||||
return
|
||||
|
||||
|
||||
/datum/controller/occupations/proc/FillAIPosition()
|
||||
var/ai_selected = 0
|
||||
var/datum/job/job = GetJob("AI")
|
||||
if(!job) return 0
|
||||
if(ticker.mode.name == "AI malfunction") // malf. AIs are pre-selected before jobs
|
||||
for (var/datum/mind/mAI in ticker.mode.malf_ai)
|
||||
AssignRole(mAI.current, "AI")
|
||||
ai_selected++
|
||||
if(ai_selected) return 1
|
||||
return 0
|
||||
|
||||
for(var/i = job.total_positions, i > 0, i--)
|
||||
for(var/level = 1 to 3)
|
||||
var/list/candidates = list()
|
||||
candidates = FindOccupationCandidates(job, level)
|
||||
if(candidates.len)
|
||||
var/mob/new_player/candidate = pick(candidates)
|
||||
if(AssignRole(candidate, "AI"))
|
||||
ai_selected++
|
||||
break
|
||||
if(ai_selected) return 1
|
||||
return 0
|
||||
|
||||
|
||||
/** Proc DivideOccupations
|
||||
* fills var "assigned_role" for all ready players.
|
||||
* This proc must not have any side effect besides of modifying "assigned_role".
|
||||
**/
|
||||
/datum/controller/occupations/proc/DivideOccupations()
|
||||
//Setup new player list and get the jobs list
|
||||
Debug("Running DO")
|
||||
SetupOccupations()
|
||||
|
||||
//Holder for Triumvirate is stored in the ticker, this just processes it
|
||||
if(ticker)
|
||||
for(var/datum/job/ai/A in occupations)
|
||||
if(ticker.triai)
|
||||
A.spawn_positions = 3
|
||||
|
||||
//Get the players who are ready
|
||||
for(var/mob/new_player/player in player_list)
|
||||
if(player.ready && player.mind && !player.mind.assigned_role)
|
||||
unassigned += player
|
||||
|
||||
Debug("DO, Len: [unassigned.len]")
|
||||
if(unassigned.len == 0) return 0
|
||||
|
||||
//Scale number of open security officer slots to population
|
||||
setup_officer_positions()
|
||||
|
||||
//Shuffle players and jobs
|
||||
unassigned = shuffle(unassigned)
|
||||
|
||||
HandleFeedbackGathering()
|
||||
|
||||
//People who wants to be assistants, sure, go on.
|
||||
Debug("DO, Running Assistant Check 1")
|
||||
var/datum/job/assist = new /datum/job/assistant()
|
||||
var/list/assistant_candidates = FindOccupationCandidates(assist, 3)
|
||||
Debug("AC1, Candidates: [assistant_candidates.len]")
|
||||
for(var/mob/new_player/player in assistant_candidates)
|
||||
Debug("AC1 pass, Player: [player]")
|
||||
AssignRole(player, "Assistant")
|
||||
assistant_candidates -= player
|
||||
Debug("DO, AC1 end")
|
||||
|
||||
//Select one head
|
||||
Debug("DO, Running Head Check")
|
||||
FillHeadPosition()
|
||||
Debug("DO, Head Check end")
|
||||
|
||||
//Check for an AI
|
||||
Debug("DO, Running AI Check")
|
||||
FillAIPosition()
|
||||
Debug("DO, AI Check end")
|
||||
|
||||
//Other jobs are now checked
|
||||
Debug("DO, Running Standard Check")
|
||||
|
||||
|
||||
// New job giving system by Donkie
|
||||
// This will cause lots of more loops, but since it's only done once it shouldn't really matter much at all.
|
||||
// Hopefully this will add more randomness and fairness to job giving.
|
||||
|
||||
// Loop through all levels from high to low
|
||||
var/list/shuffledoccupations = shuffle(occupations)
|
||||
for(var/level = 1 to 3)
|
||||
//Check the head jobs first each level
|
||||
CheckHeadPositions(level)
|
||||
|
||||
// Loop through all unassigned players
|
||||
for(var/mob/new_player/player in unassigned)
|
||||
|
||||
// Loop through all jobs
|
||||
for(var/datum/job/job in shuffledoccupations) // SHUFFLE ME BABY
|
||||
if(!job)
|
||||
continue
|
||||
|
||||
if(jobban_isbanned(player, job.title))
|
||||
Debug("DO isbanned failed, Player: [player], Job:[job.title]")
|
||||
continue
|
||||
|
||||
if(!job.player_old_enough(player.client))
|
||||
Debug("DO player not old enough, Player: [player], Job:[job.title]")
|
||||
continue
|
||||
|
||||
if(config.enforce_human_authority && (job.title in command_positions) && player.client.prefs.pref_species.id != "human")
|
||||
Debug("DO non-human failed, Player: [player], Job:[job.title]")
|
||||
continue
|
||||
|
||||
|
||||
// If the player wants that job on this level, then try give it to him.
|
||||
if(player.client.prefs.GetJobDepartment(job, level) & job.flag)
|
||||
|
||||
// If the job isn't filled
|
||||
if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1)
|
||||
Debug("DO pass, Player: [player], Level:[level], Job:[job.title]")
|
||||
AssignRole(player, job.title)
|
||||
unassigned -= player
|
||||
break
|
||||
|
||||
// Hand out random jobs to the people who didn't get any in the last check
|
||||
// Also makes sure that they got their preference correct
|
||||
for(var/mob/new_player/player in unassigned)
|
||||
if(jobban_isbanned(player, "Assistant"))
|
||||
GiveRandomJob(player) //you get to roll for random before everyone else just to be sure you don't get assistant. you're so speshul
|
||||
|
||||
for(var/mob/new_player/player in unassigned)
|
||||
if(player.client.prefs.userandomjob)
|
||||
GiveRandomJob(player)
|
||||
|
||||
Debug("DO, Standard Check end")
|
||||
|
||||
Debug("DO, Running AC2")
|
||||
|
||||
// For those who wanted to be assistant if their preferences were filled, here you go.
|
||||
for(var/mob/new_player/player in unassigned)
|
||||
Debug("AC2 Assistant located, Player: [player]")
|
||||
AssignRole(player, "Assistant")
|
||||
return 1
|
||||
|
||||
//Gives the player the stuff he should have with his rank
|
||||
/datum/controller/occupations/proc/EquipRank(var/mob/living/H, var/rank, var/joined_late = 0)
|
||||
|
||||
var/datum/job/job = GetJob(rank)
|
||||
|
||||
H.job = rank
|
||||
|
||||
//If we joined at roundstart we should be positioned at our workstation
|
||||
if(!joined_late)
|
||||
var/obj/S = null
|
||||
for(var/obj/effect/landmark/start/sloc in landmarks_list)
|
||||
if(sloc.name != rank) continue
|
||||
if(locate(/mob/living) in sloc.loc) continue
|
||||
S = sloc
|
||||
break
|
||||
if(!S)
|
||||
S = locate("start*[rank]") // use old stype
|
||||
if(istype(S, /obj/effect/landmark/start) && istype(S.loc, /turf))
|
||||
H.loc = S.loc
|
||||
|
||||
if(H.mind)
|
||||
H.mind.assigned_role = rank
|
||||
|
||||
if(job)
|
||||
var/new_mob = job.equip(H)
|
||||
if(ismob(new_mob))
|
||||
H = new_mob
|
||||
job.apply_fingerprints(H)
|
||||
|
||||
H << "<b>You are the [rank].</b>"
|
||||
H << "<b>As the [rank] you answer directly to [job.supervisors]. Special circumstances may change this.</b>"
|
||||
H << "<b>To speak on your departments radio, use the :h button. To see others, look closely at your headset.</b>"
|
||||
if(job.req_admin_notify)
|
||||
H << "<b>You are playing a job that is important for Game Progression. If you have to disconnect, please notify the admins via adminhelp.</b>"
|
||||
|
||||
H.update_hud() // Tmp fix for Github issue 1006. TODO: make all procs in update_icons.dm do client.screen |= equipment no matter what.
|
||||
return 1
|
||||
|
||||
|
||||
/datum/controller/occupations/proc/setup_officer_positions()
|
||||
var/officer_positions = 5 //Number of open security officer positions at round start
|
||||
|
||||
if(config.security_scaling_coeff > 0)
|
||||
officer_positions = min(12, max(5, round(unassigned.len/config.security_scaling_coeff))) //Scale between 5 and 12 officers
|
||||
var/datum/job/J = job_master.GetJob("Security Officer")
|
||||
if(J || J.spawn_positions > 0)
|
||||
Debug("Setting open security officer positions to [officer_positions]")
|
||||
J.total_positions = officer_positions
|
||||
J.spawn_positions = officer_positions
|
||||
for(var/i=officer_positions-5, i>0, i--) //Spawn some extra eqipment lockers if we have more than 5 officers
|
||||
if(secequipment.len)
|
||||
var/spawnloc = secequipment[1]
|
||||
new /obj/structure/closet/secure_closet/security(spawnloc)
|
||||
secequipment -= spawnloc
|
||||
else //We ran out of spare locker spawns!
|
||||
break
|
||||
|
||||
/datum/controller/occupations/proc/LoadJobs(jobsfile) //ran during round setup, reads info from jobs.txt -- Urist
|
||||
if(!config.load_jobs_from_txt)
|
||||
return 0
|
||||
|
||||
var/list/jobEntries = file2list(jobsfile)
|
||||
|
||||
for(var/job in jobEntries)
|
||||
if(!job)
|
||||
continue
|
||||
|
||||
job = trim(job)
|
||||
if (!length(job))
|
||||
continue
|
||||
|
||||
var/pos = findtext(job, "=")
|
||||
var/name = null
|
||||
var/value = null
|
||||
|
||||
if(pos)
|
||||
name = copytext(job, 1, pos)
|
||||
value = copytext(job, pos + 1)
|
||||
else
|
||||
continue
|
||||
|
||||
if(name && value)
|
||||
var/datum/job/J = GetJob(name)
|
||||
if(!J) continue
|
||||
J.total_positions = text2num(value)
|
||||
J.spawn_positions = text2num(value)
|
||||
if(name == "AI" || name == "Cyborg")//I dont like this here but it will do for now
|
||||
J.total_positions = 0
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
/datum/controller/occupations/proc/HandleFeedbackGathering()
|
||||
for(var/datum/job/job in occupations)
|
||||
var/tmp_str = "|[job.title]|"
|
||||
|
||||
var/level1 = 0 //high
|
||||
var/level2 = 0 //medium
|
||||
var/level3 = 0 //low
|
||||
var/level4 = 0 //never
|
||||
var/level5 = 0 //banned
|
||||
var/level6 = 0 //account too young
|
||||
for(var/mob/new_player/player in player_list)
|
||||
if(!(player.ready && player.mind && !player.mind.assigned_role))
|
||||
continue //This player is not ready
|
||||
if(jobban_isbanned(player, job.title))
|
||||
level5++
|
||||
continue
|
||||
if(!job.player_old_enough(player.client))
|
||||
level6++
|
||||
continue
|
||||
if(player.client.prefs.GetJobDepartment(job, 1) & job.flag)
|
||||
level1++
|
||||
else if(player.client.prefs.GetJobDepartment(job, 2) & job.flag)
|
||||
level2++
|
||||
else if(player.client.prefs.GetJobDepartment(job, 3) & job.flag)
|
||||
level3++
|
||||
else level4++ //not selected
|
||||
|
||||
tmp_str += "HIGH=[level1]|MEDIUM=[level2]|LOW=[level3]|NEVER=[level4]|BANNED=[level5]|YOUNG=[level6]|-"
|
||||
feedback_add_details("job_preferences",tmp_str)
|
||||
var/datum/subsystem/job/SSjob
|
||||
|
||||
/datum/subsystem/job
|
||||
name = "Jobs"
|
||||
priority = 5
|
||||
|
||||
var/list/occupations = list() //List of all jobs
|
||||
var/list/unassigned = list() //Players who need jobs
|
||||
var/list/job_debug = list() //Debug info
|
||||
|
||||
/datum/subsystem/job/New()
|
||||
NEW_SS_GLOBAL(SSjob)
|
||||
|
||||
|
||||
/datum/subsystem/job/Initialize()
|
||||
SetupOccupations()
|
||||
LoadJobs("config/jobs.txt")
|
||||
..()
|
||||
|
||||
|
||||
/datum/subsystem/job/proc/SetupOccupations(faction = "Station")
|
||||
occupations = list()
|
||||
var/list/all_jobs = typesof(/datum/job)
|
||||
if(!all_jobs.len)
|
||||
world << "<span class='userdanger'>Error setting up jobs, no job datums found</span>"
|
||||
return 0
|
||||
|
||||
for(var/J in all_jobs)
|
||||
var/datum/job/job = new J()
|
||||
if(!job) continue
|
||||
if(job.faction != faction) continue
|
||||
if(!job.config_check()) continue
|
||||
occupations += job
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
/datum/subsystem/job/proc/Debug(text)
|
||||
if(!Debug2) return 0
|
||||
job_debug.Add(text)
|
||||
return 1
|
||||
|
||||
|
||||
/datum/subsystem/job/proc/GetJob(rank)
|
||||
if(!rank) return null
|
||||
for(var/datum/job/J in occupations)
|
||||
if(!J) continue
|
||||
if(J.title == rank) return J
|
||||
return null
|
||||
|
||||
/datum/subsystem/job/proc/AssignRole(mob/new_player/player, rank, latejoin=0)
|
||||
Debug("Running AR, Player: [player], Rank: [rank], LJ: [latejoin]")
|
||||
if(player && player.mind && rank)
|
||||
var/datum/job/job = GetJob(rank)
|
||||
if(!job) return 0
|
||||
if(jobban_isbanned(player, rank)) return 0
|
||||
if(!job.player_old_enough(player.client)) return 0
|
||||
var/position_limit = job.total_positions
|
||||
if(!latejoin)
|
||||
position_limit = job.spawn_positions
|
||||
if((job.current_positions < position_limit) || position_limit == -1)
|
||||
Debug("Player: [player] is now Rank: [rank], JCP:[job.current_positions], JPL:[position_limit]")
|
||||
player.mind.assigned_role = rank
|
||||
unassigned -= player
|
||||
job.current_positions++
|
||||
return 1
|
||||
Debug("AR has failed, Player: [player], Rank: [rank]")
|
||||
return 0
|
||||
|
||||
|
||||
/datum/subsystem/job/proc/FindOccupationCandidates(datum/job/job, level, flag)
|
||||
Debug("Running FOC, Job: [job], Level: [level], Flag: [flag]")
|
||||
var/list/candidates = list()
|
||||
for(var/mob/new_player/player in unassigned)
|
||||
if(jobban_isbanned(player, job.title))
|
||||
Debug("FOC isbanned failed, Player: [player]")
|
||||
continue
|
||||
if(!job.player_old_enough(player.client))
|
||||
Debug("FOC player not old enough, Player: [player]")
|
||||
continue
|
||||
if(flag && (!player.client.prefs.be_special & flag))
|
||||
Debug("FOC flag failed, Player: [player], Flag: [flag], ")
|
||||
continue
|
||||
|
||||
if(config.enforce_human_authority && (job.title in command_positions) && player.client.prefs.pref_species.id != "human")
|
||||
Debug("FOC non-human failed, Player: [player]")
|
||||
continue
|
||||
|
||||
if(player.client.prefs.GetJobDepartment(job, level) & job.flag)
|
||||
Debug("FOC pass, Player: [player], Level:[level]")
|
||||
candidates += player
|
||||
return candidates
|
||||
|
||||
/datum/subsystem/job/proc/GiveRandomJob(mob/new_player/player)
|
||||
Debug("GRJ Giving random job, Player: [player]")
|
||||
for(var/datum/job/job in shuffle(occupations))
|
||||
if(!job)
|
||||
continue
|
||||
|
||||
if(istype(job, GetJob("Assistant"))) // We don't want to give him assistant, that's boring!
|
||||
continue
|
||||
|
||||
if(job in command_positions) //If you want a command position, select it!
|
||||
continue
|
||||
|
||||
if(jobban_isbanned(player, job.title))
|
||||
Debug("GRJ isbanned failed, Player: [player], Job: [job.title]")
|
||||
continue
|
||||
|
||||
if(!job.player_old_enough(player.client))
|
||||
Debug("GRJ player not old enough, Player: [player]")
|
||||
continue
|
||||
|
||||
if(config.enforce_human_authority && (job.title in command_positions) && player.client.prefs.pref_species.id != "human")
|
||||
Debug("GRJ non-human failed, Player: [player]")
|
||||
continue
|
||||
|
||||
|
||||
if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1)
|
||||
Debug("GRJ Random job given, Player: [player], Job: [job]")
|
||||
AssignRole(player, job.title)
|
||||
unassigned -= player
|
||||
break
|
||||
|
||||
/datum/subsystem/job/proc/ResetOccupations()
|
||||
for(var/mob/new_player/player in player_list)
|
||||
if((player) && (player.mind))
|
||||
player.mind.assigned_role = null
|
||||
player.mind.special_role = null
|
||||
SetupOccupations()
|
||||
unassigned = list()
|
||||
return
|
||||
|
||||
|
||||
//This proc is called before the level loop of DivideOccupations() and will try to select a head, ignoring ALL non-head preferences for every level until
|
||||
//it locates a head or runs out of levels to check
|
||||
//This is basically to ensure that there's atleast a few heads in the round
|
||||
/datum/subsystem/job/proc/FillHeadPosition()
|
||||
for(var/level = 1 to 3)
|
||||
for(var/command_position in command_positions)
|
||||
var/datum/job/job = GetJob(command_position)
|
||||
if(!job) continue
|
||||
var/list/candidates = FindOccupationCandidates(job, level)
|
||||
if(!candidates.len) continue
|
||||
var/mob/new_player/candidate = pick(candidates)
|
||||
if(AssignRole(candidate, command_position))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
//This proc is called at the start of the level loop of DivideOccupations() and will cause head jobs to be checked before any other jobs of the same level
|
||||
//This is also to ensure we get as many heads as possible
|
||||
/datum/subsystem/job/proc/CheckHeadPositions(level)
|
||||
for(var/command_position in command_positions)
|
||||
var/datum/job/job = GetJob(command_position)
|
||||
if(!job) continue
|
||||
var/list/candidates = FindOccupationCandidates(job, level)
|
||||
if(!candidates.len) continue
|
||||
var/mob/new_player/candidate = pick(candidates)
|
||||
AssignRole(candidate, command_position)
|
||||
return
|
||||
|
||||
|
||||
/datum/subsystem/job/proc/FillAIPosition()
|
||||
var/ai_selected = 0
|
||||
var/datum/job/job = GetJob("AI")
|
||||
if(!job) return 0
|
||||
if(ticker.mode.name == "AI malfunction") // malf. AIs are pre-selected before jobs
|
||||
for (var/datum/mind/mAI in ticker.mode.malf_ai)
|
||||
AssignRole(mAI.current, "AI")
|
||||
ai_selected++
|
||||
if(ai_selected) return 1
|
||||
return 0
|
||||
|
||||
for(var/i = job.total_positions, i > 0, i--)
|
||||
for(var/level = 1 to 3)
|
||||
var/list/candidates = list()
|
||||
candidates = FindOccupationCandidates(job, level)
|
||||
if(candidates.len)
|
||||
var/mob/new_player/candidate = pick(candidates)
|
||||
if(AssignRole(candidate, "AI"))
|
||||
ai_selected++
|
||||
break
|
||||
if(ai_selected) return 1
|
||||
return 0
|
||||
|
||||
|
||||
/** Proc DivideOccupations
|
||||
* fills var "assigned_role" for all ready players.
|
||||
* This proc must not have any side effect besides of modifying "assigned_role".
|
||||
**/
|
||||
/datum/subsystem/job/proc/DivideOccupations()
|
||||
//Setup new player list and get the jobs list
|
||||
Debug("Running DO")
|
||||
SetupOccupations()
|
||||
|
||||
//Holder for Triumvirate is stored in the ticker, this just processes it
|
||||
if(ticker)
|
||||
for(var/datum/job/ai/A in occupations)
|
||||
if(ticker.triai)
|
||||
A.spawn_positions = 3
|
||||
|
||||
//Get the players who are ready
|
||||
for(var/mob/new_player/player in player_list)
|
||||
if(player.ready && player.mind && !player.mind.assigned_role)
|
||||
unassigned += player
|
||||
|
||||
Debug("DO, Len: [unassigned.len]")
|
||||
if(unassigned.len == 0) return 0
|
||||
|
||||
//Scale number of open security officer slots to population
|
||||
setup_officer_positions()
|
||||
|
||||
//Shuffle players and jobs
|
||||
unassigned = shuffle(unassigned)
|
||||
|
||||
HandleFeedbackGathering()
|
||||
|
||||
//People who wants to be assistants, sure, go on.
|
||||
Debug("DO, Running Assistant Check 1")
|
||||
var/datum/job/assist = new /datum/job/assistant()
|
||||
var/list/assistant_candidates = FindOccupationCandidates(assist, 3)
|
||||
Debug("AC1, Candidates: [assistant_candidates.len]")
|
||||
for(var/mob/new_player/player in assistant_candidates)
|
||||
Debug("AC1 pass, Player: [player]")
|
||||
AssignRole(player, "Assistant")
|
||||
assistant_candidates -= player
|
||||
Debug("DO, AC1 end")
|
||||
|
||||
//Select one head
|
||||
Debug("DO, Running Head Check")
|
||||
FillHeadPosition()
|
||||
Debug("DO, Head Check end")
|
||||
|
||||
//Check for an AI
|
||||
Debug("DO, Running AI Check")
|
||||
FillAIPosition()
|
||||
Debug("DO, AI Check end")
|
||||
|
||||
//Other jobs are now checked
|
||||
Debug("DO, Running Standard Check")
|
||||
|
||||
|
||||
// New job giving system by Donkie
|
||||
// This will cause lots of more loops, but since it's only done once it shouldn't really matter much at all.
|
||||
// Hopefully this will add more randomness and fairness to job giving.
|
||||
|
||||
// Loop through all levels from high to low
|
||||
var/list/shuffledoccupations = shuffle(occupations)
|
||||
for(var/level = 1 to 3)
|
||||
//Check the head jobs first each level
|
||||
CheckHeadPositions(level)
|
||||
|
||||
// Loop through all unassigned players
|
||||
for(var/mob/new_player/player in unassigned)
|
||||
|
||||
// Loop through all jobs
|
||||
for(var/datum/job/job in shuffledoccupations) // SHUFFLE ME BABY
|
||||
if(!job)
|
||||
continue
|
||||
|
||||
if(jobban_isbanned(player, job.title))
|
||||
Debug("DO isbanned failed, Player: [player], Job:[job.title]")
|
||||
continue
|
||||
|
||||
if(!job.player_old_enough(player.client))
|
||||
Debug("DO player not old enough, Player: [player], Job:[job.title]")
|
||||
continue
|
||||
|
||||
if(config.enforce_human_authority && (job.title in command_positions) && player.client.prefs.pref_species.id != "human")
|
||||
Debug("DO non-human failed, Player: [player], Job:[job.title]")
|
||||
continue
|
||||
|
||||
|
||||
// If the player wants that job on this level, then try give it to him.
|
||||
if(player.client.prefs.GetJobDepartment(job, level) & job.flag)
|
||||
|
||||
// If the job isn't filled
|
||||
if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1)
|
||||
Debug("DO pass, Player: [player], Level:[level], Job:[job.title]")
|
||||
AssignRole(player, job.title)
|
||||
unassigned -= player
|
||||
break
|
||||
|
||||
// Hand out random jobs to the people who didn't get any in the last check
|
||||
// Also makes sure that they got their preference correct
|
||||
for(var/mob/new_player/player in unassigned)
|
||||
if(jobban_isbanned(player, "Assistant"))
|
||||
GiveRandomJob(player) //you get to roll for random before everyone else just to be sure you don't get assistant. you're so speshul
|
||||
|
||||
for(var/mob/new_player/player in unassigned)
|
||||
if(player.client.prefs.userandomjob)
|
||||
GiveRandomJob(player)
|
||||
|
||||
Debug("DO, Standard Check end")
|
||||
|
||||
Debug("DO, Running AC2")
|
||||
|
||||
// For those who wanted to be assistant if their preferences were filled, here you go.
|
||||
for(var/mob/new_player/player in unassigned)
|
||||
Debug("AC2 Assistant located, Player: [player]")
|
||||
AssignRole(player, "Assistant")
|
||||
return 1
|
||||
|
||||
//Gives the player the stuff he should have with his rank
|
||||
/datum/subsystem/job/proc/EquipRank(mob/living/H, rank, joined_late=0)
|
||||
var/datum/job/job = GetJob(rank)
|
||||
|
||||
H.job = rank
|
||||
|
||||
//If we joined at roundstart we should be positioned at our workstation
|
||||
if(!joined_late)
|
||||
var/obj/S = null
|
||||
for(var/obj/effect/landmark/start/sloc in landmarks_list)
|
||||
if(sloc.name != rank) continue
|
||||
if(locate(/mob/living) in sloc.loc) continue
|
||||
S = sloc
|
||||
break
|
||||
if(!S)
|
||||
S = locate("start*[rank]") // use old stype
|
||||
if(istype(S, /obj/effect/landmark/start) && istype(S.loc, /turf))
|
||||
H.loc = S.loc
|
||||
|
||||
if(H.mind)
|
||||
H.mind.assigned_role = rank
|
||||
|
||||
if(job)
|
||||
var/new_mob = job.equip(H)
|
||||
if(ismob(new_mob))
|
||||
H = new_mob
|
||||
job.apply_fingerprints(H)
|
||||
|
||||
H << "<b>You are the [rank].</b>"
|
||||
H << "<b>As the [rank] you answer directly to [job.supervisors]. Special circumstances may change this.</b>"
|
||||
H << "<b>To speak on your departments radio, use the :h button. To see others, look closely at your headset.</b>"
|
||||
if(job.req_admin_notify)
|
||||
H << "<b>You are playing a job that is important for Game Progression. If you have to disconnect, please notify the admins via adminhelp.</b>"
|
||||
|
||||
H.update_hud() // Tmp fix for Github issue 1006. TODO: make all procs in update_icons.dm do client.screen |= equipment no matter what.
|
||||
return 1
|
||||
|
||||
|
||||
/datum/subsystem/job/proc/setup_officer_positions()
|
||||
var/officer_positions = 5 //Number of open security officer positions at round start
|
||||
|
||||
if(config.security_scaling_coeff > 0)
|
||||
officer_positions = min(12, max(5, round(unassigned.len/config.security_scaling_coeff))) //Scale between 5 and 12 officers
|
||||
var/datum/job/J = SSjob.GetJob("Security Officer")
|
||||
if(J || J.spawn_positions > 0)
|
||||
Debug("Setting open security officer positions to [officer_positions]")
|
||||
J.total_positions = officer_positions
|
||||
J.spawn_positions = officer_positions
|
||||
for(var/i=officer_positions-5, i>0, i--) //Spawn some extra eqipment lockers if we have more than 5 officers
|
||||
if(secequipment.len)
|
||||
var/spawnloc = secequipment[1]
|
||||
new /obj/structure/closet/secure_closet/security(spawnloc)
|
||||
secequipment -= spawnloc
|
||||
else //We ran out of spare locker spawns!
|
||||
break
|
||||
|
||||
/datum/subsystem/job/proc/LoadJobs(jobsfile) //ran during round setup, reads info from jobs.txt -- Urist
|
||||
if(!config.load_jobs_from_txt)
|
||||
return 0
|
||||
|
||||
var/list/jobEntries = file2list(jobsfile)
|
||||
|
||||
for(var/job in jobEntries)
|
||||
if(!job)
|
||||
continue
|
||||
|
||||
job = trim(job)
|
||||
if (!length(job))
|
||||
continue
|
||||
|
||||
var/pos = findtext(job, "=")
|
||||
var/name = null
|
||||
var/value = null
|
||||
|
||||
if(pos)
|
||||
name = copytext(job, 1, pos)
|
||||
value = copytext(job, pos + 1)
|
||||
else
|
||||
continue
|
||||
|
||||
if(name && value)
|
||||
var/datum/job/J = GetJob(name)
|
||||
if(!J) continue
|
||||
J.total_positions = text2num(value)
|
||||
J.spawn_positions = text2num(value)
|
||||
if(name == "AI" || name == "Cyborg")//I dont like this here but it will do for now
|
||||
J.total_positions = 0
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
/datum/subsystem/job/proc/HandleFeedbackGathering()
|
||||
for(var/datum/job/job in occupations)
|
||||
var/tmp_str = "|[job.title]|"
|
||||
|
||||
var/level1 = 0 //high
|
||||
var/level2 = 0 //medium
|
||||
var/level3 = 0 //low
|
||||
var/level4 = 0 //never
|
||||
var/level5 = 0 //banned
|
||||
var/level6 = 0 //account too young
|
||||
for(var/mob/new_player/player in player_list)
|
||||
if(!(player.ready && player.mind && !player.mind.assigned_role))
|
||||
continue //This player is not ready
|
||||
if(jobban_isbanned(player, job.title))
|
||||
level5++
|
||||
continue
|
||||
if(!job.player_old_enough(player.client))
|
||||
level6++
|
||||
continue
|
||||
if(player.client.prefs.GetJobDepartment(job, 1) & job.flag)
|
||||
level1++
|
||||
else if(player.client.prefs.GetJobDepartment(job, 2) & job.flag)
|
||||
level2++
|
||||
else if(player.client.prefs.GetJobDepartment(job, 3) & job.flag)
|
||||
level3++
|
||||
else level4++ //not selected
|
||||
|
||||
tmp_str += "HIGH=[level1]|MEDIUM=[level2]|LOW=[level3]|NEVER=[level4]|BANNED=[level5]|YOUNG=[level6]|-"
|
||||
feedback_add_details("job_preferences",tmp_str)
|
||||
@@ -0,0 +1,130 @@
|
||||
var/datum/subsystem/lighting/SSlighting
|
||||
|
||||
#define MC_AVERAGE(average, current) (0.8*(average) + 0.2*(current))
|
||||
#define LIGHTING_ICON 'icons/effects/ss13_dark_alpha6.dmi'
|
||||
#define LIGHTING_LAYER 10 //Drawing layer for lighting overlays
|
||||
|
||||
/datum/subsystem/lighting
|
||||
name = "Lighting"
|
||||
wait = 5
|
||||
priority = 1
|
||||
|
||||
var/list/lighting_images = list() //replaces lighting_states (use lighting_images.len) ~carn
|
||||
var/list/lights = list() //list of all datum/light_source
|
||||
var/lights_workload = 0 //stats on the largest number of lights (max lights.len)
|
||||
var/list/changed_turfs = list() //list of all turfs which need moving to a new lighting subarea
|
||||
var/changed_turfs_workload = 0 //stats on the largest number of turfs changed (max changed_turfs.len)
|
||||
|
||||
|
||||
/datum/subsystem/lighting/New()
|
||||
NEW_SS_GLOBAL(SSlighting)
|
||||
|
||||
//cache lighting images
|
||||
if(!lighting_images.len)
|
||||
for(var/icon_state in icon_states(LIGHTING_ICON))
|
||||
lighting_images += image(LIGHTING_ICON, null, icon_state, LIGHTING_LAYER)
|
||||
|
||||
return ..()
|
||||
|
||||
|
||||
/datum/subsystem/lighting/stat_entry()
|
||||
stat(name, "[round(cost,0.001)]ds (CPU:[round(cpu,1)]%) L:[round(lights_workload,1)]/T:[round(changed_turfs_workload,1)]")
|
||||
|
||||
|
||||
//Workhorse of lighting. It cycles through each light to see which ones need their effects updating. It updates their
|
||||
//effects and then processes every turf in the queue, moving the turfs to the corresponing lighting sub-area.
|
||||
//All queue lists prune themselves, which will cause lights with no luminosity to be garbage collected (cheaper and safer
|
||||
//than deleting them).
|
||||
//By using queues we are ensuring we don't perform more updates than are necessary
|
||||
/datum/subsystem/lighting/fire()
|
||||
lights_workload = MC_AVERAGE(lights_workload, lights.len)
|
||||
var/i=1
|
||||
for(var/thing in lights)
|
||||
if(thing && !thing:check()) //yes, cry that I'm using the : operator, it's much faster looping like this. And this gets called a lot. Dealwithit.
|
||||
++i
|
||||
continue
|
||||
lights.Cut(i, i+1)
|
||||
|
||||
changed_turfs_workload = MC_AVERAGE(changed_turfs_workload, changed_turfs.len)
|
||||
for(var/thing in changed_turfs)
|
||||
if(thing && thing:lighting_changed)
|
||||
thing:shift_to_subarea()
|
||||
changed_turfs.Cut()
|
||||
|
||||
|
||||
//same as above except it attempts to shift ALL turfs in the world regardless of lighting_changed status
|
||||
//Does not loop. Should be run prior to process() being called for the first time.
|
||||
//Note: if we get additional z-levels at runtime (e.g. if the gateway thin ever gets finished) we can initialize specific
|
||||
//z-levels with the z_level argument
|
||||
/datum/subsystem/lighting/Initialize(timeofday, z_level)
|
||||
var/i=1
|
||||
for(var/thing in lights)
|
||||
if(thing && !thing:check())
|
||||
++i
|
||||
continue
|
||||
lights.Cut(i, i+1)
|
||||
|
||||
var/z_start = 1
|
||||
var/z_finish = world.maxz
|
||||
if(1 <= z_level && z_level <= world.maxz)
|
||||
z_level = round(z_level)
|
||||
z_start = z_level
|
||||
z_finish = z_level
|
||||
|
||||
for(var/z=z_start, z<=z_finish, ++z)
|
||||
for(var/x=1, x<=world.maxx, ++x)
|
||||
for(var/y=1, y<=world.maxy, ++y)
|
||||
var/turf/T = locate(x,y,z)
|
||||
if(T)
|
||||
T.shift_to_subarea()
|
||||
|
||||
if(z_level)
|
||||
//we need to loop through to clear only shifted turfs from the list. or we will cause errors
|
||||
i=1
|
||||
for(var/thing in changed_turfs)
|
||||
if(thing && thing:z < z_start && z_finish < thing:z)
|
||||
++i
|
||||
continue
|
||||
changed_turfs.Cut(i, i+1)
|
||||
else
|
||||
changed_turfs.Cut()
|
||||
|
||||
..()
|
||||
|
||||
//Used to strip valid information from an existing instance and transfer it to the replacement. i.e. when a crash occurs
|
||||
//It works by using spawn(-1) to transfer the data, if there is a runtime the data does not get transfered but the loop
|
||||
//does not crash
|
||||
/datum/subsystem/lighting/Recover()
|
||||
if(!istype(SSlighting.changed_turfs))
|
||||
SSlighting.changed_turfs = list()
|
||||
if(!istype(SSlighting.lights))
|
||||
SSlighting.lights = list()
|
||||
|
||||
if(istype(SSlighting.lighting_images))
|
||||
lighting_images = SSlighting.lighting_images
|
||||
|
||||
for(var/datum/light_source/L in SSlighting.lights)
|
||||
spawn(-1) //so we don't crash the loop (inefficient)
|
||||
L.check()
|
||||
lights += L //If we didn't runtime then this will get transferred over
|
||||
|
||||
for(var/turf/T in changed_turfs)
|
||||
if(T.lighting_changed)
|
||||
spawn(-1)
|
||||
T.shift_to_subarea()
|
||||
|
||||
var/msg = "## DEBUG: [time2text(world.timeofday)] [name] subsystem restarted. Reports:\n"
|
||||
for(var/varname in SSlighting.vars)
|
||||
switch(varname)
|
||||
if("tag","bestF","type","parent_type","vars") continue
|
||||
else
|
||||
var/varval1 = SSlighting.vars[varname]
|
||||
var/varval2 = vars[varname]
|
||||
if(istype(varval1,/list))
|
||||
varval1 = "/list([length(varval1)])"
|
||||
varval2 = "/list([length(varval2)])"
|
||||
msg += "\t [varname] = [varval1] -> [varval2]\n"
|
||||
world.log << msg
|
||||
|
||||
#undef LIGHTING_ICON
|
||||
#undef LIGHTING_LAYER
|
||||
@@ -0,0 +1,31 @@
|
||||
var/datum/subsystem/machines/SSmachine
|
||||
|
||||
/datum/subsystem/machines
|
||||
name = "Machines"
|
||||
priority = 9
|
||||
|
||||
var/list/processing = list()
|
||||
|
||||
|
||||
/datum/subsystem/machines/Initialize()
|
||||
fire()
|
||||
..()
|
||||
|
||||
|
||||
/datum/subsystem/machines/New()
|
||||
NEW_SS_GLOBAL(SSmachine)
|
||||
|
||||
|
||||
/datum/subsystem/machines/stat_entry()
|
||||
stat(name, "[round(cost,0.001)]ds (CPU:[round(cpu,1)]%)\t[processing.len]")
|
||||
|
||||
|
||||
/datum/subsystem/machines/fire()
|
||||
var/seconds = wait * 0.1
|
||||
for(var/thing in processing)
|
||||
if(thing && (thing:process(seconds) != PROCESS_KILL))
|
||||
if(thing:use_power)
|
||||
thing:auto_use_power()
|
||||
continue
|
||||
processing.Remove(thing)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
var/datum/subsystem/mobs/SSmob
|
||||
|
||||
/datum/subsystem/mobs
|
||||
name = "Mobs"
|
||||
priority = 4
|
||||
|
||||
|
||||
/datum/subsystem/mobs/New()
|
||||
NEW_SS_GLOBAL(SSmob)
|
||||
|
||||
|
||||
/datum/subsystem/mobs/stat_entry()
|
||||
stat(name, "[round(cost,0.001)]ds (CPU:[round(cpu,1)]%) [mob_list.len]")
|
||||
|
||||
|
||||
/datum/subsystem/mobs/fire()
|
||||
var/seconds = wait * 0.1
|
||||
var/i=1
|
||||
for(var/thing in mob_list)
|
||||
if(thing)
|
||||
thing:Life(seconds)
|
||||
++i
|
||||
continue
|
||||
mob_list.Cut(i, i+1)
|
||||
@@ -0,0 +1,30 @@
|
||||
var/datum/subsystem/nano/SSnano
|
||||
|
||||
/datum/subsystem/nano
|
||||
name = "NanoUI"
|
||||
can_fire = 1
|
||||
wait = 5
|
||||
priority = 16
|
||||
|
||||
var/list/open_uis = list() //a list of current open /nanoui UIs, grouped by src_object and ui_key
|
||||
var/list/processing_uis = list() //a list of current open /nanoui UIs, not grouped, for use in processing
|
||||
|
||||
|
||||
/datum/subsystem/nano/New()
|
||||
NEW_SS_GLOBAL(SSnano)
|
||||
|
||||
|
||||
/datum/subsystem/nano/stat_entry()
|
||||
stat(name, "[round(cost,0.001)]ds (CPU:[round(cpu,1)]%) [processing_uis.len]")
|
||||
|
||||
|
||||
/datum/subsystem/nano/fire()
|
||||
var/i=1
|
||||
for(var/thing in SSnano.processing_uis)
|
||||
if(thing)
|
||||
var/datum/nanoui/ui = thing
|
||||
if(ui.src_object && ui.user)
|
||||
ui.process()
|
||||
++i
|
||||
continue
|
||||
processing_uis.Cut(i, i+1)
|
||||
@@ -0,0 +1,42 @@
|
||||
var/datum/subsystem/objects/SSobj
|
||||
|
||||
/datum/subsystem/objects
|
||||
name = "Objects"
|
||||
priority = 12
|
||||
|
||||
var/list/processing = list()
|
||||
|
||||
/datum/subsystem/objects/New()
|
||||
NEW_SS_GLOBAL(SSobj)
|
||||
|
||||
/datum/subsystem/objects/Initialize()
|
||||
for(var/atom/movable/AM in world)
|
||||
AM.initialize()
|
||||
for(var/turf/simulated/floor/F in world)
|
||||
F.MakeDirty()
|
||||
..()
|
||||
|
||||
|
||||
/datum/subsystem/objects/stat_entry()
|
||||
stat(name, "[round(cost,0.001)]ds\t(CPU:[round(cpu,1)]%)\t[processing.len]")
|
||||
|
||||
|
||||
/datum/subsystem/objects/fire()
|
||||
var/i=1
|
||||
for(var/thing in SSobj.processing)
|
||||
if(thing)
|
||||
thing:process(wait)
|
||||
++i
|
||||
continue
|
||||
SSobj.processing.Cut(i, i+1)
|
||||
|
||||
|
||||
/* This is FPSS13 code, has not yet been ported/implemented
|
||||
/obj/New()
|
||||
..()
|
||||
if(map_ready)
|
||||
spawn(0)
|
||||
if(garbage_collecting)
|
||||
return
|
||||
initialize()
|
||||
*/
|
||||
@@ -0,0 +1,36 @@
|
||||
var/datum/subsystem/pipenets/SSpipe
|
||||
|
||||
/datum/subsystem/pipenets
|
||||
name = "Pipenets"
|
||||
priority = 11
|
||||
|
||||
var/list/networks = list()
|
||||
|
||||
/datum/subsystem/pipenets/New()
|
||||
NEW_SS_GLOBAL(SSpipe)
|
||||
|
||||
/datum/subsystem/pipenets/Initialize()
|
||||
set background = BACKGROUND_ENABLED
|
||||
|
||||
//is it possible to combine all these procs into initialize() ??
|
||||
for(var/obj/machinery/atmospherics/M in world)
|
||||
M.build_network()
|
||||
|
||||
for(var/obj/machinery/atmospherics/unary/U in world)
|
||||
if(istype(U, /obj/machinery/atmospherics/unary/vent_pump))
|
||||
var/obj/machinery/atmospherics/unary/vent_pump/V = U
|
||||
V.broadcast_status()
|
||||
else if(istype(U, /obj/machinery/atmospherics/unary/vent_scrubber))
|
||||
var/obj/machinery/atmospherics/unary/vent_scrubber/V = U
|
||||
V.broadcast_status()
|
||||
|
||||
..()
|
||||
|
||||
/datum/subsystem/pipenets/fire()
|
||||
var/i=1
|
||||
for(var/thing in networks)
|
||||
if(thing)
|
||||
thing:process()
|
||||
++i
|
||||
continue
|
||||
networks.Cut(i, i+1)
|
||||
@@ -0,0 +1,18 @@
|
||||
var/datum/subsystem/power/SSpower
|
||||
|
||||
/datum/subsystem/power
|
||||
name = "Power"
|
||||
priority = 10
|
||||
|
||||
var/list/powernets = list()
|
||||
|
||||
/datum/subsystem/power/New()
|
||||
NEW_SS_GLOBAL(SSpower)
|
||||
|
||||
/datum/subsystem/power/Initialize()
|
||||
makepowernets()
|
||||
..()
|
||||
|
||||
/datum/subsystem/power/fire()
|
||||
for(var/datum/powernet/Powernet in powernets)
|
||||
Powernet.reset()
|
||||
@@ -0,0 +1,46 @@
|
||||
var/datum/subsystem/radio/radio_controller
|
||||
|
||||
/datum/subsystem/radio
|
||||
name = "Radio"
|
||||
priority = 18
|
||||
|
||||
var/list/datum/radio_frequency/frequencies = list()
|
||||
|
||||
/datum/subsystem/radio/New()
|
||||
NEW_SS_GLOBAL(radio_controller)
|
||||
|
||||
/datum/subsystem/radio/proc/add_object(obj/device as obj, var/new_frequency as num, var/filter = null as text|null)
|
||||
var/f_text = num2text(new_frequency)
|
||||
var/datum/radio_frequency/frequency = frequencies[f_text]
|
||||
|
||||
if(!frequency)
|
||||
frequency = new
|
||||
frequency.frequency = new_frequency
|
||||
frequencies[f_text] = frequency
|
||||
|
||||
frequency.add_listener(device, filter)
|
||||
return frequency
|
||||
|
||||
/datum/subsystem/radio/proc/remove_object(obj/device, old_frequency)
|
||||
var/f_text = num2text(old_frequency)
|
||||
var/datum/radio_frequency/frequency = frequencies[f_text]
|
||||
|
||||
if(frequency)
|
||||
frequency.remove_listener(device)
|
||||
|
||||
if(frequency.devices.len == 0)
|
||||
del(frequency)
|
||||
frequencies -= f_text
|
||||
|
||||
return 1
|
||||
|
||||
/datum/subsystem/radio/proc/return_frequency(var/new_frequency as num)
|
||||
var/f_text = num2text(new_frequency)
|
||||
var/datum/radio_frequency/frequency = frequencies[f_text]
|
||||
|
||||
if(!frequency)
|
||||
frequency = new
|
||||
frequency.frequency = new_frequency
|
||||
frequencies[f_text] = frequency
|
||||
|
||||
return frequency
|
||||
@@ -0,0 +1,18 @@
|
||||
/datum/subsystem/server_maint
|
||||
name = "Server Tasks"
|
||||
wait = 6000
|
||||
priority = 19
|
||||
|
||||
/datum/subsystem/server_maint/fire()
|
||||
//handle kicking inactive players
|
||||
if(config.kick_inactive > 0)
|
||||
for(var/client/C in clients)
|
||||
if(C.is_afk(INACTIVITY_KICK))
|
||||
if(!istype(C.mob, /mob/dead))
|
||||
log_access("AFK: [key_name(C)]")
|
||||
C << "<span class='danger'>You have been inactive for more than 10 minutes and have been disconnected.</span>"
|
||||
del(C)
|
||||
|
||||
if(config.sql_enabled)
|
||||
sql_poll_players()
|
||||
sql_poll_admins()
|
||||
@@ -0,0 +1,325 @@
|
||||
var/datum/subsystem/shuttle/SSshuttle
|
||||
|
||||
/datum/subsystem/shuttle
|
||||
name = "Shuttles"
|
||||
wait = 10
|
||||
priority = 3
|
||||
|
||||
var/list/mobile = list()
|
||||
var/list/stationary = list()
|
||||
var/list/transit = list()
|
||||
|
||||
//emergency shuttle stuff
|
||||
var/obj/docking_port/mobile/emergency/emergency
|
||||
var/emergencyCallTime = 6000 //time taken for emergency shuttle to reach the station when called (in deciseconds)
|
||||
var/emergencyDockTime = 1800 //time taken for emergency shuttle to leave again once it has docked (in deciseconds)
|
||||
var/emergencyEscapeTime = 1200 //time taken for emergency shuttle to reach a safe distance after leaving station (in deciseconds)
|
||||
var/area/emergencyLastCallLoc
|
||||
var/emergencyAlwaysFakeRecall
|
||||
var/emergencyFakeRecall
|
||||
|
||||
//supply shuttle stuff
|
||||
var/obj/docking_port/mobile/supply/supply
|
||||
var/ordernum = 1 //order number given to next order
|
||||
var/points = 50 //number of trade-points we have
|
||||
var/points_per_decisecond = 0.005 //points gained every decisecond
|
||||
var/points_per_slip = 2 //points gained per slip returned
|
||||
var/points_per_crate = 5 //points gained per crate returned
|
||||
var/points_per_intel = 100 //points gained per intel returned
|
||||
var/points_per_plasma = 5 //points gained per plasma returned
|
||||
var/centcom_message = "" //Remarks from Centcom on how well you checked the last order.
|
||||
var/list/discoveredPlants = list() //Unique typepaths for unusual things we've already sent CentComm, associated with their potencies
|
||||
var/list/shoppinglist = list()
|
||||
var/list/requestlist = list()
|
||||
var/list/supply_packs = list()
|
||||
var/datum/round_event/shuttle_loan/shuttle_loan
|
||||
var/sold_atoms = ""
|
||||
|
||||
|
||||
/datum/subsystem/shuttle/New()
|
||||
NEW_SS_GLOBAL(SSshuttle)
|
||||
|
||||
|
||||
/datum/subsystem/shuttle/Initialize()
|
||||
if(!emergency)
|
||||
WARNING("No /obj/docking_port/mobile/emergency placed on the map!")
|
||||
if(!supply)
|
||||
WARNING("No /obj/docking_port/mobile/supply placed on the map!")
|
||||
|
||||
ordernum = rand(1,9000)
|
||||
|
||||
for(var/typepath in typesof(/datum/supply_packs))
|
||||
if(typepath == /datum/supply_packs)
|
||||
continue
|
||||
var/datum/supply_packs/P = new typepath()
|
||||
if(P.name == "HEADER") continue // To filter out group headers
|
||||
supply_packs["[P.type]"] = P
|
||||
|
||||
..()
|
||||
|
||||
|
||||
/datum/subsystem/shuttle/fire()
|
||||
points += points_per_decisecond * wait
|
||||
|
||||
var/i=1
|
||||
for(var/thing in mobile)
|
||||
if(thing)
|
||||
var/obj/docking_port/mobile/P = thing
|
||||
P.check()
|
||||
++i
|
||||
continue
|
||||
mobile.Cut(i, i+1)
|
||||
|
||||
/datum/subsystem/shuttle/proc/getShuttle(id)
|
||||
for(var/obj/docking_port/mobile/M in mobile)
|
||||
if(M.id == id)
|
||||
return M
|
||||
WARNING("couldn't find shuttle with id: [id]")
|
||||
|
||||
/datum/subsystem/shuttle/proc/getDock(id)
|
||||
for(var/obj/docking_port/stationary/S in stationary)
|
||||
if(S.id == id)
|
||||
return S
|
||||
WARNING("couldn't find dock with id: [id]")
|
||||
|
||||
/datum/subsystem/shuttle/proc/requestEvac(mob/user, call_reason)
|
||||
if(!emergency)
|
||||
ERROR("There is no emergency shuttle! The game will be unresolvable. This is likely due to a mapping error")
|
||||
return
|
||||
|
||||
if(world.time - round_start_time < config.shuttle_refuel_delay)
|
||||
user << "The emergency shuttle is refueling. Please wait another [abs(round(((world.time - round_start_time) - config.shuttle_refuel_delay)/600))] minutes before trying again."
|
||||
return
|
||||
|
||||
switch(emergency.mode)
|
||||
if(SHUTTLE_RECALL)
|
||||
user << "The emergency shuttle may not be called while returning to Centcom."
|
||||
return
|
||||
if(SHUTTLE_CALL)
|
||||
user << "The emergency shuttle is already on its way."
|
||||
return
|
||||
if(SHUTTLE_DOCKED)
|
||||
user << "The emergency shuttle is already here."
|
||||
return
|
||||
if(SHUTTLE_ESCAPE)
|
||||
user << "The emergency shuttle is moving away to a safe distance."
|
||||
return
|
||||
|
||||
call_reason = strip_html_properly(trim(call_reason))
|
||||
|
||||
if(length(call_reason) < CALL_SHUTTLE_REASON_LENGTH)
|
||||
user << "You must provide a reason."
|
||||
return
|
||||
|
||||
var/area/signal_origin = get_area(user)
|
||||
var/emergency_reason = "\nNature of emergency:\n\n[call_reason]"
|
||||
if(seclevel2num(get_security_level()) == SEC_LEVEL_RED) // There is a serious threat we gotta move no time to give them five minutes.
|
||||
emergency.request(null, 0.5, signal_origin, emergency_reason, 1)
|
||||
else
|
||||
emergency.request(null, 1, signal_origin, emergency_reason, 0)
|
||||
|
||||
log_game("[key_name(user)] has called the shuttle.")
|
||||
message_admins("[key_name_admin(user)] has called the shuttle.")
|
||||
|
||||
return
|
||||
|
||||
/datum/subsystem/shuttle/proc/cancelEvac(mob/user)
|
||||
if(emergency.mode != SHUTTLE_CALL)
|
||||
return
|
||||
|
||||
if(ticker.mode.name == "meteor")
|
||||
return
|
||||
|
||||
if((seclevel2num(get_security_level()) == SEC_LEVEL_RED))
|
||||
if(emergency.timeLeft(1) < emergencyCallTime * 0.25)
|
||||
return
|
||||
else if(emergency.timeLeft(1) < emergencyCallTime * 0.5)
|
||||
return
|
||||
|
||||
emergency.cancel(get_area(user))
|
||||
log_game("[key_name(user)] has recalled the shuttle.")
|
||||
message_admins("[key_name_admin(user)] has recalled the shuttle.")
|
||||
return 1
|
||||
|
||||
/datum/subsystem/shuttle/proc/autoEvac()
|
||||
var/callShuttle = 1
|
||||
|
||||
for(var/thing in shuttle_caller_list)
|
||||
if(istype(thing, /mob/living/silicon/ai))
|
||||
var/mob/living/silicon/ai/AI = thing
|
||||
if(AI.stat || !AI.client)
|
||||
continue
|
||||
else if(istype(thing, /obj/machinery/computer/communications))
|
||||
var/obj/machinery/computer/communications/C = thing
|
||||
if(C.stat & BROKEN)
|
||||
continue
|
||||
|
||||
var/turf/T = get_turf(thing)
|
||||
if(T && T.z == 1)
|
||||
callShuttle = 0
|
||||
break
|
||||
|
||||
if(callShuttle)
|
||||
if(emergency.mode < SHUTTLE_DOCKED)
|
||||
emergency.request(null, 2.5)
|
||||
log_game("There is no means of calling the shuttle anymore. Shuttle automatically called.")
|
||||
message_admins("All the communications consoles were destroyed and all AIs are inactive. Shuttle called.")
|
||||
|
||||
//try to move/request to dockHome if possible, otherwise dockAway. Mainly used for admin buttons
|
||||
/datum/subsystem/shuttle/proc/toggleShuttle(shuttleId, dockHome, dockAway, timed)
|
||||
var/obj/docking_port/mobile/M = getShuttle(shuttleId)
|
||||
if(!M)
|
||||
return 1
|
||||
var/obj/docking_port/stationary/dockedAt = M.get_docked()
|
||||
var/destination = dockHome
|
||||
if(dockedAt && dockedAt.id == dockHome)
|
||||
destination = dockAway
|
||||
if(timed)
|
||||
if(M.request(getDock(destination)))
|
||||
return 2
|
||||
else
|
||||
if(M.dock(getDock(destination)))
|
||||
return 2
|
||||
return 0 //dock successful
|
||||
|
||||
|
||||
/datum/subsystem/shuttle/proc/moveShuttle(shuttleId, dockId, timed)
|
||||
var/obj/docking_port/mobile/M = getShuttle(shuttleId)
|
||||
if(!M)
|
||||
return 1
|
||||
if(timed)
|
||||
if(M.request(getDock(dockId)))
|
||||
return 2
|
||||
else
|
||||
if(M.dock(getDock(dockId)))
|
||||
return 2
|
||||
return 0 //dock successful
|
||||
|
||||
|
||||
/*
|
||||
/proc/push_mob_back(var/mob/living/L, var/dir)
|
||||
if(iscarbon(L) && isturf(L.loc))
|
||||
if(prob(88))
|
||||
var/turf/T = get_step(L, dir)
|
||||
if(T)
|
||||
for(var/obj/O in T) // For doors and such (kinda ugly but we can't have people opening doors)
|
||||
if(!O.CanPass(L, L.loc, 1))
|
||||
return
|
||||
L.Move(get_step(L, dir), dir)
|
||||
*/
|
||||
|
||||
/datum/supply_order
|
||||
var/ordernum
|
||||
var/datum/supply_packs/object = null
|
||||
var/orderedby = null
|
||||
var/orderedbyRank
|
||||
var/comment = null
|
||||
|
||||
/datum/supply_order/proc/generateRequisition(atom/_loc)
|
||||
if(!object)
|
||||
return
|
||||
|
||||
var/obj/item/weapon/paper/reqform = new /obj/item/weapon/paper(_loc)
|
||||
reqform.name = "requisition form - [object.name]"
|
||||
reqform.info += "<h3>[station_name] Supply Requisition Form</h3><hr>"
|
||||
reqform.info += "INDEX: #[ordernum]<br>"
|
||||
reqform.info += "REQUESTED BY: [orderedby]<br>"
|
||||
reqform.info += "RANK: [orderedbyRank]<br>"
|
||||
reqform.info += "REASON: [comment]<br>"
|
||||
reqform.info += "SUPPLY CRATE TYPE: [object.name]<br>"
|
||||
reqform.info += "ACCESS RESTRICTION: [replacetext(get_access_desc(object.access))]<br>"
|
||||
reqform.info += "CONTENTS:<br>"
|
||||
reqform.info += object.manifest
|
||||
reqform.info += "<hr>"
|
||||
reqform.info += "STAMP BELOW TO APPROVE THIS REQUISITION:<br>"
|
||||
|
||||
reqform.update_icon() //Fix for appearing blank when printed.
|
||||
|
||||
return reqform
|
||||
|
||||
/datum/supply_order/proc/createObject(atom/_loc, errors=0)
|
||||
if(!object)
|
||||
return
|
||||
|
||||
//create the crate
|
||||
var/atom/Crate = new object.containertype(_loc)
|
||||
Crate.name = "[object.containername] [comment ? "([comment])":"" ]"
|
||||
if(object.access)
|
||||
Crate:req_access = list(text2num(object.access))
|
||||
|
||||
//create the manifest slip
|
||||
var/obj/item/weapon/paper/manifest/slip = new /obj/item/weapon/paper/manifest()
|
||||
slip.erroneous = errors
|
||||
slip.points = object.cost
|
||||
slip.ordernumber = ordernum
|
||||
|
||||
var/stationName = (errors & MANIFEST_ERROR_NAME) ? new_station_name() : station_name()
|
||||
var/packagesAmt = SSshuttle.shoppinglist.len + ((errors & MANIFEST_ERROR_COUNT) ? 0 : rand(1,2))
|
||||
|
||||
slip.info = "<h3>[command_name()] Shipping Manifest</h3><hr><br>"
|
||||
slip.info +="Order #[ordernum]<br>"
|
||||
slip.info +="Destination: [stationName]<br>"
|
||||
slip.info +="[packagesAmt] PACKAGES IN THIS SHIPMENT<br>"
|
||||
slip.info +="CONTENTS:<br><ul>"
|
||||
|
||||
//we now create the actual contents
|
||||
var/list/contains
|
||||
if(istype(object, /datum/supply_packs/misc/randomised))
|
||||
var/datum/supply_packs/misc/randomised/SO = object
|
||||
contains = list()
|
||||
if(object.contains.len)
|
||||
for(var/j=1, j<=SO.num_contained, j++)
|
||||
contains += pick(object.contains)
|
||||
else
|
||||
contains = object.contains
|
||||
|
||||
for(var/typepath in contains)
|
||||
if(!typepath) continue
|
||||
var/atom/A = new typepath(Crate)
|
||||
if(object.amount && A.vars.Find("amount") && A:amount)
|
||||
A:amount = object.amount
|
||||
slip.info += "<li>[A.name]</li>" //add the item to the manifest (even if it was misplaced)
|
||||
|
||||
if((errors & MANIFEST_ERROR_ITEM))
|
||||
//secure and large crates cannot lose items
|
||||
if(findtext("[object.containertype]", "/secure/") || findtext("[object.containertype]","/largecrate/"))
|
||||
errors &= ~MANIFEST_ERROR_ITEM
|
||||
else
|
||||
var/lostAmt = max(round(Crate.contents.len/10), 1)
|
||||
//lose some of the items
|
||||
while(--lostAmt >= 0)
|
||||
qdel(pick(Crate.contents))
|
||||
|
||||
//manifest finalisation
|
||||
slip.info += "</ul><br>"
|
||||
slip.info += "CHECK CONTENTS AND STAMP BELOW THE LINE TO CONFIRM RECEIPT OF GOODS<hr>" // And now this is actually meaningful.
|
||||
slip.loc = Crate
|
||||
|
||||
return Crate
|
||||
|
||||
/datum/subsystem/shuttle/proc/generateSupplyOrder(packId, _orderedby, _orderedbyRank, _comment)
|
||||
if(!packId)
|
||||
return
|
||||
var/datum/supply_packs/P = supply_packs["[packId]"]
|
||||
if(!P)
|
||||
return
|
||||
|
||||
var/datum/supply_order/O = new()
|
||||
O.ordernum = ordernum++
|
||||
O.object = P
|
||||
O.orderedby = _orderedby
|
||||
O.orderedbyRank = _orderedbyRank
|
||||
O.comment = _comment
|
||||
|
||||
shoppinglist += O
|
||||
|
||||
return O
|
||||
|
||||
/*
|
||||
/datum/subsystem/shuttle/proc/getShuttleFromArea(area/A)
|
||||
if(!A)
|
||||
return
|
||||
for(var/obj/docking_port/mobile/M in SSshuttle.mobile)
|
||||
if(M.areaInstance == A)
|
||||
return M
|
||||
*/
|
||||
@@ -0,0 +1,141 @@
|
||||
/obj/docking_port/mobile/emergency
|
||||
name = "emergency shuttle"
|
||||
id = "emergency"
|
||||
|
||||
dwidth = 9
|
||||
width = 22
|
||||
height = 11
|
||||
dir = 4
|
||||
travelDir = -90
|
||||
|
||||
/obj/docking_port/mobile/emergency/New()
|
||||
..()
|
||||
SSshuttle.emergency = src
|
||||
|
||||
/obj/docking_port/mobile/emergency/timeLeft(divisor)
|
||||
if(divisor <= 0)
|
||||
divisor = 10
|
||||
if(!timer)
|
||||
return round(SSshuttle.emergencyCallTime/divisor, 1)
|
||||
|
||||
var/dtime = world.time - timer
|
||||
switch(mode)
|
||||
if(SHUTTLE_ESCAPE)
|
||||
dtime = max(SSshuttle.emergencyEscapeTime - dtime, 0)
|
||||
if(SHUTTLE_DOCKED)
|
||||
dtime = max(SSshuttle.emergencyDockTime - dtime, 0)
|
||||
else
|
||||
dtime = max(SSshuttle.emergencyCallTime - dtime, 0)
|
||||
return round(dtime/divisor, 1)
|
||||
|
||||
/obj/docking_port/mobile/emergency/request(obj/docking_port/stationary/S, coefficient=1, area/signalOrigin, reason, redAlert)
|
||||
SSshuttle.emergencyCallTime = initial(SSshuttle.emergencyCallTime) * coefficient
|
||||
switch(mode)
|
||||
if(SHUTTLE_RECALL)
|
||||
mode = SHUTTLE_CALL
|
||||
timer = world.time - timeLeft(1)
|
||||
if(SHUTTLE_IDLE)
|
||||
mode = SHUTTLE_CALL
|
||||
timer = world.time
|
||||
if(SHUTTLE_CALL)
|
||||
if(world.time < timer) //this is just failsafe
|
||||
timer = world.time
|
||||
else
|
||||
return
|
||||
|
||||
SSshuttle.emergencyLastCallLoc = signalOrigin
|
||||
|
||||
priority_announce("The emergency shuttle has been called. [redAlert ? "Red Alert state confirmed: Dispatching priority shuttle. " : "" ]It will arrive in [timeLeft(600)] minutes.[reason][SSshuttle.emergencyLastCallLoc ? "\n\nCall signal traced. Results can be viewed on any communcations console." : "" ]", null, 'sound/AI/shuttlecalled.ogg', "Priority")
|
||||
|
||||
if(SSshuttle.emergencyAlwaysFakeRecall)
|
||||
if((seclevel2num(get_security_level()) == SEC_LEVEL_RED))
|
||||
SSshuttle.emergencyFakeRecall = rand(0.2, 0.5)
|
||||
else
|
||||
SSshuttle.emergencyFakeRecall = rand(0.5, 0.8)
|
||||
|
||||
|
||||
/obj/docking_port/mobile/emergency/cancel(area/signalOrigin)
|
||||
if(mode != SHUTTLE_CALL)
|
||||
return
|
||||
|
||||
timer = world.time - timeLeft(1)
|
||||
mode = SHUTTLE_RECALL
|
||||
|
||||
if(prob(70))
|
||||
SSshuttle.emergencyLastCallLoc = signalOrigin
|
||||
priority_announce("The emergency shuttle has been recalled.[SSshuttle.emergencyLastCallLoc ? " Recall signal traced. Results can be viewed on any communcations console." : "" ]", null, 'sound/AI/shuttlerecalled.ogg', "Priority")
|
||||
|
||||
/*
|
||||
/obj/docking_port/mobile/emergency/findTransitDock()
|
||||
. = SSshuttle.getDock("emergency_transit")
|
||||
if(.) return .
|
||||
return ..()
|
||||
*/
|
||||
|
||||
|
||||
/obj/docking_port/mobile/emergency/check()
|
||||
if(!timer)
|
||||
return
|
||||
|
||||
var/time_left = timeLeft(1)
|
||||
switch(mode)
|
||||
if(SHUTTLE_RECALL)
|
||||
if(time_left <= 0)
|
||||
mode = SHUTTLE_IDLE
|
||||
timer = 0
|
||||
if(SHUTTLE_CALL)
|
||||
if(time_left < SSshuttle.emergencyFakeRecall * SSshuttle.emergencyCallTime) //recall due to fake recalls
|
||||
cancel()
|
||||
SSshuttle.emergencyFakeRecall = 0
|
||||
else if(time_left <= 0)
|
||||
//move emergency shuttle to station
|
||||
if(dock(SSshuttle.getDock("emergency_home")))
|
||||
setTimer(20)
|
||||
return
|
||||
mode = SHUTTLE_DOCKED
|
||||
timer = world.time
|
||||
if(SHUTTLE_DOCKED)
|
||||
if(time_left <= 0)
|
||||
//move each escape pod to its corresponding transit dock
|
||||
for(var/obj/docking_port/mobile/pod/M in SSshuttle.mobile)
|
||||
M.enterTransit()
|
||||
//now move the actual emergency shuttle to its transit dock
|
||||
enterTransit()
|
||||
mode = SHUTTLE_ESCAPE
|
||||
timer = world.time
|
||||
if(SHUTTLE_ESCAPE)
|
||||
if(time_left <= 0)
|
||||
//move each escape pod to its corresponding escape dock
|
||||
for(var/obj/docking_port/mobile/pod/M in SSshuttle.mobile)
|
||||
M.dock(SSshuttle.getDock("[M.id]_away"))
|
||||
//now move the actual emergency shuttle to centcomm
|
||||
dock(SSshuttle.getDock("emergency_away"))
|
||||
mode = SHUTTLE_ENDGAME
|
||||
timer = 0
|
||||
|
||||
|
||||
/obj/docking_port/mobile/pod
|
||||
name = "escape pod"
|
||||
id = "pod"
|
||||
|
||||
dwidth = 1
|
||||
width = 3
|
||||
height = 4
|
||||
|
||||
New()
|
||||
if(id == "pod")
|
||||
WARNING("[type] id has not been changed from the default. Use the id convention \"pod1\" \"pod2\" etc.")
|
||||
..()
|
||||
|
||||
request()
|
||||
return
|
||||
|
||||
cancel()
|
||||
return
|
||||
|
||||
/*
|
||||
findTransitDock()
|
||||
. = SSshuttle.getDock("[id]_transit")
|
||||
if(.) return .
|
||||
return ..()
|
||||
*/
|
||||
@@ -0,0 +1,541 @@
|
||||
/obj/item/weapon/paper/manifest
|
||||
name = "supply manifest"
|
||||
var/erroneous = 0
|
||||
var/points = 0
|
||||
var/ordernumber = 0
|
||||
|
||||
/obj/docking_port/mobile/supply
|
||||
name = "supply shuttle"
|
||||
id = "supply"
|
||||
callTime = 1200
|
||||
|
||||
dir = 8
|
||||
travelDir = 90
|
||||
width = 12
|
||||
dwidth = 5
|
||||
height = 7
|
||||
|
||||
/obj/docking_port/mobile/supply/New()
|
||||
..()
|
||||
SSshuttle.supply = src
|
||||
|
||||
/obj/docking_port/mobile/supply/canMove()
|
||||
if(z == ZLEVEL_STATION)
|
||||
return forbidden_atoms_check(areaInstance)
|
||||
return ..()
|
||||
|
||||
/obj/docking_port/mobile/supply/request(obj/docking_port/stationary/S)
|
||||
if(mode != SHUTTLE_IDLE)
|
||||
return 2
|
||||
return ..()
|
||||
|
||||
/obj/docking_port/mobile/supply/dock()
|
||||
. = ..()
|
||||
if(.) return .
|
||||
|
||||
buy()
|
||||
sell()
|
||||
|
||||
/obj/docking_port/mobile/supply/proc/buy()
|
||||
if(z != ZLEVEL_STATION) //we only buy when we are -at- the station
|
||||
return 1
|
||||
|
||||
if(!SSshuttle.shoppinglist.len)
|
||||
return 2
|
||||
|
||||
var/list/emptyTurfs = list()
|
||||
for(var/turf/simulated/shuttle/T in areaInstance)
|
||||
if(T.density || T.contents.len) continue
|
||||
emptyTurfs += T
|
||||
|
||||
for(var/datum/supply_order/SO in SSshuttle.shoppinglist)
|
||||
if(!SO.object) continue
|
||||
|
||||
var/turf/T = pick_n_take(emptyTurfs) //turf we will place it in
|
||||
if(!T)
|
||||
SSshuttle.shoppinglist.Cut(1, SSshuttle.shoppinglist.Find(SO))
|
||||
return
|
||||
|
||||
var/errors = 0
|
||||
if(prob(5))
|
||||
errors |= MANIFEST_ERROR_COUNT
|
||||
if(prob(5))
|
||||
errors |= MANIFEST_ERROR_NAME
|
||||
if(prob(5))
|
||||
errors |= MANIFEST_ERROR_ITEM
|
||||
SO.createObject(T, errors)
|
||||
|
||||
SSshuttle.shoppinglist.Cut()
|
||||
|
||||
|
||||
/obj/docking_port/mobile/supply/proc/sell()
|
||||
if(z != ZLEVEL_CENTCOM) //we only sell when we are -at- centcomm
|
||||
return 1
|
||||
|
||||
var/plasma_count = 0
|
||||
var/intel_count = 0
|
||||
var/crate_count = 0
|
||||
|
||||
var/msg = ""
|
||||
var/pointsEarned
|
||||
|
||||
for(var/atom/movable/MA in areaInstance)
|
||||
SSshuttle.sold_atoms += " [MA.name]"
|
||||
if(MA.anchored) continue
|
||||
|
||||
// Must be in a crate (or a critter crate)!
|
||||
if(istype(MA,/obj/structure/closet/crate) || istype(MA,/obj/structure/closet/critter))
|
||||
SSshuttle.sold_atoms += ":"
|
||||
++crate_count
|
||||
|
||||
var/find_slip = 1
|
||||
for(var/thing in MA)
|
||||
// Sell manifests
|
||||
SSshuttle.sold_atoms += " [thing:name]"
|
||||
if(find_slip && istype(thing,/obj/item/weapon/paper/manifest))
|
||||
var/obj/item/weapon/paper/manifest/slip = thing
|
||||
// TODO: Check for a signature, too.
|
||||
if(slip.stamped && slip.stamped.len) //yes, the clown stamp will work. clown is the highest authority on the station, it makes sense
|
||||
// Did they mark it as erroneous?
|
||||
var/denied = 0
|
||||
for(var/i=1,i<=slip.stamped.len,i++)
|
||||
if(slip.stamped[i] == /obj/item/weapon/stamp/denied)
|
||||
denied = 1
|
||||
if(slip.erroneous && denied) // Caught a mistake by Centcom (IDEA: maybe Centcom rarely gets offended by this)
|
||||
pointsEarned = slip.points - SSshuttle.points_per_crate
|
||||
SSshuttle.points += pointsEarned // For now, give a full refund for paying attention (minus the crate cost)
|
||||
msg += "<font color=green>+[pointsEarned]</font>: Station correctly denied package [slip.ordernumber]: "
|
||||
if(slip.erroneous & MANIFEST_ERROR_NAME)
|
||||
msg += "Destination station incorrect. "
|
||||
else if(slip.erroneous & MANIFEST_ERROR_COUNT)
|
||||
msg += "Packages incorrectly counted. "
|
||||
else if(slip.erroneous & MANIFEST_ERROR_ITEM)
|
||||
msg += "Package incomplete. "
|
||||
msg += "Points refunded.<BR>"
|
||||
else if(!slip.erroneous && !denied) // Approving a proper order awards the relatively tiny points_per_slip
|
||||
SSshuttle.points += SSshuttle.points_per_slip
|
||||
msg += "<font color=green>+[SSshuttle.points_per_slip]</font>: Package [slip.ordernumber] accorded.<BR>"
|
||||
else // You done goofed.
|
||||
if(slip.erroneous)
|
||||
msg += "<font color=red>+0</font>: Station approved package [slip.ordernumber] despite error: "
|
||||
if(slip.erroneous & MANIFEST_ERROR_NAME)
|
||||
msg += "Destination station incorrect."
|
||||
else if(slip.erroneous & MANIFEST_ERROR_COUNT)
|
||||
msg += "Packages incorrectly counted."
|
||||
else if(slip.erroneous & MANIFEST_ERROR_ITEM)
|
||||
msg += "We found unshipped items on our dock."
|
||||
msg += " Be more vigilant.<BR>"
|
||||
else
|
||||
pointsEarned = round(SSshuttle.points_per_crate - slip.points)
|
||||
SSshuttle.points += pointsEarned
|
||||
msg += "<font color=red>[pointsEarned]</font>: Station denied package [slip.ordernumber]. Our records show no fault on our part.<BR>"
|
||||
find_slip = 0
|
||||
continue
|
||||
|
||||
// Sell plasma
|
||||
if(istype(thing, /obj/item/stack/sheet/mineral/plasma))
|
||||
var/obj/item/stack/sheet/mineral/plasma/P = thing
|
||||
plasma_count += P.amount
|
||||
|
||||
// Sell syndicate intel
|
||||
if(istype(thing, /obj/item/documents/syndicate))
|
||||
++intel_count
|
||||
|
||||
if(istype(thing, /obj/item/seeds))
|
||||
var/obj/item/seeds/S = thing
|
||||
if(S.rarity == 0) // Mundane species
|
||||
msg += "<font color=red>+0</font>: We don't need samples of mundane species \"[capitalize(S.species)]\".<BR>"
|
||||
else if(SSshuttle.discoveredPlants[S.type]) // This species has already been sent to CentComm
|
||||
var/potDiff = S.potency - SSshuttle.discoveredPlants[S.type] // Compare it to the previous best
|
||||
if(potDiff > 0) // This sample is better
|
||||
SSshuttle.discoveredPlants[S.type] = S.potency
|
||||
msg += "<font color=green>+[potDiff]</font>: New sample of \"[capitalize(S.species)]\" is superior. Good work.<BR>"
|
||||
SSshuttle.points += potDiff
|
||||
else // This sample is worthless
|
||||
msg += "<font color=red>+0</font>: New sample of \"[capitalize(S.species)]\" is not more potent than existing sample ([SSshuttle.discoveredPlants[S.type]] potency).<BR>"
|
||||
else // This is a new discovery!
|
||||
SSshuttle.discoveredPlants[S.type] = S.potency
|
||||
msg += "<font color=green>+[S.rarity]</font>: New species discovered: \"[capitalize(S.species)]\". Excellent work.<BR>"
|
||||
SSshuttle.points += S.rarity // That's right, no bonus for potency. Send a crappy sample first to "show improvement" later
|
||||
qdel(MA)
|
||||
SSshuttle.sold_atoms += "."
|
||||
|
||||
if(plasma_count > 0)
|
||||
pointsEarned = round(plasma_count * SSshuttle.points_per_plasma)
|
||||
msg += "<font color=green>+[pointsEarned]</font>: Received [plasma_count] unit(s) of exotic material.<BR>"
|
||||
SSshuttle.points += pointsEarned
|
||||
|
||||
if(intel_count > 0)
|
||||
pointsEarned = round(intel_count * SSshuttle.points_per_intel)
|
||||
msg += "<font color=green>+[pointsEarned]</font>: Received [intel_count] article(s) of enemy intelligence.<BR>"
|
||||
SSshuttle.points += pointsEarned
|
||||
|
||||
if(crate_count > 0)
|
||||
pointsEarned = round(crate_count * SSshuttle.points_per_crate)
|
||||
msg += "<font color=green>+[pointsEarned]</font>: Received [crate_count] crate(s).<BR>"
|
||||
SSshuttle.points += pointsEarned
|
||||
|
||||
SSshuttle.centcom_message = msg
|
||||
|
||||
|
||||
/proc/forbidden_atoms_check(atom/A)
|
||||
if(A)
|
||||
if(istype(A,/mob/living))
|
||||
return 1
|
||||
if(istype(A,/obj))
|
||||
if(istype(A,/obj/effect/blob))
|
||||
return 1
|
||||
if(istype(A,/obj/effect/spider/spiderling))
|
||||
return 1
|
||||
if(istype(A,/obj/item/weapon/disk/nuclear))
|
||||
return 1
|
||||
if(istype(A,/obj/machinery/nuclearbomb))
|
||||
return 1
|
||||
if(istype(A,/obj/item/device/radio/beacon))
|
||||
return 1
|
||||
|
||||
for(var/thing in A)
|
||||
if(.(thing))
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
|
||||
/obj/machinery/computer/ordercomp/attack_hand(var/mob/user as mob)
|
||||
if(..())
|
||||
return
|
||||
user.set_machine(src)
|
||||
var/dat
|
||||
if(temp)
|
||||
dat = temp
|
||||
else
|
||||
dat += {"<div class='statusDisplay'>Shuttle Location: [SSshuttle.supply.name]<BR>
|
||||
<HR>Supply Points: [SSshuttle.points]<BR></div>
|
||||
|
||||
<BR>\n<A href='?src=\ref[src];order=categories'>Request items</A><BR><BR>
|
||||
<A href='?src=\ref[src];vieworders=1'>View approved orders</A><BR><BR>
|
||||
<A href='?src=\ref[src];viewrequests=1'>View requests</A><BR><BR>
|
||||
<A href='?src=\ref[user];mach_close=computer'>Close</A>"}
|
||||
|
||||
// Removing the old window method but leaving it here for reference
|
||||
//user << browse(dat, "window=computer;size=575x450")
|
||||
//onclose(user, "computer")
|
||||
|
||||
// Added the new browser window method
|
||||
var/datum/browser/popup = new(user, "computer", "Supply Ordering Console", 575, 450)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.open()
|
||||
return
|
||||
|
||||
/obj/machinery/computer/ordercomp/Topic(href, href_list)
|
||||
if(..())
|
||||
return
|
||||
|
||||
if( isturf(loc) && (in_range(src, usr) || istype(usr, /mob/living/silicon)) )
|
||||
usr.set_machine(src)
|
||||
|
||||
if(href_list["order"])
|
||||
if(href_list["order"] == "categories")
|
||||
//all_supply_groups
|
||||
//Request what?
|
||||
last_viewed_group = "categories"
|
||||
temp = "<div class='statusDisplay'><b>Supply points: [SSshuttle.points]</b><BR>"
|
||||
temp += "<A href='?src=\ref[src];mainmenu=1'>Main Menu</A><BR></div><BR>"
|
||||
temp += "<b>Select a category</b><BR><BR>"
|
||||
for(var/cat in all_supply_groups )
|
||||
temp += "<A href='?src=\ref[src];order=[cat]'>[get_supply_group_name(cat)]</A><BR>"
|
||||
else
|
||||
last_viewed_group = href_list["order"]
|
||||
var/cat = text2num(last_viewed_group)
|
||||
temp = "<div class='statusDisplay'><b>Supply points: [SSshuttle.points]</b><BR>"
|
||||
temp += "<A href='?src=\ref[src];order=categories'>Back to all categories</A><BR></div><BR>"
|
||||
temp += "<b>Request from: [get_supply_group_name(cat)]</b><BR><BR>"
|
||||
for(var/supply_type in SSshuttle.supply_packs )
|
||||
var/datum/supply_packs/N = SSshuttle.supply_packs[supply_type]
|
||||
if(N.hidden || N.contraband || N.group != cat) continue //Have to send the type instead of a reference to
|
||||
temp += "<A href='?src=\ref[src];doorder=[supply_type]'>[N.name]</A> Cost: [N.cost]<BR>" //the obj because it would get caught by the garbage
|
||||
else if (href_list["doorder"])
|
||||
if(world.time < reqtime)
|
||||
say("[world.time - reqtime] seconds remaining until another requisition form may be printed.")
|
||||
return
|
||||
|
||||
//Find the correct supply_pack datum
|
||||
if(!SSshuttle.supply_packs[href_list["doorder"]]) return
|
||||
|
||||
var/timeout = world.time + 600
|
||||
var/reason = stripped_input(usr,"Reason:","Why do you require this item?","")
|
||||
if(world.time > timeout) return
|
||||
if(!reason) return
|
||||
|
||||
var/idname = "*None Provided*"
|
||||
var/idrank = "*None Provided*"
|
||||
if(ishuman(usr))
|
||||
var/mob/living/carbon/human/H = usr
|
||||
idname = H.get_authentification_name()
|
||||
idrank = H.get_assignment()
|
||||
else if(issilicon(usr))
|
||||
idname = usr.real_name
|
||||
|
||||
var/datum/supply_order/O = SSshuttle.generateSupplyOrder(href_list["doorder"], idname, idrank, reason)
|
||||
if(!O) return
|
||||
O.generateRequisition(loc)
|
||||
|
||||
reqtime = (world.time + 5) % 1e5
|
||||
|
||||
temp = "Thanks for your request. The cargo team will process it as soon as possible.<BR>"
|
||||
temp += "<BR><A href='?src=\ref[src];order=[last_viewed_group]'>Back</A> <A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
|
||||
else if (href_list["vieworders"])
|
||||
temp = "<A href='?src=\ref[src];mainmenu=1'>Main Menu</A><BR><BR>Current approved orders: <BR><BR>"
|
||||
for(var/datum/supply_order/SO in SSshuttle.shoppinglist)
|
||||
temp += "[SO.object.name] approved by [SO.orderedby] [SO.comment ? "([SO.comment])":""]<BR>"
|
||||
temp += "<BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
|
||||
else if (href_list["viewrequests"])
|
||||
temp = "Current requests: <BR><BR>"
|
||||
for(var/datum/supply_order/SO in SSshuttle.requestlist)
|
||||
temp += "#[SO.ordernum] - [SO.object.name] requested by [SO.orderedby]<BR>"
|
||||
temp += "<BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
|
||||
else if (href_list["mainmenu"])
|
||||
temp = null
|
||||
|
||||
add_fingerprint(usr)
|
||||
updateUsrDialog()
|
||||
return
|
||||
|
||||
/obj/machinery/computer/ordercomp/say_quote(text)
|
||||
return "flashes, \"[text]\""
|
||||
|
||||
|
||||
/obj/machinery/computer/supplycomp/attack_hand(var/mob/user as mob)
|
||||
if(!allowed(user))
|
||||
user << "<span class='warning'>Access Denied.</span>"
|
||||
return
|
||||
|
||||
if(..())
|
||||
return
|
||||
user.set_machine(src)
|
||||
post_signal("supply")
|
||||
var/dat
|
||||
if (temp)
|
||||
dat = temp
|
||||
else
|
||||
var/atDepot = (SSshuttle.supply.getDockedId() == "supply_away")
|
||||
var/inTransit = (SSshuttle.supply.mode != SHUTTLE_IDLE)
|
||||
var/canOrder = atDepot && !inTransit
|
||||
|
||||
dat += {"<div class='statusDisplay'><B>Supply shuttle</B><HR>
|
||||
Location: [SSshuttle.supply.getStatusText()]<BR>
|
||||
<HR>\nSupply Points: [SSshuttle.points]<BR>\n</div><BR>
|
||||
[canOrder ? "\n<A href='?src=\ref[src];order=categories'>Order items</A><BR>\n<BR>" : "\n*Must be away to order items*<BR>\n<BR>"]
|
||||
[inTransit ? "\n*Shuttle already called*<BR>\n<BR>": atDepot ? "\n<A href='?src=\ref[src];send=1'>Send to station</A><BR>\n<BR>":"\n<A href='?src=\ref[src];send=1'>Send to centcom</A><BR>\n<BR>"]
|
||||
[SSshuttle.shuttle_loan ? (SSshuttle.shuttle_loan.dispatched ? "\n*Shuttle loaned to Centcom*<BR>\n<BR>" : "\n<A href='?src=\ref[src];send=1;loan=1'>Loan shuttle to Centcom (5 mins duration)</A><BR>\n<BR>") : "\n*No pending external shuttle requests*<BR>\n<BR>"]
|
||||
\n<A href='?src=\ref[src];viewrequests=1'>View requests</A><BR>\n<BR>
|
||||
\n<A href='?src=\ref[src];vieworders=1'>View orders</A><BR>\n<BR>
|
||||
\n<A href='?src=\ref[user];mach_close=computer'>Close</A><BR>
|
||||
<HR>\n<B>Central Command messages:</B><BR> [SSshuttle.centcom_message ? SSshuttle.centcom_message : "Remember to stamp and send back the supply manifests."]"}
|
||||
|
||||
var/datum/browser/popup = new(user, "computer", "Supply Shuttle Console", 700, 455)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
return
|
||||
|
||||
/obj/machinery/computer/supplycomp/emag_act(mob/user)
|
||||
if(!hacked)
|
||||
user << "<span class='notice'>Special supplies unlocked.</span>"
|
||||
hacked = 1
|
||||
|
||||
/obj/machinery/computer/supplycomp/Topic(href, href_list)
|
||||
if(..())
|
||||
return
|
||||
|
||||
if(isturf(loc) && ( in_range(src, usr) || istype(usr, /mob/living/silicon) ) )
|
||||
usr.set_machine(src)
|
||||
|
||||
//Calling the shuttle
|
||||
if(href_list["send"])
|
||||
if(SSshuttle.supply.canMove())
|
||||
if(SSshuttle.shuttle_loan)
|
||||
temp = "The supply shuttle must be docked to send new commands.<BR><BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
else
|
||||
temp = "For safety reasons the automated supply shuttle cannot transport live organisms, classified nuclear weaponry or homing beacons.<BR><BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
|
||||
else if(SSshuttle.supply.getDockedId() == "supply_home")
|
||||
if(href_list["loan"] && SSshuttle.shuttle_loan)
|
||||
if(!SSshuttle.shuttle_loan.dispatched)
|
||||
SSshuttle.shuttle_loan.loan_shuttle()
|
||||
temp = "The supply shuttle has been loaned to Centcom.<BR><BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
post_signal("supply")
|
||||
else
|
||||
temp = "You can not loan the supply shuttle at this time.<BR><BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
else
|
||||
temp = "The supply shuttle has departed.<BR><BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
SSshuttle.toggleShuttle("supply", "supply_home", "supply_away", 1)
|
||||
investigate_log("[usr.key] has sent the supply shuttle away. Remaining points: [SSshuttle.points]. Shuttle contents:[SSshuttle.sold_atoms].", "cargo")
|
||||
else
|
||||
if(href_list["loan"] && SSshuttle.shuttle_loan)
|
||||
if(!SSshuttle.shuttle_loan.dispatched)
|
||||
SSshuttle.shuttle_loan.loan_shuttle()
|
||||
temp = "The supply shuttle has been loaned to Centcom.<BR><BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
post_signal("supply")
|
||||
else
|
||||
temp = "You can not loan the supply shuttle at this time.<BR><BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
else
|
||||
if(!SSshuttle.supply.request(SSshuttle.getDock("supply_home")))
|
||||
temp = "The supply shuttle has been called and will arrive in [SSshuttle.supply.timeLeft(600)] minutes.<BR><BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
post_signal("supply")
|
||||
|
||||
else if (href_list["order"])
|
||||
if(SSshuttle.supply.mode != SHUTTLE_IDLE) return
|
||||
if(href_list["order"] == "categories")
|
||||
//all_supply_groups
|
||||
//Request what?
|
||||
last_viewed_group = "categories"
|
||||
temp = "<div class='statusDisplay'><b>Supply points: [SSshuttle.points]</b><BR>"
|
||||
temp += "<A href='?src=\ref[src];mainmenu=1'>Main Menu</A><BR></div><BR>"
|
||||
temp += "<b>Select a category</b><BR><BR>"
|
||||
for(var/cat in all_supply_groups )
|
||||
temp += "<A href='?src=\ref[src];order=[cat]'>[get_supply_group_name(cat)]</A><BR>"
|
||||
else
|
||||
last_viewed_group = href_list["order"]
|
||||
var/cat = text2num(last_viewed_group)
|
||||
temp = "<div class='statusDisplay'><b>Supply points: [SSshuttle.points]</b><BR>"
|
||||
temp += "<A href='?src=\ref[src];order=categories'>Back to all categories</A><BR></div><BR>"
|
||||
temp += "<b>Request from: [get_supply_group_name(cat)]</b><BR><BR>"
|
||||
for(var/supply_type in SSshuttle.supply_packs )
|
||||
var/datum/supply_packs/N = SSshuttle.supply_packs[supply_type]
|
||||
if((N.hidden && !hacked) || (N.contraband && !can_order_contraband) || N.group != cat)
|
||||
continue //Have to send the type instead of a reference to
|
||||
temp += "<A href='?src=\ref[src];doorder=[supply_type]'>[N.name]</A> Cost: [N.cost]<BR>" //the obj because it would get caught by the garbage
|
||||
|
||||
/*temp = "Supply points: [supply_shuttle.points]<BR><HR><BR>Request what?<BR><BR>"
|
||||
|
||||
for(var/supply_name in supply_shuttle.supply_packs )
|
||||
var/datum/supply_packs/N = supply_shuttle.supply_packs[supply_name]
|
||||
if(N.hidden && !hacked) continue
|
||||
if(N.contraband && !can_order_contraband) continue
|
||||
temp += "<A href='?src=\ref[src];doorder=[supply_name]'>[supply_name]</A> Cost: [N.cost]<BR>" //the obj because it would get caught by the garbage
|
||||
temp += "<BR><A href='?src=\ref[src];mainmenu=1'>OK</A>"*/
|
||||
|
||||
else if (href_list["doorder"])
|
||||
if(world.time < reqtime)
|
||||
say("[world.time - reqtime] seconds remaining until another requisition form may be printed.")
|
||||
return
|
||||
|
||||
//Find the correct supply_pack datum
|
||||
if(!SSshuttle.supply_packs[href_list["doorder"]])
|
||||
return
|
||||
|
||||
var/timeout = world.time + 600
|
||||
var/reason = stripped_input(usr,"Reason:","Why do you require this item?","")
|
||||
if(world.time > timeout) return
|
||||
// if(!reason) return
|
||||
|
||||
var/idname = "*None Provided*"
|
||||
var/idrank = "*None Provided*"
|
||||
if(ishuman(usr))
|
||||
var/mob/living/carbon/human/H = usr
|
||||
idname = H.get_authentification_name()
|
||||
idrank = H.get_assignment()
|
||||
else if(issilicon(usr))
|
||||
idname = usr.real_name
|
||||
|
||||
var/datum/supply_order/O = SSshuttle.generateSupplyOrder(href_list["doorder"], idname, idrank, reason)
|
||||
if(!O) return
|
||||
O.generateRequisition(loc)
|
||||
|
||||
reqtime = (world.time + 5) % 1e5
|
||||
|
||||
temp = "Order request placed.<BR>"
|
||||
temp += "<BR><A href='?src=\ref[src];order=[last_viewed_group]'>Back</A> | <A href='?src=\ref[src];mainmenu=1'>Main Menu</A> | <A href='?src=\ref[src];confirmorder=[O.ordernum]'>Authorize Order</A>"
|
||||
|
||||
else if(href_list["confirmorder"])
|
||||
//Find the correct supply_order datum
|
||||
var/ordernum = text2num(href_list["confirmorder"])
|
||||
var/datum/supply_order/O
|
||||
var/datum/supply_packs/P
|
||||
temp = "Invalid Request"
|
||||
for(var/i=1, i<=SSshuttle.requestlist.len, i++)
|
||||
var/datum/supply_order/SO = SSshuttle.requestlist[i]
|
||||
if(SO && SO.ordernum == ordernum)
|
||||
O = SO
|
||||
P = O.object
|
||||
if(SSshuttle.points >= P.cost)
|
||||
SSshuttle.requestlist.Cut(i,i+1)
|
||||
SSshuttle.points -= P.cost
|
||||
SSshuttle.shoppinglist += O
|
||||
temp = "Thanks for your order."
|
||||
investigate_log("[usr.key] has authorized an order for [P.name]. Remaining points: [SSshuttle.points].", "cargo")
|
||||
else
|
||||
temp = "Not enough supply points."
|
||||
break
|
||||
temp += "<BR><BR><A href='?src=\ref[src];viewrequests=1'>Back</A> <A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
|
||||
else if (href_list["vieworders"])
|
||||
temp = "<A href='?src=\ref[src];mainmenu=1'>Main Menu</A><BR><BR>Current approved orders: <BR><BR>"
|
||||
for(var/datum/supply_order/SO in SSshuttle.shoppinglist)
|
||||
temp += "#[SO.ordernum] - [SO.object.name] approved by [SO.orderedby][SO.comment ? " ([SO.comment])":""]<BR>"// <A href='?src=\ref[src];cancelorder=[S]'>(Cancel)</A><BR>"
|
||||
temp += "<BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
/*
|
||||
else if (href_list["cancelorder"])
|
||||
var/datum/supply_order/remove_supply = href_list["cancelorder"]
|
||||
supply_shuttle_shoppinglist -= remove_supply
|
||||
supply_shuttle_points += remove_supply.object.cost
|
||||
temp += "Canceled: [remove_supply.object.name]<BR><BR><BR>"
|
||||
|
||||
for(var/S in supply_shuttle_shoppinglist)
|
||||
var/datum/supply_order/SO = S
|
||||
temp += "[SO.object.name] approved by [SO.orderedby][SO.comment ? " ([SO.comment])":""] <A href='?src=\ref[src];cancelorder=[S]'>(Cancel)</A><BR>"
|
||||
temp += "<BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
*/
|
||||
else if (href_list["viewrequests"])
|
||||
temp = "Current requests: <BR><BR>"
|
||||
for(var/datum/supply_order/SO in SSshuttle.requestlist)
|
||||
temp += "#[SO.ordernum] - [SO.object.name] requested by [SO.orderedby] [SSshuttle.supply.mode != SHUTTLE_IDLE ? "":SSshuttle.supply.getDockedId() == "supply_away" ? "":"<A href='?src=\ref[src];confirmorder=[SO.ordernum]'>Approve</A> <A href='?src=\ref[src];rreq=[SO.ordernum]'>Remove</A>"]<BR>"
|
||||
|
||||
temp += "<BR><A href='?src=\ref[src];clearreq=1'>Clear list</A>"
|
||||
temp += "<BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
|
||||
else if (href_list["rreq"])
|
||||
var/ordernum = text2num(href_list["rreq"])
|
||||
temp = "Invalid Request.<BR>"
|
||||
for(var/i=1, i<=SSshuttle.requestlist.len, i++)
|
||||
var/datum/supply_order/SO = SSshuttle.requestlist[i]
|
||||
if(SO && SO.ordernum == ordernum)
|
||||
SSshuttle.requestlist.Cut(i,i+1)
|
||||
temp = "Request removed.<BR>"
|
||||
break
|
||||
temp += "<BR><A href='?src=\ref[src];viewrequests=1'>Back</A> <A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
|
||||
else if (href_list["clearreq"])
|
||||
SSshuttle.requestlist.Cut()
|
||||
temp = "List cleared.<BR>"
|
||||
temp += "<BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
|
||||
else if (href_list["mainmenu"])
|
||||
temp = null
|
||||
|
||||
add_fingerprint(usr)
|
||||
updateUsrDialog()
|
||||
return
|
||||
|
||||
/obj/machinery/computer/supplycomp/proc/post_signal(var/command)
|
||||
|
||||
var/datum/radio_frequency/frequency = radio_controller.return_frequency(1435)
|
||||
|
||||
if(!frequency) return
|
||||
|
||||
var/datum/signal/status_signal = new
|
||||
status_signal.source = src
|
||||
status_signal.transmission_method = 1
|
||||
status_signal.data["command"] = command
|
||||
|
||||
frequency.post_signal(src, status_signal)
|
||||
|
||||
|
||||
/obj/machinery/computer/supplycomp/say_quote(text)
|
||||
return "flashes, \"[text]\""
|
||||
@@ -0,0 +1,51 @@
|
||||
var/datum/subsystem/sun/SSsun
|
||||
|
||||
/datum/subsystem/sun
|
||||
name = "Sun"
|
||||
wait = 600
|
||||
priority = 2
|
||||
|
||||
var/angle
|
||||
var/dx
|
||||
var/dy
|
||||
var/rate
|
||||
var/list/solars = list()
|
||||
|
||||
/datum/subsystem/sun/New()
|
||||
NEW_SS_GLOBAL(SSsun)
|
||||
|
||||
angle = rand (0,360) // the station position to the sun is randomised at round start
|
||||
rate = rand(50,200)/100 // 50% - 200% of standard rotation
|
||||
if(prob(50)) // same chance to rotate clockwise than counter-clockwise
|
||||
rate = -rate
|
||||
|
||||
/datum/subsystem/sun/fire()
|
||||
angle = (360 + angle + rate * 6) % 360 // increase/decrease the angle to the sun, adjusted by the rate
|
||||
|
||||
// now calculate and cache the (dx,dy) increments for line drawing
|
||||
var/s = sin(angle)
|
||||
var/c = cos(angle)
|
||||
|
||||
// Either "abs(s) < abs(c)" or "abs(s) >= abs(c)"
|
||||
// In both cases, the greater is greater than 0, so, no "if 0" check is needed for the divisions
|
||||
|
||||
if(abs(s) < abs(c))
|
||||
dx = s / abs(c)
|
||||
dy = c / abs(c)
|
||||
else
|
||||
dx = s / abs(s)
|
||||
dy = c / abs(s)
|
||||
|
||||
//now tell the solar control computers to update their status and linked devices
|
||||
for(var/obj/machinery/power/solar_control/SC in solars)
|
||||
if(!SC.powernet)
|
||||
solars.Remove(SC)
|
||||
continue
|
||||
SC.update()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,421 +1,430 @@
|
||||
var/global/datum/controller/gameticker/ticker
|
||||
var/round_start_time = 0
|
||||
|
||||
#define GAME_STATE_PREGAME 1
|
||||
#define GAME_STATE_SETTING_UP 2
|
||||
#define GAME_STATE_PLAYING 3
|
||||
#define GAME_STATE_FINISHED 4
|
||||
|
||||
/datum/controller/gameticker
|
||||
var/const/restart_timeout = 250
|
||||
var/current_state = GAME_STATE_PREGAME
|
||||
|
||||
var/hide_mode = 0
|
||||
var/datum/game_mode/mode = null
|
||||
var/event_time = null
|
||||
var/event = 0
|
||||
|
||||
var/login_music // music played in pregame lobby
|
||||
|
||||
var/list/datum/mind/minds = list()//The people in the game. Used for objective tracking.
|
||||
|
||||
var/Bible_icon_state // icon_state the chaplain has chosen for his bible
|
||||
var/Bible_item_state // item_state the chaplain has chosen for his bible
|
||||
var/Bible_name // name of the bible
|
||||
var/Bible_deity_name
|
||||
|
||||
var/list/syndicate_coalition = list() // list of traitor-compatible factions
|
||||
var/list/factions = list() // list of all factions
|
||||
var/list/availablefactions = list() // list of factions with openings
|
||||
|
||||
var/pregame_timeleft = 0
|
||||
|
||||
var/delay_end = 0 //if set to nonzero, the round will not restart on it's own
|
||||
|
||||
var/triai = 0//Global holder for Triumvirate
|
||||
|
||||
/datum/controller/gameticker/proc/pregame()
|
||||
|
||||
login_music = pickweight(list('sound/ambience/title2.ogg' = 49, 'sound/ambience/title1.ogg' = 49, 'sound/ambience/clown.ogg' = 2)) // choose title music!
|
||||
if(events.holiday == "April Fool's Day")
|
||||
login_music = 'sound/ambience/clown.ogg'
|
||||
for(var/client/C in clients)
|
||||
C.playtitlemusic()
|
||||
|
||||
do
|
||||
if(config)
|
||||
pregame_timeleft = config.lobby_countdown
|
||||
else
|
||||
ERROR("configuration was null when retrieving the lobby_countdown value.")
|
||||
pregame_timeleft = 120
|
||||
world << "<B><FONT color='blue'>Welcome to the pre-game lobby!</FONT></B>"
|
||||
world << "Please, setup your character and select ready. Game will start in [pregame_timeleft] seconds"
|
||||
while(current_state == GAME_STATE_PREGAME)
|
||||
sleep(10)
|
||||
if(going)
|
||||
pregame_timeleft--
|
||||
|
||||
if(pregame_timeleft <= 0)
|
||||
current_state = GAME_STATE_SETTING_UP
|
||||
while (!setup())
|
||||
|
||||
/datum/controller/gameticker/proc/setup()
|
||||
//Create and announce mode
|
||||
if(master_mode=="secret")
|
||||
src.hide_mode = 1
|
||||
var/list/datum/game_mode/runnable_modes
|
||||
if((master_mode=="random") || (master_mode=="secret"))
|
||||
runnable_modes = config.get_runnable_modes()
|
||||
|
||||
if((master_mode=="secret") && (secret_force_mode != "secret"))
|
||||
var/datum/game_mode/smode = config.pick_mode(secret_force_mode)
|
||||
if (!smode.can_start())
|
||||
message_admins("\blue Unable to force secret [secret_force_mode]. [smode.required_players] players and [smode.required_enemies] eligible antagonists needed.")
|
||||
else
|
||||
src.mode = smode
|
||||
|
||||
if(!src.mode)
|
||||
if (runnable_modes.len==0)
|
||||
current_state = GAME_STATE_PREGAME
|
||||
world << "<B>Unable to choose playable game mode.</B> Reverting to pre-game lobby."
|
||||
return 0
|
||||
src.mode = pickweight(runnable_modes)
|
||||
|
||||
else
|
||||
src.mode = config.pick_mode(master_mode)
|
||||
if (!src.mode.can_start())
|
||||
world << "<B>Unable to start [mode.name].</B> Not enough players, [mode.required_players] players and [mode.required_enemies] eligible antagonists needed. Reverting to pre-game lobby."
|
||||
del(mode)
|
||||
current_state = GAME_STATE_PREGAME
|
||||
job_master.ResetOccupations()
|
||||
return 0
|
||||
|
||||
//Configure mode and assign player to special mode stuff
|
||||
var/can_continue = 0
|
||||
if (src.mode.pre_setup_before_jobs) can_continue = src.mode.pre_setup()
|
||||
job_master.DivideOccupations() //Distribute jobs
|
||||
if (!src.mode.pre_setup_before_jobs) can_continue = src.mode.pre_setup()
|
||||
|
||||
if(!Debug2)
|
||||
if(!can_continue)
|
||||
del(mode)
|
||||
current_state = GAME_STATE_PREGAME
|
||||
world << "<B>Error setting up [master_mode].</B> Reverting to pre-game lobby."
|
||||
job_master.ResetOccupations()
|
||||
return 0
|
||||
else
|
||||
world << "<span class='notice'>DEBUG: Bypassing prestart checks..."
|
||||
|
||||
if(hide_mode)
|
||||
var/list/modes = new
|
||||
for (var/datum/game_mode/M in runnable_modes)
|
||||
modes+=M.name
|
||||
modes = sortList(modes)
|
||||
world << "<B>The current game mode is - Secret!</B>"
|
||||
world << "<B>Possibilities:</B> [english_list(modes)]"
|
||||
else
|
||||
src.mode.announce()
|
||||
|
||||
round_start_time = world.time
|
||||
|
||||
supply_shuttle.process() //Start the supply shuttle regenerating points
|
||||
master_controller.process() //Start master_controller.process()
|
||||
lighting_controller.process() //Start processing DynamicAreaLighting updates
|
||||
|
||||
sleep(10)
|
||||
|
||||
create_characters() //Create player characters and transfer them
|
||||
collect_minds()
|
||||
equip_characters()
|
||||
data_core.manifest()
|
||||
current_state = GAME_STATE_PLAYING
|
||||
|
||||
spawn(0)//Forking here so we dont have to wait for this to finish
|
||||
mode.post_setup()
|
||||
//Cleanup some stuff
|
||||
for(var/obj/effect/landmark/start/S in landmarks_list)
|
||||
//Deleting Startpoints but we need the ai point to AI-ize people later
|
||||
if (S.name != "AI")
|
||||
qdel(S)
|
||||
world << "<FONT color='blue'><B>Welcome to [station_name()], enjoy your stay!</B></FONT>"
|
||||
world << sound('sound/AI/welcome.ogg') // Skie
|
||||
//Holiday Round-start stuff ~Carn
|
||||
if(events.holiday)
|
||||
world << "<font color='blue'>and...</font>"
|
||||
world << "<h4>Happy [events.holiday] Everybody!</h4>"
|
||||
|
||||
if(!admins.len)
|
||||
send2irc("Server", "Round just started with no admins online!")
|
||||
auto_toggle_ooc(0) // Turn it off
|
||||
|
||||
if(config.sql_enabled)
|
||||
spawn(3000)
|
||||
statistic_cycle() // Polls population totals regularly and stores them in an SQL DB
|
||||
return 1
|
||||
|
||||
/datum/controller/gameticker
|
||||
//station_explosion used to be a variable for every mob's hud. Which was a waste!
|
||||
//Now we have a general cinematic centrally held within the gameticker....far more efficient!
|
||||
var/obj/screen/cinematic = null
|
||||
|
||||
//Plus it provides an easy way to make cinematics for other events. Just use this as a template
|
||||
/datum/controller/gameticker/proc/station_explosion_cinematic(var/station_missed=0, var/override = null)
|
||||
if( cinematic ) return //already a cinematic in progress!
|
||||
auto_toggle_ooc(1) // Turn it on
|
||||
//initialise our cinematic screen object
|
||||
cinematic = new(src)
|
||||
cinematic.icon = 'icons/effects/station_explosion.dmi'
|
||||
cinematic.icon_state = "station_intact"
|
||||
cinematic.layer = 20
|
||||
cinematic.mouse_opacity = 0
|
||||
cinematic.screen_loc = "1,0"
|
||||
|
||||
var/obj/structure/stool/bed/temp_buckle = new(src)
|
||||
if(station_missed)
|
||||
for(var/mob/M in mob_list)
|
||||
M.buckled = temp_buckle //buckles the mob so it can't do anything
|
||||
if(M.client)
|
||||
M.client.screen += cinematic //show every client the cinematic
|
||||
else //nuke kills everyone on z-level 1 to prevent "hurr-durr I survived"
|
||||
for(var/mob/M in mob_list)
|
||||
M.buckled = temp_buckle
|
||||
if(M.client)
|
||||
M.client.screen += cinematic
|
||||
if(M.stat != DEAD)
|
||||
var/turf/T = get_turf(M)
|
||||
if(T && T.z==1)
|
||||
M.death(0) //no mercy
|
||||
|
||||
//Now animate the cinematic
|
||||
switch(station_missed)
|
||||
if(1) //nuke was nearby but (mostly) missed
|
||||
if( mode && !override )
|
||||
override = mode.name
|
||||
switch( override )
|
||||
if("nuclear emergency") //Nuke wasn't on station when it blew up
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
flick("station_intact_fade_red",cinematic)
|
||||
cinematic.icon_state = "summary_nukefail"
|
||||
if("fake") //The round isn't over, we're just freaking people out for fun
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
world << sound('sound/items/bikehorn.ogg')
|
||||
flick("summary_selfdes",cinematic)
|
||||
else
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
//flick("end",cinematic)
|
||||
|
||||
|
||||
if(2) //nuke was nowhere nearby //TODO: a really distant explosion animation
|
||||
sleep(50)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
|
||||
|
||||
else //station was destroyed
|
||||
if( mode && !override )
|
||||
override = mode.name
|
||||
switch( override )
|
||||
if("nuclear emergency") //Nuke Ops successfully bombed the station
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
flick("station_explode_fade_red",cinematic)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
cinematic.icon_state = "summary_nukewin"
|
||||
if("AI malfunction") //Malf (screen,explosion,summary)
|
||||
flick("intro_malf",cinematic)
|
||||
sleep(76)
|
||||
flick("station_explode_fade_red",cinematic)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
cinematic.icon_state = "summary_malf"
|
||||
if("blob") //Station nuked (nuke,explosion,summary)
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
flick("station_explode_fade_red",cinematic)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
cinematic.icon_state = "summary_selfdes"
|
||||
else //Station nuked (nuke,explosion,summary)
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
flick("station_explode_fade_red", cinematic)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
cinematic.icon_state = "summary_selfdes"
|
||||
//If its actually the end of the round, wait for it to end.
|
||||
//Otherwise if its a verb it will continue on afterwards.
|
||||
sleep(300)
|
||||
|
||||
if(cinematic) qdel(cinematic) //end the cinematic
|
||||
if(temp_buckle) qdel(temp_buckle) //release everybody
|
||||
return
|
||||
|
||||
|
||||
/datum/controller/gameticker/proc/create_characters()
|
||||
for(var/mob/new_player/player in player_list)
|
||||
if(player.ready && player.mind)
|
||||
joined_player_list += player.ckey
|
||||
if(player.mind.assigned_role=="AI")
|
||||
player.close_spawn_windows()
|
||||
player.AIize()
|
||||
else
|
||||
player.create_character()
|
||||
qdel(player)
|
||||
else
|
||||
player.new_player_panel()
|
||||
|
||||
|
||||
/datum/controller/gameticker/proc/collect_minds()
|
||||
for(var/mob/living/player in player_list)
|
||||
if(player.mind)
|
||||
ticker.minds += player.mind
|
||||
|
||||
|
||||
/datum/controller/gameticker/proc/equip_characters()
|
||||
var/captainless=1
|
||||
for(var/mob/living/carbon/human/player in player_list)
|
||||
if(player && player.mind && player.mind.assigned_role)
|
||||
if(player.mind.assigned_role == "Captain")
|
||||
captainless=0
|
||||
if(player.mind.assigned_role != "MODE")
|
||||
job_master.EquipRank(player, player.mind.assigned_role, 0)
|
||||
if(captainless)
|
||||
for(var/mob/M in player_list)
|
||||
if(!istype(M,/mob/new_player))
|
||||
M << "Captainship not forced on anyone."
|
||||
|
||||
|
||||
/datum/controller/gameticker/proc/process()
|
||||
if(current_state != GAME_STATE_PLAYING)
|
||||
return 0
|
||||
|
||||
mode.process()
|
||||
|
||||
emergency_shuttle.process()
|
||||
|
||||
if(!mode.explosion_in_progress && mode.check_finished())
|
||||
current_state = GAME_STATE_FINISHED
|
||||
auto_toggle_ooc(1) // Turn it on
|
||||
spawn
|
||||
declare_completion()
|
||||
|
||||
spawn(50)
|
||||
if (mode.station_was_nuked)
|
||||
feedback_set_details("end_proper","nuke")
|
||||
if(!delay_end)
|
||||
world << "\blue <B>Rebooting due to destruction of station in [restart_timeout/10] seconds</B>"
|
||||
else
|
||||
feedback_set_details("end_proper","proper completion")
|
||||
if(!delay_end)
|
||||
world << "\blue <B>Restarting in [restart_timeout/10] seconds</B>"
|
||||
|
||||
|
||||
if(blackbox)
|
||||
blackbox.save_all_data_to_sql()
|
||||
|
||||
if(!delay_end)
|
||||
sleep(restart_timeout)
|
||||
kick_clients_in_lobby("\red The round came to an end with you in the lobby.", 1) //second parameter ensures only afk clients are kicked
|
||||
world.Reboot()
|
||||
else
|
||||
world << "\blue <B>An admin has delayed the round end</B>"
|
||||
|
||||
return 1
|
||||
|
||||
/datum/controller/gameticker/proc/getfactionbyname(var/name)
|
||||
for(var/datum/faction/F in factions)
|
||||
if(F.name == name)
|
||||
return F
|
||||
|
||||
|
||||
/datum/controller/gameticker/proc/declare_completion()
|
||||
var/station_evacuated
|
||||
if(emergency_shuttle.location > 0)
|
||||
station_evacuated = 1
|
||||
var/num_survivors = 0
|
||||
var/num_escapees = 0
|
||||
|
||||
world << "<BR><BR><BR><FONT size=3><B>The round has ended.</B></FONT>"
|
||||
|
||||
//Player status report
|
||||
for(var/mob/Player in mob_list)
|
||||
if(Player.mind && !isnewplayer(Player))
|
||||
if(Player.stat != DEAD && !isbrain(Player))
|
||||
num_survivors++
|
||||
if(station_evacuated) //If the shuttle has already left the station
|
||||
var/turf/playerTurf = get_turf(Player)
|
||||
if(playerTurf.z != 2)
|
||||
Player << "<font color='blue'><b>You managed to survive, but were marooned on [station_name()]...</b></FONT>"
|
||||
else
|
||||
num_escapees++
|
||||
Player << "<font color='green'><b>You managed to survive the events on [station_name()] as [Player.real_name].</b></FONT>"
|
||||
else
|
||||
Player << "<font color='green'><b>You managed to survive the events on [station_name()] as [Player.real_name].</b></FONT>"
|
||||
else
|
||||
Player << "<font color='red'><b>You did not survive the events on [station_name()]...</b></FONT>"
|
||||
|
||||
//Round statistics report
|
||||
var/datum/station_state/end_state = new /datum/station_state()
|
||||
end_state.count()
|
||||
var/station_integrity = round( 100.0 * start_state.score(end_state), 0.1)
|
||||
|
||||
world << "<BR>[TAB]Shift Duration: <B>[round(world.time / 36000)]:[add_zero("[world.time / 600 % 60]", 2)]:[world.time / 100 % 6][world.time / 100 % 10]</B>"
|
||||
world << "<BR>[TAB]Station Integrity: <B>[mode.station_was_nuked ? "<font color='red'>Destroyed</font>" : "[station_integrity]%"]</B>"
|
||||
if(joined_player_list.len)
|
||||
world << "<BR>[TAB]Total Population: <B>[joined_player_list.len]</B>"
|
||||
if(station_evacuated)
|
||||
world << "<BR>[TAB]Evacuation Rate: <B>[num_escapees] ([round((num_escapees/joined_player_list.len)*100, 0.1)]%)</B>"
|
||||
else
|
||||
world << "<BR>[TAB]Survival Rate: <B>[num_survivors] ([round((num_survivors/joined_player_list.len)*100, 0.1)]%)</B>"
|
||||
world << "<BR>"
|
||||
|
||||
//Silicon laws report
|
||||
for (var/mob/living/silicon/ai/aiPlayer in mob_list)
|
||||
if (aiPlayer.stat != 2 && aiPlayer.mind)
|
||||
world << "<b>[aiPlayer.name] (Played by: [aiPlayer.mind.key])'s laws at the end of the round were:</b>"
|
||||
aiPlayer.show_laws(1)
|
||||
else if (aiPlayer.mind) //if the dead ai has a mind, use its key instead
|
||||
world << "<b>[aiPlayer.name] (Played by: [aiPlayer.mind.key])'s laws when it was deactivated were:</b>"
|
||||
aiPlayer.show_laws(1)
|
||||
|
||||
if (aiPlayer.connected_robots.len)
|
||||
var/robolist = "<b>[aiPlayer.real_name]'s minions were:</b> "
|
||||
for(var/mob/living/silicon/robot/robo in aiPlayer.connected_robots)
|
||||
robolist += "[robo.name][robo.stat?" (Deactivated) (Played by: [robo.mind.key]), ":" (Played by: [robo.mind.key]), "]"
|
||||
world << "[robolist]"
|
||||
for (var/mob/living/silicon/robot/robo in mob_list)
|
||||
if (!robo.connected_ai && robo.mind)
|
||||
if (robo.stat != 2)
|
||||
world << "<b>[robo.name] (Played by: [robo.mind.key]) survived as an AI-less borg! Its laws were:</b>"
|
||||
else
|
||||
world << "<b>[robo.name] (Played by: [robo.mind.key]) was unable to survive the rigors of being a cyborg without an AI. Its laws were:</b>"
|
||||
|
||||
if(robo) //How the hell do we lose robo between here and the world messages directly above this?
|
||||
robo.laws.show_laws(world)
|
||||
|
||||
mode.declare_completion()//To declare normal completion.
|
||||
|
||||
//calls auto_declare_completion_* for all modes
|
||||
for(var/handler in typesof(/datum/game_mode/proc))
|
||||
if (findtext("[handler]","auto_declare_completion_"))
|
||||
call(mode, handler)()
|
||||
|
||||
//Print a list of antagonists to the server log
|
||||
var/list/total_antagonists = list()
|
||||
//Look into all mobs in world, dead or alive
|
||||
for(var/datum/mind/Mind in minds)
|
||||
var/temprole = Mind.special_role
|
||||
if(temprole) //if they are an antagonist of some sort.
|
||||
if(temprole in total_antagonists) //If the role exists already, add the name to it
|
||||
total_antagonists[temprole] += ", [Mind.name]([Mind.key])"
|
||||
else
|
||||
total_antagonists.Add(temprole) //If the role doesnt exist in the list, create it and add the mob
|
||||
total_antagonists[temprole] += ": [Mind.name]([Mind.key])"
|
||||
|
||||
//Now print them all into the log!
|
||||
log_game("Antagonists at round end were...")
|
||||
for(var/i in total_antagonists)
|
||||
log_game("[i]s[total_antagonists[i]].")
|
||||
|
||||
return 1
|
||||
var/round_start_time = 0
|
||||
|
||||
var/datum/subsystem/ticker/ticker
|
||||
|
||||
/datum/subsystem/ticker
|
||||
name = "Ticker"
|
||||
can_fire = 1
|
||||
priority = 0
|
||||
|
||||
var/restart_timeout = 250 //delay when restarting server
|
||||
var/current_state = GAME_STATE_STARTUP //state of current round (used by process()) Use the defines GAME_STATE_* !
|
||||
|
||||
var/hide_mode = 0
|
||||
var/datum/game_mode/mode = null
|
||||
var/event_time = null
|
||||
var/event = 0
|
||||
|
||||
var/login_music //music played in pregame lobby
|
||||
|
||||
var/list/datum/mind/minds = list() //The characters in the game. Used for objective tracking.
|
||||
|
||||
//These bible variables should be a preference
|
||||
var/Bible_icon_state //icon_state the chaplain has chosen for his bible
|
||||
var/Bible_item_state //item_state the chaplain has chosen for his bible
|
||||
var/Bible_name //name of the bible
|
||||
var/Bible_deity_name //name of chaplin's deity
|
||||
|
||||
var/list/syndicate_coalition = list() //list of traitor-compatible factions
|
||||
var/list/factions = list() //list of all factions
|
||||
var/list/availablefactions = list() //list of factions with openings
|
||||
|
||||
var/delay_end = 0 //if set true, the round will not restart on it's own
|
||||
|
||||
var/triai = 0 //Global holder for Triumvirate
|
||||
var/tipped = 0 //Did we broadcast the tip of the day yet?
|
||||
|
||||
var/timeLeft = 1200 //pregame timer
|
||||
|
||||
var/totalPlayers = 0 //used for pregame stats on statpanel
|
||||
var/totalPlayersReady = 0 //used for pregame stats on statpanel
|
||||
|
||||
var/obj/screen/cinematic = null //used for station explosion cinematic
|
||||
|
||||
|
||||
/datum/subsystem/ticker/New()
|
||||
NEW_SS_GLOBAL(ticker)
|
||||
|
||||
login_music = pickweight(list('sound/ambience/title2.ogg' = 49, 'sound/ambience/title1.ogg' = 49, 'sound/ambience/clown.ogg' = 2)) // choose title music!
|
||||
if(SSevent.holiday == "April Fool's Day")
|
||||
login_music = 'sound/ambience/clown.ogg'
|
||||
|
||||
/datum/subsystem/ticker/Initialize()
|
||||
if(!syndicate_code_phrase) syndicate_code_phrase = generate_code_phrase()
|
||||
if(!syndicate_code_response) syndicate_code_response = generate_code_phrase()
|
||||
setupGenetics()
|
||||
setupFactions()
|
||||
..()
|
||||
|
||||
/datum/subsystem/ticker/fire()
|
||||
switch(current_state)
|
||||
if(GAME_STATE_STARTUP)
|
||||
timeLeft = config.lobby_countdown * 10
|
||||
world << "<B><FONT color='blue'>Welcome to the pre-game lobby!</FONT></B>"
|
||||
world << "Please, setup your character and select ready. Game will start in [config.lobby_countdown] seconds"
|
||||
current_state = GAME_STATE_PREGAME
|
||||
|
||||
if(GAME_STATE_PREGAME)
|
||||
//lobby stats for statpanels
|
||||
totalPlayers = 0
|
||||
totalPlayersReady = 0
|
||||
for(var/mob/new_player/player in player_list)
|
||||
++totalPlayers
|
||||
if(player.ready)
|
||||
++totalPlayersReady
|
||||
|
||||
//countdown
|
||||
timeLeft -= wait
|
||||
|
||||
if(timeLeft <= 30 && !tipped)
|
||||
send_random_tip()
|
||||
tipped = 1
|
||||
|
||||
if(timeLeft <= 0)
|
||||
current_state = GAME_STATE_SETTING_UP
|
||||
|
||||
if(GAME_STATE_SETTING_UP)
|
||||
if(!setup())
|
||||
//setup failed
|
||||
current_state = GAME_STATE_STARTUP
|
||||
|
||||
if(GAME_STATE_PLAYING)
|
||||
mode.process(wait * 0.1)
|
||||
|
||||
if(!mode.explosion_in_progress && mode.check_finished())
|
||||
current_state = GAME_STATE_FINISHED
|
||||
auto_toggle_ooc(1) // Turn it on
|
||||
declare_completion()
|
||||
spawn(50)
|
||||
if(mode.station_was_nuked)
|
||||
feedback_set_details("end_proper","nuke")
|
||||
if(!delay_end)
|
||||
world << "\blue <B>Rebooting due to destruction of station in [restart_timeout/10] seconds</B>"
|
||||
else
|
||||
feedback_set_details("end_proper","proper completion")
|
||||
if(!delay_end)
|
||||
world << "\blue <B>Restarting in [restart_timeout/10] seconds</B>"
|
||||
|
||||
|
||||
if(blackbox)
|
||||
blackbox.save_all_data_to_sql()
|
||||
|
||||
if(delay_end)
|
||||
world << "\blue <B>An admin has delayed the round end</B>"
|
||||
else
|
||||
sleep(restart_timeout)
|
||||
kick_clients_in_lobby("\red The round came to an end with you in the lobby.", 1) //second parameter ensures only afk clients are kicked
|
||||
world.Reboot()
|
||||
|
||||
|
||||
/datum/subsystem/ticker/proc/setup()
|
||||
//Create and announce mode
|
||||
var/list/datum/game_mode/runnable_modes
|
||||
if(master_mode == "random" || master_mode == "secret")
|
||||
runnable_modes = config.get_runnable_modes()
|
||||
|
||||
if(master_mode == "secret")
|
||||
hide_mode = 1
|
||||
if(secret_force_mode != "secret")
|
||||
var/datum/game_mode/smode = config.pick_mode(secret_force_mode)
|
||||
if(!smode.can_start())
|
||||
message_admins("\blue Unable to force secret [secret_force_mode]. [smode.required_players] players and [smode.required_enemies] eligible antagonists needed.")
|
||||
else
|
||||
mode = smode
|
||||
|
||||
if(!mode)
|
||||
if(!runnable_modes.len)
|
||||
world << "<B>Unable to choose playable game mode.</B> Reverting to pre-game lobby."
|
||||
return 0
|
||||
mode = pickweight(runnable_modes)
|
||||
|
||||
else
|
||||
mode = config.pick_mode(master_mode)
|
||||
if(!mode.can_start())
|
||||
world << "<B>Unable to start [mode.name].</B> Not enough players, [mode.required_players] players and [mode.required_enemies] eligible antagonists needed. Reverting to pre-game lobby."
|
||||
del(mode)
|
||||
SSjob.ResetOccupations()
|
||||
return 0
|
||||
|
||||
//Configure mode and assign player to special mode stuff
|
||||
var/can_continue = 0
|
||||
if(mode.pre_setup_before_jobs)
|
||||
can_continue = src.mode.pre_setup()
|
||||
SSjob.DivideOccupations() //Distribute jobs
|
||||
if(!mode.pre_setup_before_jobs)
|
||||
can_continue = src.mode.pre_setup()
|
||||
|
||||
if(!Debug2)
|
||||
if(!can_continue)
|
||||
del(mode)
|
||||
world << "<B>Error setting up [master_mode].</B> Reverting to pre-game lobby."
|
||||
SSjob.ResetOccupations()
|
||||
return 0
|
||||
else
|
||||
world << "<span class='notice'>DEBUG: Bypassing prestart checks..."
|
||||
|
||||
if(hide_mode)
|
||||
var/list/modes = new
|
||||
for (var/datum/game_mode/M in runnable_modes)
|
||||
modes += M.name
|
||||
modes = sortList(modes)
|
||||
world << "<B>The current game mode is - Secret!</B>"
|
||||
world << "<B>Possibilities:</B> [english_list(modes)]"
|
||||
else
|
||||
mode.announce()
|
||||
|
||||
current_state = GAME_STATE_PLAYING
|
||||
auto_toggle_ooc(0) // Turn it off
|
||||
round_start_time = world.time
|
||||
|
||||
create_characters() //Create player characters and transfer them
|
||||
collect_minds()
|
||||
equip_characters()
|
||||
data_core.manifest()
|
||||
|
||||
master_controller.roundHasStarted()
|
||||
|
||||
|
||||
world << "<FONT color='blue'><B>Welcome to [station_name()], enjoy your stay!</B></FONT>"
|
||||
world << sound('sound/AI/welcome.ogg') // Skie
|
||||
//Holiday Round-start stuff ~Carn
|
||||
if(SSevent.holiday)
|
||||
world << "<font color='blue'>and...</font>"
|
||||
world << "<h4>Happy [SSevent.holiday] Everybody!</h4>"
|
||||
|
||||
|
||||
spawn(0)//Forking here so we dont have to wait for this to finish
|
||||
mode.post_setup()
|
||||
//Cleanup some stuff
|
||||
for(var/obj/effect/landmark/start/S in landmarks_list)
|
||||
//Deleting Startpoints but we need the ai point to AI-ize people later
|
||||
if(S.name != "AI")
|
||||
qdel(S)
|
||||
|
||||
if(!admins.len)
|
||||
send2irc("Server", "Round just started with no admins online!")
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
//Plus it provides an easy way to make cinematics for other events. Just use this as a template
|
||||
/datum/subsystem/ticker/proc/station_explosion_cinematic(var/station_missed=0, var/override = null)
|
||||
if( cinematic ) return //already a cinematic in progress!
|
||||
auto_toggle_ooc(1) // Turn it on
|
||||
//initialise our cinematic screen object
|
||||
cinematic = new /obj/screen{icon='icons/effects/station_explosion.dmi';icon_state="station_intact";layer=20;mouse_opacity=0;screen_loc="1,0";}(src)
|
||||
|
||||
var/obj/structure/stool/bed/temp_buckle = new(src)
|
||||
if(station_missed)
|
||||
for(var/mob/M in mob_list)
|
||||
M.buckled = temp_buckle //buckles the mob so it can't do anything
|
||||
if(M.client)
|
||||
M.client.screen += cinematic //show every client the cinematic
|
||||
else //nuke kills everyone on z-level 1 to prevent "hurr-durr I survived"
|
||||
for(var/mob/M in mob_list)
|
||||
M.buckled = temp_buckle
|
||||
if(M.client)
|
||||
M.client.screen += cinematic
|
||||
if(M.stat != DEAD)
|
||||
var/turf/T = get_turf(M)
|
||||
if(T && T.z==1)
|
||||
M.death(0) //no mercy
|
||||
|
||||
//Now animate the cinematic
|
||||
switch(station_missed)
|
||||
if(1) //nuke was nearby but (mostly) missed
|
||||
if( mode && !override )
|
||||
override = mode.name
|
||||
switch( override )
|
||||
if("nuclear emergency") //Nuke wasn't on station when it blew up
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
flick("station_intact_fade_red",cinematic)
|
||||
cinematic.icon_state = "summary_nukefail"
|
||||
if("fake") //The round isn't over, we're just freaking people out for fun
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
world << sound('sound/items/bikehorn.ogg')
|
||||
flick("summary_selfdes",cinematic)
|
||||
else
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
//flick("end",cinematic)
|
||||
|
||||
|
||||
if(2) //nuke was nowhere nearby //TODO: a really distant explosion animation
|
||||
sleep(50)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
|
||||
|
||||
else //station was destroyed
|
||||
if( mode && !override )
|
||||
override = mode.name
|
||||
switch( override )
|
||||
if("nuclear emergency") //Nuke Ops successfully bombed the station
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
flick("station_explode_fade_red",cinematic)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
cinematic.icon_state = "summary_nukewin"
|
||||
if("AI malfunction") //Malf (screen,explosion,summary)
|
||||
flick("intro_malf",cinematic)
|
||||
sleep(76)
|
||||
flick("station_explode_fade_red",cinematic)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
cinematic.icon_state = "summary_malf"
|
||||
if("blob") //Station nuked (nuke,explosion,summary)
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
flick("station_explode_fade_red",cinematic)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
cinematic.icon_state = "summary_selfdes"
|
||||
else //Station nuked (nuke,explosion,summary)
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
flick("station_explode_fade_red", cinematic)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
cinematic.icon_state = "summary_selfdes"
|
||||
//If its actually the end of the round, wait for it to end.
|
||||
//Otherwise if its a verb it will continue on afterwards.
|
||||
sleep(300)
|
||||
|
||||
if(cinematic) qdel(cinematic) //end the cinematic
|
||||
if(temp_buckle) qdel(temp_buckle) //release everybody
|
||||
return
|
||||
|
||||
|
||||
|
||||
/datum/subsystem/ticker/proc/create_characters()
|
||||
for(var/mob/new_player/player in player_list)
|
||||
if(player.ready && player.mind)
|
||||
joined_player_list += player.ckey
|
||||
if(player.mind.assigned_role=="AI")
|
||||
player.close_spawn_windows()
|
||||
player.AIize()
|
||||
else
|
||||
player.create_character()
|
||||
qdel(player)
|
||||
else
|
||||
player.new_player_panel()
|
||||
|
||||
|
||||
/datum/subsystem/ticker/proc/collect_minds()
|
||||
for(var/mob/living/player in player_list)
|
||||
if(player.mind)
|
||||
ticker.minds += player.mind
|
||||
|
||||
|
||||
/datum/subsystem/ticker/proc/equip_characters()
|
||||
var/captainless=1
|
||||
for(var/mob/living/carbon/human/player in player_list)
|
||||
if(player && player.mind && player.mind.assigned_role)
|
||||
if(player.mind.assigned_role == "Captain")
|
||||
captainless=0
|
||||
if(player.mind.assigned_role != "MODE")
|
||||
SSjob.EquipRank(player, player.mind.assigned_role, 0)
|
||||
if(captainless)
|
||||
for(var/mob/M in player_list)
|
||||
if(!istype(M,/mob/new_player))
|
||||
M << "Captainship not forced on anyone."
|
||||
|
||||
|
||||
|
||||
/datum/subsystem/ticker/proc/declare_completion()
|
||||
var/station_evacuated
|
||||
if(SSshuttle.emergency.mode >= SHUTTLE_ESCAPE)
|
||||
station_evacuated = 1
|
||||
var/num_survivors = 0
|
||||
var/num_escapees = 0
|
||||
|
||||
world << "<BR><BR><BR><FONT size=3><B>The round has ended.</B></FONT>"
|
||||
|
||||
//Player status report
|
||||
for(var/mob/Player in mob_list)
|
||||
if(Player.mind && !isnewplayer(Player))
|
||||
if(Player.stat != DEAD && !isbrain(Player))
|
||||
num_survivors++
|
||||
if(station_evacuated) //If the shuttle has already left the station
|
||||
var/turf/playerTurf = get_turf(Player)
|
||||
if(!playerTurf || playerTurf.z != ZLEVEL_CENTCOM)
|
||||
Player << "<font color='blue'><b>You managed to survive, but were marooned on [station_name()]...</b></FONT>"
|
||||
else
|
||||
num_escapees++
|
||||
Player << "<font color='green'><b>You managed to survive the events on [station_name()] as [Player.real_name].</b></FONT>"
|
||||
else
|
||||
Player << "<font color='green'><b>You managed to survive the events on [station_name()] as [Player.real_name].</b></FONT>"
|
||||
else
|
||||
Player << "<font color='red'><b>You did not survive the events on [station_name()]...</b></FONT>"
|
||||
|
||||
//Round statistics report
|
||||
var/datum/station_state/end_state = new /datum/station_state()
|
||||
end_state.count()
|
||||
var/station_integrity = round( 100.0 * start_state.score(end_state), 0.1)
|
||||
|
||||
world << "<BR>[TAB]Shift Duration: <B>[round(world.time / 36000)]:[add_zero("[world.time / 600 % 60]", 2)]:[world.time / 100 % 6][world.time / 100 % 10]</B>"
|
||||
world << "<BR>[TAB]Station Integrity: <B>[mode.station_was_nuked ? "<font color='red'>Destroyed</font>" : "[station_integrity]%"]</B>"
|
||||
if(joined_player_list.len)
|
||||
world << "<BR>[TAB]Total Population: <B>[joined_player_list.len]</B>"
|
||||
if(station_evacuated)
|
||||
world << "<BR>[TAB]Evacuation Rate: <B>[num_escapees] ([round((num_escapees/joined_player_list.len)*100, 0.1)]%)</B>"
|
||||
else
|
||||
world << "<BR>[TAB]Survival Rate: <B>[num_survivors] ([round((num_survivors/joined_player_list.len)*100, 0.1)]%)</B>"
|
||||
world << "<BR>"
|
||||
|
||||
//Silicon laws report
|
||||
for (var/mob/living/silicon/ai/aiPlayer in mob_list)
|
||||
if (aiPlayer.stat != 2 && aiPlayer.mind)
|
||||
world << "<b>[aiPlayer.name] (Played by: [aiPlayer.mind.key])'s laws at the end of the round were:</b>"
|
||||
aiPlayer.show_laws(1)
|
||||
else if (aiPlayer.mind) //if the dead ai has a mind, use its key instead
|
||||
world << "<b>[aiPlayer.name] (Played by: [aiPlayer.mind.key])'s laws when it was deactivated were:</b>"
|
||||
aiPlayer.show_laws(1)
|
||||
|
||||
if (aiPlayer.connected_robots.len)
|
||||
var/robolist = "<b>[aiPlayer.real_name]'s minions were:</b> "
|
||||
for(var/mob/living/silicon/robot/robo in aiPlayer.connected_robots)
|
||||
robolist += "[robo.name][robo.stat?" (Deactivated) (Played by: [robo.mind.key]), ":" (Played by: [robo.mind.key]), "]"
|
||||
world << "[robolist]"
|
||||
for (var/mob/living/silicon/robot/robo in mob_list)
|
||||
if (!robo.connected_ai && robo.mind)
|
||||
if (robo.stat != 2)
|
||||
world << "<b>[robo.name] (Played by: [robo.mind.key]) survived as an AI-less borg! Its laws were:</b>"
|
||||
else
|
||||
world << "<b>[robo.name] (Played by: [robo.mind.key]) was unable to survive the rigors of being a cyborg without an AI. Its laws were:</b>"
|
||||
|
||||
if(robo) //How the hell do we lose robo between here and the world messages directly above this?
|
||||
robo.laws.show_laws(world)
|
||||
|
||||
mode.declare_completion()//To declare normal completion.
|
||||
|
||||
//calls auto_declare_completion_* for all modes
|
||||
for(var/handler in typesof(/datum/game_mode/proc))
|
||||
if (findtext("[handler]","auto_declare_completion_"))
|
||||
call(mode, handler)()
|
||||
|
||||
//Print a list of antagonists to the server log
|
||||
var/list/total_antagonists = list()
|
||||
//Look into all mobs in world, dead or alive
|
||||
for(var/datum/mind/Mind in minds)
|
||||
var/temprole = Mind.special_role
|
||||
if(temprole) //if they are an antagonist of some sort.
|
||||
if(temprole in total_antagonists) //If the role exists already, add the name to it
|
||||
total_antagonists[temprole] += ", [Mind.name]([Mind.key])"
|
||||
else
|
||||
total_antagonists.Add(temprole) //If the role doesnt exist in the list, create it and add the mob
|
||||
total_antagonists[temprole] += ": [Mind.name]([Mind.key])"
|
||||
|
||||
//Now print them all into the log!
|
||||
log_game("Antagonists at round end were...")
|
||||
for(var/i in total_antagonists)
|
||||
log_game("[i]s[total_antagonists[i]].")
|
||||
|
||||
return 1
|
||||
|
||||
/datum/subsystem/ticker/proc/send_random_tip()
|
||||
var/list/randomtips = file2list("config/tips.txt")
|
||||
if(randomtips.len)
|
||||
world << "<font color='purple'><b>Tip of the round: </b>[strip_html_properly(pick(randomtips))]</font>"
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
var/datum/controller/vote/vote = new()
|
||||
var/datum/subsystem/vote/SSvote
|
||||
|
||||
/datum/subsystem/vote
|
||||
name = "Vote"
|
||||
can_fire = 1
|
||||
wait = 10
|
||||
priority = -1
|
||||
|
||||
/datum/controller/vote
|
||||
var/initiator = null
|
||||
var/started_timeofday = null
|
||||
var/started_time = null
|
||||
var/time_remaining = 0
|
||||
var/mode = null
|
||||
var/question = null
|
||||
@@ -10,44 +15,28 @@ var/datum/controller/vote/vote = new()
|
||||
var/list/voted = list()
|
||||
var/list/voting = list()
|
||||
|
||||
/datum/controller/vote/New()
|
||||
if(vote != src)
|
||||
if(istype(vote))
|
||||
del(vote)
|
||||
vote = src
|
||||
/datum/subsystem/vote/New()
|
||||
NEW_SS_GLOBAL(SSvote)
|
||||
|
||||
/datum/controller/vote/proc/process() //called by master_controller
|
||||
/datum/subsystem/vote/fire() //called by master_controller
|
||||
if(mode)
|
||||
time_remaining = started_timeofday + config.vote_period
|
||||
if(world.timeofday < started_timeofday)
|
||||
time_remaining -= 864000
|
||||
time_remaining = round((time_remaining - world.timeofday)/10)
|
||||
time_remaining = round((started_time + config.vote_period - world.time)/10)
|
||||
|
||||
var/i=1
|
||||
if(time_remaining < 0)
|
||||
result()
|
||||
while(i<=voting.len)
|
||||
var/client/C = voting[i]
|
||||
if(C)
|
||||
C << browse(null,"window=vote;can_close=0")
|
||||
i++
|
||||
for(var/client/C in voting)
|
||||
C << browse(null, "window=vote;can_close=0")
|
||||
reset()
|
||||
else
|
||||
var/datum/browser/client_popup
|
||||
while(i<=voting.len)
|
||||
var/client/C = voting[i]
|
||||
if(C)
|
||||
//C << browse(vote.interface(C),"window=vote;can_close=0")
|
||||
client_popup = new(C, "vote", "Voting Panel")
|
||||
client_popup.set_window_options("can_close=0")
|
||||
client_popup.set_content(vote.interface(C))
|
||||
client_popup.open(0)
|
||||
for(var/client/C in voting)
|
||||
client_popup = new(C, "vote", "Voting Panel")
|
||||
client_popup.set_window_options("can_close=0")
|
||||
client_popup.set_content(interface(C))
|
||||
client_popup.open(0)
|
||||
|
||||
i++
|
||||
else
|
||||
voting.Cut(i,i+1)
|
||||
|
||||
/datum/controller/vote/proc/reset()
|
||||
/datum/subsystem/vote/proc/reset()
|
||||
initiator = null
|
||||
time_remaining = 0
|
||||
mode = null
|
||||
@@ -56,7 +45,7 @@ var/datum/controller/vote/vote = new()
|
||||
voted.Cut()
|
||||
voting.Cut()
|
||||
|
||||
/datum/controller/vote/proc/get_result()
|
||||
/datum/subsystem/vote/proc/get_result()
|
||||
//get the highest number of votes
|
||||
var/greatest_votes = 0
|
||||
var/total_votes = 0
|
||||
@@ -86,7 +75,7 @@ var/datum/controller/vote/vote = new()
|
||||
. += option
|
||||
return .
|
||||
|
||||
/datum/controller/vote/proc/announce_result()
|
||||
/datum/subsystem/vote/proc/announce_result()
|
||||
var/list/winners = get_result()
|
||||
var/text
|
||||
if(winners.len > 0)
|
||||
@@ -111,7 +100,7 @@ var/datum/controller/vote/vote = new()
|
||||
world << "\n<font color='purple'>[text]</font>"
|
||||
return .
|
||||
|
||||
/datum/controller/vote/proc/result()
|
||||
/datum/subsystem/vote/proc/result()
|
||||
. = announce_result()
|
||||
var/restart = 0
|
||||
if(.)
|
||||
@@ -137,7 +126,7 @@ var/datum/controller/vote/vote = new()
|
||||
|
||||
return .
|
||||
|
||||
/datum/controller/vote/proc/submit_vote(var/vote)
|
||||
/datum/subsystem/vote/proc/submit_vote(var/vote)
|
||||
if(mode)
|
||||
if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder)
|
||||
return 0
|
||||
@@ -148,13 +137,11 @@ var/datum/controller/vote/vote = new()
|
||||
return vote
|
||||
return 0
|
||||
|
||||
/datum/controller/vote/proc/initiate_vote(var/vote_type, var/initiator_key)
|
||||
/datum/subsystem/vote/proc/initiate_vote(var/vote_type, var/initiator_key)
|
||||
if(!mode)
|
||||
if(started_timeofday != null)
|
||||
var/next_allowed_timeofday = (started_timeofday + config.vote_delay)
|
||||
if(world.timeofday < started_timeofday)
|
||||
next_allowed_timeofday -= 864000
|
||||
if(next_allowed_timeofday > world.timeofday)
|
||||
if(started_time != null)
|
||||
var/next_allowed_time = (started_time + config.vote_delay)
|
||||
if(next_allowed_time > world.time)
|
||||
return 0
|
||||
|
||||
reset()
|
||||
@@ -171,17 +158,17 @@ var/datum/controller/vote/vote = new()
|
||||
else return 0
|
||||
mode = vote_type
|
||||
initiator = initiator_key
|
||||
started_timeofday = world.timeofday
|
||||
started_time = world.time
|
||||
var/text = "[capitalize(mode)] vote started by [initiator]."
|
||||
if(mode == "custom")
|
||||
text += "\n[question]"
|
||||
log_vote(text)
|
||||
world << "\n<font color='purple'><b>[text]</b>\nType <b>vote</b> to place your votes.\nYou have [config.vote_period/10] seconds to vote.</font>"
|
||||
world << "\n<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>"
|
||||
time_remaining = round(config.vote_period/10)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/controller/vote/proc/interface(var/client/C)
|
||||
/datum/subsystem/vote/proc/interface(var/client/C)
|
||||
if(!C) return
|
||||
var/admin = 0
|
||||
var/trialmin = 0
|
||||
@@ -229,7 +216,7 @@ var/datum/controller/vote/vote = new()
|
||||
return .
|
||||
|
||||
|
||||
/datum/controller/vote/Topic(href,href_list[],hsrc)
|
||||
/datum/subsystem/vote/Topic(href,href_list[],hsrc)
|
||||
if(!usr || !usr.client) return //not necessary but meh...just in-case somebody does something stupid
|
||||
switch(href_list["vote"])
|
||||
if("close")
|
||||
@@ -263,9 +250,8 @@ var/datum/controller/vote/vote = new()
|
||||
set category = "OOC"
|
||||
set name = "Vote"
|
||||
|
||||
if(vote)
|
||||
//src << browse(vote.interface(client),"window=vote;can_close=0")
|
||||
var/datum/browser/popup = new(src, "vote", "Voting Panel")
|
||||
popup.set_window_options("can_close=0")
|
||||
popup.set_content(vote.interface(client))
|
||||
popup.open(0)
|
||||
var/datum/browser/popup = new(src, "vote", "Voting Panel")
|
||||
popup.set_window_options("can_close=0")
|
||||
popup.set_content(SSvote.interface(client))
|
||||
popup.open(0)
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
#define NEW_SS_GLOBAL(varname) if(varname != src){if(istype(varname)){Recover();qdel(varname);}varname = src;}
|
||||
|
||||
/datum/subsystem
|
||||
//things you will want to define
|
||||
var/name //name of the subsystem
|
||||
var/priority = 0 //priority affects order of initialization. Higher priorities are initialized first, lower priorities later. Can be decimal and negative values.
|
||||
var/wait = 20 //time to wait (in deciseconds) between each call to fire(). Must be a positive integer.
|
||||
|
||||
//things you will probably want to leave alone
|
||||
var/can_fire = 0 //prevent fire() calls
|
||||
var/last_fire = 0 //last world.time we called fire()
|
||||
var/next_fire = 0 //scheduled world.time for next fire()
|
||||
var/cpu = 0 //cpu-usage stats (somewhat vague)
|
||||
var/cost = 0 //average time to execute
|
||||
var/times_fired = 0 //number of times we have called fire()
|
||||
|
||||
//used to initialize the subsystem BEFORE the map has loaded
|
||||
/datum/subsystem/New()
|
||||
|
||||
//previously, this would have been named 'process()' but that name is used everywhere for different things!
|
||||
//fire() seems more suitable. This is the procedure that gets called every 'wait' deciseconds.
|
||||
//fire(), and the procs it calls, SHOULD NOT HAVE ANY SLEEP OPERATIONS in them!
|
||||
//YE BE WARNED!
|
||||
/datum/subsystem/proc/fire()
|
||||
can_fire = 0
|
||||
|
||||
//used to initialize the subsystem AFTER the map has loaded
|
||||
/datum/subsystem/proc/Initialize(start_timeofday)
|
||||
var/time = (world.timeofday - start_timeofday) / 10
|
||||
var/msg = "Initialized [name] SubSystem within [time] seconds"
|
||||
world << "<span class='userdanger'>[msg]</span>"
|
||||
world.log << msg
|
||||
|
||||
//hook for printing stats to the "MC" statuspanel for admins to see performance and related stats etc.
|
||||
/datum/subsystem/proc/stat_entry()
|
||||
stat(name, "[round(cost,0.001)]ds\t(CPU:[round(cpu,1)]%)")
|
||||
|
||||
//could be used to postpone a costly subsystem for one cycle
|
||||
//for instance, during cpu intensive operations like explosions
|
||||
/datum/subsystem/proc/postpone()
|
||||
if(next_fire - world.time < wait)
|
||||
next_fire += wait
|
||||
|
||||
//usually called via datum/subsystem/New() when replacing a subsystem (i.e. due to a recurring crash)
|
||||
//should attempt to salvage what it can from the old instance of subsystem
|
||||
/datum/subsystem/proc/Recover()
|
||||
@@ -1,808 +0,0 @@
|
||||
//Config stuff
|
||||
#define SUPPLY_DOCKZ 2 //Z-level of the Dock.
|
||||
#define SUPPLY_STATIONZ 1 //Z-level of the Station.
|
||||
#define SUPPLY_STATION_AREATYPE "/area/supply/station" //Type of the supply shuttle area for station
|
||||
#define SUPPLY_DOCK_AREATYPE "/area/supply/dock" //Type of the supply shuttle area for dock
|
||||
|
||||
var/global/datum/controller/supply_shuttle/supply_shuttle
|
||||
|
||||
/area/supply/station //DO NOT TURN THE lighting_use_dynamic STUFF ON FOR SHUTTLES. IT BREAKS THINGS.
|
||||
name = "supply shuttle"
|
||||
icon_state = "shuttle3"
|
||||
luminosity = 1
|
||||
lighting_use_dynamic = 0
|
||||
requires_power = 0
|
||||
|
||||
/area/supply/dock //DO NOT TURN THE lighting_use_dynamic STUFF ON FOR SHUTTLES. IT BREAKS THINGS.
|
||||
name = "supply shuttle"
|
||||
icon_state = "shuttle3"
|
||||
luminosity = 1
|
||||
lighting_use_dynamic = 0
|
||||
requires_power = 0
|
||||
|
||||
//SUPPLY PACKS MOVED TO /code/defines/obj/supplypacks.dm
|
||||
|
||||
/obj/structure/plasticflaps //HOW DO YOU CALL THOSE THINGS ANYWAY
|
||||
name = "plastic flaps"
|
||||
desc = "Definitely can't get past those. No way."
|
||||
icon = 'icons/obj/stationobjs.dmi' //Change this.
|
||||
icon_state = "plasticflaps"
|
||||
density = 0
|
||||
anchored = 1
|
||||
layer = 4
|
||||
|
||||
/obj/structure/plasticflaps/CanPass(atom/movable/A, turf/T)
|
||||
if(istype(A) && A.checkpass(PASSGLASS))
|
||||
return prob(60)
|
||||
|
||||
var/obj/structure/stool/bed/B = A
|
||||
if (istype(A, /obj/structure/stool/bed) && B.buckled_mob)//if it's a bed/chair and someone is buckled, it will not pass
|
||||
return 0
|
||||
|
||||
else if(istype(A, /mob/living)) // You Shall Not Pass!
|
||||
var/mob/living/M = A
|
||||
if(!M.lying && !istype(M, /mob/living/carbon/monkey) && !istype(M, /mob/living/carbon/slime)) //If your not laying down, or a small creature, no pass.
|
||||
return 0
|
||||
return ..()
|
||||
|
||||
/obj/structure/plasticflaps/ex_act(severity, target)
|
||||
..()
|
||||
switch(severity)
|
||||
if (1)
|
||||
qdel(src)
|
||||
if (2)
|
||||
if (prob(50))
|
||||
qdel(src)
|
||||
if (3)
|
||||
if (prob(5))
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/plasticflaps/mining //A specific type for mining that doesn't allow airflow because of them damn crates
|
||||
name = "airtight plastic flaps"
|
||||
desc = "Heavy duty, airtight, plastic flaps."
|
||||
|
||||
/obj/structure/plasticflaps/mining/New() //set the turf below the flaps to block air
|
||||
var/turf/T = get_turf(loc)
|
||||
if(T)
|
||||
T.blocks_air = 1
|
||||
..()
|
||||
|
||||
/obj/structure/plasticflaps/mining/Destroy() //lazy hack to set the turf to allow air to pass if it's a simulated floor //wow this is terrible
|
||||
var/turf/T = get_turf(loc)
|
||||
if(T)
|
||||
if(istype(T, /turf/simulated/floor))
|
||||
T.blocks_air = 0
|
||||
..()
|
||||
|
||||
/obj/machinery/computer/supplycomp
|
||||
name = "supply shuttle console"
|
||||
desc = "Used to order supplies."
|
||||
icon = 'icons/obj/computer.dmi'
|
||||
icon_state = "supply"
|
||||
req_access = list(access_cargo)
|
||||
circuit = /obj/item/weapon/circuitboard/supplycomp
|
||||
var/temp = null
|
||||
var/reqtime = 0 //Cooldown for requisitions - Quarxink
|
||||
var/hacked = 0
|
||||
var/can_order_contraband = 0
|
||||
var/last_viewed_group = "categories"
|
||||
|
||||
|
||||
/obj/machinery/computer/ordercomp
|
||||
name = "supply ordering console"
|
||||
desc = "Used to order supplies from cargo staff."
|
||||
icon = 'icons/obj/computer.dmi'
|
||||
icon_state = "request"
|
||||
circuit = /obj/item/weapon/circuitboard/ordercomp
|
||||
var/temp = null
|
||||
var/reqtime = 0 //Cooldown for requisitions - Quarxink
|
||||
var/last_viewed_group = "categories"
|
||||
|
||||
/*
|
||||
/obj/effect/marker/supplymarker
|
||||
icon_state = "X"
|
||||
icon = 'icons/misc/mark.dmi'
|
||||
name = "X"
|
||||
invisibility = 101
|
||||
anchored = 1
|
||||
opacity = 0
|
||||
*/
|
||||
|
||||
/datum/supply_order
|
||||
var/ordernum
|
||||
var/datum/supply_packs/object = null
|
||||
var/orderedby = null
|
||||
var/comment = null
|
||||
|
||||
/datum/controller/supply_shuttle
|
||||
var/processing = 1
|
||||
var/processing_interval = 300
|
||||
var/iteration = 0
|
||||
//supply points
|
||||
var/points = 50
|
||||
var/points_per_process = 1
|
||||
var/points_per_slip = 2
|
||||
var/points_per_crate = 5
|
||||
var/points_per_intel = 100
|
||||
var/plasma_per_point = 0.2 //5 points per plasma sheet due to increased rarity
|
||||
var/centcom_message = "" // Remarks from Centcom on how well you checked the last order.
|
||||
// Unique typepaths for unusual things we've already sent CentComm, associated with their potencies
|
||||
var/list/discoveredPlants = list()
|
||||
//control
|
||||
var/ordernum
|
||||
var/list/shoppinglist = list()
|
||||
var/list/requestlist = list()
|
||||
var/list/supply_packs = list()
|
||||
//shuttle movement
|
||||
var/at_station = 0
|
||||
var/movetime = 1200
|
||||
var/moving = 0
|
||||
var/eta_timeofday
|
||||
var/eta
|
||||
//shuttle loan
|
||||
var/datum/round_event/shuttle_loan/shuttle_loan
|
||||
//for logging
|
||||
var/sold_atoms = ""
|
||||
|
||||
/datum/controller/supply_shuttle/New()
|
||||
ordernum = rand(1,9000)
|
||||
for(var/typepath in (typesof(/datum/supply_packs) - /datum/supply_packs))
|
||||
var/datum/supply_packs/P = new typepath()
|
||||
if(P.name == "HEADER") continue // To filter out group headers
|
||||
supply_packs[P.name] = P
|
||||
|
||||
//Supply shuttle ticker - handles supply point regenertion and shuttle travelling between centcom and the station
|
||||
/datum/controller/supply_shuttle/proc/process()
|
||||
|
||||
spawn(0)
|
||||
set background = BACKGROUND_ENABLED
|
||||
while(1)
|
||||
if(processing)
|
||||
iteration++
|
||||
points += points_per_process
|
||||
|
||||
if(moving == 1)
|
||||
var/ticksleft = (eta_timeofday - world.timeofday)
|
||||
if(ticksleft > 0)
|
||||
eta = round(ticksleft/600,1)
|
||||
else
|
||||
eta = 0
|
||||
send()
|
||||
|
||||
|
||||
sleep(processing_interval)
|
||||
|
||||
/datum/controller/supply_shuttle/proc/send()
|
||||
var/area/from
|
||||
var/area/dest
|
||||
switch(at_station)
|
||||
if(1)
|
||||
from = locate(SUPPLY_STATION_AREATYPE)
|
||||
dest = locate(SUPPLY_DOCK_AREATYPE)
|
||||
at_station = 0
|
||||
if(0)
|
||||
from = locate(SUPPLY_DOCK_AREATYPE)
|
||||
dest = locate(SUPPLY_STATION_AREATYPE)
|
||||
at_station = 1
|
||||
dest.clear_docking_area()
|
||||
moving = 0
|
||||
|
||||
from.move_contents_to(dest)
|
||||
|
||||
//Check whether the shuttle is allowed to move
|
||||
/datum/controller/supply_shuttle/proc/can_move()
|
||||
if(moving) return 0
|
||||
|
||||
var/area/shuttle = locate(/area/supply/station)
|
||||
if(!shuttle) return 0
|
||||
|
||||
if(forbidden_atoms_check(shuttle) && at_station)
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
//To stop things being sent to centcom which should not be sent to centcom Recursively checks for these types.
|
||||
/datum/controller/supply_shuttle/proc/forbidden_atoms_check(atom/A)
|
||||
if(istype(A,/mob/living))
|
||||
return 1
|
||||
if(istype(A,/obj/item/weapon/disk/nuclear))
|
||||
return 1
|
||||
if(istype(A,/obj/machinery/nuclearbomb))
|
||||
return 1
|
||||
if(istype(A,/obj/item/device/radio/beacon))
|
||||
return 1
|
||||
if(istype(A,/obj/effect/blob))
|
||||
return 1
|
||||
if(istype(A,/obj/effect/spider/spiderling))
|
||||
return 1
|
||||
|
||||
for(var/i=1, i<=A.contents.len, i++)
|
||||
var/atom/B = A.contents[i]
|
||||
if(.(B))
|
||||
return 1
|
||||
|
||||
//Sellin
|
||||
/datum/controller/supply_shuttle/proc/sell()
|
||||
var/shuttle_at
|
||||
if(at_station) shuttle_at = SUPPLY_STATION_AREATYPE
|
||||
else shuttle_at = SUPPLY_DOCK_AREATYPE
|
||||
|
||||
var/area/shuttle = locate(shuttle_at)
|
||||
if(!shuttle) return
|
||||
|
||||
var/plasma_count = 0
|
||||
var/intel_count = 0
|
||||
var/crate_count = 0
|
||||
|
||||
centcom_message = ""
|
||||
|
||||
for(var/atom/movable/MA in shuttle)
|
||||
sold_atoms += " [MA.name]"
|
||||
if(MA.anchored) continue
|
||||
|
||||
|
||||
// Must be in a crate (or a critter crate)!
|
||||
if(istype(MA,/obj/structure/closet/crate) || istype(MA,/obj/structure/closet/critter))
|
||||
sold_atoms += ":"
|
||||
crate_count++
|
||||
var/find_slip = 1
|
||||
|
||||
for(var/atom in MA)
|
||||
// Sell manifests
|
||||
var/atom/A = atom
|
||||
sold_atoms += " [A.name]"
|
||||
if(find_slip && istype(A,/obj/item/weapon/paper/manifest))
|
||||
var/obj/item/weapon/paper/manifest/slip = A
|
||||
// TODO: Check for a signature, too.
|
||||
if(slip.stamped && slip.stamped.len) //yes, the clown stamp will work. clown is the highest authority on the station, it makes sense
|
||||
// Did they mark it as erroneous?
|
||||
var/denied = 0
|
||||
for(var/i=1,i<=slip.stamped.len,i++)
|
||||
if(slip.stamped[i] == /obj/item/weapon/stamp/denied)
|
||||
denied = 1
|
||||
if(slip.erroneous && denied) // Caught a mistake by Centcom (IDEA: maybe Centcom rarely gets offended by this)
|
||||
points += slip.points-points_per_crate // For now, give a full refund for paying attention (minus the crate cost)
|
||||
centcom_message += "<font color=green>+[slip.points-points_per_crate]</font>: Station correctly denied package [slip.ordernumber]: "
|
||||
if(slip.erroneous & MANIFEST_ERROR_NAME)
|
||||
centcom_message += "Destination station incorrect. "
|
||||
else if(slip.erroneous & MANIFEST_ERROR_COUNT)
|
||||
centcom_message += "Packages incorrectly counted. "
|
||||
else if(slip.erroneous & MANIFEST_ERROR_ITEM)
|
||||
centcom_message += "Package incomplete. "
|
||||
centcom_message += "Points refunded.<BR>"
|
||||
else if(!slip.erroneous && !denied) // Approving a proper order awards the relatively tiny points_per_slip
|
||||
points += points_per_slip
|
||||
centcom_message += "<font color=green>+1</font>: Package [slip.ordernumber] accorded.<BR>"
|
||||
else // You done goofed.
|
||||
if(slip.erroneous)
|
||||
centcom_message += "<font color=red>+0</font>: Station approved package [slip.ordernumber] despite error: "
|
||||
if(slip.erroneous & MANIFEST_ERROR_NAME)
|
||||
centcom_message += "Destination station incorrect."
|
||||
else if(slip.erroneous & MANIFEST_ERROR_COUNT)
|
||||
centcom_message += "Packages incorrectly counted."
|
||||
else if(slip.erroneous & MANIFEST_ERROR_ITEM)
|
||||
centcom_message += "We found unshipped items on our dock."
|
||||
centcom_message += " Be more vigilant.<BR>"
|
||||
else
|
||||
points -= slip.points-points_per_crate
|
||||
centcom_message += "<font color=red>-[slip.points-points_per_crate]</font>: Station denied package [slip.ordernumber]. Our records show no fault on our part.<BR>"
|
||||
find_slip = 0
|
||||
continue
|
||||
|
||||
// Sell plasma
|
||||
if(istype(A, /obj/item/stack/sheet/mineral/plasma))
|
||||
var/obj/item/stack/sheet/mineral/plasma/P = A
|
||||
plasma_count += P.amount
|
||||
|
||||
// Sell syndicate intel
|
||||
if(istype(A, /obj/item/documents/syndicate))
|
||||
intel_count += 1
|
||||
|
||||
if(istype(A, /obj/item/seeds))
|
||||
var/obj/item/seeds/S = A
|
||||
if(S.rarity == 0) // Mundane species
|
||||
centcom_message += "<font color=red>+0</font>: We don't need samples of mundane species \"[capitalize(S.species)]\".<BR>"
|
||||
else if(discoveredPlants[S.type]) // This species has already been sent to CentComm
|
||||
var/potDiff = S.potency - discoveredPlants[S.type] // Compare it to the previous best
|
||||
if(potDiff > 0) // This sample is better
|
||||
discoveredPlants[S.type] = S.potency
|
||||
centcom_message += "<font color=green>+[potDiff]</font>: New sample of \"[capitalize(S.species)]\" is superior. Good work.<BR>"
|
||||
points += potDiff
|
||||
else // This sample is worthless
|
||||
centcom_message += "<font color=red>+0</font>: New sample of \"[capitalize(S.species)]\" is not more potent than existing sample ([discoveredPlants[S.type]] potency).<BR>"
|
||||
else // This is a new discovery!
|
||||
discoveredPlants[S.type] = S.potency
|
||||
centcom_message += "<font color=green>+[S.rarity]</font>: New species discovered: \"[capitalize(S.species)]\". Excellent work.<BR>"
|
||||
points += S.rarity // That's right, no bonus for potency. Send a crappy sample first to "show improvement" later
|
||||
qdel(MA)
|
||||
sold_atoms += "."
|
||||
|
||||
if(plasma_count)
|
||||
centcom_message += "<font color=green>+[round(plasma_count/plasma_per_point)]</font>: Received [plasma_count] unit(s) of exotic material.<BR>"
|
||||
points += round(plasma_count / plasma_per_point)
|
||||
|
||||
if(intel_count)
|
||||
centcom_message += "<font color=green>+[round(intel_count*points_per_intel)]</font>: Received [intel_count] article(s) of enemy intelligence.<BR>"
|
||||
points += round(intel_count*points_per_intel)
|
||||
|
||||
if(crate_count)
|
||||
centcom_message += "<font color=green>+[round(crate_count*points_per_crate)]</font>: Received [crate_count] crate(s).<BR>"
|
||||
points += crate_count * points_per_crate
|
||||
|
||||
//Buyin
|
||||
/datum/controller/supply_shuttle/proc/buy()
|
||||
if(!shoppinglist.len) return
|
||||
|
||||
var/shuttle_at
|
||||
if(at_station) shuttle_at = SUPPLY_STATION_AREATYPE
|
||||
else shuttle_at = SUPPLY_DOCK_AREATYPE
|
||||
|
||||
var/area/shuttle = locate(shuttle_at)
|
||||
if(!shuttle) return
|
||||
|
||||
var/list/clear_turfs = list()
|
||||
|
||||
for(var/turf/T in shuttle)
|
||||
if(T.density || T.contents.len) continue
|
||||
clear_turfs += T
|
||||
|
||||
for(var/S in shoppinglist)
|
||||
if(!clear_turfs.len) break
|
||||
var/i = rand(1,clear_turfs.len)
|
||||
var/turf/pickedloc = clear_turfs[i]
|
||||
clear_turfs.Cut(i,i+1)
|
||||
|
||||
var/datum/supply_order/SO = S
|
||||
var/datum/supply_packs/SP = SO.object
|
||||
|
||||
var/atom/A = new SP.containertype(pickedloc)
|
||||
A.name = "[SP.containername] [SO.comment ? "([SO.comment])":"" ]"
|
||||
|
||||
//supply manifest generation begin
|
||||
|
||||
var/obj/item/weapon/paper/manifest/slip = new /obj/item/weapon/paper/manifest(A)
|
||||
|
||||
var printed_station_name = station_name()
|
||||
if(prob(5))
|
||||
printed_station_name = new_station_name()
|
||||
slip.erroneous |= MANIFEST_ERROR_NAME // They got our station name wrong. BASTARDS!
|
||||
// IDEA: Have Centcom accidentally send random low-value crates in large orders, give large bonus for returning them intact.
|
||||
var printed_packages_amount = supply_shuttle.shoppinglist.len
|
||||
if(prob(5))
|
||||
printed_packages_amount += rand(1,2) // I considered rand(-2,2), but that could be zero. Heh.
|
||||
slip.erroneous |= MANIFEST_ERROR_COUNT // They typoed the number of crates in this shipment. It won't match the other manifests.
|
||||
|
||||
slip.points = SP.cost
|
||||
slip.ordernumber = SO.ordernum
|
||||
slip.info = "<h3>[command_name()] Shipping Manifest</h3><hr><br>"
|
||||
slip.info +="Order #[SO.ordernum]<br>"
|
||||
slip.info +="Destination: [printed_station_name]<br>"
|
||||
slip.info +="[printed_packages_amount] PACKAGES IN THIS SHIPMENT<br>"
|
||||
slip.info +="CONTENTS:<br><ul>"
|
||||
|
||||
//spawn the stuff, finish generating the manifest while you're at it
|
||||
if(SP.access)
|
||||
A:req_access = list()
|
||||
A:req_access += text2num(SP.access)
|
||||
|
||||
var/list/contains
|
||||
if(istype(SP,/datum/supply_packs/misc/randomised))
|
||||
var/datum/supply_packs/misc/randomised/SPR = SP
|
||||
contains = list()
|
||||
if(SPR.contains.len)
|
||||
for(var/j=1,j<=SPR.num_contained,j++)
|
||||
contains += pick(SPR.contains)
|
||||
else
|
||||
contains = SP.contains
|
||||
|
||||
for(var/typepath in contains)
|
||||
if(!typepath) continue
|
||||
var/atom/B2 = new typepath(A)
|
||||
if(SP.amount && B2:amount) B2:amount = SP.amount
|
||||
slip.info += "<li>[B2.name]</li>" //add the item to the manifest (even if it was misplaced)
|
||||
// If it has multiple items, there's a 1% of each going missing... Not for secure crates or those large wooden ones, though.
|
||||
if(contains.len > 1 && prob(1) && !findtext(SP.containertype,"/secure/") && !findtext(SP.containertype,"/largecrate/"))
|
||||
slip.erroneous |= MANIFEST_ERROR_ITEM // This item was not included in the shipment!
|
||||
qdel(B2) // Lost in space... or the loading dock.
|
||||
|
||||
//manifest finalisation
|
||||
slip.info += "</ul><br>"
|
||||
slip.info += "CHECK CONTENTS AND STAMP BELOW THE LINE TO CONFIRM RECEIPT OF GOODS<hr>" // And now this is actually meaningful.
|
||||
|
||||
supply_shuttle.shoppinglist.Cut()
|
||||
return
|
||||
|
||||
/obj/item/weapon/paper/manifest
|
||||
name = "supply manifest"
|
||||
var/erroneous = 0
|
||||
var/points = 0
|
||||
var/ordernumber = 0
|
||||
|
||||
/obj/machinery/computer/ordercomp/attack_hand(var/mob/user as mob)
|
||||
if(..())
|
||||
return
|
||||
user.set_machine(src)
|
||||
var/dat
|
||||
if(temp)
|
||||
dat = temp
|
||||
else
|
||||
dat += {"<div class='statusDisplay'>Shuttle Location: [supply_shuttle.moving ? "Moving to station ([supply_shuttle.eta] Mins.)":supply_shuttle.at_station ? "Station":"Dock"]<BR>
|
||||
<HR>Supply Points: [supply_shuttle.points]<BR></div>
|
||||
|
||||
<BR>\n<A href='?src=\ref[src];order=categories'>Request items</A><BR><BR>
|
||||
<A href='?src=\ref[src];vieworders=1'>View approved orders</A><BR><BR>
|
||||
<A href='?src=\ref[src];viewrequests=1'>View requests</A><BR><BR>
|
||||
<A href='?src=\ref[user];mach_close=computer'>Close</A>"}
|
||||
|
||||
// Removing the old window method but leaving it here for reference
|
||||
//user << browse(dat, "window=computer;size=575x450")
|
||||
//onclose(user, "computer")
|
||||
|
||||
// Added the new browser window method
|
||||
var/datum/browser/popup = new(user, "computer", "Supply Ordering Console", 575, 450)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.open()
|
||||
return
|
||||
|
||||
/obj/machinery/computer/ordercomp/Topic(href, href_list)
|
||||
if(..())
|
||||
return
|
||||
|
||||
if( isturf(loc) && (in_range(src, usr) || istype(usr, /mob/living/silicon)) )
|
||||
usr.set_machine(src)
|
||||
|
||||
if(href_list["order"])
|
||||
if(href_list["order"] == "categories")
|
||||
//all_supply_groups
|
||||
//Request what?
|
||||
last_viewed_group = "categories"
|
||||
temp = "<div class='statusDisplay'><b>Supply points: [supply_shuttle.points]</b><BR>"
|
||||
temp += "<A href='?src=\ref[src];mainmenu=1'>Main Menu</A><BR></div><BR>"
|
||||
temp += "<b>Select a category</b><BR><BR>"
|
||||
for(var/cat in all_supply_groups )
|
||||
temp += "<A href='?src=\ref[src];order=[cat]'>[get_supply_group_name(cat)]</A><BR>"
|
||||
else
|
||||
last_viewed_group = href_list["order"]
|
||||
var/cat = text2num(last_viewed_group)
|
||||
temp = "<div class='statusDisplay'><b>Supply points: [supply_shuttle.points]</b><BR>"
|
||||
temp += "<A href='?src=\ref[src];order=categories'>Back to all categories</A><BR></div><BR>"
|
||||
temp += "<b>Request from: [get_supply_group_name(cat)]</b><BR><BR>"
|
||||
for(var/supply_name in supply_shuttle.supply_packs )
|
||||
var/datum/supply_packs/N = supply_shuttle.supply_packs[supply_name]
|
||||
if(N.hidden || N.contraband || N.group != cat) continue //Have to send the type instead of a reference to
|
||||
temp += "<A href='?src=\ref[src];doorder=[supply_name]'>[supply_name]</A> Cost: [N.cost]<BR>" //the obj because it would get caught by the garbage
|
||||
else if (href_list["doorder"])
|
||||
if(world.time < reqtime)
|
||||
say("[world.time - reqtime] seconds remaining until another requisition form may be printed.")
|
||||
return
|
||||
|
||||
//Find the correct supply_pack datum
|
||||
var/datum/supply_packs/P = supply_shuttle.supply_packs[href_list["doorder"]]
|
||||
if(!istype(P) || P.hidden || P.contraband) //href protection
|
||||
return
|
||||
|
||||
var/timeout = world.time + 600
|
||||
var/reason = stripped_input(usr,"Reason:","Why do you require this item?","")
|
||||
if(world.time > timeout) return
|
||||
if(!reason) return
|
||||
|
||||
var/idname = "*None Provided*"
|
||||
var/idrank = "*None Provided*"
|
||||
if(ishuman(usr))
|
||||
var/mob/living/carbon/human/H = usr
|
||||
idname = H.get_authentification_name()
|
||||
idrank = H.get_assignment()
|
||||
else if(issilicon(usr))
|
||||
idname = usr.real_name
|
||||
|
||||
supply_shuttle.ordernum++
|
||||
var/obj/item/weapon/paper/reqform = new /obj/item/weapon/paper(loc)
|
||||
reqform.name = "requisition form - [P.name]"
|
||||
reqform.info += "<h3>[station_name] Supply Requisition Form</h3><hr>"
|
||||
reqform.info += "INDEX: #[supply_shuttle.ordernum]<br>"
|
||||
reqform.info += "REQUESTED BY: [idname]<br>"
|
||||
reqform.info += "RANK: [idrank]<br>"
|
||||
reqform.info += "REASON: [reason]<br>"
|
||||
reqform.info += "SUPPLY CRATE TYPE: [P.name]<br>"
|
||||
reqform.info += "ACCESS RESTRICTION: [replacetext(get_access_desc(P.access))]<br>"
|
||||
reqform.info += "CONTENTS:<br>"
|
||||
reqform.info += P.manifest
|
||||
reqform.info += "<hr>"
|
||||
reqform.info += "STAMP BELOW TO APPROVE THIS REQUISITION:<br>"
|
||||
|
||||
reqform.update_icon() //Fix for appearing blank when printed.
|
||||
reqtime = (world.time + 5) % 1e5
|
||||
|
||||
//make our supply_order datum
|
||||
var/datum/supply_order/O = new /datum/supply_order()
|
||||
O.ordernum = supply_shuttle.ordernum
|
||||
O.object = P
|
||||
O.orderedby = idname
|
||||
supply_shuttle.requestlist += O
|
||||
|
||||
temp = "Thanks for your request. The cargo team will process it as soon as possible.<BR>"
|
||||
temp += "<BR><A href='?src=\ref[src];order=[last_viewed_group]'>Back</A> <A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
|
||||
else if (href_list["vieworders"])
|
||||
temp = "<A href='?src=\ref[src];mainmenu=1'>Main Menu</A><BR><BR>Current approved orders: <BR><BR>"
|
||||
for(var/S in supply_shuttle.shoppinglist)
|
||||
var/datum/supply_order/SO = S
|
||||
temp += "[SO.object.name] approved by [SO.orderedby] [SO.comment ? "([SO.comment])":""]<BR>"
|
||||
temp += "<BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
|
||||
else if (href_list["viewrequests"])
|
||||
temp = "Current requests: <BR><BR>"
|
||||
for(var/S in supply_shuttle.requestlist)
|
||||
var/datum/supply_order/SO = S
|
||||
temp += "#[SO.ordernum] - [SO.object.name] requested by [SO.orderedby]<BR>"
|
||||
temp += "<BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
|
||||
else if (href_list["mainmenu"])
|
||||
temp = null
|
||||
|
||||
add_fingerprint(usr)
|
||||
updateUsrDialog()
|
||||
return
|
||||
|
||||
/obj/machinery/computer/ordercomp/say_quote(text)
|
||||
return "flashes, \"[text]\""
|
||||
|
||||
|
||||
/obj/machinery/computer/supplycomp/attack_hand(var/mob/user as mob)
|
||||
if(!allowed(user))
|
||||
user << "<span class='warning'>Access Denied.</span>"
|
||||
return
|
||||
|
||||
if(..())
|
||||
return
|
||||
user.set_machine(src)
|
||||
post_signal("supply")
|
||||
var/dat
|
||||
if (temp)
|
||||
dat = temp
|
||||
else
|
||||
dat += {"<div class='statusDisplay'><B>Supply shuttle</B><HR>
|
||||
\nLocation: [supply_shuttle.moving ? "Moving to station ([supply_shuttle.eta] Mins.)":supply_shuttle.at_station ? "Station":"Away"]<BR>
|
||||
<HR>\nSupply Points: [supply_shuttle.points]<BR>\n</div><BR>
|
||||
[supply_shuttle.moving ? "\n*Must be away to order items*<BR>\n<BR>":supply_shuttle.at_station ? "\n*Must be away to order items*<BR>\n<BR>":"\n<A href='?src=\ref[src];order=categories'>Order items</A><BR>\n<BR>"]
|
||||
[supply_shuttle.moving ? "\n*Shuttle already called*<BR>\n<BR>":supply_shuttle.at_station ? "\n<A href='?src=\ref[src];send=1'>Send away</A><BR>\n<BR>":"\n<A href='?src=\ref[src];send=1'>Send to station</A><BR>\n<BR>"]
|
||||
[supply_shuttle.shuttle_loan ? (supply_shuttle.shuttle_loan.dispatched ? "\n*Shuttle loaned to Centcom*<BR>\n<BR>" : "\n<A href='?src=\ref[src];send=1;loan=1'>Loan shuttle to Centcom (5 mins duration)</A><BR>\n<BR>") : "\n*No pending external shuttle requests*<BR>\n<BR>"]
|
||||
\n<A href='?src=\ref[src];viewrequests=1'>View requests</A><BR>\n<BR>
|
||||
\n<A href='?src=\ref[src];vieworders=1'>View orders</A><BR>\n<BR>
|
||||
\n<A href='?src=\ref[user];mach_close=computer'>Close</A><BR>
|
||||
<HR>\n<B>Central Command messages:</B><BR> [supply_shuttle.centcom_message ? supply_shuttle.centcom_message : "Remember to stamp and send back the supply manifests."]"}
|
||||
|
||||
var/datum/browser/popup = new(user, "computer", "Supply Shuttle Console", 700, 455)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
return
|
||||
|
||||
/obj/machinery/computer/supplycomp/emag_act(user as mob)
|
||||
if(!hacked)
|
||||
user << "<span class='notice'>Special supplies unlocked.</span>"
|
||||
hacked = 1
|
||||
|
||||
/obj/machinery/computer/supplycomp/Topic(href, href_list)
|
||||
if(!supply_shuttle)
|
||||
world.log << "## ERROR: Eek. The supply_shuttle controller datum is missing somehow."
|
||||
return
|
||||
if(..())
|
||||
return
|
||||
|
||||
if(isturf(loc) && ( in_range(src, usr) || istype(usr, /mob/living/silicon) ) )
|
||||
usr.set_machine(src)
|
||||
|
||||
//Calling the shuttle
|
||||
if(href_list["send"])
|
||||
if(!supply_shuttle.can_move())
|
||||
if(supply_shuttle.shuttle_loan)
|
||||
temp = "The supply shuttle must be docked to send new commands.<BR><BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
else
|
||||
temp = "For safety reasons the automated supply shuttle cannot transport live organisms, classified nuclear weaponry or homing beacons.<BR><BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
|
||||
else if(supply_shuttle.at_station)
|
||||
if(href_list["loan"] && supply_shuttle.shuttle_loan)
|
||||
if(!supply_shuttle.shuttle_loan.dispatched)
|
||||
supply_shuttle.sell()
|
||||
supply_shuttle.send()
|
||||
supply_shuttle.shuttle_loan.loan_shuttle()
|
||||
temp = "The supply shuttle has been loaned to Centcom.<BR><BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
post_signal("supply")
|
||||
else
|
||||
temp = "You can not loan the supply shuttle at this time.<BR><BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
else
|
||||
temp = "The supply shuttle has departed.<BR><BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
supply_shuttle.moving = -1
|
||||
supply_shuttle.sell()
|
||||
supply_shuttle.send()
|
||||
investigate_log("[usr.key] has sent the supply shuttle away. Remaining points: [supply_shuttle.points]. Shuttle contents:[supply_shuttle.sold_atoms].", "cargo")
|
||||
else
|
||||
if(href_list["loan"] && supply_shuttle.shuttle_loan)
|
||||
if(!supply_shuttle.shuttle_loan.dispatched)
|
||||
supply_shuttle.shuttle_loan.loan_shuttle()
|
||||
temp = "The supply shuttle has been loaned to Centcom.<BR><BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
post_signal("supply")
|
||||
else
|
||||
temp = "You can not loan the supply shuttle at this time.<BR><BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
else
|
||||
supply_shuttle.buy()
|
||||
temp = "The supply shuttle has been called and will arrive in [round(supply_shuttle.movetime/600,1)] minutes.<BR><BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
supply_shuttle.moving = 1
|
||||
supply_shuttle.eta_timeofday = (world.timeofday + supply_shuttle.movetime) % 864000
|
||||
post_signal("supply")
|
||||
|
||||
else if (href_list["order"])
|
||||
if(supply_shuttle.moving) return
|
||||
if(href_list["order"] == "categories")
|
||||
//all_supply_groups
|
||||
//Request what?
|
||||
last_viewed_group = "categories"
|
||||
temp = "<div class='statusDisplay'><b>Supply points: [supply_shuttle.points]</b><BR>"
|
||||
temp += "<A href='?src=\ref[src];mainmenu=1'>Main Menu</A><BR></div><BR>"
|
||||
temp += "<b>Select a category</b><BR><BR>"
|
||||
for(var/cat in all_supply_groups )
|
||||
temp += "<A href='?src=\ref[src];order=[cat]'>[get_supply_group_name(cat)]</A><BR>"
|
||||
else
|
||||
last_viewed_group = href_list["order"]
|
||||
var/cat = text2num(last_viewed_group)
|
||||
temp = "<div class='statusDisplay'><b>Supply points: [supply_shuttle.points]</b><BR>"
|
||||
temp += "<A href='?src=\ref[src];order=categories'>Back to all categories</A><BR></div><BR>"
|
||||
temp += "<b>Request from: [get_supply_group_name(cat)]</b><BR><BR>"
|
||||
for(var/supply_name in supply_shuttle.supply_packs )
|
||||
var/datum/supply_packs/N = supply_shuttle.supply_packs[supply_name]
|
||||
if((N.hidden && !hacked) || (N.contraband && !can_order_contraband) || N.group != cat) //Have to send the type instead of a reference to
|
||||
continue //the obj because it would get caught by the garbage
|
||||
temp += "<A href='?src=\ref[src];doorder=[supply_name]'>[supply_name]</A> Cost: [N.cost]<BR>"
|
||||
|
||||
/*temp = "Supply points: [supply_shuttle.points]<BR><HR><BR>Request what?<BR><BR>"
|
||||
|
||||
for(var/supply_name in supply_shuttle.supply_packs )
|
||||
var/datum/supply_packs/N = supply_shuttle.supply_packs[supply_name]
|
||||
if(N.hidden && !hacked) continue
|
||||
if(N.contraband && !can_order_contraband) continue
|
||||
temp += "<A href='?src=\ref[src];doorder=[supply_name]'>[supply_name]</A> Cost: [N.cost]<BR>" //the obj because it would get caught by the garbage
|
||||
temp += "<BR><A href='?src=\ref[src];mainmenu=1'>OK</A>"*/
|
||||
|
||||
else if (href_list["doorder"])
|
||||
if(world.time < reqtime)
|
||||
say("[world.time - reqtime] seconds remaining until another requisition form may be printed.")
|
||||
return
|
||||
|
||||
//Find the correct supply_pack datum
|
||||
var/datum/supply_packs/P = supply_shuttle.supply_packs[href_list["doorder"]]
|
||||
if(!istype(P) || (P.hidden && !hacked) || (P.contraband && !can_order_contraband)) //href exploit protection
|
||||
return
|
||||
|
||||
var/timeout = world.time + 600
|
||||
var/reason = stripped_input(usr,"Reason:","Why do you require this item?","")
|
||||
if(world.time > timeout) return
|
||||
// if(!reason) return
|
||||
|
||||
var/idname = "*None Provided*"
|
||||
var/idrank = "*None Provided*"
|
||||
if(ishuman(usr))
|
||||
var/mob/living/carbon/human/H = usr
|
||||
idname = H.get_authentification_name()
|
||||
idrank = H.get_assignment()
|
||||
else if(issilicon(usr))
|
||||
idname = usr.real_name
|
||||
|
||||
supply_shuttle.ordernum++
|
||||
var/obj/item/weapon/paper/reqform = new /obj/item/weapon/paper(loc)
|
||||
reqform.name = "requisition form - [P.name]"
|
||||
reqform.info += "<h3>[station_name] Supply Requisition Form</h3><hr>"
|
||||
reqform.info += "INDEX: #[supply_shuttle.ordernum]<br>"
|
||||
reqform.info += "REQUESTED BY: [idname]<br>"
|
||||
reqform.info += "RANK: [idrank]<br>"
|
||||
reqform.info += "REASON: [reason]<br>"
|
||||
reqform.info += "SUPPLY CRATE TYPE: [P.name]<br>"
|
||||
reqform.info += "ACCESS RESTRICTION: [replacetext(get_access_desc(P.access))]<br>"
|
||||
reqform.info += "CONTENTS:<br>"
|
||||
reqform.info += P.manifest
|
||||
reqform.info += "<hr>"
|
||||
reqform.info += "STAMP BELOW TO APPROVE THIS REQUISITION:<br>"
|
||||
|
||||
reqform.update_icon() //Fix for appearing blank when printed.
|
||||
reqtime = (world.time + 5) % 1e5
|
||||
|
||||
//make our supply_order datum
|
||||
var/datum/supply_order/O = new /datum/supply_order()
|
||||
O.ordernum = supply_shuttle.ordernum
|
||||
O.object = P
|
||||
O.orderedby = idname
|
||||
supply_shuttle.requestlist += O
|
||||
|
||||
temp = "Order request placed.<BR>"
|
||||
temp += "<BR><A href='?src=\ref[src];order=[last_viewed_group]'>Back</A> | <A href='?src=\ref[src];mainmenu=1'>Main Menu</A> | <A href='?src=\ref[src];confirmorder=[O.ordernum]'>Authorize Order</A>"
|
||||
|
||||
else if(href_list["confirmorder"])
|
||||
//Find the correct supply_order datum
|
||||
var/ordernum = text2num(href_list["confirmorder"])
|
||||
var/datum/supply_order/O
|
||||
var/datum/supply_packs/P
|
||||
temp = "Invalid Request"
|
||||
for(var/i=1, i<=supply_shuttle.requestlist.len, i++)
|
||||
var/datum/supply_order/SO = supply_shuttle.requestlist[i]
|
||||
if(SO && SO.ordernum == ordernum)
|
||||
O = SO
|
||||
P = O.object
|
||||
if(supply_shuttle.points >= P.cost)
|
||||
supply_shuttle.requestlist.Cut(i,i+1)
|
||||
supply_shuttle.points -= P.cost
|
||||
supply_shuttle.shoppinglist += O
|
||||
temp = "Thanks for your order."
|
||||
investigate_log("[usr.key] has authorized an order for [P.name]. Remaining points: [supply_shuttle.points].", "cargo")
|
||||
else
|
||||
temp = "Not enough supply points."
|
||||
break
|
||||
temp += "<BR><BR><A href='?src=\ref[src];viewrequests=1'>Back</A> <A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
|
||||
else if (href_list["vieworders"])
|
||||
temp = "<A href='?src=\ref[src];mainmenu=1'>Main Menu</A><BR><BR>Current approved orders: <BR><BR>"
|
||||
for(var/S in supply_shuttle.shoppinglist)
|
||||
var/datum/supply_order/SO = S
|
||||
temp += "#[SO.ordernum] - [SO.object.name] approved by [SO.orderedby][SO.comment ? " ([SO.comment])":""]<BR>"// <A href='?src=\ref[src];cancelorder=[S]'>(Cancel)</A><BR>"
|
||||
temp += "<BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
/*
|
||||
else if (href_list["cancelorder"])
|
||||
var/datum/supply_order/remove_supply = href_list["cancelorder"]
|
||||
supply_shuttle_shoppinglist -= remove_supply
|
||||
supply_shuttle_points += remove_supply.object.cost
|
||||
temp += "Canceled: [remove_supply.object.name]<BR><BR><BR>"
|
||||
|
||||
for(var/S in supply_shuttle_shoppinglist)
|
||||
var/datum/supply_order/SO = S
|
||||
temp += "[SO.object.name] approved by [SO.orderedby][SO.comment ? " ([SO.comment])":""] <A href='?src=\ref[src];cancelorder=[S]'>(Cancel)</A><BR>"
|
||||
temp += "<BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
*/
|
||||
else if (href_list["viewrequests"])
|
||||
temp = "Current requests: <BR><BR>"
|
||||
for(var/S in supply_shuttle.requestlist)
|
||||
var/datum/supply_order/SO = S
|
||||
temp += "#[SO.ordernum] - [SO.object.name] requested by [SO.orderedby] [supply_shuttle.moving ? "":supply_shuttle.at_station ? "":"<A href='?src=\ref[src];confirmorder=[SO.ordernum]'>Approve</A> <A href='?src=\ref[src];rreq=[SO.ordernum]'>Remove</A>"]<BR>"
|
||||
|
||||
temp += "<BR><A href='?src=\ref[src];clearreq=1'>Clear list</A>"
|
||||
temp += "<BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
|
||||
else if (href_list["rreq"])
|
||||
var/ordernum = text2num(href_list["rreq"])
|
||||
temp = "Invalid Request.<BR>"
|
||||
for(var/i=1, i<=supply_shuttle.requestlist.len, i++)
|
||||
var/datum/supply_order/SO = supply_shuttle.requestlist[i]
|
||||
if(SO && SO.ordernum == ordernum)
|
||||
supply_shuttle.requestlist.Cut(i,i+1)
|
||||
temp = "Request removed.<BR>"
|
||||
break
|
||||
temp += "<BR><A href='?src=\ref[src];viewrequests=1'>Back</A> <A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
|
||||
else if (href_list["clearreq"])
|
||||
supply_shuttle.requestlist.Cut()
|
||||
temp = "List cleared.<BR>"
|
||||
temp += "<BR><A href='?src=\ref[src];mainmenu=1'>Main Menu</A>"
|
||||
|
||||
else if (href_list["mainmenu"])
|
||||
temp = null
|
||||
|
||||
add_fingerprint(usr)
|
||||
updateUsrDialog()
|
||||
return
|
||||
|
||||
/obj/machinery/computer/supplycomp/proc/post_signal(var/command)
|
||||
|
||||
var/datum/radio_frequency/frequency = radio_controller.return_frequency(1435)
|
||||
|
||||
if(!frequency) return
|
||||
|
||||
var/datum/signal/status_signal = new
|
||||
status_signal.source = src
|
||||
status_signal.transmission_method = 1
|
||||
status_signal.data["command"] = command
|
||||
|
||||
frequency.post_signal(src, status_signal)
|
||||
|
||||
|
||||
/obj/machinery/computer/supplycomp/say_quote(text)
|
||||
return "flashes, \"[text]\""
|
||||
|
||||
|
||||
+11
-41
@@ -1,7 +1,7 @@
|
||||
//TODO: rewrite and standardise all controller datums to the datum/controller type
|
||||
//TODO: allow all controllers to be deleted for clean restarts (see WIP master controller stuff) - MC done - lighting done
|
||||
|
||||
/client/proc/restart_controller(controller in list("Master","Failsafe","Lighting","Supply Shuttle"))
|
||||
/client/proc/restart_controller(controller in list("Master","Failsafe"))
|
||||
set category = "Debug"
|
||||
set name = "Restart Controller"
|
||||
set desc = "Restart one of the various periodic loop controllers for the game (be careful!)"
|
||||
@@ -17,18 +17,12 @@
|
||||
if("Failsafe")
|
||||
new /datum/controller/failsafe()
|
||||
feedback_add_details("admin_verb","RFailsafe")
|
||||
if("Lighting")
|
||||
new /datum/controller/lighting()
|
||||
lighting_controller.process()
|
||||
feedback_add_details("admin_verb","RLighting")
|
||||
if("Supply Shuttle")
|
||||
supply_shuttle.process()
|
||||
feedback_add_details("admin_verb","RSupply")
|
||||
|
||||
message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.")
|
||||
return
|
||||
|
||||
|
||||
/client/proc/debug_controller(controller in list("Master","Failsafe","Ticker","Lighting","Garbage","Air","Jobs","Sun","Radio","Supply Shuttle","Emergency Shuttle","Configuration","pAI", "Cameras", "Events"))
|
||||
/client/proc/debug_controller(controller in list("Master","Failsafe","Ticker","Jobs","Radio","Configuration", "Cameras"))
|
||||
set category = "Debug"
|
||||
set name = "Debug Controller"
|
||||
set desc = "Debug the various periodic loop controllers for the game (be careful!)"
|
||||
@@ -37,48 +31,24 @@
|
||||
switch(controller)
|
||||
if("Master")
|
||||
debug_variables(master_controller)
|
||||
feedback_add_details("admin_verb","DMC")
|
||||
|
||||
if("Failsafe")
|
||||
debug_variables(Failsafe)
|
||||
feedback_add_details("admin_verb","DFailsafe")
|
||||
|
||||
if("Ticker")
|
||||
debug_variables(ticker)
|
||||
feedback_add_details("admin_verb","DTicker")
|
||||
if("Lighting")
|
||||
debug_variables(lighting_controller)
|
||||
feedback_add_details("admin_verb","DLighting")
|
||||
if("Garbage")
|
||||
debug_variables(garbage)
|
||||
feedback_add_details("admin_verb","DGarbage")
|
||||
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")
|
||||
debug_variables(SSjob)
|
||||
|
||||
if("Radio")
|
||||
debug_variables(radio_controller)
|
||||
feedback_add_details("admin_verb","DRadio")
|
||||
if("Supply Shuttle")
|
||||
debug_variables(supply_shuttle)
|
||||
feedback_add_details("admin_verb","DSupply")
|
||||
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("Events")
|
||||
debug_variables(events)
|
||||
feedback_add_details("admin_verb","DEvents")
|
||||
|
||||
message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.")
|
||||
return
|
||||
|
||||
@@ -79,6 +79,105 @@
|
||||
if(foundrecord)
|
||||
foundrecord.fields["rank"] = assignment
|
||||
|
||||
/obj/effect/datacore/proc/get_manifest(monochrome, OOC)
|
||||
var/list/heads = list()
|
||||
var/list/sec = list()
|
||||
var/list/eng = list()
|
||||
var/list/med = list()
|
||||
var/list/sci = list()
|
||||
var/list/civ = list()
|
||||
var/list/bot = list()
|
||||
var/list/misc = list()
|
||||
var/dat = {"
|
||||
<head><style>
|
||||
.manifest {border-collapse:collapse;}
|
||||
.manifest td, th {border:1px solid [monochrome?"black":"#DEF; background-color:white; color:black"]; padding:.25em}
|
||||
.manifest th {height: 2em; [monochrome?"border-top-width: 3px":"background-color: #48C; color:white"]}
|
||||
.manifest tr.head th { [monochrome?"border-top-width: 1px":"background-color: #488;"] }
|
||||
.manifest td:first-child {text-align:right}
|
||||
.manifest tr.alt td {[monochrome?"border-top-width: 2px":"background-color: #DEF"]}
|
||||
</style></head>
|
||||
<table class="manifest" width='350px'>
|
||||
<tr class='head'><th>Name</th><th>Rank</th></tr>
|
||||
"}
|
||||
var/even = 0
|
||||
// sort mobs
|
||||
for(var/datum/data/record/t in data_core.general)
|
||||
var/name = t.fields["name"]
|
||||
var/rank = t.fields["rank"]
|
||||
var/department = 0
|
||||
if(rank in command_positions)
|
||||
heads[name] = rank
|
||||
department = 1
|
||||
if(rank in security_positions)
|
||||
sec[name] = rank
|
||||
department = 1
|
||||
if(rank in engineering_positions)
|
||||
eng[name] = rank
|
||||
department = 1
|
||||
if(rank in medical_positions)
|
||||
med[name] = rank
|
||||
department = 1
|
||||
if(rank in science_positions)
|
||||
sci[name] = rank
|
||||
department = 1
|
||||
if(rank in civilian_positions)
|
||||
civ[name] = rank
|
||||
department = 1
|
||||
if(rank in nonhuman_positions)
|
||||
bot[name] = rank
|
||||
department = 1
|
||||
if(!department && !(name in heads))
|
||||
misc[name] = rank
|
||||
if(heads.len > 0)
|
||||
dat += "<tr><th colspan=3>Heads</th></tr>"
|
||||
for(name in heads)
|
||||
dat += "<tr[even ? " class='alt'" : ""]><td>[name]</td><td>[heads[name]]</td></tr>"
|
||||
even = !even
|
||||
if(sec.len > 0)
|
||||
dat += "<tr><th colspan=3>Security</th></tr>"
|
||||
for(name in sec)
|
||||
dat += "<tr[even ? " class='alt'" : ""]><td>[name]</td><td>[sec[name]]</td></tr>"
|
||||
even = !even
|
||||
if(eng.len > 0)
|
||||
dat += "<tr><th colspan=3>Engineering</th></tr>"
|
||||
for(name in eng)
|
||||
dat += "<tr[even ? " class='alt'" : ""]><td>[name]</td><td>[eng[name]]</td></tr>"
|
||||
even = !even
|
||||
if(med.len > 0)
|
||||
dat += "<tr><th colspan=3>Medical</th></tr>"
|
||||
for(name in med)
|
||||
dat += "<tr[even ? " class='alt'" : ""]><td>[name]</td><td>[med[name]]</td></tr>"
|
||||
even = !even
|
||||
if(sci.len > 0)
|
||||
dat += "<tr><th colspan=3>Science</th></tr>"
|
||||
for(name in sci)
|
||||
dat += "<tr[even ? " class='alt'" : ""]><td>[name]</td><td>[sci[name]]</td></tr>"
|
||||
even = !even
|
||||
if(civ.len > 0)
|
||||
dat += "<tr><th colspan=3>Civilian</th></tr>"
|
||||
for(name in civ)
|
||||
dat += "<tr[even ? " class='alt'" : ""]><td>[name]</td><td>[civ[name]]</td></tr>"
|
||||
even = !even
|
||||
// in case somebody is insane and added them to the manifest, why not
|
||||
if(bot.len > 0)
|
||||
dat += "<tr><th colspan=3>Silicon</th></tr>"
|
||||
for(name in bot)
|
||||
dat += "<tr[even ? " class='alt'" : ""]><td>[name]</td><td>[bot[name]]</td></tr>"
|
||||
even = !even
|
||||
// misc guys
|
||||
if(misc.len > 0)
|
||||
dat += "<tr><th colspan=3>Miscellaneous</th></tr>"
|
||||
for(name in misc)
|
||||
dat += "<tr[even ? " class='alt'" : ""]><td>[name]</td><td>[misc[name]]</td></tr>"
|
||||
even = !even
|
||||
|
||||
dat += "</table>"
|
||||
dat = replacetext(dat, "\n", "")
|
||||
dat = replacetext(dat, "\t", "")
|
||||
return dat
|
||||
|
||||
|
||||
var/record_id_num = 1001
|
||||
/obj/effect/datacore/proc/manifest_inject(var/mob/living/carbon/human/H)
|
||||
if(H.mind && (H.mind.assigned_role != "MODE"))
|
||||
|
||||
@@ -128,7 +128,7 @@ var/list/diseases = typesof(/datum/disease) - /datum/disease
|
||||
|
||||
/datum/disease/proc/process()
|
||||
if(!holder)
|
||||
active_diseases -= src
|
||||
SSdisease.processing -= src
|
||||
return
|
||||
|
||||
if(prob(infectivity))
|
||||
@@ -169,7 +169,7 @@ var/list/diseases = typesof(/datum/disease) - /datum/disease
|
||||
cure()
|
||||
return
|
||||
|
||||
active_diseases += src
|
||||
SSdisease.processing += src
|
||||
|
||||
|
||||
/datum/disease/proc/IsSame(var/datum/disease/D)
|
||||
@@ -187,7 +187,7 @@ var/list/diseases = typesof(/datum/disease) - /datum/disease
|
||||
|
||||
|
||||
/datum/disease/Del()
|
||||
active_diseases.Remove(src)
|
||||
SSdisease.processing.Remove(src)
|
||||
..()
|
||||
|
||||
|
||||
|
||||
@@ -402,7 +402,7 @@ var/list/advance_cures = list(
|
||||
D.AssignName(new_name)
|
||||
D.Refresh()
|
||||
|
||||
for(var/datum/disease/advance/AD in active_diseases)
|
||||
for(var/datum/disease/advance/AD in SSdisease.processing)
|
||||
AD.Refresh()
|
||||
|
||||
for(var/mob/living/carbon/human/H in shuffle(living_mob_list))
|
||||
@@ -420,7 +420,7 @@ var/list/advance_cures = list(
|
||||
/*
|
||||
/mob/verb/test()
|
||||
|
||||
for(var/datum/disease/D in active_diseases)
|
||||
for(var/datum/disease/D in SSdisease.processing)
|
||||
src << "<a href='?_src_=vars;Vars=\ref[D]'>[D.name] - [D.holder]</a>"
|
||||
*/
|
||||
|
||||
|
||||
@@ -33,10 +33,10 @@ Bonus
|
||||
if(3, 4)
|
||||
M << "<span class='notice'>[pick("You hear a ringing in your ear.", "Your ears pop.")]</span>"
|
||||
if(5)
|
||||
if(!(M.sdisabilities & DEAF))
|
||||
if(!(M.ear_deaf))
|
||||
M << "<span class='danger'>Your ears pop and begin ringing loudly!</span>"
|
||||
M.sdisabilities |= DEAF
|
||||
M.ear_deaf = INFINITY //Shall be enough
|
||||
spawn(200)
|
||||
if(M)
|
||||
M.sdisabilities &= ~DEAF
|
||||
M.ear_deaf = 0
|
||||
return
|
||||
@@ -41,8 +41,9 @@ Bonus
|
||||
M.eye_blurry = 20
|
||||
M.eye_stat += 5
|
||||
if (M.eye_stat >= 10)
|
||||
M.disabilities |= NEARSIGHTED
|
||||
if (prob(M.eye_stat - 10 + 1) && !(M.sdisabilities & BLIND))
|
||||
M.disabilities |= NEARSIGHT
|
||||
if (prob(M.eye_stat - 10 + 1) && !(M.eye_blind))
|
||||
M << "<span class='danger'>You go blind!</span>"
|
||||
M.sdisabilities |= BLIND
|
||||
M.disabilities |= BLIND
|
||||
M.eye_blind = 1
|
||||
return
|
||||
+1
-1
@@ -66,7 +66,7 @@
|
||||
if(current) //remove ourself from our old body's mind variable
|
||||
current.mind = null
|
||||
|
||||
nanomanager.user_transferred(current, new_character)
|
||||
SSnano.user_transferred(current, new_character)
|
||||
|
||||
if(key)
|
||||
if(new_character.key != key) //if we're transfering into a body with a key associated which is not ours
|
||||
|
||||
@@ -0,0 +1,327 @@
|
||||
/var/global/list/mutations_list = list()
|
||||
|
||||
/datum/mutation/
|
||||
|
||||
var/name
|
||||
|
||||
/datum/mutation/New()
|
||||
|
||||
mutations_list[name] = src
|
||||
|
||||
/datum/mutation/human
|
||||
|
||||
var/dna_block
|
||||
var/quality
|
||||
var/get_chance = 100
|
||||
var/lowest_value = 256 * 8
|
||||
var/text_indication = ""
|
||||
var/list/visual_indicators = list()
|
||||
|
||||
/datum/mutation/human/proc/force_give(mob/living/carbon/human/owner)
|
||||
set_block(owner)
|
||||
on_acquiring(owner)
|
||||
|
||||
/datum/mutation/human/proc/set_se(se_string)
|
||||
if(!se_string || lentext(se_string) < DNA_STRUC_ENZYMES_BLOCKS * DNA_BLOCK_SIZE) return
|
||||
var/before = copytext(se_string, 1, (dna_block * DNA_BLOCK_SIZE) + 1)
|
||||
var/injection = num2hex(lowest_value + rand(1, 256 * 6))
|
||||
var/after = copytext(se_string, (dna_block * DNA_BLOCK_SIZE) + DNA_BLOCK_SIZE + 1)
|
||||
return before + injection + after
|
||||
|
||||
/datum/mutation/human/proc/set_block(mob/living/carbon/human/owner)
|
||||
owner.dna.struc_enzymes = set_se(owner.dna.struc_enzymes)
|
||||
|
||||
/datum/mutation/human/proc/check_block_string(se_string)
|
||||
if(!se_string || lentext(se_string) < DNA_STRUC_ENZYMES_BLOCKS * DNA_BLOCK_SIZE) return 0
|
||||
if(hex2num(getblock(se_string, dna_block)) >= lowest_value)
|
||||
return 1
|
||||
|
||||
/datum/mutation/human/proc/check_block(mob/living/carbon/human/owner)
|
||||
if(check_block_string(owner.dna.struc_enzymes))
|
||||
if(prob(get_chance))
|
||||
. = on_acquiring(owner)
|
||||
else
|
||||
. = on_losing(owner)
|
||||
|
||||
/datum/mutation/human/proc/on_acquiring(mob/living/carbon/human/owner)
|
||||
if(src in owner.dna.mutations)
|
||||
return 1
|
||||
owner.dna.mutations.Add(src)
|
||||
gain_indication(owner)
|
||||
owner << text_indication
|
||||
|
||||
/datum/mutation/human/proc/gain_indication(mob/living/carbon/human/owner)
|
||||
owner.overlays.Add(visual_indicators)
|
||||
/*
|
||||
var/list/result_overlays = list()
|
||||
var/list/limbs = owner_get_limbs(NON_MECHANICAL|NON_AMPUTATED) //I dunno how its done by RR but i assume something like this, proc that returns the list of limbs based on what types of limbs to return in argument
|
||||
for(var/obj/limb/L in limbs)
|
||||
result_overlays[L.identificator] = visual_indicators[L.identificator] //visual_indicators is where overlays icons are stored, they are all created on new of each mutation, i assume you will change it to linked list for easyness, but for now its just a list
|
||||
return owner.redraw_overlays(result_overlays, MUTATION_LAYER) //Currently mutations draw the overlays themselves but i assume if dismemberment will be overriding lots of shit like maybe clothes or something else mutations will just pass the shit to redraw proc
|
||||
*/
|
||||
/datum/mutation/human/proc/lose_indication(mob/living/carbon/human/owner)
|
||||
owner.overlays.Remove(visual_indicators)
|
||||
|
||||
/datum/mutation/human/proc/on_attack_hand(mob/living/carbon/human/owner, atom/target)
|
||||
return
|
||||
|
||||
/datum/mutation/human/proc/on_ranged_attack(mob/living/carbon/human/owner, atom/target)
|
||||
return
|
||||
|
||||
/datum/mutation/human/proc/on_life(mob/living/carbon/human/owner)
|
||||
return
|
||||
|
||||
/datum/mutation/human/proc/on_losing(mob/living/carbon/human/owner)
|
||||
if(owner.dna.mutations.Remove(src))
|
||||
lose_indication(owner)
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/datum/mutation/human/hulk
|
||||
|
||||
name = "Hulk"
|
||||
quality = POSITIVE
|
||||
get_chance = 5
|
||||
lowest_value = 256 * 14
|
||||
text_indication = "<span class='notice'>Your muscles hurt!</span>"
|
||||
|
||||
/datum/mutation/human/hulk/New()
|
||||
..()
|
||||
visual_indicators |= image("icon"='icons/effects/genetics.dmi', "icon_state"="hulk_f_s", "layer"=-MUTATIONS_LAYER)
|
||||
visual_indicators |= image("icon"='icons/effects/genetics.dmi', "icon_state"="hulk_m_s", "layer"=-MUTATIONS_LAYER)
|
||||
|
||||
/datum/mutation/human/hulk/on_acquiring(mob/living/carbon/human/owner)
|
||||
if(..()) return
|
||||
var/status = CANSTUN | CANWEAKEN | CANPARALYSE | CANPUSH
|
||||
owner.status_flags &= ~status
|
||||
|
||||
/datum/mutation/human/hulk/on_attack_hand(mob/living/carbon/human/owner, atom/target)
|
||||
return target.attack_hulk(owner)
|
||||
|
||||
/datum/mutation/human/hulk/gain_indication(mob/living/carbon/human/owner)
|
||||
var/g = (owner.gender == FEMALE) ? 1 : 2
|
||||
owner.overlays += visual_indicators[g]
|
||||
|
||||
/datum/mutation/human/hulk/on_life(mob/living/carbon/human/owner)
|
||||
if(owner.health < 25)
|
||||
on_losing(owner)
|
||||
owner << "<span class='danger'>You suddenly feel very weak.</span>"
|
||||
owner.Weaken(3)
|
||||
owner.emote("collapse")
|
||||
|
||||
/datum/mutation/human/hulk/on_losing(mob/living/carbon/human/owner)
|
||||
..()
|
||||
owner.status_flags |= CANSTUN | CANWEAKEN | CANPARALYSE | CANPUSH
|
||||
|
||||
/datum/mutation/human/telekinesis
|
||||
|
||||
name = "Telekinesis"
|
||||
quality = POSITIVE
|
||||
get_chance = 10
|
||||
lowest_value = 256 * 14
|
||||
text_indication = "<span class='notice'>You feel smarter!</span>"
|
||||
|
||||
/datum/mutation/human/telekinesis/New()
|
||||
..()
|
||||
visual_indicators |= image("icon"='icons/effects/genetics.dmi', "icon_state"="telekinesishead_s", "layer"=-MUTATIONS_LAYER)
|
||||
|
||||
/datum/mutation/human/telekinesis/on_ranged_attack(mob/living/carbon/human/owner, atom/target)
|
||||
target.attack_tk(owner)
|
||||
|
||||
/datum/mutation/human/cold_resistance
|
||||
|
||||
name = "Cold Resistance"
|
||||
quality = POSITIVE
|
||||
get_chance = 10
|
||||
lowest_value = 256 * 12
|
||||
text_indication = "<span class='notice'>Your body feels warm!</span>"
|
||||
|
||||
/datum/mutation/human/cold_resistance/New()
|
||||
..()
|
||||
visual_indicators |= image("icon"='icons/effects/genetics.dmi', "icon_state"="fire_s", "layer"=-MUTATIONS_LAYER)
|
||||
|
||||
/datum/mutation/human/cold_resistance/on_life(mob/living/carbon/human/owner)
|
||||
if(owner.getFireLoss())
|
||||
if(prob(1))
|
||||
owner.heal_organ_damage(0,1) //Is this really needed?
|
||||
|
||||
/datum/mutation/human/x_ray
|
||||
|
||||
name = "X Ray Vision"
|
||||
quality = POSITIVE
|
||||
get_chance = 10
|
||||
lowest_value = 256 * 15
|
||||
text_indication = "<span class='notice'>The walls suddenly disappear!</span>"
|
||||
|
||||
/datum/mutation/human/x_ray/on_acquiring(mob/living/carbon/human/owner)
|
||||
if(..()) return
|
||||
on_life(owner)
|
||||
|
||||
/datum/mutation/human/x_ray/on_life(mob/living/carbon/human/owner)
|
||||
owner.sight |= SEE_MOBS|SEE_OBJS|SEE_TURFS
|
||||
owner.see_in_dark = 8
|
||||
owner.see_invisible = SEE_INVISIBLE_LEVEL_TWO
|
||||
|
||||
/datum/mutation/human/x_ray/on_losing(mob/living/carbon/human/owner)
|
||||
if(..()) return
|
||||
owner.see_in_dark = initial(owner.see_in_dark)
|
||||
owner.see_invisible = initial(owner.see_invisible)
|
||||
owner.sight = initial(owner.sight)
|
||||
|
||||
/datum/mutation/human/nearsight
|
||||
|
||||
name = "Near Sightness"
|
||||
quality = MINOR_NEGATIVE
|
||||
text_indication = "<span class='danger'>You can't see very well.</span>"
|
||||
|
||||
/datum/mutation/human/nearsight/on_acquiring(mob/living/carbon/human/owner)
|
||||
if(..()) return
|
||||
owner.disabilities |= NEARSIGHT
|
||||
|
||||
/datum/mutation/human/nearsight/on_losing(mob/living/carbon/human/owner)
|
||||
if(..()) return
|
||||
owner.disabilities &= ~NEARSIGHT
|
||||
|
||||
/datum/mutation/human/epilepsy
|
||||
|
||||
name = "Epilepsy"
|
||||
quality = NEGATIVE
|
||||
text_indication = "<span class='danger'>You get a headache.</span>"
|
||||
|
||||
/datum/mutation/human/epilepsy/on_life(mob/living/carbon/human/owner)
|
||||
if ((prob(1) && owner.paralysis < 1))
|
||||
owner << "<span class='danger'>You have a seizure!</span>"
|
||||
for(var/mob/O in viewers(owner, null) - owner)
|
||||
O.show_message(text("<span class='userdanger'>[src] starts having a seizure!</span>"), 1)
|
||||
owner.Paralyse(10)
|
||||
owner.Jitter(1000)
|
||||
|
||||
/datum/mutation/human/bad_dna
|
||||
|
||||
name = "Unstable DNA"
|
||||
quality = NEGATIVE
|
||||
text_indication = "<span class='danger'>You feel strange.</span>"
|
||||
|
||||
/datum/mutation/human/bad_dna/on_acquiring(mob/living/carbon/human/owner)
|
||||
if(prob(95))
|
||||
if(prob(50))
|
||||
randmutb(owner)
|
||||
else
|
||||
randmuti(owner)
|
||||
else
|
||||
randmutg(owner)
|
||||
on_losing(owner)
|
||||
|
||||
/datum/mutation/human/cough
|
||||
|
||||
name = "Cough"
|
||||
quality = MINOR_NEGATIVE
|
||||
text_indication = "<span class='danger'>You start coughing.</span>"
|
||||
|
||||
/datum/mutation/human/cough/on_life(mob/living/carbon/human/owner)
|
||||
if((prob(5) && owner.paralysis <= 1))
|
||||
owner.drop_item()
|
||||
owner.emote("cough")
|
||||
|
||||
/datum/mutation/human/clumsy
|
||||
|
||||
name = "Clumsiness"
|
||||
quality = MINOR_NEGATIVE
|
||||
text_indication = "<span class='danger'>You feel lightheaded.</span>"
|
||||
|
||||
/datum/mutation/human/clumsy/on_acquiring(mob/living/carbon/human/owner)
|
||||
if(..()) return
|
||||
owner.disabilities |= CLUMSY
|
||||
|
||||
/datum/mutation/human/clumsy/on_losing(mob/living/carbon/human/owner)
|
||||
if(..()) return
|
||||
owner.disabilities &= ~CLUMSY
|
||||
|
||||
/datum/mutation/human/tourettes
|
||||
|
||||
name = "Tourettes Syndrome"
|
||||
quality = NEGATIVE
|
||||
text_indication = "<span class='danger'>You twitch.</span>"
|
||||
|
||||
/datum/mutation/human/tourettes/on_life(mob/living/carbon/human/owner)
|
||||
if((prob(10) && owner.paralysis <= 1))
|
||||
owner.Stun(10)
|
||||
switch(rand(1, 3))
|
||||
if(1)
|
||||
owner.emote("twitch")
|
||||
if(2 to 3)
|
||||
owner.say("[prob(50) ? ";" : ""][pick("SHIT", "PISS", "FUCK", "CUNT", "COCKSUCKER", "MOTHERFUCKER", "TITS")]")
|
||||
var/x_offset_old = owner.pixel_x
|
||||
var/y_offset_old = owner.pixel_y
|
||||
var/x_offset = owner.pixel_x + rand(-2,2)
|
||||
var/y_offset = owner.pixel_y + rand(-1,1)
|
||||
animate(owner, pixel_x = x_offset, pixel_y = y_offset, time = 1)
|
||||
animate(owner, pixel_x = x_offset_old, pixel_y = y_offset_old, time = 1)
|
||||
|
||||
/datum/mutation/human/nervousness
|
||||
|
||||
name = "Nervousness"
|
||||
quality = MINOR_NEGATIVE
|
||||
text_indication = "<span class='danger'>You feel nervous.</span>"
|
||||
|
||||
/datum/mutation/human/nervousness/on_life(mob/living/carbon/human/owner)
|
||||
if(prob(10))
|
||||
owner.stuttering = max(10, owner.stuttering)
|
||||
|
||||
/datum/mutation/human/deaf
|
||||
|
||||
name = "Deafness"
|
||||
quality = NEGATIVE
|
||||
text_indication = "<span class='danger'>You can't seem to hear anything.</span>"
|
||||
|
||||
/datum/mutation/human/deaf/on_acquiring(mob/living/carbon/human/owner)
|
||||
if(..()) return
|
||||
owner.disabilities |= DEAF
|
||||
|
||||
/datum/mutation/human/deaf/on_losing(mob/living/carbon/human/owner)
|
||||
if(..()) return
|
||||
owner.disabilities &= ~DEAF
|
||||
|
||||
/datum/mutation/human/blind
|
||||
|
||||
name = "Blindness"
|
||||
quality = NEGATIVE
|
||||
text_indication = "<span class='danger'>You can't seem to see anything.</span>"
|
||||
|
||||
/datum/mutation/human/blind/on_acquiring(mob/living/carbon/human/owner)
|
||||
if(..()) return
|
||||
owner.disabilities |= BLIND
|
||||
|
||||
/datum/mutation/human/blind/on_losing(mob/living/carbon/human/owner)
|
||||
if(..()) return
|
||||
owner.disabilities &= ~BLIND
|
||||
|
||||
/datum/mutation/human/race
|
||||
|
||||
name = "Monkified"
|
||||
quality = NEGATIVE
|
||||
|
||||
/datum/mutation/human/race/on_acquiring(mob/living/carbon/human/owner)
|
||||
if(..()) return
|
||||
. = owner.monkeyize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_KEEPSE)
|
||||
|
||||
/datum/mutation/human/race/gain_indication(mob/living/carbon/human/owner)
|
||||
return
|
||||
|
||||
/datum/mutation/human/race/lose_indication(mob/living/carbon/monkey/owner)
|
||||
return
|
||||
|
||||
/datum/mutation/human/race/on_losing(mob/living/carbon/monkey/owner)
|
||||
if(..()) return
|
||||
. = owner.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_KEEPSE)
|
||||
|
||||
/datum/mutation/human/laser_eyes
|
||||
|
||||
name = "Laser Eyes"
|
||||
quality = POSITIVE
|
||||
dna_block = NON_SCANNABLE
|
||||
text_indication = "<span class='notice'>You feel pressure building up behind your eyes.</span>"
|
||||
|
||||
/datum/mutation/human/laser_eyes/on_ranged_attack(mob/living/carbon/human/owner, atom/target)
|
||||
if(owner.a_intent == "harm")
|
||||
owner.LaserEyes(target)
|
||||
@@ -17,14 +17,14 @@
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/genetic/cast(list/targets)
|
||||
|
||||
for(var/mob/living/target in targets)
|
||||
target.mutations.Add(mutations)
|
||||
for(var/mob/living/carbon/target in targets)
|
||||
for(var/A in mutations)
|
||||
target.dna.add_mutation(A)
|
||||
target.disabilities |= disabilities
|
||||
target.update_mutations() //update target's mutation overlays
|
||||
spawn(duration)
|
||||
if(target && !target.gc_destroyed)
|
||||
target.mutations.Remove(mutations)
|
||||
for(var/A in mutations)
|
||||
target.dna.remove_mutation(A)
|
||||
target.disabilities &= ~disabilities
|
||||
target.update_mutations()
|
||||
|
||||
return
|
||||
@@ -42,7 +42,7 @@
|
||||
include_user = 1
|
||||
centcom_cancast = 0
|
||||
|
||||
mutations = list(LASER, HULK)
|
||||
mutations = list(LASEREYES, HULK)
|
||||
duration = 300
|
||||
cooldown_min = 300 //25 deciseconds reduction per rank
|
||||
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
#define SOLAR_UPDATE_TIME 600 //duration between two updates of the whole sun/solars positions
|
||||
|
||||
/datum/sun
|
||||
var/angle
|
||||
var/dx
|
||||
var/dy
|
||||
var/rate
|
||||
var/list/solars // for debugging purposes, references solars_list at the constructor
|
||||
var/solar_next_update // last time the sun position was checked and adjusted
|
||||
|
||||
/datum/sun/New()
|
||||
|
||||
solars = solars_list
|
||||
rate = rand(50,200)/100 // 50% - 200% of standard rotation
|
||||
if(prob(50)) // same chance to rotate clockwise than counter-clockwise
|
||||
rate = -rate
|
||||
solar_next_update = world.time // init the timer
|
||||
angle = rand (0,360) // the station position to the sun is randomised at round start
|
||||
|
||||
// calculate the sun's position given the time of day
|
||||
// at the standard rate (100%) the angle is increase/decreased by 6 degrees every minute.
|
||||
// a full rotation thus take a game hour in that case
|
||||
/datum/sun/proc/calc_position()
|
||||
|
||||
if(world.time < solar_next_update) //if less than 60 game secondes have passed, do nothing
|
||||
return;
|
||||
|
||||
angle = (360 + angle + rate * 6) % 360 // increase/decrease the angle to the sun, adjusted by the rate
|
||||
|
||||
solar_next_update += SOLAR_UPDATE_TIME // since we updated the angle, set the proper time for the next loop
|
||||
|
||||
// now calculate and cache the (dx,dy) increments for line drawing
|
||||
|
||||
var/s = sin(angle)
|
||||
var/c = cos(angle)
|
||||
|
||||
// Either "abs(s) < abs(c)" or "abs(s) >= abs(c)"
|
||||
// In both cases, the greater is greater than 0, so, no "if 0" check is needed for the divisions
|
||||
|
||||
if( abs(s) < abs(c))
|
||||
|
||||
dx = s / abs(c)
|
||||
dy = c / abs(c)
|
||||
|
||||
else
|
||||
dx = s/abs(s)
|
||||
dy = c / abs(s)
|
||||
|
||||
//now tell the solar control computers to update their status and linked devices
|
||||
for(var/obj/machinery/power/solar_control/SC in solars_list)
|
||||
if(!SC.powernet)
|
||||
solars_list.Remove(SC)
|
||||
continue
|
||||
SC.update()
|
||||
|
||||
|
||||
|
||||
+10
-16
@@ -219,9 +219,9 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine
|
||||
|
||||
/datum/supply_packs/security/taser
|
||||
name = "Stun Guns Crate"
|
||||
contains = list(/obj/item/weapon/gun/energy/taser,
|
||||
/obj/item/weapon/gun/energy/taser,
|
||||
/obj/item/weapon/gun/energy/taser)
|
||||
contains = list(/obj/item/weapon/gun/energy/gun/advtaser,
|
||||
/obj/item/weapon/gun/energy/gun/advtaser,
|
||||
/obj/item/weapon/gun/energy/gun/advtaser)
|
||||
cost = 15
|
||||
containername = "stun gun crate"
|
||||
|
||||
@@ -266,16 +266,8 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine
|
||||
cost = 20
|
||||
containername = "riot shields crate"
|
||||
|
||||
/datum/supply_packs/security/armory/bullethelmet
|
||||
name = "Tactical Helmets Crate"
|
||||
contains = list(/obj/item/clothing/head/helmet/bulletproof,
|
||||
/obj/item/clothing/head/helmet/bulletproof,
|
||||
/obj/item/clothing/head/helmet/bulletproof)
|
||||
cost = 15
|
||||
containername = "tactical helmets crate"
|
||||
|
||||
/datum/supply_packs/security/armory/bulletarmor
|
||||
name = "Tactical Armor Crate"
|
||||
name = "Bulletproof Armor Crate"
|
||||
contains = list(/obj/item/clothing/suit/armor/bulletproof,
|
||||
/obj/item/clothing/suit/armor/bulletproof,
|
||||
/obj/item/clothing/suit/armor/bulletproof)
|
||||
@@ -404,9 +396,9 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine
|
||||
|
||||
/datum/supply_packs/engineering/powergamermitts
|
||||
name = "Insulated Gloves Crate"
|
||||
contains = list(/obj/item/clothing/gloves/yellow,
|
||||
/obj/item/clothing/gloves/yellow,
|
||||
/obj/item/clothing/gloves/yellow)
|
||||
contains = list(/obj/item/clothing/gloves/color/yellow,
|
||||
/obj/item/clothing/gloves/color/yellow,
|
||||
/obj/item/clothing/gloves/color/yellow)
|
||||
cost = 20 //Made of pure-grade bullshittinium
|
||||
containername = "insulated gloves crate"
|
||||
|
||||
@@ -590,6 +582,8 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine
|
||||
/obj/item/weapon/reagent_containers/glass/bottle/pierrot_throat,
|
||||
/obj/item/weapon/reagent_containers/glass/bottle/brainrot,
|
||||
/obj/item/weapon/reagent_containers/glass/bottle/hullucigen_virion,
|
||||
/obj/item/weapon/reagent_containers/glass/bottle/anxiety,
|
||||
/obj/item/weapon/reagent_containers/glass/bottle/beesease,
|
||||
/obj/item/weapon/storage/box/syringes,
|
||||
/obj/item/weapon/storage/box/beakers,
|
||||
/obj/item/weapon/reagent_containers/glass/bottle/mutagen)
|
||||
@@ -1088,7 +1082,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine
|
||||
/obj/item/weapon/bikehorn,
|
||||
/obj/item/clothing/under/rank/mime,
|
||||
/obj/item/clothing/shoes/sneakers/black,
|
||||
/obj/item/clothing/gloves/white,
|
||||
/obj/item/clothing/gloves/color/white,
|
||||
/obj/item/clothing/mask/gas/mime,
|
||||
/obj/item/clothing/head/beret,
|
||||
/obj/item/clothing/suit/suspenders,
|
||||
|
||||
@@ -280,7 +280,7 @@ var/list/uplink_items = list()
|
||||
gamemodes = list(/datum/game_mode/nuclear)
|
||||
|
||||
/datum/uplink_item/ammo/machinegun
|
||||
name = "Box Magazine - 7.62×51mm"
|
||||
name = "Box Magazine - 7.62x51mm"
|
||||
desc = "A 50-round magazine of 7.62x51mm ammunition for use in the L6 SAW machinegun. By the time you need to use this, you'll already be on a pile of corpses."
|
||||
item = /obj/item/ammo_box/magazine/m762
|
||||
cost = 12
|
||||
|
||||
@@ -27,7 +27,7 @@ var/const/AUTOLATHE_DISABLE_WIRE = 4
|
||||
var/obj/machinery/autolathe/A = holder
|
||||
switch(index)
|
||||
if(AUTOLATHE_HACK_WIRE)
|
||||
A.hacked = !mended
|
||||
A.adjust_hacked(!mended)
|
||||
if(AUTOLATHE_SHOCK_WIRE)
|
||||
A.shocked = !mended
|
||||
if(AUTOLATHE_DISABLE_WIRE)
|
||||
@@ -39,10 +39,10 @@ var/const/AUTOLATHE_DISABLE_WIRE = 4
|
||||
var/obj/machinery/autolathe/A = holder
|
||||
switch(index)
|
||||
if(AUTOLATHE_HACK_WIRE)
|
||||
A.hacked = !A.hacked
|
||||
A.adjust_hacked(!A.hacked)
|
||||
spawn(50)
|
||||
if(A && !IsIndexCut(index))
|
||||
A.hacked = 0
|
||||
A.adjust_hacked(0)
|
||||
Interact(usr)
|
||||
if(AUTOLATHE_SHOCK_WIRE)
|
||||
A.shocked = !A.shocked
|
||||
|
||||
@@ -110,14 +110,7 @@ proc/sql_report_cyborg_death(var/mob/living/silicon/robot/H)
|
||||
log_game("SQL ERROR during death reporting. Error : \[[err]\]\n")
|
||||
|
||||
|
||||
proc/statistic_cycle()
|
||||
if(!config.sql_enabled)
|
||||
return
|
||||
while(1)
|
||||
sql_poll_players()
|
||||
sleep(600)
|
||||
sql_poll_admins()
|
||||
sleep(6000) // Poll every ten minutes
|
||||
|
||||
|
||||
//This proc is used for feedback. It is executed at round end.
|
||||
proc/sql_commit_feedback()
|
||||
|
||||
@@ -59,23 +59,14 @@ var/list/teleportlocs = list()
|
||||
|
||||
proc/process_teleport_locs()
|
||||
for(var/area/AR in world)
|
||||
if(istype(AR, /area/shuttle) || istype(AR, /area/syndicate_station) || istype(AR, /area/wizard_station)) continue
|
||||
if(istype(AR, /area/shuttle) || istype(AR, /area/wizard_station)) continue
|
||||
if(teleportlocs.Find(AR.name)) continue
|
||||
var/turf/picked = pick(get_area_turfs(AR.type))
|
||||
if (picked.z == 1)
|
||||
teleportlocs += AR.name
|
||||
teleportlocs[AR.name] = AR
|
||||
|
||||
var/not_in_order = 0
|
||||
do
|
||||
not_in_order = 0
|
||||
if(teleportlocs.len <= 1)
|
||||
break
|
||||
for(var/i = 1, i <= (teleportlocs.len - 1), i++)
|
||||
if(sorttext(teleportlocs[i], teleportlocs[i+1]) == -1)
|
||||
teleportlocs.Swap(i, i+1)
|
||||
not_in_order = 1
|
||||
while(not_in_order)
|
||||
sortTim(teleportlocs, /proc/cmp_text_dsc)
|
||||
|
||||
var/list/ghostteleportlocs = list()
|
||||
|
||||
@@ -90,16 +81,7 @@ proc/process_ghost_teleport_locs()
|
||||
ghostteleportlocs += AR.name
|
||||
ghostteleportlocs[AR.name] = AR
|
||||
|
||||
var/not_in_order = 0
|
||||
do
|
||||
not_in_order = 0
|
||||
if(ghostteleportlocs.len <= 1)
|
||||
break
|
||||
for(var/i = 1, i <= (ghostteleportlocs.len - 1), i++)
|
||||
if(sorttext(ghostteleportlocs[i], ghostteleportlocs[i+1]) == -1)
|
||||
ghostteleportlocs.Swap(i, i+1)
|
||||
not_in_order = 1
|
||||
while(not_in_order)
|
||||
sortTim(ghostteleportlocs, /proc/cmp_text_dsc)
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
@@ -135,166 +117,15 @@ proc/process_ghost_teleport_locs()
|
||||
//place to another. Look at escape shuttle for example.
|
||||
//All shuttles show now be under shuttle since we have smooth-wall code.
|
||||
|
||||
/area/shuttle //DO NOT TURN THE lighting_use_dynamic STUFF ON FOR SHUTTLES. IT BREAKS THINGS.
|
||||
/area/shuttle
|
||||
name = "\improper Shuttle"
|
||||
requires_power = 0
|
||||
luminosity = 1
|
||||
lighting_use_dynamic = 0
|
||||
var/push_dir = SOUTH
|
||||
var/destination
|
||||
|
||||
/area/shuttle/arrival
|
||||
name = "\improper Arrival Shuttle"
|
||||
|
||||
/area/shuttle/arrival/pre_game
|
||||
icon_state = "shuttle2"
|
||||
destination = /area/shuttle/arrival/station
|
||||
|
||||
/area/shuttle/arrival/station
|
||||
icon_state = "shuttle"
|
||||
destination = /area/shuttle/arrival/pre_game
|
||||
|
||||
/area/shuttle/escape
|
||||
name = "\improper Emergency Shuttle"
|
||||
|
||||
/area/shuttle/escape/station
|
||||
name = "\improper Emergency Shuttle Station"
|
||||
icon_state = "shuttle2"
|
||||
destination = /area/shuttle/escape/transit
|
||||
|
||||
/area/shuttle/escape/centcom
|
||||
name = "\improper Emergency Shuttle Centcom"
|
||||
icon_state = "shuttle"
|
||||
destination = /area/shuttle/escape/station
|
||||
|
||||
/area/shuttle/escape/transit // the area to pass through for 3 minute transit
|
||||
name = "\improper Emergency Shuttle Transit"
|
||||
icon_state = "shuttle"
|
||||
destination = /area/shuttle/escape/centcom
|
||||
|
||||
/area/shuttle/escape_pod1
|
||||
name = "\improper Escape Pod One"
|
||||
|
||||
/area/shuttle/escape_pod1/station
|
||||
icon_state = "shuttle2"
|
||||
destination = /area/shuttle/escape_pod1/transit
|
||||
|
||||
/area/shuttle/escape_pod1/centcom
|
||||
icon_state = "shuttle"
|
||||
destination = /area/shuttle/escape_pod1/station
|
||||
|
||||
/area/shuttle/escape_pod1/transit
|
||||
icon_state = "shuttle"
|
||||
destination = /area/shuttle/escape_pod1/centcom
|
||||
lighting_use_dynamic = 1
|
||||
has_gravity = 1
|
||||
|
||||
|
||||
mob_activate(var/mob/living/L)
|
||||
push_mob_back(L, push_dir)
|
||||
|
||||
/area/shuttle/escape_pod2
|
||||
name = "\improper Escape Pod Two"
|
||||
|
||||
/area/shuttle/escape_pod2/station
|
||||
icon_state = "shuttle2"
|
||||
destination = /area/shuttle/escape_pod2/transit
|
||||
|
||||
/area/shuttle/escape_pod2/centcom
|
||||
icon_state = "shuttle"
|
||||
destination = /area/shuttle/escape_pod2/station
|
||||
|
||||
/area/shuttle/escape_pod2/transit
|
||||
icon_state = "shuttle"
|
||||
destination = /area/shuttle/escape_pod2/centcom
|
||||
|
||||
mob_activate(var/mob/living/L)
|
||||
push_mob_back(L, push_dir)
|
||||
|
||||
/area/shuttle/escape_pod3
|
||||
name = "\improper Escape Pod Three"
|
||||
push_dir = WEST
|
||||
|
||||
/area/shuttle/escape_pod3/station
|
||||
icon_state = "shuttle2"
|
||||
destination = /area/shuttle/escape_pod3/transit
|
||||
|
||||
/area/shuttle/escape_pod3/centcom
|
||||
icon_state = "shuttle"
|
||||
destination = /area/shuttle/escape_pod3/station
|
||||
|
||||
/area/shuttle/escape_pod3/transit
|
||||
icon_state = "shuttle"
|
||||
destination = /area/shuttle/escape_pod3/centcom
|
||||
|
||||
mob_activate(var/mob/living/L)
|
||||
push_mob_back(L, push_dir)
|
||||
|
||||
/area/shuttle/escape_pod4 //Renaming areas 2hard
|
||||
name = "\improper Escape Pod Four"
|
||||
push_dir = WEST
|
||||
|
||||
/area/shuttle/escape_pod4/station
|
||||
icon_state = "shuttle2"
|
||||
destination = /area/shuttle/escape_pod4/transit
|
||||
|
||||
/area/shuttle/escape_pod4/centcom
|
||||
icon_state = "shuttle"
|
||||
destination = /area/shuttle/escape_pod4/station
|
||||
|
||||
/area/shuttle/escape_pod4/transit
|
||||
icon_state = "shuttle"
|
||||
destination = /area/shuttle/escape_pod4/centcom
|
||||
|
||||
mob_activate(var/mob/living/L)
|
||||
push_mob_back(L, push_dir)
|
||||
|
||||
/area/shuttle/mining
|
||||
name = "\improper Mining Shuttle"
|
||||
|
||||
/area/shuttle/mining/station
|
||||
icon_state = "shuttle2"
|
||||
destination = /area/shuttle/mining/outpost
|
||||
|
||||
/area/shuttle/mining/outpost
|
||||
icon_state = "shuttle"
|
||||
destination = /area/shuttle/mining/station
|
||||
|
||||
/area/shuttle/laborcamp
|
||||
name = "\improper Labor Camp Shuttle"
|
||||
|
||||
/area/shuttle/laborcamp/station
|
||||
icon_state = "shuttle"
|
||||
destination = /area/shuttle/laborcamp/outpost
|
||||
|
||||
/area/shuttle/laborcamp/outpost
|
||||
icon_state = "shuttle"
|
||||
destination = /area/shuttle/laborcamp/station
|
||||
|
||||
/area/shuttle/transport1/centcom
|
||||
icon_state = "shuttle"
|
||||
name = "\improper Transport Shuttle Centcom"
|
||||
destination = /area/shuttle/transport1/station
|
||||
|
||||
/area/shuttle/transport1/station
|
||||
icon_state = "shuttle"
|
||||
name = "\improper Transport Shuttle"
|
||||
destination = /area/shuttle/transport1/centcom
|
||||
|
||||
/area/shuttle/prison/
|
||||
name = "\improper Prison Shuttle"
|
||||
|
||||
/area/shuttle/specops/centcom
|
||||
name = "\improper Special Ops Shuttle"
|
||||
icon_state = "shuttlered"
|
||||
destination = /area/shuttle/specops/station
|
||||
|
||||
/area/shuttle/specops/station
|
||||
name = "\improper Special Ops Shuttle"
|
||||
icon_state = "shuttlered2"
|
||||
destination = /area/shuttle/specops/centcom
|
||||
|
||||
/area/shuttle/thunderdome
|
||||
name = "honk"
|
||||
|
||||
/area/start // will be unused once kurper gets his login interface patch done
|
||||
/area/start
|
||||
name = "start area"
|
||||
icon_state = "start"
|
||||
requires_power = 0
|
||||
@@ -302,8 +133,6 @@ proc/process_ghost_teleport_locs()
|
||||
lighting_use_dynamic = 0
|
||||
has_gravity = 1
|
||||
|
||||
// === end remove
|
||||
|
||||
// CENTCOM
|
||||
|
||||
/area/centcom
|
||||
@@ -410,52 +239,6 @@ proc/process_ghost_teleport_locs()
|
||||
|
||||
//ENEMY
|
||||
|
||||
//names are used
|
||||
/area/syndicate_station
|
||||
name = "\improper Syndicate Station"
|
||||
icon_state = "yellow"
|
||||
requires_power = 0
|
||||
|
||||
/area/syndicate_station/start
|
||||
name = "\improper Syndicate Forward Operating Base"
|
||||
icon_state = "yellow"
|
||||
has_gravity = 1
|
||||
|
||||
/area/syndicate_station/southwest
|
||||
name = "\improper south-west of SS13"
|
||||
icon_state = "southwest"
|
||||
|
||||
/area/syndicate_station/northwest
|
||||
name = "\improper north-west of SS13"
|
||||
icon_state = "northwest"
|
||||
|
||||
/area/syndicate_station/northeast
|
||||
name = "\improper north-east of SS13"
|
||||
icon_state = "northeast"
|
||||
|
||||
/area/syndicate_station/southeast
|
||||
name = "\improper south-east of SS13"
|
||||
icon_state = "southeast"
|
||||
|
||||
/area/syndicate_station/north
|
||||
name = "\improper north of SS13"
|
||||
icon_state = "north"
|
||||
|
||||
/area/syndicate_station/south
|
||||
name = "\improper south of SS13"
|
||||
icon_state = "south"
|
||||
|
||||
/area/syndicate_station/commssat
|
||||
name = "\improper south of the communication satellite"
|
||||
icon_state = "south"
|
||||
|
||||
/area/syndicate_station/mining
|
||||
name = "\improper north east of the mining asteroid"
|
||||
icon_state = "north"
|
||||
|
||||
/area/syndicate_station/transit
|
||||
name = "\improper hyperspace"
|
||||
icon_state = "shuttle"
|
||||
|
||||
/area/wizard_station
|
||||
name = "\improper Wizard's Den"
|
||||
@@ -1759,28 +1542,11 @@ proc/process_ghost_teleport_locs()
|
||||
|
||||
// CENTCOM
|
||||
var/list/centcom_areas = list (
|
||||
/area/centcom,
|
||||
/area/shuttle/escape/centcom,
|
||||
/area/shuttle/escape_pod1/centcom,
|
||||
/area/shuttle/escape_pod2/centcom,
|
||||
/area/shuttle/escape_pod3/centcom,
|
||||
/area/shuttle/escape_pod4/centcom,
|
||||
/area/shuttle/transport1/centcom,
|
||||
/area/shuttle/specops/centcom,
|
||||
/area/centcom
|
||||
)
|
||||
|
||||
//SPACE STATION 13
|
||||
var/list/the_station_areas = list (
|
||||
/area/shuttle/arrival,
|
||||
/area/shuttle/escape/station,
|
||||
/area/shuttle/escape_pod1/station,
|
||||
/area/shuttle/escape_pod2/station,
|
||||
/area/shuttle/escape_pod3/station,
|
||||
/area/shuttle/escape_pod4/station,
|
||||
/area/shuttle/mining/station,
|
||||
/area/shuttle/transport1/station,
|
||||
// /area/shuttle/transport2/station, //not present on map
|
||||
/area/shuttle/specops/station,
|
||||
/area/atmos,
|
||||
/area/maintenance,
|
||||
/area/hallway,
|
||||
|
||||
@@ -346,9 +346,6 @@
|
||||
if(L.&& L.client)
|
||||
L.client.played = 0
|
||||
|
||||
/area/proc/mob_activate(var/mob/living/L)
|
||||
return
|
||||
|
||||
/proc/has_gravity(atom/AT, turf/T)
|
||||
if(!T)
|
||||
T = get_turf(AT)
|
||||
@@ -362,7 +359,7 @@
|
||||
if(T && gravity_generators["[T.z]"] && length(gravity_generators["[T.z]"]))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/*
|
||||
/area/proc/clear_docking_area()
|
||||
var/list/dstturfs = list()
|
||||
var/throwy = world.maxy
|
||||
@@ -398,3 +395,4 @@
|
||||
if(ismob(bug))
|
||||
continue
|
||||
qdel(bug)*/
|
||||
*/
|
||||
|
||||
@@ -41,6 +41,13 @@
|
||||
var/mob/living/M = src
|
||||
M.take_organ_damage(20)
|
||||
|
||||
/atom/proc/attack_hulk(mob/living/carbon/human/hulk, do_attack_animation = 0)
|
||||
if(do_attack_animation)
|
||||
hulk.changeNext_move(CLICK_CD_MELEE)
|
||||
add_logs(hulk, src, "punched", "hulk powers", admin=0)
|
||||
hulk.do_attack_animation(src)
|
||||
return
|
||||
|
||||
/atom/proc/CheckParts()
|
||||
return
|
||||
|
||||
@@ -395,4 +402,7 @@ var/list/blood_splatter_icons = list()
|
||||
return
|
||||
|
||||
/atom/proc/emag_act()
|
||||
return
|
||||
|
||||
/atom/proc/narsie_act()
|
||||
return
|
||||
|
||||
+12
-49
@@ -64,7 +64,6 @@
|
||||
so i made radios not use the radio controller.
|
||||
*/
|
||||
var/list/all_radios = list()
|
||||
|
||||
/proc/add_radio(var/obj/item/radio, freq)
|
||||
if(!freq || !radio)
|
||||
return
|
||||
@@ -181,48 +180,8 @@ var/const/RADIO_CLEANBOT = "10"
|
||||
var/const/RADIO_FLOORBOT = "11"
|
||||
var/const/RADIO_MEDBOT = "12"
|
||||
|
||||
var/global/datum/controller/radio/radio_controller
|
||||
/datum/radio_frequency
|
||||
|
||||
datum/controller/radio
|
||||
var/list/datum/radio_frequency/frequencies = list()
|
||||
|
||||
datum/controller/radio/proc/add_object(obj/device as obj, var/new_frequency as num, var/filter = null as text|null)
|
||||
var/f_text = num2text(new_frequency)
|
||||
var/datum/radio_frequency/frequency = frequencies[f_text]
|
||||
|
||||
if(!frequency)
|
||||
frequency = new
|
||||
frequency.frequency = new_frequency
|
||||
frequencies[f_text] = frequency
|
||||
|
||||
frequency.add_listener(device, filter)
|
||||
return frequency
|
||||
|
||||
datum/controller/radio/proc/remove_object(obj/device, old_frequency)
|
||||
var/f_text = num2text(old_frequency)
|
||||
var/datum/radio_frequency/frequency = frequencies[f_text]
|
||||
|
||||
if(frequency)
|
||||
frequency.remove_listener(device)
|
||||
|
||||
if(frequency.devices.len == 0)
|
||||
frequencies -= f_text
|
||||
del(frequency)
|
||||
|
||||
return 1
|
||||
|
||||
datum/controller/radio/proc/return_frequency(var/new_frequency as num)
|
||||
var/f_text = num2text(new_frequency)
|
||||
var/datum/radio_frequency/frequency = frequencies[f_text]
|
||||
|
||||
if(!frequency)
|
||||
frequency = new
|
||||
frequency.frequency = new_frequency
|
||||
frequencies[f_text] = frequency
|
||||
|
||||
return frequency
|
||||
|
||||
datum/radio_frequency
|
||||
var/frequency as num
|
||||
var/list/list/obj/devices = list()
|
||||
|
||||
@@ -263,19 +222,23 @@ datum/radio_frequency/proc/add_listener(obj/device as obj, var/filter as text|nu
|
||||
if (!filter)
|
||||
filter = "_default"
|
||||
|
||||
if(!devices[filter])
|
||||
devices[filter] = list()
|
||||
devices[filter] += device
|
||||
var/list/devices_line = devices[filter]
|
||||
if(!devices_line)
|
||||
devices_line = list()
|
||||
devices[filter] = devices_line
|
||||
devices_line += device
|
||||
|
||||
|
||||
datum/radio_frequency/proc/remove_listener(obj/device)
|
||||
for(var/devices_filter in devices)
|
||||
var/list/devices_line = devices[devices_filter]
|
||||
devices_line -= device
|
||||
|
||||
if (devices_line.len==0)
|
||||
if(!devices_line)
|
||||
devices -= devices_filter
|
||||
del(devices_line)
|
||||
devices_line -= device
|
||||
if(!devices_line.len)
|
||||
devices -= devices_filter
|
||||
|
||||
|
||||
|
||||
|
||||
var/list/pointers = list()
|
||||
|
||||
+122
-184
@@ -23,6 +23,28 @@
|
||||
var/datum/species/species = new /datum/species/human() //The type of mutant race the player is if applicable (i.e. potato-man)
|
||||
var/mutant_color = "FFF" // What color you are if you have certain speciess
|
||||
var/real_name //Stores the real name of the person who originally got this dna datum. Used primarely for changelings,
|
||||
var/list/mutations = list() //All mutations are from now on here
|
||||
var/mob/living/carbon/holder
|
||||
|
||||
/datum/dna/New(mob/living/carbon/new_holder)
|
||||
if(new_holder && istype(new_holder))
|
||||
holder = new_holder
|
||||
|
||||
/datum/dna/proc/add_mutation(mutation_name)
|
||||
var/datum/mutation/human/HM = mutations_list[mutation_name]
|
||||
HM.on_acquiring(holder)
|
||||
|
||||
/datum/dna/proc/remove_mutation(mutation_name)
|
||||
var/datum/mutation/human/HM = mutations_list[mutation_name]
|
||||
HM.on_losing(holder)
|
||||
|
||||
/datum/dna/proc/check_mutation(mutation_name)
|
||||
var/datum/mutation/human/HM = mutations_list[mutation_name]
|
||||
return mutations.Find(HM)
|
||||
|
||||
/datum/dna/proc/remove_all_mutations()
|
||||
for(var/datum/mutation/human/HM in mutations)
|
||||
HM.on_losing(holder)
|
||||
|
||||
/datum/dna/proc/generate_uni_identity(mob/living/carbon/character)
|
||||
. = ""
|
||||
@@ -51,20 +73,25 @@
|
||||
|
||||
/datum/dna/proc/generate_struc_enzymes(mob/living/carbon/character)
|
||||
var/list/L = list("0","1","2","3","4","5","6")
|
||||
. = ""
|
||||
for(var/i=1, i<=DNA_STRUC_ENZYMES_BLOCKS, i++)
|
||||
if(i == RACEBLOCK)
|
||||
. += construct_block(istype(character,/mob/living/carbon/monkey)+1, 2)
|
||||
var/list/sorting = list()
|
||||
sorting.len = 14
|
||||
var/result
|
||||
for(var/datum/mutation/human/A in good_mutations + bad_mutations + not_good_mutations)
|
||||
if(A.name == RACEMUT && istype(character,/mob/living/carbon/monkey))
|
||||
sorting[A.dna_block] = num2hex(A.lowest_value + rand(0, 256 * 6), DNA_BLOCK_SIZE)
|
||||
character.dna.mutations.Add(mutations_list[RACEMUT])
|
||||
else
|
||||
. += random_string(DNA_BLOCK_SIZE, L)
|
||||
return .
|
||||
sorting[A.dna_block] = random_string(DNA_BLOCK_SIZE, L)
|
||||
|
||||
for(var/B in sorting)
|
||||
result += B
|
||||
return result
|
||||
|
||||
/datum/dna/proc/generate_unique_enzymes(mob/living/carbon/character)
|
||||
. = ""
|
||||
if(istype(character))
|
||||
real_name = character.real_name
|
||||
. += md5(character.real_name)
|
||||
reg_dna[.] = real_name
|
||||
else
|
||||
. += repeat_string(DNA_UNIQUE_ENZYMES_LEN, "0")
|
||||
return .
|
||||
@@ -131,7 +158,7 @@
|
||||
return character.dna
|
||||
|
||||
/proc/create_dna(mob/living/carbon/C, datum/species/S) //don't use this unless you're about to use hardset_dna or ready_dna
|
||||
C.dna = new /datum/dna()
|
||||
C.dna = new /datum/dna(C)
|
||||
if(S) C.dna.species = new S() // do not remove; this is here to prevent runtimes
|
||||
|
||||
/////////////////////////// DNA DATUM
|
||||
@@ -172,9 +199,13 @@
|
||||
return
|
||||
|
||||
/proc/randmutb(mob/living/carbon/M)
|
||||
var/datum/mutation/human/HM = pick(bad_mutations | not_good_mutations)
|
||||
HM.on_acquiring(M)
|
||||
return randmut(M, bad_se_blocks)
|
||||
|
||||
/proc/randmutg(mob/living/carbon/M)
|
||||
var/datum/mutation/human/HM = pick(good_mutations)
|
||||
HM.on_acquiring(M)
|
||||
return randmut(M, good_se_blocks | op_se_blocks)
|
||||
|
||||
/proc/randmuti(mob/living/carbon/M)
|
||||
@@ -234,99 +265,15 @@
|
||||
if(!check_dna_integrity(M))
|
||||
return 0
|
||||
|
||||
M.disabilities = 0
|
||||
M.sdisabilities = 0
|
||||
M.mutations.Cut()
|
||||
var/mob/living/carbon/C = M
|
||||
var/mob/living/carbon/temp
|
||||
|
||||
M.see_in_dark = initial(M.see_in_dark)
|
||||
M.see_invisible = initial(M.see_invisible)
|
||||
for(var/A in mutations_list)
|
||||
var/datum/mutation/human/HM = mutations_list[A]
|
||||
temp = HM.check_block(C)
|
||||
if(ismob(temp))
|
||||
C = temp
|
||||
|
||||
var/list/blocks = new /list(DNA_STRUC_ENZYMES_BLOCKS) //on-off status for each block
|
||||
for(var/i in bad_se_blocks) //bad mutations
|
||||
blocks[i] = (deconstruct_block(getblock(M.dna.struc_enzymes, i), BAD_MUTATION_DIFFICULTY) == BAD_MUTATION_DIFFICULTY)
|
||||
blocks[RACEBLOCK] = (deconstruct_block(getblock(M.dna.struc_enzymes, RACEBLOCK), BAD_MUTATION_DIFFICULTY) == BAD_MUTATION_DIFFICULTY)
|
||||
for(var/i in good_se_blocks) //good mutations
|
||||
blocks[i] = (deconstruct_block(getblock(M.dna.struc_enzymes, i), GOOD_MUTATION_DIFFICULTY) == GOOD_MUTATION_DIFFICULTY)
|
||||
for(var/i in op_se_blocks) //Overpowered mutations...extra difficult to obtain
|
||||
blocks[i] = (deconstruct_block(getblock(M.dna.struc_enzymes, i), OP_MUTATION_DIFFICULTY) == OP_MUTATION_DIFFICULTY)
|
||||
|
||||
if(blocks[NEARSIGHTEDBLOCK])
|
||||
M.disabilities |= NEARSIGHTED
|
||||
M << "<span class='danger'>Your eyes feel strange.</span>"
|
||||
if(blocks[EPILEPSYBLOCK])
|
||||
M.disabilities |= EPILEPSY
|
||||
M << "<span class='danger'>You get a headache.</span>"
|
||||
if(blocks[STRANGEBLOCK])
|
||||
M << "<span class='danger'>You feel strange.</span>"
|
||||
if(prob(95))
|
||||
if(prob(50)) randmutb(M)
|
||||
else randmuti(M)
|
||||
else randmutg(M)
|
||||
if(blocks[COUGHBLOCK])
|
||||
M.disabilities |= COUGHING
|
||||
M << "<span class='danger'>You start coughing.</span>"
|
||||
if(blocks[CLUMSYBLOCK])
|
||||
M << "<span class='danger'>You feel lightheaded.</span>"
|
||||
M.mutations |= CLUMSY
|
||||
if(blocks[TOURETTESBLOCK])
|
||||
M.disabilities |= TOURETTES
|
||||
M << "<span class='danger'>You twitch.</span>"
|
||||
if(blocks[NERVOUSBLOCK])
|
||||
M.disabilities |= NERVOUS
|
||||
M << "<span class='danger'>You feel nervous.</span>"
|
||||
if(blocks[DEAFBLOCK])
|
||||
M.sdisabilities |= DEAF
|
||||
M.ear_deaf = 1
|
||||
M << "<span class='danger'>You can't seem to hear anything.</span>"
|
||||
if(blocks[BLINDBLOCK])
|
||||
M.sdisabilities |= BLIND
|
||||
M << "<span class='danger'>You can't seem to see anything.</span>"
|
||||
if(blocks[HULKBLOCK])
|
||||
if(inj || prob(10))
|
||||
M.mutations |= HULK
|
||||
M << "<span class='notice'>Your muscles hurt.</span>"
|
||||
if(blocks[XRAYBLOCK])
|
||||
if(inj || prob(30))
|
||||
M.mutations |= XRAY
|
||||
M << "<span class='notice'>The walls suddenly disappear.</span>"
|
||||
M.sight |= SEE_MOBS|SEE_OBJS|SEE_TURFS
|
||||
M.see_in_dark = 8
|
||||
M.see_invisible = SEE_INVISIBLE_LEVEL_TWO
|
||||
if(blocks[FIREBLOCK])
|
||||
if(inj || prob(30))
|
||||
M.mutations |= COLD_RESISTANCE
|
||||
M << "<span class='notice'>Your body feels warm.</span>"
|
||||
if(blocks[TELEBLOCK])
|
||||
if(inj || prob(25))
|
||||
M.mutations |= TK
|
||||
M << "<span class='notice'>You feel smarter.</span>"
|
||||
|
||||
|
||||
/* If you want the new mutations to work, UNCOMMENT THIS.
|
||||
if(istype(M, /mob/living/carbon))
|
||||
for (var/datum/mutations/mut in global_mutations)
|
||||
mut.check_mutation(M)
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////// Monkey Block
|
||||
if(blocks[RACEBLOCK])
|
||||
if(istype(M, /mob/living/carbon/human)) // human > monkey
|
||||
var/mob/living/carbon/monkey/O = M.monkeyize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPDAMAGE | TR_KEEPVIRUS)
|
||||
if(connected) //inside dna thing
|
||||
var/obj/machinery/dna_scannernew/C = connected
|
||||
O.loc = C
|
||||
C.occupant = O
|
||||
connected = null
|
||||
return 1
|
||||
else
|
||||
if(istype(M, /mob/living/carbon/monkey)) // monkey > human,
|
||||
var/mob/living/carbon/human/O = M.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPDAMAGE | TR_KEEPVIRUS)
|
||||
if(O && connected) //inside dna thing
|
||||
var/obj/machinery/dna_scannernew/C = connected
|
||||
O.loc = C
|
||||
C.occupant = O
|
||||
connected = null
|
||||
return 1
|
||||
//////////////////////////////////////////////////////////// Monkey Block
|
||||
if(M)
|
||||
M.update_icon = 1 //queue a full icon update at next life() call
|
||||
@@ -342,9 +289,7 @@
|
||||
icon_state = "scanner"
|
||||
density = 1
|
||||
var/locked = 0
|
||||
var/open = 0
|
||||
anchored = 1
|
||||
interact_offline = 1
|
||||
use_power = 1
|
||||
idle_power_usage = 50
|
||||
active_power_usage = 300
|
||||
@@ -380,11 +325,11 @@
|
||||
|
||||
//no power or maintenance
|
||||
if(stat & (NOPOWER|BROKEN))
|
||||
icon_state = initial(icon_state)+ (open ? "_open" : "") + "_unpowered"
|
||||
icon_state = initial(icon_state)+ (state_open ? "_open" : "") + "_unpowered"
|
||||
return
|
||||
|
||||
if((stat & MAINT) || panel_open)
|
||||
icon_state = initial(icon_state)+ (open ? "_open" : "") + "_maintenance"
|
||||
icon_state = initial(icon_state)+ (state_open ? "_open" : "") + "_maintenance"
|
||||
return
|
||||
|
||||
//running and someone in there
|
||||
@@ -393,23 +338,32 @@
|
||||
return
|
||||
|
||||
//running
|
||||
icon_state = initial(icon_state)+ (open ? "_open" : "")
|
||||
icon_state = initial(icon_state)+ (state_open ? "_open" : "")
|
||||
|
||||
/obj/machinery/dna_scannernew/power_change()
|
||||
..()
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/dna_scannernew/proc/toggle_open(mob/user=usr)
|
||||
if(!user)
|
||||
/obj/machinery/dna_scannernew/proc/toggle_open(var/mob/user)
|
||||
if(panel_open)
|
||||
user << "<span class='notice'>Close the maintenance panel first.</span>"
|
||||
return
|
||||
if(open) return close(user)
|
||||
else return open(user)
|
||||
|
||||
if(state_open)
|
||||
close_machine()
|
||||
return
|
||||
|
||||
else if(locked)
|
||||
user << "<span class='notice'>The bolts are locked down, securing the door shut.</span>"
|
||||
return
|
||||
|
||||
open_machine()
|
||||
|
||||
/obj/machinery/dna_scannernew/container_resist()
|
||||
var/mob/living/user = usr
|
||||
var/breakout_time = 2
|
||||
if(open || !locked) //Open and unlocked, no need to escape
|
||||
open = 1
|
||||
if(state_open || !locked) //Open and unlocked, no need to escape
|
||||
state_open = 1
|
||||
return
|
||||
user.changeNext_move(CLICK_CD_BREAKOUT)
|
||||
user.last_special = world.time + CLICK_CD_BREAKOUT
|
||||
@@ -417,104 +371,83 @@
|
||||
user.visible_message("<span class='warning'>You hear a metallic creaking from [src]!</span>")
|
||||
|
||||
if(do_after(user,(breakout_time*60*10))) //minutes * 60seconds * 10deciseconds
|
||||
if(!user || user.stat != CONSCIOUS || user.loc != src || open || !locked)
|
||||
if(!user || user.stat != CONSCIOUS || user.loc != src || state_open || !locked)
|
||||
return
|
||||
|
||||
locked = 0
|
||||
visible_message("<span class='danger'>[user] successfully broke out of [src]!</span>")
|
||||
user << "<span class='notice'>You successfully break out of [src]!</span>"
|
||||
|
||||
open(user)
|
||||
open_machine()
|
||||
|
||||
/obj/machinery/dna_scannernew/proc/close(mob/user)
|
||||
if(open)
|
||||
if(panel_open)
|
||||
user << "<span class='notice'>Close the maintenance panel first.</span>"
|
||||
return 0
|
||||
open = 0
|
||||
density = 1
|
||||
for(var/mob/living/carbon/C in loc)
|
||||
if(C.buckled) continue
|
||||
if(C.client)
|
||||
C.client.perspective = EYE_PERSPECTIVE
|
||||
C.client.eye = src
|
||||
occupant = C
|
||||
C.loc = src
|
||||
C.stop_pulling()
|
||||
break
|
||||
update_icon()
|
||||
/obj/machinery/dna_scannernew/close_machine()
|
||||
if(!state_open)
|
||||
return 0
|
||||
|
||||
// search for ghosts, if the corpse is empty and the scanner is connected to a cloner
|
||||
if(occupant)
|
||||
if(locate(/obj/machinery/computer/cloning, get_step(src, NORTH)) \
|
||||
|| locate(/obj/machinery/computer/cloning, get_step(src, SOUTH)) \
|
||||
|| locate(/obj/machinery/computer/cloning, get_step(src, EAST)) \
|
||||
|| locate(/obj/machinery/computer/cloning, get_step(src, WEST)))
|
||||
..()
|
||||
|
||||
var/mob/dead/observer/ghost = occupant.get_ghost()
|
||||
if(ghost)
|
||||
ghost << "<span class='ghostalert'>Your corpse has been placed into a cloning scanner. Return to your body if you want to be cloned!</span> (Verbs -> Ghost -> Re-enter corpse)"
|
||||
ghost << sound('sound/effects/genetics.ogg')
|
||||
return 1
|
||||
// search for ghosts, if the corpse is empty and the scanner is connected to a cloner
|
||||
if(occupant)
|
||||
if(locate(/obj/machinery/computer/cloning, get_step(src, NORTH)) \
|
||||
|| locate(/obj/machinery/computer/cloning, get_step(src, SOUTH)) \
|
||||
|| locate(/obj/machinery/computer/cloning, get_step(src, EAST)) \
|
||||
|| locate(/obj/machinery/computer/cloning, get_step(src, WEST)))
|
||||
|
||||
/obj/machinery/dna_scannernew/proc/open(mob/user)
|
||||
if(!open)
|
||||
if(panel_open)
|
||||
user << "<span class='notice'>Close the maintenance panel first.</span>"
|
||||
return
|
||||
if(locked)
|
||||
user << "<span class='notice'>The bolts are locked down, securing the door shut.</span>"
|
||||
return
|
||||
var/turf/T = get_turf(src)
|
||||
if(T)
|
||||
open = 1
|
||||
density = 0
|
||||
T.contents += contents
|
||||
if(occupant)
|
||||
if(occupant.client)
|
||||
occupant.client.eye = occupant
|
||||
occupant.client.perspective = MOB_PERSPECTIVE
|
||||
occupant = null
|
||||
update_icon()
|
||||
return 1
|
||||
var/mob/dead/observer/ghost = occupant.get_ghost()
|
||||
if(ghost)
|
||||
ghost << "<span class='ghostalert'>Your corpse has been placed into a cloning scanner. Return to your body if you want to be cloned!</span> (Verbs -> Ghost -> Re-enter corpse)"
|
||||
ghost << sound('sound/effects/genetics.ogg')
|
||||
return 1
|
||||
|
||||
/obj/machinery/dna_scannernew/open_machine()
|
||||
if(state_open)
|
||||
return 0
|
||||
|
||||
..()
|
||||
|
||||
return 1
|
||||
|
||||
/obj/machinery/dna_scannernew/relaymove(mob/user as mob)
|
||||
if(user.stat)
|
||||
if(user.stat || locked)
|
||||
return
|
||||
open(user)
|
||||
|
||||
open_machine()
|
||||
return
|
||||
|
||||
/obj/machinery/dna_scannernew/attackby(obj/item/weapon/grab/G, mob/user)
|
||||
/obj/machinery/dna_scannernew/attackby(var/obj/item/I, mob/user)
|
||||
|
||||
if(!occupant && default_deconstruction_screwdriver(user, icon_state, icon_state, G))//sent icon_state is irrelevant...
|
||||
if(!occupant && default_deconstruction_screwdriver(user, icon_state, icon_state, I))//sent icon_state is irrelevant...
|
||||
update_icon()//..since we're updating the icon here, since the scanner can be unpowered when opened/closed
|
||||
return
|
||||
|
||||
if(exchange_parts(user, G))
|
||||
if(exchange_parts(user, I))
|
||||
return
|
||||
|
||||
if(istype(G, /obj/item/weapon/crowbar))
|
||||
if(panel_open)
|
||||
for(var/obj/I in contents) // in case there is something in the scanner
|
||||
I.loc = src.loc
|
||||
default_deconstruction_crowbar(G)
|
||||
if(default_pry_open(I))
|
||||
return
|
||||
|
||||
if(!istype(G, /obj/item/weapon/grab) || !ismob(G.affecting))
|
||||
if(default_deconstruction_crowbar(I))
|
||||
return
|
||||
if(!open)
|
||||
user << "<span class='notice'>Open the scanner first.</span>"
|
||||
return
|
||||
var/mob/M = G.affecting
|
||||
M.loc = loc
|
||||
user.stop_pulling()
|
||||
qdel(G)
|
||||
|
||||
if(istype(I, /obj/item/weapon/grab))
|
||||
var/obj/item/weapon/grab/G = I
|
||||
if(!ismob(G.affecting))
|
||||
return
|
||||
|
||||
if(!state_open)
|
||||
user << "<span class='notice'>Open the scanner first.</span>"
|
||||
return
|
||||
|
||||
var/mob/M = G.affecting
|
||||
M.loc = loc
|
||||
user.stop_pulling()
|
||||
qdel(G)
|
||||
|
||||
/obj/machinery/dna_scannernew/attack_hand(mob/user)
|
||||
if(..())
|
||||
if(..(user,1,0)) //don't set the machine, since there's no dialog
|
||||
return
|
||||
|
||||
toggle_open(user)
|
||||
add_fingerprint(user)
|
||||
|
||||
/obj/machinery/dna_scannernew/blob_act()
|
||||
if(prob(75))
|
||||
@@ -608,7 +541,7 @@
|
||||
else
|
||||
occupant_status += "<span class='bad'>No subject detected</span></div></div>"
|
||||
|
||||
if(connected.open)
|
||||
if(connected.state_open)
|
||||
scanner_status = "Open"
|
||||
else
|
||||
scanner_status = "Closed"
|
||||
@@ -646,8 +579,8 @@
|
||||
status += "</div>" // Close statusDisplay div
|
||||
var/buttons = "<a href='?src=\ref[src];'>Scan</a> "
|
||||
if(connected)
|
||||
buttons += " <a href='?src=\ref[src];task=toggleopen;'>[connected.open ? "Close" : "Open"] Scanner</a> "
|
||||
if (connected.open)
|
||||
buttons += " <a href='?src=\ref[src];task=toggleopen;'>[connected.state_open ? "Close" : "Open"] Scanner</a> "
|
||||
if (connected.state_open)
|
||||
buttons += "<span class='linkOff'>[connected.locked ? "Unlock" : "Lock"] Scanner</span> "
|
||||
else
|
||||
buttons += "<a href='?src=\ref[src];task=togglelock;'>[connected.locked ? "Unlock" : "Lock"] Scanner</a> "
|
||||
@@ -866,7 +799,12 @@
|
||||
if("se")
|
||||
if(buffer_slot["SE"])
|
||||
I = new /obj/item/weapon/dnainjector(loc)
|
||||
I.fields = list("SE"=buffer_slot["SE"])
|
||||
for(var/datum/mutation/human/HM in good_mutations + bad_mutations + not_good_mutations)
|
||||
if(HM.check_block_string(buffer_slot["SE"]))
|
||||
if(prob(HM.get_chance))
|
||||
I.add_mutations.Add(HM)
|
||||
else
|
||||
I.remove_mutations.Add(HM)
|
||||
I.damage_coeff = connected.damage_coeff
|
||||
if("ui")
|
||||
if(buffer_slot["UI"])
|
||||
|
||||
@@ -99,16 +99,12 @@ var/list/blob_nodes = list()
|
||||
for(var/datum/mind/blob in infected_crew)
|
||||
greet_blob(blob)
|
||||
|
||||
if(emergency_shuttle)
|
||||
emergency_shuttle.always_fake_recall = 1
|
||||
SSshuttle.emergencyAlwaysFakeRecall = 1
|
||||
|
||||
// Disable the blob event for this round.
|
||||
if(events)
|
||||
var/datum/round_event_control/blob/B = locate() in events.control
|
||||
if(B)
|
||||
B.max_occurrences = 0 // disable the event
|
||||
else
|
||||
ERROR("Events variable is null in blob gamemode post setup.")
|
||||
var/datum/round_event_control/blob/B = locate() in SSevent.control
|
||||
if(B)
|
||||
B.max_occurrences = 0 // disable the event
|
||||
|
||||
spawn(0)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
/obj/effect/blob/core/New(loc, var/h = 200, var/client/new_overmind = null, var/new_rate = 2)
|
||||
blob_cores += src
|
||||
processing_objects.Add(src)
|
||||
SSobj.processing.Add(src)
|
||||
if(!overmind)
|
||||
create_overmind(new_overmind)
|
||||
point_rate = new_rate
|
||||
@@ -22,7 +22,7 @@
|
||||
blob_cores -= src
|
||||
if(overmind)
|
||||
qdel(overmind)
|
||||
processing_objects.Remove(src)
|
||||
SSobj.processing.Remove(src)
|
||||
..()
|
||||
|
||||
/obj/effect/blob/core/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
/obj/effect/blob/node/New(loc, var/h = 100)
|
||||
blob_nodes += src
|
||||
processing_objects.Add(src)
|
||||
SSobj.processing.Add(src)
|
||||
..(loc, h)
|
||||
|
||||
/obj/effect/blob/node/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
/obj/effect/blob/node/Destroy()
|
||||
blob_nodes -= src
|
||||
processing_objects.Remove(src)
|
||||
SSobj.processing.Remove(src)
|
||||
..()
|
||||
|
||||
/obj/effect/blob/node/Life()
|
||||
|
||||
@@ -157,9 +157,10 @@ var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon"
|
||||
changeling.current << "<b>You must complete the following tasks:</b>"
|
||||
|
||||
if (changeling.current.mind)
|
||||
if (changeling.current.mind.assigned_role == "Clown")
|
||||
changeling.current << "You have evolved beyond your clownish nature, allowing you to wield weapons without harming yourself."
|
||||
changeling.current.mutations.Remove(CLUMSY)
|
||||
var/mob/living/carbon/human/H = changeling.current
|
||||
if(H.mind.assigned_role == "Clown")
|
||||
H << "You have evolved beyond your clownish nature, allowing you to wield weapons without harming yourself."
|
||||
H.dna.remove_mutation(CLOWNMUT)
|
||||
|
||||
var/obj_count = 1
|
||||
for(var/datum/objective/objective in changeling.objectives)
|
||||
@@ -285,7 +286,7 @@ var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon"
|
||||
return
|
||||
if(!target)
|
||||
return
|
||||
if((NOCLONE in target.mutations) || (HUSK in target.mutations))
|
||||
if((target.disabilities & NOCLONE) || (target.disabilities & HUSK))
|
||||
user << "<span class='warning'>DNA of [target] is ruined beyond usability!</span>"
|
||||
return
|
||||
if(!ishuman(target))//Absorbing monkeys is entirely possible, but it can cause issues with transforming. That's what lesser form is for anyway!
|
||||
|
||||
@@ -136,7 +136,7 @@
|
||||
loc.visible_message("<span class='warning'>A grotesque blade forms around [loc.name]\'s arm!</span>", "<span class='warning'>Our arm twists and mutates, transforming it into a deadly blade.</span>", "<span class='warning'>You hear organic matter ripping and tearing!</span>")
|
||||
|
||||
/obj/item/weapon/melee/arm_blade/dropped(mob/user)
|
||||
visible_message("<span class='warning'>With a sickening crunch, [user] reforms his blade into an arm!</span>", "<span class='notice'>We assimilate the blade back into our body.</span>", "<span class='warning>You hear organic matter ripping and tearing!</span>")
|
||||
user.visible_message("<span class='warning'>With a sickening crunch, [user] reforms his blade into an arm!</span>", "<span class='notice'>We assimilate the blade back into our body.</span>", "<span class='warning>You hear organic matter ripping and tearing!</span>")
|
||||
qdel(src)
|
||||
|
||||
/obj/item/weapon/melee/arm_blade/afterattack(atom/target, mob/user, proximity)
|
||||
@@ -216,7 +216,7 @@
|
||||
if(remaining_uses < 1)
|
||||
if(ishuman(loc))
|
||||
var/mob/living/carbon/human/H = loc
|
||||
visible_message("<span class='warning'>With a sickening crunch, [H] reforms his shield into an arm!</span>", "<span class='notice'>We assimilate our shield into our body</span>", "<span class='warning>You hear organic matter ripping and tearing!</span>")
|
||||
H.visible_message("<span class='warning'>With a sickening crunch, [H] reforms his shield into an arm!</span>", "<span class='notice'>We assimilate our shield into our body</span>", "<span class='warning>You hear organic matter ripping and tearing!</span>")
|
||||
H.unEquip(src, 1)
|
||||
qdel(src)
|
||||
return 0
|
||||
@@ -260,7 +260,7 @@
|
||||
..()
|
||||
if(ismob(loc))
|
||||
loc.visible_message("<span class='warning'>[loc.name]\'s flesh rapidly inflates, forming a bloated mass around their body!</span>", "<span class='warning'>We inflate our flesh, creating a spaceproof suit!</span>", "<span class='warning'>You hear organic matter ripping and tearing!</span>")
|
||||
processing_objects += src
|
||||
SSobj.processing += src
|
||||
|
||||
/obj/item/clothing/suit/space/changeling/dropped()
|
||||
qdel(src)
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
/obj/effect/proc_holder/changeling/sting/transformation/can_sting(var/mob/user, var/mob/target)
|
||||
if(!..())
|
||||
return
|
||||
if((HUSK in target.mutations) || !check_dna_integrity(target))
|
||||
if((target.disabilities & HUSK) || !check_dna_integrity(target))
|
||||
user << "<span class='warning'>Our sting appears ineffective against its DNA.</span>"
|
||||
return 0
|
||||
return 1
|
||||
@@ -140,7 +140,7 @@ obj/effect/proc_holder/changeling/sting/blind
|
||||
/obj/effect/proc_holder/changeling/sting/blind/sting_action(var/mob/user, var/mob/target)
|
||||
add_logs(user, target, "stung", object="blind sting")
|
||||
target << "<span class='danger'>Your eyes burn horrifically!</span>"
|
||||
target.disabilities |= NEARSIGHTED
|
||||
target.disabilities |= NEARSIGHT
|
||||
target.eye_blind = 20
|
||||
target.eye_blurry = 40
|
||||
feedback_add_details("changeling_powers","BS")
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
if (mob.mind)
|
||||
if (mob.mind.assigned_role == "Clown")
|
||||
mob << "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself."
|
||||
mob.mutations.Remove(CLUMSY)
|
||||
mob.dna.remove_mutation(CLOWNMUT)
|
||||
|
||||
|
||||
var/obj/item/weapon/paper/talisman/supply/T = new(mob)
|
||||
|
||||
@@ -169,7 +169,7 @@ var/list/sacrificed = list()
|
||||
return
|
||||
var/narsie_type = /obj/singularity/narsie/large
|
||||
// Moves narsie if she was already summoned.
|
||||
var/obj/her = locate(narsie_type, machines)
|
||||
var/obj/her = locate(narsie_type, SSmachine.processing)
|
||||
if(her)
|
||||
her.loc = get_turf(src)
|
||||
return
|
||||
@@ -886,8 +886,8 @@ var/list/sacrificed = list()
|
||||
C.ear_deaf += 50
|
||||
C.show_message("<span class='danger'>The world around you suddenly becomes quiet.</span>", 3)
|
||||
affected++
|
||||
if(prob(1))
|
||||
C.sdisabilities |= DEAF
|
||||
if(prob(1) && ishuman(C))
|
||||
C.dna.add_mutation(DEAFMUT)
|
||||
if(affected)
|
||||
usr.say("Sti[pick("'","`")] kaliedir!")
|
||||
usr << "<span class='danger'>The world becomes quiet as the deafening rune dissipates into fine dust.</span>"
|
||||
@@ -926,9 +926,9 @@ var/list/sacrificed = list()
|
||||
C.eye_blurry += 50
|
||||
C.eye_blind += 20
|
||||
if(prob(5))
|
||||
C.disabilities |= NEARSIGHTED
|
||||
C.disabilities |= NEARSIGHT
|
||||
if(prob(10))
|
||||
C.sdisabilities |= BLIND
|
||||
C.disabilities |= BLIND
|
||||
C.show_message("<span class='danger'>Suddenly you see red flash that blinds you.</span>", 3)
|
||||
affected++
|
||||
if(affected)
|
||||
@@ -1030,8 +1030,7 @@ var/list/sacrificed = list()
|
||||
if(iscarbon(L))
|
||||
var/mob/living/carbon/C = L
|
||||
flick("e_flash", C.flash)
|
||||
if(C.stuttering < 1 && (!(HULK in C.mutations)))
|
||||
C.stuttering = 1
|
||||
C.stuttering = 1
|
||||
C.Weaken(1)
|
||||
C.Stun(1)
|
||||
C.show_message("<span class='danger'>The rune explodes in a bright flash.</span>", 3)
|
||||
@@ -1057,10 +1056,9 @@ var/list/sacrificed = list()
|
||||
else if(iscarbon(T))
|
||||
var/mob/living/carbon/C = T
|
||||
flick("e_flash", C.flash)
|
||||
if (!(HULK in C.mutations))
|
||||
C.silent += 15
|
||||
C.Weaken(10)
|
||||
C.Stun(10)
|
||||
C.silent += 15
|
||||
C.Weaken(10)
|
||||
C.Stun(10)
|
||||
return
|
||||
|
||||
/////////////////////////////////////////TWENTY-FIFTH RUNE
|
||||
|
||||
@@ -95,79 +95,13 @@
|
||||
|
||||
|
||||
/datum/game_mode/proc/check_finished() //to be called by ticker
|
||||
if(emergency_shuttle.location==2 || station_was_nuked)
|
||||
if(SSshuttle.emergency.mode >= SHUTTLE_ENDGAME || station_was_nuked)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
/datum/game_mode/proc/declare_completion()
|
||||
var/clients = 0
|
||||
var/surviving_humans = 0
|
||||
var/surviving_total = 0
|
||||
var/ghosts = 0
|
||||
var/escaped_humans = 0
|
||||
var/escaped_total = 0
|
||||
var/escaped_on_pod_1 = 0
|
||||
var/escaped_on_pod_2 = 0
|
||||
var/escaped_on_pod_3 = 0
|
||||
var/escaped_on_pod_5 = 0
|
||||
var/escaped_on_shuttle = 0
|
||||
|
||||
var/list/area/escape_locations = list(/area/shuttle/escape/centcom, /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod4/centcom)
|
||||
|
||||
for(var/mob/M in player_list)
|
||||
if(M.client)
|
||||
clients++
|
||||
if(ishuman(M))
|
||||
if(!M.stat)
|
||||
surviving_humans++
|
||||
if(M.loc && M.loc.loc && M.loc.loc.type in escape_locations)
|
||||
escaped_humans++
|
||||
if(!M.stat)
|
||||
surviving_total++
|
||||
if(M.loc && M.loc.loc && M.loc.loc.type in escape_locations)
|
||||
escaped_total++
|
||||
|
||||
if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape/centcom)
|
||||
escaped_on_shuttle++
|
||||
|
||||
if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod1/centcom)
|
||||
escaped_on_pod_1++
|
||||
if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod2/centcom)
|
||||
escaped_on_pod_2++
|
||||
if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod3/centcom)
|
||||
escaped_on_pod_3++
|
||||
if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod4/centcom)
|
||||
escaped_on_pod_5++
|
||||
|
||||
if(isobserver(M))
|
||||
ghosts++
|
||||
|
||||
if(clients > 0)
|
||||
feedback_set("round_end_clients",clients)
|
||||
if(ghosts > 0)
|
||||
feedback_set("round_end_ghosts",ghosts)
|
||||
if(surviving_humans > 0)
|
||||
feedback_set("survived_human",surviving_humans)
|
||||
if(surviving_total > 0)
|
||||
feedback_set("survived_total",surviving_total)
|
||||
if(escaped_humans > 0)
|
||||
feedback_set("escaped_human",escaped_humans)
|
||||
if(escaped_total > 0)
|
||||
feedback_set("escaped_total",escaped_total)
|
||||
if(escaped_on_shuttle > 0)
|
||||
feedback_set("escaped_on_shuttle",escaped_on_shuttle)
|
||||
if(escaped_on_pod_1 > 0)
|
||||
feedback_set("escaped_on_pod_1",escaped_on_pod_1)
|
||||
if(escaped_on_pod_2 > 0)
|
||||
feedback_set("escaped_on_pod_2",escaped_on_pod_2)
|
||||
if(escaped_on_pod_3 > 0)
|
||||
feedback_set("escaped_on_pod_3",escaped_on_pod_3)
|
||||
if(escaped_on_pod_5 > 0)
|
||||
feedback_set("escaped_on_pod_5",escaped_on_pod_5)
|
||||
|
||||
send2irc("Server", "Round just ended.")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
if (mob.mind)
|
||||
if (mob.mind.assigned_role == "Clown")
|
||||
mob << "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself."
|
||||
mob.mutations.Remove(CLUMSY)
|
||||
mob.dna.remove_mutation(CLOWNMUT)
|
||||
|
||||
|
||||
var/obj/item/device/flash/T = new(mob)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user