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:
Sawu
2015-01-08 03:58:31 +11:00
545 changed files with 24812 additions and 19088 deletions
+14 -6
View File
@@ -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
*/
+5 -3
View File
@@ -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)
+2 -5
View File
@@ -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
+2
View File
@@ -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
+15 -2
View File
@@ -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
+12 -1
View File
@@ -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
+14
View File
@@ -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"
+18 -5
View File
@@ -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"
+4 -4
View File
@@ -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
View File
@@ -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)
+21 -22
View File
@@ -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
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
+19
View File
@@ -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
+4 -1
View File
@@ -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
+2
View File
@@ -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
View File
@@ -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
+8
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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)
+2
View File
@@ -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
View File
@@ -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
+4 -1
View File
@@ -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()
+4
View File
@@ -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
-1
View File
@@ -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
+1 -6
View File
@@ -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
+14 -7
View File
@@ -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 = "&nbsp;&nbsp;&nbsp;&nbsp;"
var/TAB = "&nbsp;&nbsp;&nbsp;&nbsp;"
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)
*/
+1 -1
View File
@@ -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
View File
@@ -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))
-6
View File
@@ -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
View File
@@ -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
*/
+7 -10
View File
@@ -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))
+91 -122
View File
@@ -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
+19 -3
View File
@@ -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)
+9 -34
View File
@@ -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)
-130
View File
@@ -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
+77 -267
View File
@@ -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
-247
View File
@@ -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)
+134
View File
@@ -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()
+21
View File
@@ -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)
+20
View File
@@ -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)
+130
View File
@@ -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
+31
View File
@@ -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)
+24
View File
@@ -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)
+30
View File
@@ -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)
+42
View File
@@ -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()
*/
+36
View File
@@ -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)
+18
View File
@@ -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()
+46
View File
@@ -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()
+325
View File
@@ -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]\""
+51
View File
@@ -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)
+46
View File
@@ -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()
-808
View File
@@ -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
View File
@@ -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
+99
View File
@@ -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"))
+3 -3
View File
@@ -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)
..()
+2 -2
View File
@@ -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
View File
@@ -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
+327
View File
@@ -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)
+5 -5
View File
@@ -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
+1 -1
View File
@@ -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
-57
View File
@@ -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
View File
@@ -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,
+1 -1
View File
@@ -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
+3 -3
View File
@@ -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
+1 -8
View File
@@ -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()
+9 -243
View File
@@ -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,
+2 -4
View File
@@ -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)*/
*/
+10
View File
@@ -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
View File
@@ -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
View File
@@ -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"])
+4 -8
View File
@@ -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)
+2 -2
View File
@@ -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)
+2 -2
View File
@@ -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()
+5 -4
View File
@@ -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")
+1 -1
View File
@@ -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)
+9 -11
View File
@@ -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
+1 -67
View File
@@ -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
+1 -1
View File
@@ -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