Merge branch 'master' of https://github.com/ParadiseSS13/Paradise into RingRingHello

This commit is contained in:
Aurorablade
2018-03-25 23:15:37 -04:00
329 changed files with 9590 additions and 2396 deletions
+4 -2
View File
@@ -32,8 +32,10 @@ Pipelines + Other Objects -> Pipe network
var/global/datum/pipe_icon_manager/icon_manager
/obj/machinery/atmospherics/New()
if(!armor)
armor = list(melee = 25, bullet = 10, laser = 10, energy = 100, bomb = 0, bio = 100, rad = 100)
..()
atmos_machinery += src
SSair.atmos_machinery += src
if(!icon_manager)
icon_manager = new()
@@ -54,7 +56,7 @@ Pipelines + Other Objects -> Pipe network
/obj/machinery/atmospherics/Destroy()
QDEL_NULL(stored)
atmos_machinery -= src
SSair.atmos_machinery -= src
for(var/mob/living/L in src) //ventcrawling is serious business
L.remove_ventcrawl()
L.forceMove(get_turf(src))
@@ -37,11 +37,11 @@
if(!partner)
return 0
if(!air_master || air_master.current_cycle <= update_cycle)
if(!SSair || 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()
@@ -1,7 +1,6 @@
#define EXTERNAL_PRESSURE_BOUND ONE_ATMOSPHERE
#define INTERNAL_PRESSURE_BOUND 0
#define PRESSURE_CHECKS 1
#undefine
/obj/machinery/atmospherics/unary/vent_pump
icon = 'icons/atmos/vent_pump.dmi'
+2 -3
View File
@@ -1,4 +1,3 @@
var/global/list/pipe_networks = list()
var/global/list/deferred_pipenet_rebuilds = list()
/datum/pipeline
@@ -13,10 +12,10 @@ var/global/list/deferred_pipenet_rebuilds = list()
var/alert_pressure = 0
/datum/pipeline/New()
pipe_networks += src
SSair.networks += src
/datum/pipeline/Destroy()
pipe_networks -= src
SSair.networks -= src
if(air && air.volume)
temporarily_store_air()
for(var/obj/machinery/atmospherics/pipe/P in members)
+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
@@ -58,7 +58,7 @@
/obj/effect/hotspot/New()
..()
air_master.hotspots += src
SSair.hotspots += src
perform_exposure()
dir = pick(cardinal)
air_update_turf()
@@ -154,7 +154,7 @@
/obj/effect/hotspot/Destroy()
set_light(0)
air_master.hotspots -= src
SSair.hotspots -= src
DestroyTurf()
if(istype(loc, /turf/simulated))
var/turf/simulated/T = loc
+3 -3
View File
@@ -126,8 +126,8 @@ 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)
if(SSair)
SSair.add_to_active(src,command)
/atom/movable/proc/move_update_air(var/turf/T)
if(istype(T,/turf))
@@ -184,4 +184,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)
+71 -26
View File
@@ -141,9 +141,9 @@
/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
@@ -184,7 +184,7 @@
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
@@ -227,14 +227,14 @@
update_visuals()
if(!excited_group && remove == 1)
air_master.remove_from_active(src)
SSair.remove_from_active(src)
/turf/simulated/proc/archive()
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()
if(icy && !icyoverlay)
@@ -288,7 +288,7 @@
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
@@ -309,24 +309,48 @@
/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 && !pulledby)
var/turf/target = get_turf(src)
var/datum/gas_mixture/target_air = target.return_air()
if(isunsimulatedturf(target) || pressure_resistance > target_air.return_pressure())
return 0
if(pressure_difference >= throw_pressure_limit)
var/general_direction = get_edge_target_turf(src, direction)
if(last_forced_movement + 10 < air_master.current_cycle && is_valid_tochat_target(src)) //the first check prevents spamming throw to_chat
if(last_forced_movement + 10 < SSair.times_fired && is_valid_tochat_target(src)) //the first check prevents spamming throw to_chat
to_chat(src, "<span class='userdanger'>The pressure sends you flying!</span>")
if(ishuman(src))
var/mob/living/carbon/human/H = src
H.Weaken(min(pressure_difference / 50, 2))
spawn()
throw_at(general_direction, pressure_difference / 10, pressure_difference / 200, null, 0, 0, null)
last_forced_movement = air_master.current_cycle
var/max_distance = 14 // reduce by one each calculation to prevent infinate loops.
var/min_observed_pressure = INFINITY
var/turf/possible_target = get_turf(src)
while(!isunsimulatedturf(target) && max_distance > 0)
max_distance--
target_air = target.return_air()
min_observed_pressure = target_air.return_pressure()
possible_target = get_step_towards(target,general_direction)
if(istype(possible_target, /turf/space))
target = possible_target
break
if(!CanAtmosPass(possible_target))
target = possible_target
max_distance = 0
break
var/datum/gas_mixture/possible_target_air = possible_target.return_air()
if(possible_target_air.return_pressure() > min_observed_pressure)
target = possible_target
break
target = possible_target
if(max_distance)
throw_at(target, get_dist(src, target), pressure_difference / 200, null, 0, 0, null)
else
throw_at(target, pressure_difference / 10, pressure_difference / 200, null, 0, 0, null)
last_forced_movement = SSair.times_fired
return 1
else if(pressure_difference > pressure_resistance)
spawn()
step(src, direction)
last_forced_movement = air_master.current_cycle
last_forced_movement = SSair.times_fired
return 1
return 0
@@ -337,8 +361,8 @@
var/breakdown_cooldown = 0
/datum/excited_group/New()
if(air_master)
air_master.excited_groups += src
if(SSair)
SSair.excited_groups += src
/datum/excited_group/proc/add_turf(var/turf/simulated/T)
turf_list += T
@@ -348,13 +372,13 @@
/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
@@ -400,14 +424,14 @@
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
@@ -424,7 +448,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()
@@ -444,7 +468,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)
@@ -452,7 +476,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)
@@ -476,12 +500,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)
@@ -497,7 +521,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
@@ -508,3 +532,24 @@ turf/simulated/proc/radiate_to_spess() //Radiate excess tile heat to space
var/heat = thermal_conductivity*delta_temperature* \
(heat_capacity*700000/(heat_capacity+700000)) //700000 is the heat_capacity from a space turf, hardcoded here
temperature -= heat/heat_capacity
/turf/proc/Initialize_Atmos(times_fired)
CalculateAdjacentTurfs()
/turf/simulated/Initialize_Atmos(times_fired)
..()
update_visuals()
for(var/direction in cardinal)
if(!(atmos_adjacent_turfs & direction))
continue
var/turf/enemy_tile = get_step(src, direction)
if(istype(enemy_tile, /turf/simulated))
var/turf/simulated/enemy_simulated = enemy_tile
if(!air.compare(enemy_simulated.air))
excited = 1
SSair.active_turfs |= src
break
else
if(!air.check_turf_total(enemy_tile))
excited = 1
SSair.active_turfs |= src
+78
View File
@@ -0,0 +1,78 @@
#define MC_TICK_CHECK ( ( TICK_USAGE > Master.current_ticklimit || src.state != SS_RUNNING ) ? pause() : 0 )
#define MC_SPLIT_TICK_INIT(phase_count) var/original_tick_limit = Master.current_ticklimit; var/split_tick_phases = ##phase_count
#define MC_SPLIT_TICK \
if(split_tick_phases > 1){\
Master.current_ticklimit = ((original_tick_limit - TICK_USAGE) / split_tick_phases) + TICK_USAGE;\
--split_tick_phases;\
} else {\
Master.current_ticklimit = original_tick_limit;\
}
// Used to smooth out costs to try and avoid oscillation.
#define MC_AVERAGE_FAST(average, current) (0.7 * (average) + 0.3 * (current))
#define MC_AVERAGE(average, current) (0.8 * (average) + 0.2 * (current))
#define MC_AVERAGE_SLOW(average, current) (0.9 * (average) + 0.1 * (current))
#define MC_AVG_FAST_UP_SLOW_DOWN(average, current) (average > current ? MC_AVERAGE_SLOW(average, current) : MC_AVERAGE_FAST(average, current))
#define MC_AVG_SLOW_UP_FAST_DOWN(average, current) (average < current ? MC_AVERAGE_SLOW(average, current) : MC_AVERAGE_FAST(average, current))
#define NEW_SS_GLOBAL(varname) if(varname != src){if(istype(varname)){Recover();qdel(varname);}varname = src;}
#define START_PROCESSING(Processor, Datum) if (!Datum.isprocessing) {Datum.isprocessing = TRUE;Processor.processing += Datum}
#define STOP_PROCESSING(Processor, Datum) Datum.isprocessing = FALSE;Processor.processing -= Datum
//SubSystem flags (Please design any new flags so that the default is off, to make adding flags to subsystems easier)
//subsystem does not initialize.
#define SS_NO_INIT 1
//subsystem does not fire.
// (like can_fire = 0, but keeps it from getting added to the processing subsystems list)
// (Requires a MC restart to change)
#define SS_NO_FIRE 2
//subsystem only runs on spare cpu (after all non-background subsystems have ran that tick)
// SS_BACKGROUND has its own priority bracket
#define SS_BACKGROUND 4
//subsystem does not tick check, and should not run unless there is enough time (or its running behind (unless background))
#define SS_NO_TICK_CHECK 8
//Treat wait as a tick count, not DS, run every wait ticks.
// (also forces it to run first in the tick, above even SS_NO_TICK_CHECK subsystems)
// (implies all runlevels because of how it works)
// (overrides SS_BACKGROUND)
// This is designed for basically anything that works as a mini-mc (like SStimer)
#define SS_TICKER 16
//keep the subsystem's timing on point by firing early if it fired late last fire because of lag
// ie: if a 20ds subsystem fires say 5 ds late due to lag or what not, its next fire would be in 15ds, not 20ds.
#define SS_KEEP_TIMING 32
//Calculate its next fire after its fired.
// (IE: if a 5ds wait SS takes 2ds to run, its next fire should be 5ds away, not 3ds like it normally would be)
// This flag overrides SS_KEEP_TIMING
#define SS_POST_FIRE_TIMING 64
//SUBSYSTEM STATES
#define SS_IDLE 0 //aint doing shit.
#define SS_QUEUED 1 //queued to run
#define SS_RUNNING 2 //actively running
#define SS_PAUSED 3 //paused by mc_tick_check
#define SS_SLEEPING 4 //fire() slept.
#define SS_PAUSING 5 //in the middle of pausing
#define SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/##X);\
/datum/controller/subsystem/##X/New(){\
NEW_SS_GLOBAL(SS##X);\
PreInit();\
}\
/datum/controller/subsystem/##X
#define PROCESSING_SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/processing/##X);\
/datum/controller/subsystem/processing/##X/New(){\
NEW_SS_GLOBAL(SS##X);\
PreInit();\
}\
/datum/controller/subsystem/processing/##X
+38
View File
@@ -0,0 +1,38 @@
//See controllers/globals.dm
#define GLOBAL_MANAGED(X, InitValue)\
/datum/controller/global_vars/proc/InitGlobal##X(){\
##X = ##InitValue;\
gvars_datum_init_order += #X;\
}
#define GLOBAL_UNMANAGED(X) /datum/controller/global_vars/proc/InitGlobal##X() { return; }
#ifndef TESTING
#define GLOBAL_PROTECT(X)\
/datum/controller/global_vars/InitGlobal##X(){\
..();\
gvars_datum_protected_varlist[#X] = TRUE;\
}
#else
#define GLOBAL_PROTECT(X)
#endif
#define GLOBAL_REAL_VAR(X) var/global/##X
#define GLOBAL_REAL(X, Typepath) var/global##Typepath/##X
#define GLOBAL_RAW(X) /datum/controller/global_vars/var/global##X
#define GLOBAL_VAR_INIT(X, InitValue) GLOBAL_RAW(/##X); GLOBAL_MANAGED(X, InitValue)
#define GLOBAL_VAR_CONST(X, InitValue) GLOBAL_RAW(/const/##X) = InitValue; GLOBAL_UNMANAGED(X)
#define GLOBAL_LIST_INIT(X, InitValue) GLOBAL_RAW(/list/##X); GLOBAL_MANAGED(X, InitValue)
#define GLOBAL_LIST_EMPTY(X) GLOBAL_LIST_INIT(X, list())
#define GLOBAL_DATUM_INIT(X, Typepath, InitValue) GLOBAL_RAW(Typepath/##X); GLOBAL_MANAGED(X, InitValue)
#define GLOBAL_VAR(X) GLOBAL_RAW(/##X); GLOBAL_UNMANAGED(X)
#define GLOBAL_LIST(X) GLOBAL_RAW(/list/##X); GLOBAL_UNMANAGED(X)
#define GLOBAL_DATUM(X, Typepath) GLOBAL_RAW(Typepath/##X); GLOBAL_UNMANAGED(X)
+13 -5
View File
@@ -76,9 +76,17 @@
#define EMBEDDED_UNSAFE_REMOVAL_TIME 30 //A Time in ticks, total removal time = (this*item.w_class)
//Gun Stuff
#define SAWN_INTACT 0
#define SAWN_OFF 1
#define SAWN_INTACT 0
#define SAWN_OFF 1
#define WEAPON_LIGHT 0
#define WEAPON_MEDIUM 1
#define WEAPON_HEAVY 2
#define WEAPON_LIGHT 0
#define WEAPON_MEDIUM 1
#define WEAPON_HEAVY 2
#define EXPLODE_NONE 0 //Don't even ask me why we need this.
#define EXPLODE_DEVASTATE 1
#define EXPLODE_HEAVY 2
#define EXPLODE_LIGHT 3
#define EMP_HEAVY 1
#define EMP_LIGHT 2
+5
View File
@@ -27,6 +27,11 @@
#define PLASTIC_FLAPS_NORMAL 0
#define PLASTIC_FLAPS_DETACHED 1
//other construction-related things
//windows affected by nar-sie turn this color.
#define NARSIE_WINDOW_COLOUR "#7D1919"
//Material defines, for determining how much of a given material an item contains
#define MAT_METAL "$metal"
#define MAT_GLASS "$glass"
+15
View File
@@ -1,3 +1,6 @@
#define ALL ~0 //For convenience.
#define NONE 0
//FLAGS BITMASK
#define STOPSPRESSUREDMAGE 1 //This flag is used on the flags variable for SUIT and HEAD items which stop pressure damage. Note that the flag 1 was previous used as ONBACK, so it is possible for some code to use (flags & 1) when checking if something can be put on your back. Replace this code with (inv_flags & SLOT_BACK) if you see it anywhere To successfully stop you taking all pressure damage you must have both a suit and head item with this flag.
#define NODROP 2 // This flag makes it so that an item literally cannot be removed at all, or at least that's how it should be. Only deleted.
@@ -66,6 +69,7 @@
//turf-only flags
#define NOJAUNT 1
#define RPD_ALLOWED_HERE 2//By default, RPD is disabled on turfs.
//ITEM INVENTORY SLOT BITMASKS
#define SLOT_OCLOTHING 1
@@ -89,3 +93,14 @@
#define AFFECT_ROBOTIC_ORGAN 1
#define AFFECT_ORGANIC_ORGAN 2
#define AFFECT_ALL_ORGANS 3
//Fire stuff, for burn_state
#define LAVA_PROOF -2
#define FIRE_PROOF -1
#define FLAMMABLE 0
#define ON_FIRE 1
//resistance_flags
#define INDESTRUCTIBLE 64 //doesn't take damage
GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768))
+2
View File
@@ -12,6 +12,8 @@
//Objects
#define ismecha(A) (istype(A, /obj/mecha))
#define is_cleanable(A) (istype(A, /obj/effect/decal/cleanable) || istype(A, /obj/effect/rune)) //if something is cleanable
#define is_pen(W) (istype(W, /obj/item/weapon/pen))
+3
View File
@@ -21,14 +21,17 @@
#define LOW_OBJ_LAYER 2.5
#define BELOW_OPEN_DOOR_LAYER 2.6
#define BLASTDOOR_LAYER 2.65
#define OPEN_DOOR_LAYER 2.7
#define PROJECTILE_HIT_THRESHHOLD_LAYER 2.75 //projectiles won't hit objects at or below this layer if possible
#define TABLE_LAYER 2.8
#define BELOW_OBJ_LAYER 2.9
#define LOW_ITEM_LAYER 2.95
//#define OBJ_LAYER 3 //For easy recordkeeping; this is a byond define
#define CLOSED_BLASTDOOR_LAYER 3.05
#define CLOSED_DOOR_LAYER 3.1
#define CLOSED_FIREDOOR_LAYER 3.11
#define SHUTTER_LAYER 3.12 // HERE BE DRAGONS
#define ABOVE_OBJ_LAYER 3.2
#define ABOVE_WINDOW_LAYER 3.3
#define SIGN_LAYER 3.4
+17 -1
View File
@@ -30,4 +30,20 @@
#define Lcm(a, b) (abs(a) / Gcd((a), (b)) * abs(b))
#define Root(n, x) ((x) ** (1 / (n)))
#define ToDegrees(radians) ((radians) * 57.2957795) // 180 / Pi
#define ToRadians(degrees) ((degrees) * 0.0174532925) // Pi / 180
#define ToRadians(degrees) ((degrees) * 0.0174532925) // Pi / 180
//"fancy" math for calculating time in ms from tick_usage percentage and the length of ticks
//percent_of_tick_used * (ticklag * 100(to convert to ms)) / 100(percent ratio)
//collapsed to percent_of_tick_used * tick_lag
#define TICK_DELTA_TO_MS(percent_of_tick_used) ((percent_of_tick_used) * world.tick_lag)
#define TICK_USAGE_TO_MS(starting_tickusage) (TICK_DELTA_TO_MS(TICK_USAGE_REAL - starting_tickusage))
//time of day but automatically adjusts to the server going into the next day within the same round.
//for when you need a reliable time number that doesn't depend on byond time.
#define REALTIMEOFDAY (world.timeofday + (MIDNIGHT_ROLLOVER * MIDNIGHT_ROLLOVER_CHECK))
#define MIDNIGHT_ROLLOVER_CHECK ( GLOB.rollovercheck_last_timeofday != world.timeofday ? update_midnight_rollover() : GLOB.midnight_rollovers )
#define CEILING(x, y) ( -round(-(x) / (y)) * (y) )
// round() acts like floor(x, 1) by default but can't handle other values
#define FLOOR(x, y) ( round((x) / (y)) * (y) )
+3 -6
View File
@@ -294,12 +294,6 @@
#define RADIO_CONNECTION_FAIL 0
#define RADIO_CONNECTION_NON_SUBSPACE 1
//Fire stuff, for burn_state
#define LAVA_PROOF -2
#define FIRE_PROOF -1
#define FLAMMABLE 0
#define ON_FIRE 1
// Bluespace shelter deploy checks
#define SHELTER_DEPLOY_ALLOWED "allowed"
#define SHELTER_DEPLOY_BAD_TURFS "bad turfs"
@@ -347,5 +341,8 @@
#define BLOOD_STATE_XENO "xeno"
#define BLOOD_STATE_NOT_BLOODY "no blood whatsoever"
//for obj explosion block calculation
#define EXPLOSION_BLOCK_PROC -1
// The SQL version required by this version of the code
#define SQL_VERSION 2
@@ -107,6 +107,12 @@
#define AI_CHECK_WIRELESS 1
#define AI_CHECK_RADIO 2
//determines if a mob can smash through it
#define ENVIRONMENT_SMASH_NONE 0
#define ENVIRONMENT_SMASH_STRUCTURES 1 //crates, lockers, ect
#define ENVIRONMENT_SMASH_WALLS 2 //walls
#define ENVIRONMENT_SMASH_RWALLS 4 //rwalls
#define POCKET_STRIP_DELAY 40 //time taken (in deciseconds) to search somebody's pockets
#define DEFAULT_ITEM_STRIP_DELAY 40 //time taken (in deciseconds) to strip somebody
@@ -120,6 +126,7 @@
#define TINT_IMPAIR 2 //Threshold of tint level to apply weld mask overlay
#define TINT_BLIND 3 //Threshold of tint level to obscure vision fully
#define EYE_SHINE_THRESHOLD 6 //dark_view threshold past which a humanoid's eyes will 'shine' in the dark.
#define isliving(A) (istype((A), /mob/living))
#define iscarbon(A) (istype((A), /mob/living/carbon))
+1 -1
View File
@@ -31,4 +31,4 @@
if(##VARNAME == src)\
##VARNAME = null;\
else\
message_admins("Controller '[name]' asked to release global control in spite of not having it!");}\
message_admins("Controller '[name]' asked to release global control in spite of not having it!");}
+1 -1
View File
@@ -49,7 +49,7 @@
//#define STATUS_EFFECT_HISWRATH /datum/status_effect/his_wrath //His Wrath.
//#define STATUS_EFFECT_SUMMONEDGHOST /datum/status_effect/cultghost //is a cult ghost and can't use manifest runes
#define STATUS_EFFECT_SUMMONEDGHOST /datum/status_effect/cultghost //is a cult ghost and can't use manifest runes
//#define STATUS_EFFECT_CRUSHERMARK /datum/status_effect/crusher_mark //if struck with a proto-kinetic crusher, takes a ton of damage
+116
View File
@@ -0,0 +1,116 @@
//Timing subsystem
//Don't run if there is an identical unique timer active
//if the arguments to addtimer are the same as an existing timer, it doesn't create a new timer, and returns the id of the existing timer
#define TIMER_UNIQUE 1
//For unique timers: Replace the old timer rather then not start this one
#define TIMER_OVERRIDE 2
//Timing should be based on how timing progresses on clients, not the sever.
// tracking this is more expensive,
// should only be used in conjuction with things that have to progress client side, such as animate() or sound()
#define TIMER_CLIENT_TIME 4
//Timer can be stopped using deltimer()
#define TIMER_STOPPABLE 8
//To be used with TIMER_UNIQUE
//prevents distinguishing identical timers with the wait variable
#define TIMER_NO_HASH_WAIT 16
#define TIMER_NO_INVOKE_WARNING 600 //number of byond ticks that are allowed to pass before the timer subsystem thinks it hung on something
#define TIMER_ID_NULL -1
//For servers that can't do with any additional lag, set this to none in flightpacks.dm in subsystem/processing.
#define FLIGHTSUIT_PROCESSING_NONE 0
#define FLIGHTSUIT_PROCESSING_FULL 1
#define INITIALIZATION_INSSATOMS 0 //New should not call Initialize
#define INITIALIZATION_INNEW_MAPLOAD 2 //New should call Initialize(TRUE)
#define INITIALIZATION_INNEW_REGULAR 1 //New should call Initialize(FALSE)
#define INITIALIZE_HINT_NORMAL 0 //Nothing happens
#define INITIALIZE_HINT_LATELOAD 1 //Call LateInitialize
#define INITIALIZE_HINT_QDEL 2 //Call qdel on the atom
//type and all subtypes should always call Initialize in New()
#define INITIALIZE_IMMEDIATE(X) ##X/New(loc, ...){\
..();\
if(!initialized) {\
args[1] = TRUE;\
SSatoms.InitAtom(src, args);\
}\
}
// Subsystem init_order, from highest priority to lowest priority
// Subsystems shutdown in the reverse of the order they initialize in
// The numbers just define the ordering, they are meaningless otherwise.
#define INIT_ORDER_GARBAGE 19
#define INIT_ORDER_DBCORE 18
#define INIT_ORDER_BLACKBOX 17
#define INIT_ORDER_SERVER_MAINT 16
#define INIT_ORDER_INPUT 15
#define INIT_ORDER_RESEARCH 14
#define INIT_ORDER_EVENTS 13
#define INIT_ORDER_JOBS 12
#define INIT_ORDER_TRAITS 11
#define INIT_ORDER_TICKER 10
#define INIT_ORDER_MAPPING 9
#define INIT_ORDER_NETWORKS 8
#define INIT_ORDER_ATOMS 7
#define INIT_ORDER_LANGUAGE 6
#define INIT_ORDER_MACHINES 5
#define INIT_ORDER_CIRCUIT 4
#define INIT_ORDER_TIMER 1
#define INIT_ORDER_DEFAULT 0
#define INIT_ORDER_AIR -1
#define INIT_ORDER_MINIMAP -3
#define INIT_ORDER_ASSETS -4
#define INIT_ORDER_ICON_SMOOTHING -5
#define INIT_ORDER_OVERLAY -6
#define INIT_ORDER_XKEYSCORE -10
#define INIT_ORDER_STICKY_BAN -10
#define INIT_ORDER_LIGHTING -20
#define INIT_ORDER_SHUTTLE -21
#define INIT_ORDER_SQUEAK -40
#define INIT_ORDER_PATH -50
#define INIT_ORDER_PERSISTENCE -100
// Subsystem fire priority, from lowest to highest priority
// If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child)
#define FIRE_PRIORITY_IDLE_NPC 10
#define FIRE_PRIORITY_SERVER_MAINT 10
#define FIRE_PRIORITY_RESEARCH 10
#define FIRE_PRIORITY_GARBAGE 15
#define FIRE_PRIORITY_WET_FLOORS 20
#define FIRE_PRIORITY_AIR 20
#define FIRE_PRIORITY_NPC 20
#define FIRE_PRIORITY_PROCESS 25
#define FIRE_PRIORITY_THROWING 25
#define FIRE_PRIORITY_SPACEDRIFT 30
#define FIRE_PRIORITY_FIELDS 30
#define FIRE_PRIOTITY_SMOOTHING 35
#define FIRE_PRIORITY_ORBIT 35
#define FIRE_PRIORITY_NETWORKS 40
#define FIRE_PRIORITY_OBJ 40
#define FIRE_PRIORITY_ACID 40
#define FIRE_PRIOTITY_BURNING 40
#define FIRE_PRIORITY_INBOUNDS 40
#define FIRE_PRIORITY_DEFAULT 50
#define FIRE_PRIORITY_PARALLAX 65
#define FIRE_PRIORITY_FLIGHTPACKS 80
#define FIRE_PRIORITY_MOBS 100
#define FIRE_PRIORITY_TGUI 110
#define FIRE_PRIORITY_TICKER 200
#define FIRE_PRIORITY_OVERLAYS 500
#define FIRE_PRIORITY_INPUT 1000 // This must always always be the max highest priority. Player input must never be lost.
// SS runlevels
#define RUNLEVEL_INIT 0
#define RUNLEVEL_LOBBY 1
#define RUNLEVEL_SETUP 2
#define RUNLEVEL_GAME 4
#define RUNLEVEL_POSTGAME 8
#define RUNLEVELS_DEFAULT (RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME)
+7 -2
View File
@@ -1,5 +1,10 @@
#define TICK_LIMIT_RUNNING 90
#define TICK_LIMIT_TO_RUN 85
#define TICK_LIMIT_RUNNING 80
#define TICK_LIMIT_TO_RUN 70
#define TICK_LIMIT_MC 70
#define TICK_LIMIT_MC_INIT_DEFAULT 98
#define TICK_USAGE world.tick_usage //for general usage
#define TICK_USAGE_REAL world.tick_usage //to be used where the result isn't checked
#define TICK_CHECK ( world.tick_usage > TICK_LIMIT_RUNNING ? stoplag() : 0 )
#define CHECK_TICK if(world.tick_usage > TICK_LIMIT_RUNNING) stoplag()
+38
View File
@@ -0,0 +1,38 @@
/proc/cmp_numeric_dsc(a,b)
return b - a
/proc/cmp_numeric_asc(a,b)
return a - b
/proc/cmp_text_asc(a,b)
return sorttext(b,a)
/proc/cmp_text_dsc(a,b)
return sorttext(a,b)
/proc/cmp_name_asc(atom/a, atom/b)
return sorttext(b.name, a.name)
/proc/cmp_name_dsc(atom/a, atom/b)
return sorttext(a.name, b.name)
/proc/cmp_ckey_asc(client/a, client/b)
return sorttext(b.ckey, a.ckey)
/proc/cmp_ckey_dsc(client/a, client/b)
return sorttext(a.ckey, b.ckey)
/proc/cmp_subsystem_init(datum/controller/subsystem/a, datum/controller/subsystem/b)
return initial(b.init_order) - initial(a.init_order) //uses initial() so it can be used on types
/proc/cmp_subsystem_display(datum/controller/subsystem/a, datum/controller/subsystem/b)
return sorttext(b.name, a.name)
/proc/cmp_subsystem_priority(datum/controller/subsystem/a, datum/controller/subsystem/b)
return a.priority - b.priority
/proc/cmp_atom_layer_asc(atom/A,atom/B)
if(A.plane != B.plane)
return A.plane - B.plane
else
return A.layer - B.layer
+9
View File
@@ -897,3 +897,12 @@ proc/rand_hex_color()
for(var/i=0;i<6;i++)
color = color+pick(colors)
return "#[color]"
//Imagine removing pixels from the main icon that are covered by pixels from the mask icon.
proc/get_icon_difference(var/icon/main, var/icon/mask)
if(istype(main) && istype(mask))
mask.Blend(rgb(255,255,255), ICON_SUBTRACT) //Make all pixels on the mask as black as possible.
mask.Opaque(rgb(255,255,255)) //Make the transparent pixels (background) white.
mask.BecomeAlphaMask() //Make all the black pixels vanish (fully transparent), leaving only the white pixels.
main.AddAlphaMask(mask) //Make the pixels in the main icon that are in the transparent zone of the mask icon also vanish (fully transparent).
return main
+81 -1
View File
@@ -678,4 +678,84 @@ proc/dd_sortedObjectList(list/incoming)
if(isnum(key))
L |= key
else
L[key] = temp[key]
L[key] = temp[key]
//Move a single element from position fromIndex within a list, to position toIndex
//All elements in the range [1,toIndex) before the move will be before the pivot afterwards
//All elements in the range [toIndex, L.len+1) before the move will be after the pivot afterwards
//In other words, it's as if the range [fromIndex,toIndex) have been rotated using a <<< operation common to other languages.
//fromIndex and toIndex must be in the range [1,L.len+1]
//This will preserve associations ~Carnie
/proc/moveElement(list/L, fromIndex, toIndex)
if(fromIndex == toIndex || fromIndex + 1 == toIndex) //no need to move
return
if(fromIndex > toIndex)
++fromIndex //since a null will be inserted before fromIndex, the index needs to be nudged right by one
L.Insert(toIndex, null)
L.Swap(fromIndex, toIndex)
L.Cut(fromIndex, fromIndex + 1)
//Move elements [fromIndex,fromIndex+len) to [toIndex-len, toIndex)
//Same as moveElement but for ranges of elements
//This will preserve associations ~Carnie
/proc/moveRange(list/L, fromIndex, toIndex, len = 1)
var/distance = abs(toIndex - fromIndex)
if(len >= distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going. The result being, our range we are moving is shifted left or right by dist elements
if(fromIndex <= toIndex)
return //no need to move
fromIndex += len //we want to shift left instead of right
for(var/i = 0, i < distance, ++i)
L.Insert(fromIndex, null)
L.Swap(fromIndex, toIndex)
L.Cut(toIndex, toIndex + 1)
else
if(fromIndex > toIndex)
fromIndex += len
for(var/i = 0, i < len, ++i)
L.Insert(toIndex, null)
L.Swap(fromIndex, toIndex)
L.Cut(fromIndex, fromIndex + 1)
//Move elements from [fromIndex, fromIndex+len) to [toIndex, toIndex+len)
//Move any elements being overwritten by the move to the now-empty elements, preserving order
//Note: if the two ranges overlap, only the destination order will be preserved fully, since some elements will be within both ranges ~Carnie
/proc/swapRange(list/L, fromIndex, toIndex, len = 1)
var/distance = abs(toIndex - fromIndex)
if(len > distance) //there is an overlap, therefore swapping each element will require more swaps than inserting new elements
if(fromIndex < toIndex)
toIndex += len
else
fromIndex += len
for(var/i = 0, i < distance, ++i)
L.Insert(fromIndex, null)
L.Swap(fromIndex, toIndex)
L.Cut(toIndex, toIndex + 1)
else
if(toIndex > fromIndex)
var/a = toIndex
toIndex = fromIndex
fromIndex = a
for(var/i = 0, i < len, ++i)
L.Swap(fromIndex++, toIndex++)
//replaces reverseList ~Carnie
/proc/reverseRange(list/L, start = 1, end = 0)
if(L.len)
start = start % L.len
end = end % (L.len + 1)
if(start <= 0)
start += L.len
if(end <= 0)
end += L.len + 1
--end
while(start < end)
L.Swap(start++, end--)
return L
+17 -4
View File
@@ -268,7 +268,7 @@ proc/add_logs(mob/user, mob/target, what_done, var/object=null, var/addition=nul
return
msg_admin_attack("[key_name_admin(user)] [what_done] [key_name_admin(target)][object ? " with [object]" : " "][addition]")
/proc/do_mob(var/mob/user, var/mob/target, var/time = 30, var/uninterruptible = 0, progress = 1)
/proc/do_mob(var/mob/user, var/mob/target, var/time = 30, var/uninterruptible = 0, progress = 1, datum/callback/extra_checks = null)
if(!user || !target)
return 0
var/user_loc = user.loc
@@ -301,13 +301,13 @@ proc/add_logs(mob/user, mob/target, what_done, var/object=null, var/addition=nul
drifting = 0
user_loc = user.loc
if((!drifting && user.loc != user_loc) || target.loc != target_loc || user.get_active_hand() != holding || user.incapacitated() || user.lying )
if((!drifting && user.loc != user_loc) || target.loc != target_loc || user.get_active_hand() != holding || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke()))
. = 0
break
if(progress)
qdel(progbar)
/proc/do_after(mob/user, delay, needhand = 1, atom/target = null, progress = 1)
/proc/do_after(mob/user, delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null)
if(!user)
return 0
var/atom/Tloc = null
@@ -342,7 +342,7 @@ proc/add_logs(mob/user, mob/target, what_done, var/object=null, var/addition=nul
drifting = 0
Uloc = user.loc
if(!user || user.stat || user.weakened || user.stunned || (!drifting && user.loc != Uloc))
if(!user || user.stat || user.weakened || user.stunned || (!drifting && user.loc != Uloc)|| (extra_checks && !extra_checks.Invoke()))
. = 0
break
@@ -370,6 +370,19 @@ proc/add_logs(mob/user, mob/target, what_done, var/object=null, var/addition=nul
if(H.get_species() == species_name)
. = TRUE
/proc/spawn_atom_to_turf(spawn_type, target, amount, admin_spawn=FALSE, list/extra_args)
var/turf/T = get_turf(target)
if(!T)
CRASH("attempt to spawn atom type: [spawn_type] in nullspace")
var/list/new_args = list(T)
if(extra_args)
new_args += extra_args
for(var/j in 1 to amount)
var/atom/X = new spawn_type(arglist(new_args))
X.admin_spawned = admin_spawn
/proc/admin_mob_info(mob/M, mob/user = usr)
if(!ismob(M))
to_chat(user, "This can only be used on instances of type /mob")
+19
View File
@@ -0,0 +1,19 @@
//simple insertion sort - generally faster than merge for runs of 7 or smaller
/proc/sortInsert(list/L, cmp = /proc/cmp_numeric_asc, associative, fromIndex = 1, toIndex = 0)
if(L && L.len >= 2)
fromIndex = fromIndex % L.len
toIndex = toIndex % (L.len + 1)
if(fromIndex <= 0)
fromIndex += L.len
if(toIndex <= 0)
toIndex += L.len + 1
var/datum/sortInstance/SI = GLOB.sortInstance
if(!SI)
SI = new
SI.L = L
SI.cmp = cmp
SI.associative = associative
SI.binarySort(fromIndex, toIndex, fromIndex)
return L
+19
View File
@@ -0,0 +1,19 @@
//merge-sort - gernerally faster than insert sort, for runs of 7 or larger
/proc/sortMerge(list/L, cmp = /proc/cmp_numeric_asc, associative, fromIndex = 1, toIndex)
if(L && L.len >= 2)
fromIndex = fromIndex % L.len
toIndex = toIndex % (L.len + 1)
if(fromIndex <= 0)
fromIndex += L.len
if(toIndex <= 0)
toIndex += L.len + 1
var/datum/sortInstance/SI = GLOB.sortInstance
if(!SI)
SI = new
SI.L = L
SI.cmp = cmp
SI.associative = associative
SI.mergeSort(fromIndex, toIndex)
return L
+20
View File
@@ -0,0 +1,20 @@
//TimSort interface
/proc/sortTim(list/L, cmp = /proc/cmp_numeric_asc, associative, fromIndex = 1, toIndex = 0)
if(L && L.len >= 2)
fromIndex = fromIndex % L.len
toIndex = toIndex % (L.len + 1)
if(fromIndex <= 0)
fromIndex += L.len
if(toIndex <= 0)
toIndex += L.len + 1
var/datum/sortInstance/SI = GLOB.sortInstance
if(!SI)
SI = new
SI.L = L
SI.cmp = cmp
SI.associative = associative
SI.timSort(fromIndex, toIndex)
return L
+647
View File
@@ -0,0 +1,647 @@
//These are macros used to reduce on proc calls
#define fetchElement(L, i) (associative) ? L[L[i]] : L[i]
//Minimum sized sequence that will be merged. Anything smaller than this will use binary-insertion sort.
//Should be a power of 2
#define MIN_MERGE 32
//When we get into galloping mode, we stay there until both runs win less often than MIN_GALLOP consecutive times.
#define MIN_GALLOP 7
//This is a global instance to allow much of this code to be reused. The interfaces are kept separately
GLOBAL_DATUM_INIT(sortInstance, /datum/sortInstance, new())
/datum/sortInstance
//The array being sorted.
var/list/L
//The comparator proc-reference
var/cmp = /proc/cmp_numeric_asc
//whether we are sorting list keys (0: L[i]) or associated values (1: L[L[i]])
var/associative = 0
//This controls when we get *into* galloping mode. It is initialized to MIN_GALLOP.
//The mergeLo and mergeHi methods nudge it higher for random data, and lower for highly structured data.
var/minGallop = MIN_GALLOP
//Stores information regarding runs yet to be merged.
//Run i starts at runBase[i] and extends for runLen[i] elements.
//runBase[i] + runLen[i] == runBase[i+1]
var/list/runBases = list()
var/list/runLens = list()
/datum/sortInstance/proc/timSort(start, end)
runBases.Cut()
runLens.Cut()
var/remaining = end - start
//If array is small, do a 'mini-TimSort' with no merges
if(remaining < MIN_MERGE)
var/initRunLen = countRunAndMakeAscending(start, end)
binarySort(start, end, start + initRunLen)
return
//March over the array finding natural runs
//Extend any short natural runs to runs of length minRun
var/minRun = minRunLength(remaining)
do
//identify next run
var/runLen = countRunAndMakeAscending(start, end)
//if run is short, extend to min(minRun, remaining)
if(runLen < minRun)
var/force = (remaining <= minRun) ? remaining : minRun
binarySort(start, start + force, start+runLen)
runLen = force
//add data about run to queue
runBases.Add(start)
runLens.Add(runLen)
//maybe merge
mergeCollapse()
//Advance to find next run
start += runLen
remaining -= runLen
while(remaining > 0)
//Merge all remaining runs to complete sort
//ASSERT(start == end)
mergeForceCollapse();
//ASSERT(runBases.len == 1)
//reset minGallop, for successive calls
minGallop = MIN_GALLOP
return L
/*
Sorts the specified portion of the specified array using a binary
insertion sort. This is the best method for sorting small numbers
of elements. It requires O(n log n) compares, but O(n^2) data
movement (worst case).
If the initial part of the specified range is already sorted,
this method can take advantage of it: the method assumes that the
elements in range [lo,start) are already sorted
lo the index of the first element in the range to be sorted
hi the index after the last element in the range to be sorted
start the index of the first element in the range that is not already known to be sorted
*/
/datum/sortInstance/proc/binarySort(lo, hi, start)
//ASSERT(lo <= start && start <= hi)
if(start <= lo)
start = lo + 1
for(,start < hi, ++start)
var/pivot = fetchElement(L,start)
//set left and right to the index where pivot belongs
var/left = lo
var/right = start
//ASSERT(left <= right)
//[lo, left) elements <= pivot < [right, start) elements
//in other words, find where the pivot element should go using bisection search
while(left < right)
var/mid = (left + right) >> 1 //round((left+right)/2)
if(call(cmp)(fetchElement(L, mid), pivot) > 0)
right = mid
else
left = mid + 1
//ASSERT(left == right)
moveElement(L, start, left) //move pivot element to correct location in the sorted range
/*
Returns the length of the run beginning at the specified position and reverses the run if it is back-to-front
A run is the longest ascending sequence with:
a[lo] <= a[lo + 1] <= a[lo + 2] <= ...
or the longest descending sequence with:
a[lo] > a[lo + 1] > a[lo + 2] > ...
For its intended use in a stable mergesort, the strictness of the
definition of "descending" is needed so that the call can safely
reverse a descending sequence without violating stability.
*/
/datum/sortInstance/proc/countRunAndMakeAscending(lo, hi)
//ASSERT(lo < hi)
var/runHi = lo + 1
if(runHi >= hi)
return 1
var/last = fetchElement(L, lo)
var/current = fetchElement(L, runHi++)
if(call(cmp)(current, last) < 0)
while(runHi < hi)
last = current
current = fetchElement(L,runHi)
if(call(cmp)(current, last) >= 0)
break
++runHi
reverseRange(L, lo, runHi)
else
while(runHi < hi)
last = current
current = fetchElement(L,runHi)
if(call(cmp)(current, last) < 0)
break
++runHi
return runHi - lo
//Returns the minimum acceptable run length for an array of the specified length.
//Natural runs shorter than this will be extended with binarySort
/datum/sortInstance/proc/minRunLength(n)
//ASSERT(n >= 0)
var/r = 0 //becomes 1 if any bits are shifted off
while(n >= MIN_MERGE)
r |= (n & 1)
n >>= 1
return n + r
//Examines the stack of runs waiting to be merged and merges adjacent runs until the stack invariants are reestablished:
// runLen[i-3] > runLen[i-2] + runLen[i-1]
// runLen[i-2] > runLen[i-1]
//This method is called each time a new run is pushed onto the stack.
//So the invariants are guaranteed to hold for i<stackSize upon entry to the method
/datum/sortInstance/proc/mergeCollapse()
while(runBases.len >= 2)
var/n = runBases.len - 1
if(n > 1 && runLens[n - 1] <= runLens[n] + runLens[n + 1])
if(runLens[n - 1] < runLens[n + 1])
--n
mergeAt(n)
else if(runLens[n] <= runLens[n + 1])
mergeAt(n)
else
break //Invariant is established
//Merges all runs on the stack until only one remains.
//Called only once, to finalise the sort
/datum/sortInstance/proc/mergeForceCollapse()
while(runBases.len >= 2)
var/n = runBases.len - 1
if(n > 1 && runLens[n - 1] < runLens[n + 1])
--n
mergeAt(n)
//Merges the two consecutive runs at stack indices i and i+1
//Run i must be the penultimate or antepenultimate run on the stack
//In other words, i must be equal to stackSize-2 or stackSize-3
/datum/sortInstance/proc/mergeAt(i)
//ASSERT(runBases.len >= 2)
//ASSERT(i >= 1)
//ASSERT(i == runBases.len - 1 || i == runBases.len - 2)
var/base1 = runBases[i]
var/base2 = runBases[i + 1]
var/len1 = runLens[i]
var/len2 = runLens[i + 1]
//ASSERT(len1 > 0 && len2 > 0)
//ASSERT(base1 + len1 == base2)
//Record the legth of the combined runs. If i is the 3rd last run now, also slide over the last run
//(which isn't involved in this merge). The current run (i+1) goes away in any case.
runLens[i] += runLens[i + 1]
runLens.Cut(i + 1, i + 2)
runBases.Cut(i + 1, i + 2)
//Find where the first element of run2 goes in run1.
//Prior elements in run1 can be ignored (because they're already in place)
var/k = gallopRight(fetchElement(L, base2), base1, len1, 0)
//ASSERT(k >= 0)
base1 += k
len1 -= k
if(len1 == 0)
return
//Find where the last element of run1 goes in run2.
//Subsequent elements in run2 can be ignored (because they're already in place)
len2 = gallopLeft(fetchElement(L, base1 + len1 - 1), base2, len2, len2 - 1)
//ASSERT(len2 >= 0)
if(len2 == 0)
return
//Merge remaining runs, using tmp array with min(len1, len2) elements
if(len1 <= len2)
mergeLo(base1, len1, base2, len2)
else
mergeHi(base1, len1, base2, len2)
/*
Locates the position to insert key within the specified sorted range
If the range contains elements equal to key, this will return the index of the LEFTMOST of those elements
key the element to be inserted into the sorted range
base the index of the first element of the sorted range
len the length of the sorted range, must be greater than 0
hint the offset from base at which to begin the search, such that 0 <= hint < len; i.e. base <= hint < base+hint
Returns the index at which to insert element 'key'
*/
/datum/sortInstance/proc/gallopLeft(key, base, len, hint)
//ASSERT(len > 0 && hint >= 0 && hint < len)
var/lastOffset = 0
var/offset = 1
if(call(cmp)(key, fetchElement(L,base + hint)) > 0)
var/maxOffset = len - hint
while(offset < maxOffset && call(cmp)(key, fetchElement(L, base + hint + offset)) > 0)
lastOffset = offset
offset = (offset << 1) + 1
if(offset > maxOffset)
offset = maxOffset
lastOffset += hint
offset += hint
else
var/maxOffset = hint + 1
while(offset < maxOffset && call(cmp)(key, fetchElement(L, base + hint - offset)) <= 0)
lastOffset = offset
offset = (offset << 1) + 1
if(offset > maxOffset)
offset = maxOffset
var/temp = lastOffset
lastOffset = hint - offset
offset = hint - temp
//ASSERT(-1 <= lastOffset && lastOffset < offset && offset <= len)
//Now L[base+lastOffset] < key <= L[base+offset], so key belongs somewhere to the right of lastOffset but no farther than
//offset. Do a binary search with invariant L[base+lastOffset-1] < key <= L[base+offset]
++lastOffset
while(lastOffset < offset)
var/m = lastOffset + ((offset - lastOffset) >> 1)
if(call(cmp)(key, fetchElement(L,base + m)) > 0)
lastOffset = m + 1
else
offset = m
//ASSERT(lastOffset == offset)
return offset
/**
* Like gallopLeft, except that if the range contains an element equal to
* key, gallopRight returns the index after the rightmost equal element.
*
* @param key the key whose insertion point to search for
* @param a the array in which to search
* @param base the index of the first element in the range
* @param len the length of the range; must be > 0
* @param hint the index at which to begin the search, 0 <= hint < n.
* The closer hint is to the result, the faster this method will run.
* @param c the comparator used to order the range, and to search
* @return the int k, 0 <= k <= n such that a[b + k - 1] <= key < a[b + k]
*/
/datum/sortInstance/proc/gallopRight(key, base, len, hint)
//ASSERT(len > 0 && hint >= 0 && hint < len)
var/offset = 1
var/lastOffset = 0
if(call(cmp)(key, fetchElement(L, base + hint)) < 0) //key <= L[base+hint]
var/maxOffset = hint + 1 //therefore we want to insert somewhere in the range [base,base+hint] = [base+,base+(hint+1))
while(offset < maxOffset && call(cmp)(key, fetchElement(L, base + hint - offset)) < 0) //we are iterating backwards
lastOffset = offset
offset = (offset << 1) + 1 //1 3 7 15
if(offset > maxOffset)
offset = maxOffset
var/temp = lastOffset
lastOffset = hint - offset
offset = hint - temp
else //key > L[base+hint]
var/maxOffset = len - hint //therefore we want to insert somewhere in the range (base+hint,base+len) = [base+hint+1, base+hint+(len-hint))
while(offset < maxOffset && call(cmp)(key, fetchElement(L, base + hint + offset)) >= 0)
lastOffset = offset
offset = (offset << 1) + 1
if(offset > maxOffset)
offset = maxOffset
lastOffset += hint
offset += hint
//ASSERT(-1 <= lastOffset && lastOffset < offset && offset <= len)
++lastOffset
while(lastOffset < offset)
var/m = lastOffset + ((offset - lastOffset) >> 1)
if(call(cmp)(key, fetchElement(L, base + m)) < 0) //key <= L[base+m]
offset = m
else //key > L[base+m]
lastOffset = m + 1
//ASSERT(lastOffset == offset)
return offset
//Merges two adjacent runs in-place in a stable fashion.
//For performance this method should only be called when len1 <= len2!
/datum/sortInstance/proc/mergeLo(base1, len1, base2, len2)
//ASSERT(len1 > 0 && len2 > 0 && base1 + len1 == base2)
var/cursor1 = base1
var/cursor2 = base2
//degenerate cases
if(len2 == 1)
moveElement(L, cursor2, cursor1)
return
if(len1 == 1)
moveElement(L, cursor1, cursor2 + len2)
return
//Move first element of second run
moveElement(L, cursor2++, cursor1++)
--len2
outer:
while(1)
var/count1 = 0 //# of times in a row that first run won
var/count2 = 0 // " " " " " " second run won
//do the straightfoward thin until one run starts winning consistently
do
//ASSERT(len1 > 1 && len2 > 0)
if(call(cmp)(fetchElement(L, cursor2), fetchElement(L, cursor1)) < 0)
moveElement(L, cursor2++, cursor1++)
--len2
++count2
count1 = 0
if(len2 == 0)
break outer
else
++cursor1
++count1
count2 = 0
if(--len1 == 1)
break outer
while((count1 | count2) < minGallop)
//one run is winning consistently so galloping may provide huge benifits
//so try galloping, until such time as the run is no longer consistently winning
do
//ASSERT(len1 > 1 && len2 > 0)
count1 = gallopRight(fetchElement(L, cursor2), cursor1, len1, 0)
if(count1)
cursor1 += count1
len1 -= count1
if(len1 <= 1)
break outer
moveElement(L, cursor2, cursor1)
++cursor2
++cursor1
if(--len2 == 0)
break outer
count2 = gallopLeft(fetchElement(L, cursor1), cursor2, len2, 0)
if(count2)
moveRange(L, cursor2, cursor1, count2)
cursor2 += count2
cursor1 += count2
len2 -= count2
if(len2 == 0)
break outer
++cursor1
if(--len1 == 1)
break outer
--minGallop
while((count1|count2) > MIN_GALLOP)
if(minGallop < 0)
minGallop = 0
minGallop += 2; // Penalize for leaving gallop mode
if(len1 == 1)
//ASSERT(len2 > 0)
moveElement(L, cursor1, cursor2 + len2)
//else
//ASSERT(len2 == 0)
//ASSERT(len1 > 1)
/datum/sortInstance/proc/mergeHi(base1, len1, base2, len2)
//ASSERT(len1 > 0 && len2 > 0 && base1 + len1 == base2)
var/cursor1 = base1 + len1 - 1 //start at end of sublists
var/cursor2 = base2 + len2 - 1
//degenerate cases
if(len2 == 1)
moveElement(L, base2, base1)
return
if(len1 == 1)
moveElement(L, base1, cursor2 + 1)
return
moveElement(L, cursor1--, cursor2-- + 1)
--len1
outer:
while(1)
var/count1 = 0 //# of times in a row that first run won
var/count2 = 0 // " " " " " " second run won
//do the straightfoward thing until one run starts winning consistently
do
//ASSERT(len1 > 0 && len2 > 1)
if(call(cmp)(fetchElement(L, cursor2), fetchElement(L, cursor1)) < 0)
moveElement(L, cursor1--, cursor2-- + 1)
--len1
++count1
count2 = 0
if(len1 == 0)
break outer
else
--cursor2
--len2
++count2
count1 = 0
if(len2 == 1)
break outer
while((count1 | count2) < minGallop)
//one run is winning consistently so galloping may provide huge benifits
//so try galloping, until such time as the run is no longer consistently winning
do
//ASSERT(len1 > 0 && len2 > 1)
count1 = len1 - gallopRight(fetchElement(L, cursor2), base1, len1, len1 - 1) //should cursor1 be base1?
if(count1)
cursor1 -= count1
moveRange(L, cursor1 + 1, cursor2 + 1, count1) //cursor1+1 == cursor2 by definition
cursor2 -= count1
len1 -= count1
if(len1 == 0)
break outer
--cursor2
if(--len2 == 1)
break outer
count2 = len2 - gallopLeft(fetchElement(L, cursor1), cursor1 + 1, len2, len2 - 1)
if(count2)
cursor2 -= count2
len2 -= count2
if(len2 <= 1)
break outer
moveElement(L, cursor1--, cursor2-- + 1)
--len1
if(len1 == 0)
break outer
--minGallop
while((count1|count2) > MIN_GALLOP)
if(minGallop < 0)
minGallop = 0
minGallop += 2 // Penalize for leaving gallop mode
if(len2 == 1)
//ASSERT(len1 > 0)
cursor1 -= len1
moveRange(L, cursor1 + 1, cursor2 + 1, len1)
//else
//ASSERT(len1 == 0)
//ASSERT(len2 > 0)
/datum/sortInstance/proc/mergeSort(start, end)
var/remaining = end - start
//If array is small, do an insertion sort
if(remaining < MIN_MERGE)
binarySort(start, end, start/*+initRunLen*/)
return
var/minRun = minRunLength(remaining)
do
var/runLen = (remaining <= minRun) ? remaining : minRun
binarySort(start, start + runLen, start)
//add data about run to queue
runBases.Add(start)
runLens.Add(runLen)
//Advance to find next run
start += runLen
remaining -= runLen
while(remaining > 0)
while(runBases.len >= 2)
var/n = runBases.len - 1
if(n > 1 && runLens[n - 1] <= runLens[n] + runLens[n + 1])
if(runLens[n - 1] < runLens[n + 1])
--n
mergeAt2(n)
else if(runLens[n] <= runLens[n + 1])
mergeAt2(n)
else
break //Invariant is established
while(runBases.len >= 2)
var/n = runBases.len - 1
if(n > 1 && runLens[n - 1] < runLens[n + 1])
--n
mergeAt2(n)
return L
/datum/sortInstance/proc/mergeAt2(i)
var/cursor1 = runBases[i]
var/cursor2 = runBases[i + 1]
var/end1 = cursor1+runLens[i]
var/end2 = cursor2+runLens[i + 1]
var/val1 = fetchElement(L, cursor1)
var/val2 = fetchElement(L, cursor2)
while(1)
if(call(cmp)(val1, val2) <= 0)
if(++cursor1 >= end1)
break
val1 = fetchElement(L, cursor1)
else
moveElement(L, cursor2, cursor1)
if(++cursor2 >= end2)
break
++end1
++cursor1
val2 = fetchElement(L, cursor2)
//Record the legth of the combined runs. If i is the 3rd last run now, also slide over the last run
//(which isn't involved in this merge). The current run (i+1) goes away in any case.
runLens[i] += runLens[i + 1]
runLens.Cut(i + 1, i + 2)
runBases.Cut(i + 1, i + 2)
#undef MIN_GALLOP
#undef MIN_MERGE
#undef fetchElement
+8 -1
View File
@@ -165,4 +165,11 @@ proc/isDay(var/month, var/day)
else
day = "1 day"
return "[day][hour][minute][second]"
return "[day][hour][minute][second]"
GLOBAL_VAR_INIT(midnight_rollovers, 0)
GLOBAL_VAR_INIT(rollovercheck_last_timeofday, 0)
/proc/update_midnight_rollover()
if(world.timeofday < GLOB.rollovercheck_last_timeofday) //TIME IS GOING BACKWARDS!
return GLOB.midnight_rollovers++
return GLOB.midnight_rollovers
+13 -6
View File
@@ -777,15 +777,15 @@ proc/GaussRandRound(var/sigma,var/roundto)
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)
@@ -941,7 +941,7 @@ proc/GaussRandRound(var/sigma,var/roundto)
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
@@ -1337,7 +1337,7 @@ Standard way to write links -Sayu
if(covered_locations & HEAD)
return 0
if("eyes")
if(covered_locations & HEAD || face_covered & HIDEEYES || eyesmouth_covered & GLASSESCOVERSEYES)
if(face_covered & HIDEEYES || eyesmouth_covered & GLASSESCOVERSEYES || eyesmouth_covered & HEADCOVERSEYES)
return 0
if("mouth")
if(covered_locations & HEAD || face_covered & HIDEFACE || eyesmouth_covered & MASKCOVERSMOUTH)
@@ -1914,3 +1914,10 @@ var/mob/dview/dview_mob = new
for(var/atom/thing in here)
if(istype(thing, type) && (check_shift && thing.pixel_x == shift_x && thing.pixel_y == shift_y))
. += thing
//gives us the stack trace from CRASH() without ending the current proc.
/proc/stack_trace(msg)
CRASH(msg)
/datum/proc/stack_trace(msg)
CRASH(msg)
+2
View File
@@ -13,6 +13,8 @@ var/list/diona_names = file2list ("config/names/diona.txt")
var/list/verbs = file2list("config/names/verbs.txt")
var/list/adjectives = file2list("config/names/adjectives.txt")
var/list/dream_strings = file2list("config/names/dreams.txt")
var/list/nightmare_strings = file2list("config/names/nightmares.txt")
//loaded on startup because of "
//would include in rsc if ' was used
-1
View File
@@ -19,7 +19,6 @@ var/global/list/all_areas = list()
var/global/list/machines = list()
var/global/list/machine_processing = list()
var/global/list/fast_processing = list()
var/global/list/atmos_machinery = list()
var/global/list/processing_power_items = list() //items that ask to be called every cycle
var/global/list/rcd_list = list() //list of Rapid Construction Devices.
+2 -7
View File
@@ -127,9 +127,7 @@
if(A == loc || (A in loc) || (sdepth != -1 && sdepth <= 2))
// No adjacency needed
if(W)
var/resolved = A.attackby(W,src)
if(!resolved && A && W)
W.afterattack(A,src,1,params) // 1 indicates adjacency
W.melee_attack_chain(src, A, params)
else
if(ismob(A))
changeNext_move(CLICK_CD_MELEE)
@@ -145,10 +143,7 @@
if(isturf(A) || isturf(A.loc) || (sdepth != -1 && sdepth <= 1))
if(A.Adjacent(src)) // see adjacent.dm
if(W)
// Return 1 in attackby() to prevent afterattack() effects (when safely moving items for example, params)
var/resolved = A.attackby(W,src,params)
if(!resolved && A && W)
W.afterattack(A,src,1,params) // 1: clicking something Adjacent
W.melee_attack_chain(src, A, params)
else
if(ismob(A))
changeNext_move(CLICK_CD_MELEE)
+2 -7
View File
@@ -81,10 +81,7 @@
// cyborgs are prohibited from using storage items so we can I think safely remove (A.loc in contents)
if(A == loc || (A in loc) || (A in contents))
// No adjacency checks
var/resolved = A.attackby(W,src,params, params)
if(!resolved && A && W)
W.afterattack(A,src,1,params)
W.melee_attack_chain(src, A, params)
return
if(!isturf(loc))
@@ -93,9 +90,7 @@
// cyborgs are prohibited from using storage items so we can I think safely remove (A.loc && isturf(A.loc.loc))
if(isturf(A) || isturf(A.loc))
if(A.Adjacent(src)) // see adjacent.dm
var/resolved = A.attackby(W, src, params, params)
if(!resolved && A && W)
W.afterattack(A, src, 1, params)
W.melee_attack_chain(src, A, params)
return
else
W.afterattack(A, src, 0, params)
+3
View File
@@ -16,6 +16,9 @@
var/datum/hud/hud = null
appearance_flags = NO_CLIENT_COLOR
/obj/screen/take_damage()
return
/obj/screen/Destroy()
master = null
return ..()
+96 -148
View File
@@ -1,36 +1,33 @@
/obj/item/proc/melee_attack_chain(mob/user, atom/target, params)
if(pre_attackby(target, user, params))
// Return 1 in attackby() to prevent afterattack() effects (when safely moving items for example)
var/resolved = target.attackby(src, user, params)
if(!resolved && target && !qdeleted(src))
afterattack(target, user, 1, params) // 1: clicking something Adjacent
// Called when the item is in the active hand, and clicked; alternately, there is an 'activate held object' verb or you can hit pagedown.
/obj/item/proc/attack_self(mob/user)
return
/obj/item/proc/pre_attackby(atom/A, mob/living/user, params) //do stuff before attackby!
return TRUE //return FALSE to avoid calling attackby after this proc does stuff
// No comment
/atom/proc/attackby(obj/item/W, mob/living/user, params)
/atom/proc/attackby(obj/item/W, mob/user, params)
return
/atom/movable/attackby(obj/item/W, mob/living/user, params)
user.changeNext_move(CLICK_CD_MELEE)
user.do_attack_animation(src)
if(!(W.flags&NOBLUDGEON))
visible_message("<span class='danger'>[src] has been hit by [user] with [W].</span>")
/obj/attackby(obj/item/I, mob/living/user, params)
return I.attack_obj(src, user)
/mob/living/attackby(obj/item/I, mob/user, params)
/mob/living/attackby(obj/item/I, mob/living/user, params)
user.changeNext_move(CLICK_CD_MELEE)
if(attempt_harvest(I, user))
return
I.attack(src, user)
return 1
return I.attack(src, user)
// Proximity_flag is 1 if this afterattack was called on something adjacent, in your square, or on your person.
// Click parameters is the params string from byond Click() code, see that documentation.
/obj/item/proc/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
return
/obj/item/proc/attack(mob/living/M as mob, mob/living/user as mob, def_zone)
if(!istype(M)) // not sure if this is the right thing...
/obj/item/proc/attack(mob/living/M, mob/living/user, def_zone)
if(flags & (NOBLUDGEON))
return 0
var/messagesource = M
if(can_operate(M)) //Checks if mob is lying down on table for surgery
if(istype(src,/obj/item/robot_parts))//popup override for direct attach
@@ -46,7 +43,7 @@
else
return 1
if(istype(src,/obj/item/weapon/screwdriver) && M.get_species() == "Machine")
if(isscrewdriver(src) && M.get_species() == "Machine")
if(!attempt_initiate_surgery(src, M, user))
return 0
else
@@ -57,136 +54,87 @@
else
return 1
if(istype(M,/mob/living/carbon/brain))
var/mob/living/carbon/brain/B = M
messagesource = B.container
if(hitsound && force > 0)
playsound(loc, hitsound, 50, 1, -1)
/////////////////////////
if(!force)
playsound(loc, 'sound/weapons/tap.ogg', get_clamped_volume(), 1, -1)
else if(hitsound)
playsound(loc, hitsound, get_clamped_volume(), 1, -1)
user.lastattacked = M
M.lastattacker = user
add_logs(user, M, "attacked", name, "(INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])", print_attack_log = (force > 0))//print it if stuff deals damage
if(!iscarbon(user))
M.LAssailant = null
else
M.LAssailant = user
if(user != M)
user.do_attack_animation(M)
M.attacked_by(src, user, def_zone)
/////////////////////////
if(isanimal(M))
var/mob/living/simple_animal/S = M
S.attacked_by(src, user)
return 0 // No sanic-speed double-attacks for you - simple mobs will handle being attacked on their own
var/power = force
if(!istype(M, /mob/living/carbon/human))
if(istype(M, /mob/living/carbon/slime))
var/mob/living/carbon/slime/slime = M
if(prob(25))
to_chat(user, "<span class='warning'>[src] passes right through [M]!</span>")
return
if(power > 0)
slime.attacked += 10
if(slime.Discipline && prob(50)) // wow, buddy, why am I getting attacked??
slime.Discipline = 0
if(power >= 3)
if(slime.is_adult)
if(prob(5 + round(power/2)))
if(slime.Victim)
if(prob(80) && !slime.client)
slime.Discipline++
slime.Victim = null
slime.anchored = 0
spawn()
if(slime)
slime.SStun = 1
sleep(rand(5,20))
if(slime)
slime.SStun = 0
spawn(0)
if(slime)
slime.canmove = 0
step_away(slime, user)
if(prob(25 + power))
sleep(2)
if(slime && user)
step_away(slime, user)
slime.canmove = 1
else
if(prob(10 + power*2))
if(slime)
if(slime.Victim)
if(prob(80) && !slime.client)
slime.Discipline++
if(slime.Discipline == 1)
slime.attacked = 0
spawn()
if(slime)
slime.SStun = 1
sleep(rand(5,20))
if(slime)
slime.SStun = 0
slime.Victim = null
slime.anchored = 0
spawn(0)
if(slime && user)
step_away(slime, user)
slime.canmove = 0
if(prob(25 + power*4))
sleep(2)
if(slime && user)
step_away(slime, user)
slime.canmove = 1
var/showname = "."
if(user)
showname = " by [user]."
user.do_attack_animation(src)
if(!(user in viewers(M, null)))
showname = "."
for(var/mob/O in viewers(messagesource, null))
if(attack_verb.len)
O.show_message("<span class='combat danger'>[M] has been [pick(attack_verb)] with [src][showname] </span>", 1)
else
O.show_message("<span class='combat danger'>[M] has been attacked with [src][showname] </span>", 1)
if(!showname && user)
if(user.client)
to_chat(user, "<span class='combat danger'>You attack [M] with [src]. </span>")
if(istype(M, /mob/living/carbon/human))
return M:attacked_by(src, user, def_zone) //make sure to return whether we have hit or miss
else
switch(damtype)
if("brute")
if(istype(src, /mob/living/carbon/slime))
M.adjustBrainLoss(power)
else
M.take_organ_damage(power)
if(prob(33)) // Added blood for whacking non-humans too
M.add_splatter_floor()
if("fire")
M.take_organ_damage(0, power)
to_chat(M, "Aargh it burns!")
M.updatehealth()
add_logs(user, M, "attacked", name, "(INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])", print_attack_log = (force > 0))//print it if stuff deals damage
add_fingerprint(user)
//the equivalent of the standard version of attack() but for object targets.
/obj/item/proc/attack_obj(obj/O, mob/living/user)
if(flags & (NOBLUDGEON))
return
user.changeNext_move(CLICK_CD_MELEE)
user.do_attack_animation(O)
O.attacked_by(src, user)
/atom/movable/proc/attacked_by()
return
/obj/attacked_by(obj/item/I, mob/living/user)
if(I.force)
user.visible_message("<span class='danger'>[user] has hit [src] with [I]!</span>", "<span class='danger'>You hit [src] with [I]!</span>")
take_damage(I.force, I.damtype, "melee", 1)
/mob/living/attacked_by(obj/item/I, mob/living/user, def_zone)
send_item_attack_message(I, user)
if(I.force)
apply_damage(I.force, I.damtype, def_zone)
if(I.damtype == BRUTE)
if(prob(33))
I.add_mob_blood(src)
var/turf/location = get_turf(src)
add_splatter_floor(location)
if(get_dist(user, src) <= 1) //people with TK won't get smeared with blood
user.add_mob_blood(src)
return TRUE //successful attack
/mob/living/simple_animal/attacked_by(obj/item/I, mob/living/user)
if(!I.force)
user.visible_message("<span class='warning'>[user] gently taps [src] with [I].</span>",\
"<span class='warning'>This weapon is ineffective, it does no damage!</span>")
else if(I.force < force_threshold || I.damtype == STAMINA)
visible_message("<span class='warning'>[I] bounces harmlessly off of [src].</span>",\
"<span class='warning'>[I] bounces harmlessly off of [src]!</span>")
else
return ..()
// Proximity_flag is 1 if this afterattack was called on something adjacent, in your square, or on your person.
// Click parameters is the params string from byond Click() code, see that documentation.
/obj/item/proc/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
return
/obj/item/proc/get_clamped_volume()
if(w_class)
if(force)
return Clamp((force + w_class) * 4, 30, 100)// Add the item's force to its weight class and multiply by 4, then clamp the value between 30 and 100
else
return Clamp(w_class * 6, 10, 100) // Multiply the item's weight class by 6, then clamp the value between 10 and 100
/mob/living/proc/send_item_attack_message(obj/item/I, mob/living/user, hit_area)
if(I.discrete)
return
var/message_verb = "attacked"
if(I.attack_verb && I.attack_verb.len)
message_verb = "[pick(I.attack_verb)]"
else if(!I.force)
return
var/message_hit_area = ""
if(hit_area)
message_hit_area = " in the [hit_area]"
var/attack_message = "[src] has been [message_verb][message_hit_area] with [I]."
if(user in viewers(src, null))
attack_message = "[user] has [message_verb] [src][message_hit_area] with [I]!"
visible_message("<span class='combat danger'>[attack_message]</span>",\
"<span class='combat userdanger'>[attack_message]</span>")
return 1
@@ -34,8 +34,6 @@
/**
* Config vars
*/
// Process name
var/name
// Process schedule interval
// This controls how often the process would run under ideal conditions.
@@ -364,7 +362,7 @@
var/highestRunTime = round(highest_run_time, 0.001)
var/deferTime = round(cpu_defer_count / 10 * world.tick_lag, 0.01)
if(!statclick)
statclick = new (src)
statclick = new /obj/effect/statclick/debug(src)
stat("[name]", statclick.update("T#[getTicks()] | AR [averageRunTime] | LR [lastRunTime] | HR [highestRunTime] | D [deferTime]"))
/datum/controller/process/proc/catchException(var/exception/e, var/thrower)
@@ -232,7 +232,7 @@ var/global/datum/controller/processScheduler/processScheduler
stat("Processes", "Scheduler not running")
return
if(!statclick)
statclick = new (src)
statclick = new /obj/effect/statclick/debug(src)
stat("Processes", statclick.update("[processes.len] (R [running.len] / Q [queued.len] / I [idle.len])"))
for(var/datum/controller/process/p in processes)
p.statProcess()
-24
View File
@@ -1,24 +0,0 @@
var/global/datum/controller/process/fire/fire_master
/datum/controller/process/fire
var/list/burning = list()
/datum/controller/process/fire/setup()
name = "fire"
schedule_interval = 20 //every 2 seconds
log_startup_progress("Fire process starting up.")
/datum/controller/process/fire/statProcess()
..()
stat(null, "[burning.len] burning objects")
/datum/controller/process/fire/doWork()
for(var/obj/burningobj in burning)
if(burningobj.burn_state == ON_FIRE)
if(burningobj.burn_world_time < world.time)
burningobj.burn()
SCHECK
else
burning.Remove(burningobj)
DECLARE_GLOBAL_CONTROLLER(fire, fire_master)
+21
View File
@@ -189,6 +189,16 @@
var/disable_karma = 0 // Disable all karma functions and unlock karma jobs by default
// StonedMC
var/tick_limit_mc_init = TICK_LIMIT_MC_INIT_DEFAULT //SSinitialization throttling
// Highpop tickrates
var/base_mc_tick_rate = 1
var/high_pop_mc_tick_rate = 1.1
var/high_pop_mc_mode_amount = 65
var/disable_high_pop_mc_mode_amount = 60
/datum/configuration/New()
var/list/L = subtypesof(/datum/game_mode)
for(var/T in L)
@@ -597,6 +607,17 @@
if("disable_karma")
disable_karma = 1
if("tick_limit_mc_init")
tick_limit_mc_init = text2num(value)
if("base_mc_tick_rate")
base_mc_tick_rate = text2num(value)
if("high_pop_mc_tick_rate")
high_pop_mc_tick_rate = text2num(value)
if("high_pop_mc_mode_amount")
high_pop_mc_mode_amount = text2num(value)
if("disable_high_pop_mc_mode_amount")
disable_high_pop_mc_mode_amount = text2num(value)
else
diary << "Unknown setting in configuration: '[name]'"
+19
View File
@@ -0,0 +1,19 @@
/datum/controller
var/name
// The object used for the clickable stat() button.
var/obj/effect/statclick/statclick
/datum/controller/proc/Initialize()
//cleanup actions
/datum/controller/proc/Shutdown()
//when we enter dmm_suite.load_map
/datum/controller/proc/StartLoadingMap()
//when we exit dmm_suite.load_map
/datum/controller/proc/StopLoadingMap()
/datum/controller/proc/Recover()
/datum/controller/proc/stat_entry()
+85 -27
View File
@@ -1,39 +1,97 @@
var/global/datum/controller/failsafe/failsafe
GLOBAL_REAL(Failsafe, /datum/controller/failsafe)
/datum/controller/failsafe // This thing pretty much just keeps poking the controllers.
processing_interval = 100 // Poke the controllers every 10 seconds.
/*
* Controller 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 controller process() and starting a new one).
*/
/datum/controller/failsafe // This thing pretty much just keeps poking the master controller
name = "Failsafe"
// master
var/masterControllerIteration = 0
var/masterControllerAlertLevel = 0
// The length of time to check on the MC (in deciseconds).
// Set to 0 to disable.
var/processing_interval = 20
// The alert level. For every failed poke, we drop a DEFCON level. Once we hit DEFCON 1, restart the MC.
var/defcon = 5
//the world.time of the last check, so the mc can restart US if we hang.
// (Real friends look out for *eachother*)
var/lasttick = 0
// Track the MC iteration to make sure its still on track.
var/master_iteration = 0
var/running = TRUE
/datum/controller/failsafe/New()
. = ..()
// Highlander-style: there can only be one! Kill off the old and replace it with the new.
if(Failsafe != src)
if(istype(Failsafe))
qdel(Failsafe)
Failsafe = src
Initialize()
// 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))
recover()
qdel(failsafe)
/datum/controller/failsafe/Initialize()
set waitfor = 0
Failsafe.Loop()
if(!qdeleted(src))
qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us
failsafe = src
/datum/controller/failsafe/Destroy()
running = FALSE
..()
return QDEL_HINT_HARDDEL_NOW
//failsafe.process()
/datum/controller/failsafe/proc/Loop()
while(running)
lasttick = world.time
if(!Master)
// Replace the missing Master! This should never, ever happen.
new /datum/controller/master()
// Only poke it if overrides are not in effect.
if(processing_interval > 0)
if(Master.processing && Master.iteration)
// Check if processing is done yet.
if(Master.iteration == master_iteration)
switch(defcon)
if(4,5)
--defcon
if(3)
message_admins("<span class='adminnotice'>Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5 - defcon) * processing_interval] ticks.</span>")
--defcon
if(2)
to_chat(admins, "<span class='boldannounce'>Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5 - defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.</span>")
--defcon
if(1)
/datum/controller/failsafe/proc/process()
processing = 1
to_chat(admins, "<span class='boldannounce'>Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5 - defcon) * processing_interval] ticks. Killing and restarting...</span>")
--defcon
var/rtn = Recreate_MC()
if(rtn > 0)
defcon = 4
master_iteration = 0
to_chat(admins, "<span class='adminnotice'>MC restarted successfully</span>")
else if(rtn < 0)
log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0")
to_chat(admins, "<span class='boldannounce'>ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.</span>")
//if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again
//no need to handle that specially when defcon 0 can handle it
if(0) //DEFCON 0! (mc failed to restart)
var/rtn = Recreate_MC()
if(rtn > 0)
defcon = 4
master_iteration = 0
to_chat(admins, "<span class='adminnotice'>MC restarted successfully</span>")
else
defcon = min(defcon + 1,5)
master_iteration = Master.iteration
if(defcon <= 1)
sleep(processing_interval * 2)
else
sleep(processing_interval)
else
defcon = 5
sleep(initial(processing_interval))
spawn(0)
set background = BACKGROUND_ENABLED
/datum/controller/failsafe/proc/defcon_pretty()
return defcon
while(1) // More efficient than recursivly calling ourself over and over. background = 1 ensures we do not trigger an infinite loop.
iteration++
/datum/controller/failsafe/stat_entry()
if(!statclick)
statclick = new/obj/effect/statclick/debug(src, "Initializing...")
sleep(processing_interval)
stat("Failsafe Controller:", statclick.update("Defcon: [defcon_pretty()] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])"))
+69
View File
@@ -0,0 +1,69 @@
GLOBAL_REAL(GLOB, /datum/controller/global_vars)
/datum/controller/global_vars
name = "Global Variables"
var/list/gvars_datum_protected_varlist
var/list/gvars_datum_in_built_vars
var/list/gvars_datum_init_order
/datum/controller/global_vars/New()
if(GLOB)
CRASH("Multiple instances of global variable controller created")
GLOB = src
var/datum/controller/exclude_these = new
gvars_datum_in_built_vars = exclude_these.vars + list("gvars_datum_protected_varlist", "gvars_datum_in_built_vars", "gvars_datum_init_order")
qdel(exclude_these)
log_to_dd("[vars.len - gvars_datum_in_built_vars.len] global variables")
Initialize()
/datum/controller/global_vars/Destroy(force)
stack_trace("Some fucker qdel'd the global holder!")
if(!force)
return QDEL_HINT_LETMELIVE
QDEL_NULL(statclick)
gvars_datum_protected_varlist.Cut()
gvars_datum_in_built_vars.Cut()
GLOB = null
return ..()
/datum/controller/global_vars/stat_entry()
if(!statclick)
statclick = new/obj/effect/statclick/debug(src, "Initializing...")
stat("Globals:", statclick.update("Edit"))
/datum/controller/global_vars/can_vv_get(var_name)
if(gvars_datum_protected_varlist[var_name])
return FALSE
return ..()
/datum/controller/global_vars/vv_edit_var(var_name, var_value)
if(gvars_datum_protected_varlist[var_name])
return FALSE
return ..()
/datum/controller/global_vars/Initialize()
gvars_datum_init_order = list()
gvars_datum_protected_varlist = list("gvars_datum_protected_varlist" = TRUE)
var/list/global_procs = typesof(/datum/controller/global_vars/proc)
var/expected_len = vars.len - gvars_datum_in_built_vars.len
if(global_procs.len != expected_len)
warning("Unable to detect all global initialization procs! Expected [expected_len] got [global_procs.len]!")
if(global_procs.len)
var/list/expected_global_procs = vars - gvars_datum_in_built_vars
for(var/I in global_procs)
expected_global_procs -= replacetext("[I]", "InitGlobal", "")
log_to_dd("Missing procs: [expected_global_procs.Join(", ")]")
for(var/I in global_procs)
var/start_tick = world.time
call(src, I)()
var/end_tick = world.time
if(end_tick - start_tick)
warning("Global [replacetext("[I]", "InitGlobal", "")] slept during initialization!")
+612
View File
@@ -0,0 +1,612 @@
/**
* StonedMC
*
* Designed to properly split up a given tick among subsystems
* Note: if you read parts of this code and think "why is it doing it that way"
* Odds are, there is a reason
*
**/
//This is the ABSOLUTE ONLY THING that should init globally like this
GLOBAL_REAL(Master, /datum/controller/master) = new
//THIS IS THE INIT ORDER
//Master -> SSPreInit -> GLOB -> world -> config -> SSInit -> Failsafe
//GOT IT MEMORIZED?
/datum/controller/master
name = "Master"
// Are we processing (higher values increase the processing delay by n ticks)
var/processing = TRUE
// How many times have we ran
var/iteration = 0
// world.time of last fire, for tracking lag outside of the mc
var/last_run
// List of subsystems to process().
var/list/subsystems
// Vars for keeping track of tick drift.
var/init_timeofday
var/init_time
var/tickdrift = 0
var/sleep_delta = 1
var/make_runtime = 0
var/initializations_finished_with_no_players_logged_in //I wonder what this could be?
// The type of the last subsystem to be process()'d.
var/last_type_processed
var/datum/controller/subsystem/queue_head //Start of queue linked list
var/datum/controller/subsystem/queue_tail //End of queue linked list (used for appending to the list)
var/queue_priority_count = 0 //Running total so that we don't have to loop thru the queue each run to split up the tick
var/queue_priority_count_bg = 0 //Same, but for background subsystems
var/map_loading = FALSE //Are we loading in a new map?
var/current_runlevel //for scheduling different subsystems for different stages of the round
var/sleep_offline_after_initializations = TRUE
var/static/restart_clear = 0
var/static/restart_timeout = 0
var/static/restart_count = 0
var/static/random_seed
//current tick limit, assigned before running a subsystem.
//used by CHECK_TICK as well so that the procs subsystems call can obey that SS's tick limits
var/static/current_ticklimit = TICK_LIMIT_RUNNING
/datum/controller/master/New()
makeDatumRefLists()
load_configuration()
// Highlander-style: there can only be one! Kill off the old and replace it with the new.
if(!random_seed)
random_seed = rand(1, 1e9)
rand_seed(random_seed)
var/list/_subsystems = list()
subsystems = _subsystems
if(Master != src)
if(istype(Master))
Recover()
qdel(Master)
else
var/list/subsytem_types = subtypesof(/datum/controller/subsystem)
sortTim(subsytem_types, /proc/cmp_subsystem_init)
for(var/I in subsytem_types)
_subsystems += new I
Master = src
if(!GLOB)
new /datum/controller/global_vars
/datum/controller/master/Destroy()
..()
// Tell qdel() to Del() this object.
return QDEL_HINT_HARDDEL_NOW
/datum/controller/master/Shutdown()
processing = FALSE
sortTim(subsystems, /proc/cmp_subsystem_init)
reverseRange(subsystems)
for(var/datum/controller/subsystem/ss in subsystems)
log_to_dd("Shutting down [ss.name] subsystem...")
ss.Shutdown()
log_to_dd("Shutdown complete")
// Returns 1 if we created a new mc, 0 if we couldn't due to a recent restart,
// -1 if we encountered a runtime trying to recreate it
/proc/Recreate_MC()
. = -1 //so if we runtime, things know we failed
if(world.time < Master.restart_timeout)
return 0
if(world.time < Master.restart_clear)
Master.restart_count *= 0.5
var/delay = 50 * ++Master.restart_count
Master.restart_timeout = world.time + delay
Master.restart_clear = world.time + (delay * 2)
Master.processing = FALSE //stop ticking this one
try
new/datum/controller/master()
catch
return -1
return 1
/datum/controller/master/Recover()
var/msg = "## DEBUG: [time2text(world.timeofday)] MC restarted. Reports:\n"
for(var/varname in Master.vars)
switch (varname)
if("name", "tag", "bestF", "type", "parent_type", "vars", "statclick") // Built-in junk.
continue
else
var/varval = Master.vars[varname]
if(istype(varval, /datum)) // Check if it has a type var.
var/datum/D = varval
msg += "\t [varname] = [D]([D.type])\n"
else
msg += "\t [varname] = [varval]\n"
log_to_dd(msg)
var/datum/controller/subsystem/BadBoy = Master.last_type_processed
var/FireHim = FALSE
if(istype(BadBoy))
msg = null
LAZYINITLIST(BadBoy.failure_strikes)
switch(++BadBoy.failure_strikes[BadBoy.type])
if(2)
msg = "The [BadBoy.name] subsystem was the last to fire for 2 controller restarts. It will be recovered now and disabled if it happens again."
FireHim = TRUE
if(3)
msg = "The [BadBoy.name] subsystem seems to be destabilizing the MC and will be offlined."
BadBoy.flags |= SS_NO_FIRE
if(msg)
to_chat(admins, "<span class='boldannounce'>[msg]</span>")
log_to_dd(msg)
if(istype(Master.subsystems))
if(FireHim)
Master.subsystems += new BadBoy.type //NEW_SS_GLOBAL will remove the old one
subsystems = Master.subsystems
current_runlevel = Master.current_runlevel
StartProcessing(10)
else
to_chat(world, "<span class='boldannounce'>The Master Controller is having some issues, we will need to re-initialize EVERYTHING</span>")
Initialize(20, TRUE)
// Please don't stuff random bullshit here,
// Make a subsystem, give it the SS_NO_FIRE flag, and do your work in it's Initialize()
/datum/controller/master/Initialize(delay, init_sss)
set waitfor = 0
if(delay)
sleep(delay)
if(init_sss)
init_subtypes(/datum/controller/subsystem, subsystems)
to_chat(world, "<span class='boldannounce'>Initializing subsystems...</span>")
// Sort subsystems by init_order, so they initialize in the correct order.
sortTim(subsystems, /proc/cmp_subsystem_init)
var/start_timeofday = REALTIMEOFDAY
// Initialize subsystems.
current_ticklimit = config.tick_limit_mc_init
for(var/datum/controller/subsystem/SS in subsystems)
if(SS.flags & SS_NO_INIT)
continue
SS.Initialize(REALTIMEOFDAY)
CHECK_TICK
current_ticklimit = TICK_LIMIT_RUNNING
var/time = (REALTIMEOFDAY - start_timeofday) / 10
var/msg = "Initializations complete within [time] second[time == 1 ? "" : "s"]!"
to_chat(world, "<span class='boldannounce'>[msg]</span>")
log_to_dd(msg)
if(!current_runlevel)
SetRunLevel(1)
// Sort subsystems by display setting for easy access.
sortTim(subsystems, /proc/cmp_subsystem_display)
// Set world options.
if(sleep_offline_after_initializations)
world.sleep_offline = TRUE
// world.fps = CONFIG_GET(number/fps) // TIGER TODO
world.tick_lag = config.Ticklag
var/initialized_tod = REALTIMEOFDAY
sleep(1)
initializations_finished_with_no_players_logged_in = initialized_tod < REALTIMEOFDAY - 10
// Loop.
Master.StartProcessing(0)
/datum/controller/master/proc/SetRunLevel(new_runlevel)
var/old_runlevel = current_runlevel
if(isnull(old_runlevel))
old_runlevel = "NULL"
testing("MC: Runlevel changed from [old_runlevel] to [new_runlevel]")
current_runlevel = log(2, new_runlevel) + 1
if(current_runlevel < 1)
CRASH("Attempted to set invalid runlevel: [new_runlevel]")
// Starts the mc, and sticks around to restart it if the loop ever ends.
/datum/controller/master/proc/StartProcessing(delay)
set waitfor = 0
if(delay)
sleep(delay)
testing("Master starting processing")
var/rtn = Loop()
if(rtn > 0 || processing < 0)
return //this was suppose to happen.
//loop ended, restart the mc
log_game("MC crashed or runtimed, restarting")
message_admins("MC crashed or runtimed, restarting")
var/rtn2 = Recreate_MC()
if(rtn2 <= 0)
log_game("Failed to recreate MC (Error code: [rtn2]), it's up to the failsafe now")
message_admins("Failed to recreate MC (Error code: [rtn2]), it's up to the failsafe now")
Failsafe.defcon = 2
// Main loop.
/datum/controller/master/proc/Loop()
. = -1
//Prep the loop (most of this is because we want MC restarts to reset as much state as we can, and because
// local vars rock
//all this shit is here so that flag edits can be refreshed by restarting the MC. (and for speed)
var/list/tickersubsystems = list()
var/list/runlevel_sorted_subsystems = list(list(), list(), list(), list(), list(), list(), list(), list()) //ensure we always have as many runlevels as we need to operate with no subsystems (8 currently)
var/timer = world.time
for(var/thing in subsystems)
var/datum/controller/subsystem/SS = thing
if(SS.flags & SS_NO_FIRE)
continue
SS.queued_time = 0
SS.queue_next = null
SS.queue_prev = null
SS.state = SS_IDLE
if(SS.flags & SS_TICKER)
tickersubsystems += SS
timer += world.tick_lag * rand(1, 5)
SS.next_fire = timer
continue
var/ss_runlevels = SS.runlevels
var/added_to_any = FALSE
for(var/I in 1 to GLOB.bitflags.len)
if(ss_runlevels & GLOB.bitflags[I])
while(runlevel_sorted_subsystems.len < I)
runlevel_sorted_subsystems += list(list())
runlevel_sorted_subsystems[I] += SS
added_to_any = TRUE
if(!added_to_any)
WARNING("[SS.name] subsystem is not SS_NO_FIRE but also does not have any runlevels set!")
queue_head = null
queue_tail = null
//these sort by lower priorities first to reduce the number of loops needed to add subsequent SS's to the queue
//(higher subsystems will be sooner in the queue, adding them later in the loop means we don't have to loop thru them next queue add)
sortTim(tickersubsystems, /proc/cmp_subsystem_priority)
for(var/I in runlevel_sorted_subsystems)
sortTim(runlevel_sorted_subsystems, /proc/cmp_subsystem_priority)
I += tickersubsystems
var/cached_runlevel = current_runlevel
var/list/current_runlevel_subsystems = runlevel_sorted_subsystems[cached_runlevel]
init_timeofday = REALTIMEOFDAY
init_time = world.time
iteration = 1
var/error_level = 0
var/sleep_delta = 1
var/list/subsystems_to_check
//the actual loop.
while(1)
tickdrift = max(0, MC_AVERAGE_FAST(tickdrift, (((REALTIMEOFDAY - init_timeofday) - (world.time - init_time)) / world.tick_lag)))
var/starting_tick_usage = TICK_USAGE
if(processing <= 0)
current_ticklimit = TICK_LIMIT_RUNNING
sleep(10)
continue
//Anti-tick-contention heuristics:
//if there are mutiple sleeping procs running before us hogging the cpu, we have to run later.
// (because sleeps are processed in the order received, longer sleeps are more likely to run first)
if(starting_tick_usage > TICK_LIMIT_MC) //if there isn't enough time to bother doing anything this tick, sleep a bit.
sleep_delta *= 2
current_ticklimit = TICK_LIMIT_RUNNING * 0.5
sleep(world.tick_lag * (processing * sleep_delta))
continue
//Byond resumed us late. assume it might have to do the same next tick
if(last_run + CEILING(world.tick_lag * (processing * sleep_delta), world.tick_lag) < world.time)
sleep_delta += 1
sleep_delta = MC_AVERAGE_FAST(sleep_delta, 1) //decay sleep_delta
if(starting_tick_usage > (TICK_LIMIT_MC * 0.75)) //we ran 3/4 of the way into the tick
sleep_delta += 1
//debug
if(make_runtime)
var/datum/controller/subsystem/SS
SS.can_fire = 0
if(!Failsafe || (Failsafe.processing_interval > 0 && (Failsafe.lasttick + (Failsafe.processing_interval * 5)) < world.time))
new/datum/controller/failsafe() // (re)Start the failsafe.
//now do the actual stuff
if(!queue_head || !(iteration % 3))
var/checking_runlevel = current_runlevel
if(cached_runlevel != checking_runlevel)
//resechedule subsystems
cached_runlevel = checking_runlevel
current_runlevel_subsystems = runlevel_sorted_subsystems[cached_runlevel]
var/stagger = world.time
for(var/I in current_runlevel_subsystems)
var/datum/controller/subsystem/SS = I
if(SS.next_fire <= world.time)
stagger += world.tick_lag * rand(1, 5)
SS.next_fire = stagger
subsystems_to_check = current_runlevel_subsystems
else
subsystems_to_check = tickersubsystems
if(CheckQueue(subsystems_to_check) <= 0)
if(!SoftReset(tickersubsystems, runlevel_sorted_subsystems))
log_to_dd("MC: SoftReset() failed, crashing")
return
if(!error_level)
iteration++
error_level++
current_ticklimit = TICK_LIMIT_RUNNING
sleep(10)
continue
if(queue_head)
if(RunQueue() <= 0)
if(!SoftReset(tickersubsystems, runlevel_sorted_subsystems))
log_to_dd("MC: SoftReset() failed, crashing")
return
if(!error_level)
iteration++
error_level++
current_ticklimit = TICK_LIMIT_RUNNING
sleep(10)
continue
error_level--
if(!queue_head) //reset the counts if the queue is empty, in the off chance they get out of sync
queue_priority_count = 0
queue_priority_count_bg = 0
iteration++
last_run = world.time
src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta)
current_ticklimit = TICK_LIMIT_RUNNING
if(processing * sleep_delta <= world.tick_lag)
current_ticklimit -= (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc if we plan on running next tick
sleep(world.tick_lag * (processing * sleep_delta))
// This is what decides if something should run.
/datum/controller/master/proc/CheckQueue(list/subsystemstocheck)
. = 0 //so the mc knows if we runtimed
//we create our variables outside of the loops to save on overhead
var/datum/controller/subsystem/SS
var/SS_flags
for(var/thing in subsystemstocheck)
if(!thing)
subsystemstocheck -= thing
SS = thing
if(SS.state != SS_IDLE)
continue
if(SS.can_fire <= 0)
continue
if(SS.next_fire > world.time)
continue
SS_flags = SS.flags
if(SS_flags & SS_NO_FIRE)
subsystemstocheck -= SS
continue
if((SS_flags & (SS_TICKER|SS_KEEP_TIMING)) == SS_KEEP_TIMING && SS.last_fire + (SS.wait * 0.75) > world.time)
continue
SS.enqueue()
. = 1
// Run thru the queue of subsystems to run, running them while balancing out their allocated tick precentage
/datum/controller/master/proc/RunQueue()
. = 0
var/datum/controller/subsystem/queue_node
var/queue_node_flags
var/queue_node_priority
var/queue_node_paused
var/current_tick_budget
var/tick_precentage
var/tick_remaining
var/ran = TRUE //this is right
var/ran_non_ticker = FALSE
var/bg_calc //have we swtiched current_tick_budget to background mode yet?
var/tick_usage
//keep running while we have stuff to run and we haven't gone over a tick
// this is so subsystems paused eariler can use tick time that later subsystems never used
while(ran && queue_head && TICK_USAGE < TICK_LIMIT_MC)
ran = FALSE
bg_calc = FALSE
current_tick_budget = queue_priority_count
queue_node = queue_head
while(queue_node)
if(ran && TICK_USAGE > TICK_LIMIT_RUNNING)
break
queue_node_flags = queue_node.flags
queue_node_priority = queue_node.queued_priority
//super special case, subsystems where we can't make them pause mid way through
//if we can't run them this tick (without going over a tick)
//we bump up their priority and attempt to run them next tick
//(unless we haven't even ran anything this tick, since its unlikely they will ever be able run
// in those cases, so we just let them run)
if(queue_node_flags & SS_NO_TICK_CHECK)
if(queue_node.tick_usage > TICK_LIMIT_RUNNING - TICK_USAGE && ran_non_ticker)
queue_node.queued_priority += queue_priority_count * 0.1
queue_priority_count -= queue_node_priority
queue_priority_count += queue_node.queued_priority
current_tick_budget -= queue_node_priority
queue_node = queue_node.queue_next
continue
if((queue_node_flags & SS_BACKGROUND) && !bg_calc)
current_tick_budget = queue_priority_count_bg
bg_calc = TRUE
tick_remaining = TICK_LIMIT_RUNNING - TICK_USAGE
if(current_tick_budget > 0 && queue_node_priority > 0)
tick_precentage = tick_remaining / (current_tick_budget / queue_node_priority)
else
tick_precentage = tick_remaining
tick_precentage = max(tick_precentage*0.5, tick_precentage - queue_node.tick_overrun)
current_ticklimit = round(TICK_USAGE + tick_precentage)
if(!(queue_node_flags & SS_TICKER))
ran_non_ticker = TRUE
ran = TRUE
queue_node_paused = (queue_node.state == SS_PAUSED || queue_node.state == SS_PAUSING)
last_type_processed = queue_node
queue_node.state = SS_RUNNING
tick_usage = TICK_USAGE
var/state = queue_node.ignite(queue_node_paused)
tick_usage = TICK_USAGE - tick_usage
if(state == SS_RUNNING)
state = SS_IDLE
current_tick_budget -= queue_node_priority
if(tick_usage < 0)
tick_usage = 0
queue_node.tick_overrun = max(0, MC_AVG_FAST_UP_SLOW_DOWN(queue_node.tick_overrun, tick_usage - tick_precentage))
queue_node.state = state
if(state == SS_PAUSED)
queue_node.paused_ticks++
queue_node.paused_tick_usage += tick_usage
queue_node = queue_node.queue_next
continue
queue_node.ticks = MC_AVERAGE(queue_node.ticks, queue_node.paused_ticks)
tick_usage += queue_node.paused_tick_usage
queue_node.tick_usage = MC_AVERAGE_FAST(queue_node.tick_usage, tick_usage)
queue_node.cost = MC_AVERAGE_FAST(queue_node.cost, TICK_DELTA_TO_MS(tick_usage))
queue_node.paused_ticks = 0
queue_node.paused_tick_usage = 0
if(queue_node_flags & SS_BACKGROUND) //update our running total
queue_priority_count_bg -= queue_node_priority
else
queue_priority_count -= queue_node_priority
queue_node.last_fire = world.time
queue_node.times_fired++
if(queue_node_flags & SS_TICKER)
queue_node.next_fire = world.time + (world.tick_lag * queue_node.wait)
else if(queue_node_flags & SS_POST_FIRE_TIMING)
queue_node.next_fire = world.time + queue_node.wait + (world.tick_lag * (queue_node.tick_overrun / 100))
else if(queue_node_flags & SS_KEEP_TIMING)
queue_node.next_fire += queue_node.wait
else
queue_node.next_fire = queue_node.queued_time + queue_node.wait + (world.tick_lag * (queue_node.tick_overrun / 100))
queue_node.queued_time = 0
//remove from queue
queue_node.dequeue()
queue_node = queue_node.queue_next
. = 1
//resets the queue, and all subsystems, while filtering out the subsystem lists
// called if any mc's queue procs runtime or exit improperly.
/datum/controller/master/proc/SoftReset(list/ticker_SS, list/runlevel_SS)
. = 0
log_to_dd("MC: SoftReset called, resetting MC queue state.")
if(!istype(subsystems) || !istype(ticker_SS) || !istype(runlevel_SS))
log_to_dd("MC: SoftReset: Bad list contents: '[subsystems]' '[ticker_SS]' '[runlevel_SS]'")
return
var/subsystemstocheck = subsystems + ticker_SS
for(var/I in runlevel_SS)
subsystemstocheck |= I
for(var/thing in subsystemstocheck)
var/datum/controller/subsystem/SS = thing
if(!SS || !istype(SS))
//list(SS) is so if a list makes it in the subsystem list, we remove the list, not the contents
subsystems -= list(SS)
ticker_SS -= list(SS)
for(var/I in runlevel_SS)
I -= list(SS)
log_to_dd("MC: SoftReset: Found bad entry in subsystem list, '[SS]'")
continue
if(SS.queue_next && !istype(SS.queue_next))
log_to_dd("MC: SoftReset: Found bad data in subsystem queue, queue_next = '[SS.queue_next]'")
SS.queue_next = null
if(SS.queue_prev && !istype(SS.queue_prev))
log_to_dd("MC: SoftReset: Found bad data in subsystem queue, queue_prev = '[SS.queue_prev]'")
SS.queue_prev = null
SS.queued_priority = 0
SS.queued_time = 0
SS.state = SS_IDLE
if(queue_head && !istype(queue_head))
log_to_dd("MC: SoftReset: Found bad data in subsystem queue, queue_head = '[queue_head]'")
queue_head = null
if(queue_tail && !istype(queue_tail))
log_to_dd("MC: SoftReset: Found bad data in subsystem queue, queue_tail = '[queue_tail]'")
queue_tail = null
queue_priority_count = 0
queue_priority_count_bg = 0
log_to_dd("MC: SoftReset: Finished.")
. = 1
/datum/controller/master/stat_entry()
if(!statclick)
statclick = new/obj/effect/statclick/debug(src, "Initializing...")
stat("Byond", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%))")
stat("Master Controller", statclick.update("(TickRate:[Master.processing]) (Iteration:[Master.iteration])"))
// Currently unimplemented
/datum/controller/master/StartLoadingMap()
//disallow more than one map to load at once, multithreading it will just cause race conditions
while(map_loading)
stoplag()
for(var/S in subsystems)
var/datum/controller/subsystem/SS = S
SS.StartLoadingMap()
map_loading = TRUE
/datum/controller/master/StopLoadingMap(bounds = null)
map_loading = FALSE
for(var/S in subsystems)
var/datum/controller/subsystem/SS = S
SS.StopLoadingMap()
/datum/controller/master/proc/UpdateTickRate()
if(!processing)
return
var/client_count = length(clients)
if(client_count < config.disable_high_pop_mc_mode_amount)
processing = config.base_mc_tick_rate
else if(client_count > config.high_pop_mc_mode_amount)
processing = config.high_pop_mc_tick_rate
+3 -37
View File
@@ -1,6 +1,6 @@
//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
// old deprecated rusted piece of shit MC
// All this does now is misc. world init stuff because too lazy to put it somewhere else
// It used to run all of the repeating processes controlling the game but now the SMC and Process Scheduler do that
var/global/datum/controller/game_controller/master_controller //Set in world.New()
@@ -10,16 +10,6 @@ var/global/last_tick_duration = 0
var/global/air_processing_killed = 0
var/global/pipe_processing_killed = 0
/datum/controller
var/processing = 0
var/iteration = 0
var/processing_interval = 0
// Dummy object to let us click it to debug while in the stat panel
var/obj/effect/statclick/debug/statclick
/datum/controller/proc/recover() // If we are replacing an existing controller (due to a crash) we attempt to preserve as much as we can.
/datum/controller/game_controller
var/list/shuttle_list // For debugging and VV
@@ -46,8 +36,6 @@ var/global/pipe_processing_killed = 0
return QDEL_HINT_HARDDEL_NOW
/datum/controller/game_controller/proc/setup()
world.tick_lag = config.Ticklag
preloadTemplates()
if(!config.disable_away_missions)
createRandomZlevel()
@@ -94,26 +82,4 @@ var/global/pipe_processing_killed = 0
count++
log_startup_progress(" Initialized [count] objects in [stop_watch(watch)]s.")
watch = start_watch()
count = 0
log_startup_progress("Initializing atmospherics machinery...")
for(var/obj/machinery/atmospherics/unary/U in machines)
if(istype(U, /obj/machinery/atmospherics/unary/vent_pump))
var/obj/machinery/atmospherics/unary/vent_pump/T = U
T.broadcast_status()
count++
else if(istype(U, /obj/machinery/atmospherics/unary/vent_scrubber))
var/obj/machinery/atmospherics/unary/vent_scrubber/T = U
T.broadcast_status()
count++
log_startup_progress(" Initialized [count] atmospherics machines in [stop_watch(watch)]s.")
watch = start_watch()
count = 0
log_startup_progress("Initializing pipe networks...")
for(var/obj/machinery/atmospherics/machine in machines)
machine.build_network()
count++
log_startup_progress(" Initialized [count] pipes in [stop_watch(watch)]s.")
log_startup_progress("Finished object initializations in [stop_watch(overwatch)]s.")
+215
View File
@@ -0,0 +1,215 @@
/datum/controller/subsystem
// Metadata; you should define these.
name = "fire coderbus" //name of the subsystem
var/init_order = INIT_ORDER_DEFAULT //order of initialization. Higher numbers are initialized first, lower numbers later. Use defines in __DEFINES/subsystems.dm for easy understanding of order.
var/wait = 20 //time to wait (in deciseconds) between each call to fire(). Must be a positive integer.
var/priority = FIRE_PRIORITY_DEFAULT //When mutiple subsystems need to run in the same tick, higher priority subsystems will run first and be given a higher share of the tick before MC_TICK_CHECK triggers a sleep
var/flags = 0 //see MC.dm in __DEFINES Most flags must be set on world start to take full effect. (You can also restart the mc to force them to process again)
var/initialized = FALSE //set to TRUE after it has been initialized, will obviously never be set if the subsystem doesn't initialize
//set to 0 to prevent fire() calls, mostly for admin use or subsystems that may be resumed later
// use the SS_NO_FIRE flag instead for systems that never fire to keep it from even being added to the list
var/can_fire = TRUE
// Bookkeeping variables; probably shouldn't mess with these.
var/last_fire = 0 //last world.time we called fire()
var/next_fire = 0 //scheduled world.time for next fire()
var/cost = 0 //average time to execute
var/tick_usage = 0 //average tick usage
var/tick_overrun = 0 //average tick overrun
var/state = SS_IDLE //tracks the current state of the ss, running, paused, etc.
var/paused_ticks = 0 //ticks this ss is taking to run right now.
var/paused_tick_usage //total tick_usage of all of our runs while pausing this run
var/ticks = 1 //how many ticks does this ss take to run on avg.
var/times_fired = 0 //number of times we have called fire()
var/queued_time = 0 //time we entered the queue, (for timing and priority reasons)
var/queued_priority //we keep a running total to make the math easier, if priority changes mid-fire that would break our running total, so we store it here
//linked list stuff for the queue
var/datum/controller/subsystem/queue_next
var/datum/controller/subsystem/queue_prev
var/runlevels = RUNLEVELS_DEFAULT //points of the game at which the SS can fire
var/static/list/failure_strikes //How many times we suspect a subsystem type has crashed the MC, 3 strikes and you're out!
//Do not override
///datum/controller/subsystem/New()
// Used to initialize the subsystem BEFORE the map has loaded
// Called AFTER Recover if that is called
// Prefer to use Initialize if possible
/datum/controller/subsystem/proc/PreInit()
return
//This is used so the mc knows when the subsystem sleeps. do not override.
/datum/controller/subsystem/proc/ignite(resumed = 0)
set waitfor = 0
. = SS_SLEEPING
fire(resumed)
. = state
if(state == SS_SLEEPING)
state = SS_IDLE
if(state == SS_PAUSING)
var/QT = queued_time
enqueue()
state = SS_PAUSED
queued_time = QT
//previously, this would have been named 'process()' but that name is used everywhere for different things!
//fire() seems more suitable. This is the procedure that gets called every 'wait' deciseconds.
//Sleeping in here prevents future fires until returned.
/datum/controller/subsystem/proc/fire(resumed = 0)
flags |= SS_NO_FIRE
throw EXCEPTION("Subsystem [src]([type]) does not fire() but did not set the SS_NO_FIRE flag. Please add the SS_NO_FIRE flag to any subsystem that doesn't fire so it doesn't get added to the processing list and waste cpu.")
/datum/controller/subsystem/Destroy()
dequeue()
can_fire = 0
flags |= SS_NO_FIRE
Master.subsystems -= src
return ..()
//Queue it to run.
// (we loop thru a linked list until we get to the end or find the right point)
// (this lets us sort our run order correctly without having to re-sort the entire already sorted list)
/datum/controller/subsystem/proc/enqueue()
var/SS_priority = priority
var/SS_flags = flags
var/datum/controller/subsystem/queue_node
var/queue_node_priority
var/queue_node_flags
for(queue_node = Master.queue_head; queue_node; queue_node = queue_node.queue_next)
queue_node_priority = queue_node.queued_priority
queue_node_flags = queue_node.flags
if(queue_node_flags & SS_TICKER)
if(!(SS_flags & SS_TICKER))
continue
if(queue_node_priority < SS_priority)
break
else if(queue_node_flags & SS_BACKGROUND)
if(!(SS_flags & SS_BACKGROUND))
break
if(queue_node_priority < SS_priority)
break
else
if(SS_flags & SS_BACKGROUND)
continue
if(SS_flags & SS_TICKER)
break
if(queue_node_priority < SS_priority)
break
queued_time = world.time
queued_priority = SS_priority
state = SS_QUEUED
if(SS_flags & SS_BACKGROUND) //update our running total
Master.queue_priority_count_bg += SS_priority
else
Master.queue_priority_count += SS_priority
queue_next = queue_node
if(!queue_node)//we stopped at the end, add to tail
queue_prev = Master.queue_tail
if(Master.queue_tail)
Master.queue_tail.queue_next = src
else //empty queue, we also need to set the head
Master.queue_head = src
Master.queue_tail = src
else if(queue_node == Master.queue_head)//insert at start of list
Master.queue_head.queue_prev = src
Master.queue_head = src
queue_prev = null
else
queue_node.queue_prev.queue_next = src
queue_prev = queue_node.queue_prev
queue_node.queue_prev = src
/datum/controller/subsystem/proc/dequeue()
if(queue_next)
queue_next.queue_prev = queue_prev
if(queue_prev)
queue_prev.queue_next = queue_next
if(src == Master.queue_tail)
Master.queue_tail = queue_prev
if(src == Master.queue_head)
Master.queue_head = queue_next
queued_time = 0
if(state == SS_QUEUED)
state = SS_IDLE
/datum/controller/subsystem/proc/pause()
. = 1
switch(state)
if(SS_RUNNING)
state = SS_PAUSED
if(SS_SLEEPING)
state = SS_PAUSING
//used to initialize the subsystem AFTER the map has loaded
/datum/controller/subsystem/Initialize(start_timeofday)
initialized = TRUE
var/time = (REALTIMEOFDAY - start_timeofday) / 10
var/msg = "Initialized [name] subsystem within [time] second[time == 1 ? "" : "s"]!"
to_chat(world, "<span class='boldannounce'>[msg]</span>")
log_to_dd(msg)
return time
//hook for printing stats to the "MC" statuspanel for admins to see performance and related stats etc.
/datum/controller/subsystem/stat_entry(msg)
if(!statclick)
statclick = new/obj/effect/statclick/debug(src, "Initializing...")
if(can_fire && !(SS_NO_FIRE & flags))
msg = "[round(cost, 1)]ms|[round(tick_usage, 1)]%([round(tick_overrun, 1)]%)|[round(ticks, 0.1)]\t[msg]"
else
msg = "OFFLINE\t[msg]"
var/title = name
if(can_fire)
title = "\[[state_letter()]][title]"
stat(title, statclick.update(msg))
/datum/controller/subsystem/proc/state_letter()
switch(state)
if(SS_RUNNING)
. = "R"
if(SS_QUEUED)
. = "Q"
if(SS_PAUSED, SS_PAUSING)
. = "P"
if(SS_SLEEPING)
. = "S"
if(SS_IDLE)
. = " "
//could be used to postpone a costly subsystem for (default one) var/cycles, cycles
//for instance, during cpu intensive operations like explosions
/datum/controller/subsystem/proc/postpone(cycles = 1)
if(next_fire - world.time < wait)
next_fire += (wait*cycles)
//usually called via datum/controller/subsystem/New() when replacing a subsystem (i.e. due to a recurring crash)
//should attempt to salvage what it can from the old instance of subsystem
/datum/controller/subsystem/Recover()
/datum/controller/subsystem/vv_edit_var(var_name, var_value)
switch(var_name)
if("can_fire")
//this is so the subsystem doesn't rapid fire to make up missed ticks causing more lag
if(var_value)
next_fire = world.time + wait
if("queued_priority") //editing this breaks things.
return 0
. = ..()
+370
View File
@@ -0,0 +1,370 @@
#define SSAIR_DEFERREDPIPENETS 1
#define SSAIR_PIPENETS 2
#define SSAIR_ATMOSMACHINERY 3
#define SSAIR_ACTIVETURFS 4
#define SSAIR_EXCITEDGROUPS 5
#define SSAIR_HIGHPRESSURE 6
#define SSAIR_HOTSPOTS 7
#define SSAIR_SUPERCONDUCTIVITY 8
SUBSYSTEM_DEF(air)
name = "Atmospherics"
init_order = INIT_ORDER_AIR
priority = FIRE_PRIORITY_AIR
wait = 5
flags = SS_BACKGROUND
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/cost_turfs = 0
var/cost_groups = 0
var/cost_highpressure = 0
var/cost_hotspots = 0
var/cost_superconductivity = 0
var/cost_pipenets = 0
var/cost_deferred_pipenets = 0
var/cost_atmos_machinery = 0
var/list/excited_groups = list()
var/list/active_turfs = list()
var/list/hotspots = list()
var/list/networks = list()
var/list/atmos_machinery = list()
var/list/pipe_init_dirs_cache = list()
//Special functions lists
var/list/active_super_conductivity = list()
var/list/high_pressure_delta = list()
var/list/currentrun = list()
var/currentpart = SSAIR_PIPENETS
/datum/controller/subsystem/air/stat_entry(msg)
msg += "C:{"
msg += "AT:[round(cost_turfs,1)]|"
msg += "EG:[round(cost_groups,1)]|"
msg += "HP:[round(cost_highpressure,1)]|"
msg += "HS:[round(cost_hotspots,1)]|"
msg += "SC:[round(cost_superconductivity,1)]|"
msg += "PN:[round(cost_pipenets,1)]|"
msg += "DPN:[round(cost_deferred_pipenets,1)]|"
msg += "AM:[round(cost_atmos_machinery,1)]"
msg += "} "
msg += "AT:[active_turfs.len]|"
msg += "EG:[excited_groups.len]|"
msg += "HS:[hotspots.len]|"
msg += "PN:[networks.len]|"
msg += "HP:[high_pressure_delta.len]|"
msg += "AS:[active_super_conductivity.len]|"
msg += "AT/MS:[round((cost ? active_turfs.len/cost : 0),0.1)]"
..(msg)
/datum/controller/subsystem/air/Initialize(timeofday)
setup_overlays() // Assign icons and such for gas-turf-overlays
setup_allturfs()
setup_atmos_machinery()
setup_pipenets()
..()
/datum/controller/subsystem/air/fire(resumed = 0)
var/timer = TICK_USAGE_REAL
if(currentpart == SSAIR_DEFERREDPIPENETS || !resumed)
process_deferred_pipenets(resumed)
cost_deferred_pipenets = MC_AVERAGE(cost_deferred_pipenets, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
if(state != SS_RUNNING)
return
resumed = 0
currentpart = SSAIR_PIPENETS
if(currentpart == SSAIR_PIPENETS || !resumed)
process_pipenets(resumed)
cost_pipenets = MC_AVERAGE(cost_pipenets, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
if(state != SS_RUNNING)
return
resumed = 0
currentpart = SSAIR_ATMOSMACHINERY
if(currentpart == SSAIR_ATMOSMACHINERY)
timer = TICK_USAGE_REAL
process_atmos_machinery(resumed)
cost_atmos_machinery = MC_AVERAGE(cost_atmos_machinery, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
if(state != SS_RUNNING)
return
resumed = 0
currentpart = SSAIR_ACTIVETURFS
if(currentpart == SSAIR_ACTIVETURFS)
timer = TICK_USAGE_REAL
process_active_turfs(resumed)
cost_turfs = MC_AVERAGE(cost_turfs, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
if(state != SS_RUNNING)
return
resumed = 0
currentpart = SSAIR_EXCITEDGROUPS
if(currentpart == SSAIR_EXCITEDGROUPS)
timer = TICK_USAGE_REAL
process_excited_groups(resumed)
cost_groups = MC_AVERAGE(cost_groups, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
if(state != SS_RUNNING)
return
resumed = 0
currentpart = SSAIR_HIGHPRESSURE
if(currentpart == SSAIR_HIGHPRESSURE)
timer = TICK_USAGE_REAL
process_high_pressure_delta(resumed)
cost_highpressure = MC_AVERAGE(cost_highpressure, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
if(state != SS_RUNNING)
return
resumed = 0
currentpart = SSAIR_HOTSPOTS
if(currentpart == SSAIR_HOTSPOTS)
timer = TICK_USAGE_REAL
process_hotspots(resumed)
cost_hotspots = MC_AVERAGE(cost_hotspots, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
if(state != SS_RUNNING)
return
resumed = 0
currentpart = SSAIR_SUPERCONDUCTIVITY
if(currentpart == SSAIR_SUPERCONDUCTIVITY)
timer = TICK_USAGE_REAL
process_super_conductivity(resumed)
cost_superconductivity = MC_AVERAGE(cost_superconductivity, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
if(state != SS_RUNNING)
return
resumed = 0
currentpart = SSAIR_PIPENETS
/datum/controller/subsystem/air/proc/process_deferred_pipenets(resumed = 0)
if(!resumed)
src.currentrun = deferred_pipenet_rebuilds.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/obj/machinery/atmospherics/A = currentrun[currentrun.len]
currentrun.len--
if(A)
A.build_network()
else
deferred_pipenet_rebuilds.Remove(A)
if(MC_TICK_CHECK)
return
/datum/controller/subsystem/air/proc/process_pipenets(resumed = 0)
if(!resumed)
src.currentrun = networks.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/datum/pipeline/thing = currentrun[currentrun.len]
currentrun.len--
if(thing)
thing.process()
else
networks.Remove(thing)
if(MC_TICK_CHECK)
return
/datum/controller/subsystem/air/proc/process_atmos_machinery(resumed = 0)
var/seconds = wait * 0.1
if(!resumed)
src.currentrun = atmos_machinery.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/obj/machinery/M = currentrun[currentrun.len]
currentrun.len--
if(!M || (M.process_atmos(seconds) == PROCESS_KILL))
atmos_machinery.Remove(M)
if(MC_TICK_CHECK)
return
/datum/controller/subsystem/air/proc/process_super_conductivity(resumed = 0)
if(!resumed)
src.currentrun = active_super_conductivity.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/turf/simulated/T = currentrun[currentrun.len]
currentrun.len--
T.super_conduct()
if(MC_TICK_CHECK)
return
/datum/controller/subsystem/air/proc/process_hotspots(resumed = 0)
if(!resumed)
src.currentrun = hotspots.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/obj/effect/hotspot/H = currentrun[currentrun.len]
currentrun.len--
if(H)
H.process()
else
hotspots -= H
if(MC_TICK_CHECK)
return
/datum/controller/subsystem/air/proc/process_high_pressure_delta(resumed = 0)
while(high_pressure_delta.len)
var/turf/simulated/T = high_pressure_delta[high_pressure_delta.len]
high_pressure_delta.len--
T.high_pressure_movements()
T.pressure_difference = 0
if(MC_TICK_CHECK)
return
/datum/controller/subsystem/air/proc/process_active_turfs(resumed = 0)
//cache for sanic speed
var/fire_count = times_fired
if(!resumed)
src.currentrun = active_turfs.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/turf/simulated/T = currentrun[currentrun.len]
currentrun.len--
if(T)
T.process_cell(fire_count)
if(MC_TICK_CHECK)
return
/datum/controller/subsystem/air/proc/process_excited_groups(resumed = 0)
if(!resumed)
src.currentrun = excited_groups.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/datum/excited_group/EG = currentrun[currentrun.len]
currentrun.len--
EG.breakdown_cooldown++
if(EG.breakdown_cooldown == 10)
EG.self_breakdown()
else if(EG.breakdown_cooldown >= 20)
EG.dismantle()
if(MC_TICK_CHECK)
return
/datum/controller/subsystem/air/proc/remove_from_active(turf/simulated/T)
active_turfs -= T
active_super_conductivity -= T // bug: if a turf is hit by ex_act 1 while processing, it can end up in super conductivity as /turf/space and cause runtimes
if(currentpart == SSAIR_ACTIVETURFS || currentpart == SSAIR_SUPERCONDUCTIVITY)
currentrun -= T
if(istype(T))
T.excited = 0
if(T.excited_group)
T.excited_group.garbage_collect()
/datum/controller/subsystem/air/proc/add_to_active(turf/simulated/T, blockchanges = 1)
if(istype(T) && T.air)
T.excited = 1
active_turfs |= T
if(currentpart == SSAIR_ACTIVETURFS)
currentrun |= 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/controller/subsystem/air/proc/setup_allturfs(var/list/turfs_to_init = block(locate(1, 1, 1), locate(world.maxx, world.maxy, world.maxz)))
var/list/active_turfs = src.active_turfs
for(var/thing in turfs_to_init)
var/turf/T = thing
active_turfs -= T
if(T.blocks_air)
continue
T.Initialize_Atmos(times_fired)
CHECK_TICK
/turf/simulated/proc/resolve_active_graph()
. = list()
var/datum/excited_group/EG = excited_group
if(blocks_air || !air)
return
if(!EG)
EG = new
EG.add_turf(src)
for(var/turf/simulated/ET in atmos_adjacent_turfs)
if(ET.blocks_air || !ET.air)
continue
var/ET_EG = ET.excited_group
if(ET_EG)
if(ET_EG != EG)
EG.merge_groups(ET_EG)
EG = excited_group //merge_groups() may decide to replace our current EG
else
EG.add_turf(ET)
if(!ET.excited)
ET.excited = 1
. += ET
/datum/controller/subsystem/air/proc/setup_atmos_machinery()
var/watch = start_watch()
var/count = 0
log_startup_progress("Initializing atmospherics machinery...")
for(var/obj/machinery/atmospherics/unary/U in machines)
if(istype(U, /obj/machinery/atmospherics/unary/vent_pump))
var/obj/machinery/atmospherics/unary/vent_pump/T = U
T.broadcast_status()
count++
else if(istype(U, /obj/machinery/atmospherics/unary/vent_scrubber))
var/obj/machinery/atmospherics/unary/vent_scrubber/T = U
T.broadcast_status()
count++
log_startup_progress(" Initialized [count] atmospherics machines in [stop_watch(watch)]s.")
//this can't be done with setup_atmos_machinery() because
// all atmos machinery has to initalize before the first
// pipenet can be built.
/datum/controller/subsystem/air/proc/setup_pipenets()
var/watch = start_watch()
var/count = 0
log_startup_progress("Initializing pipe networks...")
for(var/obj/machinery/atmospherics/machine in machines)
machine.build_network()
count++
log_startup_progress(" Initialized [count] pipes in [stop_watch(watch)]s.")
/datum/controller/subsystem/air/proc/setup_overlays()
plmaster = new /obj/effect/overlay()
plmaster.icon = 'icons/effects/tile_effects.dmi'
plmaster.icon_state = "plasma"
plmaster.layer = FLY_LAYER
plmaster.mouse_opacity = 0
slmaster = new /obj/effect/overlay()
slmaster.icon = 'icons/effects/tile_effects.dmi'
slmaster.icon_state = "sleeping_agent"
slmaster.layer = FLY_LAYER
slmaster.mouse_opacity = 0
icemaster = new /obj/effect/overlay()
icemaster.icon = 'icons/turf/overlays.dmi'
icemaster.icon_state = "snowfloor"
icemaster.layer = TURF_LAYER + 0.1
icemaster.mouse_opacity = 0
#undef SSAIR_PIPENETS
#undef SSAIR_ATMOSMACHINERY
#undef SSAIR_ACTIVETURFS
#undef SSAIR_EXCITEDGROUPS
#undef SSAIR_HIGHPRESSURE
#undef SSAIR_HOTSPOT
#undef SSAIR_SUPERCONDUCTIVITY
+37
View File
@@ -0,0 +1,37 @@
SUBSYSTEM_DEF(fires)
name = "Fires"
priority = FIRE_PRIOTITY_BURNING
flags = SS_NO_INIT|SS_BACKGROUND
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/list/currentrun = list()
var/list/processing = list()
/datum/controller/subsystem/fires/stat_entry()
..("P:[processing.len]")
/datum/controller/subsystem/fires/fire(resumed = 0)
if(!resumed)
src.currentrun = processing.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/obj/O = currentrun[currentrun.len]
currentrun.len--
if(!O || qdeleted(O))
processing -= O
if(MC_TICK_CHECK)
return
continue
if(O.burn_state == ON_FIRE)
if(O.burn_world_time < world.time)
O.burn()
else
processing -= O
if(MC_TICK_CHECK)
return
+59
View File
@@ -0,0 +1,59 @@
SUBSYSTEM_DEF(spacedrift)
name = "Space Drift"
priority = FIRE_PRIORITY_SPACEDRIFT
wait = 5
flags = SS_NO_INIT|SS_KEEP_TIMING
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/list/currentrun = list()
var/list/processing = list()
/datum/controller/subsystem/spacedrift/stat_entry()
..("P:[processing.len]")
/datum/controller/subsystem/spacedrift/fire(resumed = 0)
if(!resumed)
src.currentrun = processing.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(currentrun.len)
var/atom/movable/AM = currentrun[currentrun.len]
currentrun.len--
if(!AM)
processing -= AM
if (MC_TICK_CHECK)
return
continue
if(AM.inertia_next_move > world.time)
if (MC_TICK_CHECK)
return
continue
if(!AM.loc || AM.loc != AM.inertia_last_loc || AM.Process_Spacemove(0))
AM.inertia_dir = 0
if(!AM.inertia_dir)
AM.inertia_last_loc = null
processing -= AM
if (MC_TICK_CHECK)
return
continue
var/old_dir = AM.dir
var/old_loc = AM.loc
AM.inertia_moving = TRUE
step(AM, AM.inertia_dir)
AM.inertia_moving = FALSE
AM.inertia_next_move = world.time + AM.inertia_move_delay
if(AM.loc == old_loc)
AM.inertia_dir = 0
AM.setDir(old_dir)
AM.inertia_last_loc = AM.loc
if(MC_TICK_CHECK)
return
+148
View File
@@ -0,0 +1,148 @@
#define MAX_THROWING_DIST 512 // 2 z-levels on default width
#define MAX_TICKS_TO_MAKE_UP 3 //how many missed ticks will we attempt to make up for this run.
SUBSYSTEM_DEF(throwing)
name = "Throwing"
priority = FIRE_PRIORITY_THROWING
wait = 1
flags = SS_NO_INIT|SS_KEEP_TIMING|SS_TICKER
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/list/currentrun
var/list/processing = list()
/datum/controller/subsystem/throwing/stat_entry()
..("P:[processing.len]")
/datum/controller/subsystem/throwing/fire(resumed = 0)
if(!resumed)
src.currentrun = processing.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(length(currentrun))
var/atom/movable/AM = currentrun[currentrun.len]
var/datum/thrownthing/TT = currentrun[AM]
currentrun.len--
if(!AM || !TT)
processing -= AM
if(MC_TICK_CHECK)
return
continue
TT.tick()
if(MC_TICK_CHECK)
return
currentrun = null
/datum/thrownthing
var/atom/movable/thrownthing
var/atom/target
var/turf/target_turf
var/init_dir
var/maxrange
var/speed
var/mob/thrower
var/diagonals_first
var/dist_travelled = 0
var/start_time
var/dist_x
var/dist_y
var/dx
var/dy
var/pure_diagonal
var/diagonal_error
var/datum/callback/callback
var/paused = FALSE
var/delayed_time = 0
var/last_move = 0
/datum/thrownthing/proc/tick()
var/atom/movable/AM = thrownthing
if(!isturf(AM.loc) || !AM.throwing)
finalize()
return
if(paused)
delayed_time += world.time - last_move
return
if(dist_travelled && hitcheck()) //to catch sneaky things moving on our tile while we slept
finalize()
return
var/atom/step
last_move = world.time
//calculate how many tiles to move, making up for any missed ticks.
var/tilestomove = CEILING(min(((((world.time + world.tick_lag) - start_time + delayed_time) * speed) - (dist_travelled ? dist_travelled : -1)), speed * MAX_TICKS_TO_MAKE_UP) * (world.tick_lag * SSthrowing.wait), 1)
while(tilestomove-- > 0)
if((dist_travelled >= maxrange || AM.loc == target_turf) && has_gravity(AM, AM.loc))
finalize()
return
if(dist_travelled <= max(dist_x, dist_y)) //if we haven't reached the target yet we home in on it, otherwise we use the initial direction
step = get_step(AM, get_dir(AM, target_turf))
else
step = get_step(AM, init_dir)
if(!pure_diagonal && !diagonals_first) // not a purely diagonal trajectory and we don't want all diagonal moves to be done first
if (diagonal_error >= 0 && max(dist_x, dist_y) - dist_travelled != 1) //we do a step forward unless we're right before the target
step = get_step(AM, dx)
diagonal_error += (diagonal_error < 0) ? dist_x / 2 : -dist_y
if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge
finalize()
return
AM.Move(step, get_dir(AM, step))
if(!AM.throwing) // we hit something during our move
finalize(hit = TRUE)
return
dist_travelled++
if(dist_travelled > MAX_THROWING_DIST)
finalize()
return
/datum/thrownthing/proc/finalize(hit = FALSE, target = null)
set waitfor = 0
SSthrowing.processing -= thrownthing
//done throwing, either because it hit something or it finished moving
thrownthing.throwing = null
if(!hit)
for(var/thing in get_turf(thrownthing)) //looking for our target on the turf we land on.
var/atom/A = thing
if(A == target)
hit = 1
thrownthing.throw_impact(A, src)
break
if(!hit)
thrownthing.throw_impact(get_turf(thrownthing), src) // we haven't hit something yet and we still must, let's hit the ground.
thrownthing.newtonian_move(init_dir)
else
thrownthing.newtonian_move(init_dir)
if(target)
thrownthing.throw_impact(target, src)
if(callback)
callback.Invoke()
/datum/thrownthing/proc/hit_atom(atom/A)
finalize(hit = TRUE, target = A)
/datum/thrownthing/proc/hitcheck()
for(var/thing in get_turf(thrownthing))
var/atom/movable/AM = thing
if(AM == thrownthing)
continue
if(AM.density && !(AM.pass_flags & LETPASSTHROW) && !(AM.flags & ON_BORDER))
finalize(hit = TRUE, target = AM)
return TRUE
+13 -7
View File
@@ -17,7 +17,10 @@
message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.")
return
/client/proc/debug_controller(controller in list("Master","failsafe","Ticker","Air","Jobs","Sun","Radio","Configuration","pAI", "Cameras","Garbage", "Transfer Controller","Event","Alarm","Scheduler","Nano","Vote","Diseases","Fires","Mob","NPC AI","Shuttle","Timer","Weather","Space","Mob Hunt Server"))
/client/proc/debug_controller(controller in list("Master",
"failsafe","Scheduler","StonedMaster","Ticker","Air","Jobs","Sun","Radio","Configuration","pAI",
"Cameras","Garbage", "Transfer Controller","Event","Alarm","Nano","Vote","Fires",
"Mob","NPC AI","Shuttle","Timer","Weather","Space","Mob Hunt Server"))
set category = "Debug"
set name = "Debug Controller"
set desc = "Debug the various periodic loop controllers for the game (be careful!)"
@@ -28,13 +31,19 @@
debug_variables(master_controller)
feedback_add_details("admin_verb","DMC")
if("failsafe")
debug_variables(failsafe)
debug_variables(Failsafe)
feedback_add_details("admin_verb", "dfailsafe")
if("Scheduler")
debug_variables(processScheduler)
feedback_add_details("admin_verb","DprocessScheduler")
if("StonedMaster")
debug_variables(Master)
feedback_add_details("admin_verb","Dsmc")
if("Ticker")
debug_variables(ticker)
feedback_add_details("admin_verb","DTicker")
if("Air")
debug_variables(air_master)
debug_variables(SSair)
feedback_add_details("admin_verb","DAir")
if("Jobs")
debug_variables(job_master)
@@ -63,9 +72,6 @@
if("Garbage")
debug_variables(garbageCollector)
feedback_add_details("admin_verb","DGarbage")
if("Scheduler")
debug_variables(processScheduler)
feedback_add_details("admin_verb","DprocessScheduler")
if("Nano")
debug_variables(nanomanager)
feedback_add_details("admin_verb","DNano")
@@ -73,7 +79,7 @@
debug_variables(vote)
feedback_add_details("admin_verb","DVote")
if("Fires")
debug_variables(fire_master)
debug_variables(SSfires)
feedback_add_details("admin_verb","DFires")
if("Mob")
debug_variables(mob_master)
+5 -8
View File
@@ -20,15 +20,8 @@ Make sure spells that are removed from spell_list are actually removed and delet
Also, you never added distance checking after target is selected. I've went ahead and did that.
*/
/obj/effect/proc_holder/spell/targeted/mind_transfer/cast(list/targets, mob/user = usr, distanceoverride)
if(!targets.len)
to_chat(user, "No mind found.")
return
if(targets.len > 1)
to_chat(user, "Too many minds! You're not a hive damnit!")//Whaa...aat?
return
var/mob/living/target = targets[1]
var/mob/living/target = targets[range]
if(!(target in oview(range)) && !distanceoverride)//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it.
to_chat(user, "They are too far away!")
@@ -50,6 +43,10 @@ Also, you never added distance checking after target is selected. I've went ahea
to_chat(user, "Their mind is resisting your spell.")
return
if(istype(target, /mob/living/silicon))
to_chat(user, "You feel this enslaved being is just as dead as its cold, hard exoskeleton.")
return
var/mob/living/victim = target//The target of the spell whos body will be transferred to.
var/mob/caster = user//The wizard/whomever doing the body transferring.
+16 -19
View File
@@ -5,8 +5,8 @@
var/target
/obj/effect/statclick/New(ntarget, text)
name = text
target = ntarget
name = text
/obj/effect/statclick/proc/update(text)
name = text
@@ -15,25 +15,22 @@
/obj/effect/statclick/debug
var/class
/obj/effect/statclick/debug/New(ntarget)
name = "Initializing..."
target = ntarget
if(istype(target, /datum/controller/process))
class = "process"
else if(istype(target, /datum/controller/processScheduler))
class = "scheduler"
else if(istype(target, /datum/controller))
class = "controller"
else if(istype(target, /datum))
class = "datum"
else
class = "unknown"
// This bit is called when clicked in the stat panel
/obj/effect/statclick/debug/Click()
if(!is_admin(usr))
if(!is_admin(usr) || !target)
return
if(!class)
if(istype(target, /datum/controller/process))
class = "process"
else if(istype(target, /datum/controller/processScheduler))
class = "scheduler"
if(istype(target, /datum/controller/subsystem))
class = "subsystem"
else if(istype(target, /datum/controller))
class = "controller"
else if(istype(target, /datum))
class = "datum"
else
class = "unknown"
usr.client.debug_variables(target)
message_admins("Admin [key_name_admin(usr)] is debugging the [target] [class].")
message_admins("Admin [key_name_admin(usr)] is debugging the [target] [class].")
+7
View File
@@ -963,6 +963,13 @@ var/list/uplink_items = list()
item = /obj/item/weapon/storage/backpack/duffel/syndie/surgery
cost = 4
/datum/uplink_item/device_tools/bonerepair
name = "Prototype Bone Repair Kit"
desc = "Stolen prototype bone repair nanites. Contains one nanocalcium autoinjector and guide."
reference = "NCAI"
item = /obj/item/weapon/storage/box/syndie_kit/bonerepair
cost = 6
/datum/uplink_item/device_tools/military_belt
name = "Military Belt"
desc = "A robust seven-slot red belt made for carrying a broad variety of weapons, ammunition and explosives"
+4 -3
View File
@@ -164,9 +164,10 @@ var/const/AIRLOCK_WIRE_LIGHT = 512
//one wire for door bolts. Sending a pulse through this drops door bolts if they're not down (whether power's on or not),
//raises them if they are down (only if power's on)
if(!A.locked)
A.lock()
else
A.unlock()
if(A.lock())
A.audible_message("<span class='italics'>You hear a click from the bottom of the door.</span>", null, 1)
else if(A.unlock())
A.audible_message("<span class='italics'>You hear a click from the bottom of the door.</span>", null, 1)
if(AIRLOCK_WIRE_BACKUP_POWER1)
//two wires for backup power. Sending a pulse through either one causes a breaker to trip, but this does not disable it unless main power is down too (in which case it is disabled for 1 minute or however long it takes main power to come back, whichever is shorter).
+1 -1
View File
@@ -87,7 +87,7 @@
if(!D.welded)
D.activate_alarm()
if(D.operating)
D.nextstate = CLOSED
D.nextstate = FD_CLOSED
else if(!D.density)
spawn(0)
D.close()
+2 -3
View File
@@ -153,9 +153,8 @@
/atom/proc/emp_act(var/severity)
return
/atom/proc/bullet_act(var/obj/item/projectile/Proj, def_zone)
Proj.on_hit(src, 0, def_zone)
return 0
/atom/proc/bullet_act(obj/item/projectile/P, def_zone)
. = P.on_hit(src, 0, def_zone)
/atom/proc/in_contents_of(container)//can take class or object instance as argument
if(ispath(container))
+4 -3
View File
@@ -217,14 +217,15 @@
return 1
inertia_last_loc = loc
drift_master.processing_list[src] = src
SSspacedrift.processing[src] = src
return 1
//called when src is thrown into hit_atom
/atom/movable/proc/throw_impact(atom/hit_atom, throwingdatum)
set waitfor = 0
return hit_atom.hitby(src)
if(exists(hit_atom))
return hit_atom.hitby(src)
/atom/movable/hitby(atom/movable/AM, skipcatch, hitpush = 1, blocked)
if(!anchored && hitpush)
@@ -301,7 +302,7 @@
if(spin && !no_spin && !no_spin_thrown)
SpinAnimation(5, 1)
throw_master.processing_list[src] = TT
SSthrowing.processing[src] = TT
TT.tick()
+2
View File
@@ -128,10 +128,12 @@
/datum/dna/gene/disability/colourblindness/activate(var/mob/M, var/connected, var/flags)
..()
M.update_client_colour() //Handle the activation of the colourblindness on the mob.
M.update_icons() //Apply eyeshine as needed.
/datum/dna/gene/disability/colourblindness/deactivate(var/mob/M, var/connected, var/flags)
..()
M.update_client_colour() //Handle the deactivation of the colourblindness on the mob.
M.update_icons() //Remove eyeshine as needed.
/datum/dna/gene/disability/deaf
name="Deafness"
+2
View File
@@ -164,10 +164,12 @@
/datum/dna/gene/basic/xray/activate(mob/living/M, connected, flags)
..()
M.update_sight()
M.update_icons() //Apply eyeshine as needed.
/datum/dna/gene/basic/xray/deactivate(mob/living/M, connected, flags)
..()
M.update_sight()
M.update_icons() //Remove eyeshine as needed.
/datum/dna/gene/basic/tk
name="Telekenesis"
+4 -1
View File
@@ -34,6 +34,8 @@
maxHealth = 40
melee_damage_lower = 2
melee_damage_upper = 4
obj_damage = 20
environment_smash = ENVIRONMENT_SMASH_STRUCTURES
attacktext = "hits"
attack_sound = 'sound/weapons/genhit1.ogg'
speak_emote = list("pulses")
@@ -158,6 +160,7 @@
maxHealth = 240
melee_damage_lower = 20
melee_damage_upper = 20
obj_damage = 60
attacktext = "hits"
attack_sound = 'sound/effects/blobattack.ogg'
speak_emote = list("gurgles")
@@ -165,7 +168,7 @@
maxbodytemp = 360
force_threshold = 10
mob_size = MOB_SIZE_LARGE
environment_smash = 3
environment_smash = ENVIRONMENT_SMASH_RWALLS
gold_core_spawnable = CHEM_MOB_SPAWN_HOSTILE
pressure_resistance = 100 //100 kPa difference required to push
throw_pressure_limit = 120 //120 kPa difference required to throw
+41 -3
View File
@@ -121,10 +121,10 @@
armor = list(melee = 60, bullet = 50, laser = 30, energy = 15, bomb = 30, bio = 30, rad = 30)
/obj/item/clothing/suit/space/cult
name = "cult armour"
name = "cult armor"
icon_state = "cult_armour"
item_state = "cult_armour"
desc = "A bulky suit of armour, bristling with spikes. It looks space proof."
desc = "A bulky suit of armor, bristling with spikes. It looks space proof."
w_class = WEIGHT_CLASS_NORMAL
allowed = list(/obj/item/weapon/tome,/obj/item/weapon/melee/cultblade,/obj/item/weapon/tank)
slowdown = 1
@@ -343,7 +343,6 @@
else
to_chat(C, "<span class='danger'>The veil cannot be torn here!</span>")
/obj/item/clothing/suit/space/eva/plasmaman/cultist
name = "plasmaman cultist armor"
icon_state = "plasmaman_cult"
@@ -358,3 +357,42 @@
base_state = "plasmamanCult_helmet"
desc = "A helmet designed by cultists. It glows menacingly with unearthly flames."
armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30)
/obj/item/weapon/melee/cultblade/ghost
name = "eldritch sword"
force = 15
flags = NODROP
/obj/item/weapon/melee/cultblade/ghost/dropped(mob/living/carbon/human/user)
..()
qdel(src)
/obj/item/clothing/head/culthood/alt/ghost
flags = NODROP
/obj/item/clothing/head/culthood/alt/ghost/dropped(mob/living/carbon/human/user)
..()
qdel(src)
/obj/item/clothing/suit/cultrobes/alt/ghost
flags = NODROP
/obj/item/clothing/suit/cultrobes/alt/ghost/dropped(mob/living/carbon/human/user)
..()
qdel(src)
/obj/item/clothing/shoes/cult/ghost
flags = NODROP
/obj/item/clothing/shoes/cult/ghost/dropped(mob/living/carbon/human/user)
..()
qdel(src)
/datum/outfit/ghost_cultist
name = "Cultist Ghost"
uniform = /obj/item/clothing/under/color/black
suit = /obj/item/clothing/suit/cultrobes/alt/ghost
shoes = /obj/item/clothing/shoes/cult/ghost
head = /obj/item/clothing/head/culthood/alt/ghost
r_hand = /obj/item/weapon/melee/cultblade/ghost
+22 -18
View File
@@ -21,10 +21,10 @@
if("convert")
explanation = "We must increase our influence before we can summon [ticker.mode.cultdat.entity_name], Convert [convert_target] crew members. Take it slowly to avoid raising suspicions."
if("bloodspill")
spilltarget = 70 + rand(0,player_list.len * 3)
spilltarget = 100 + rand(0,player_list.len * 3)
explanation = "We must prepare this place for [ticker.mode.cultdat.entity_title1]'s coming. Spill blood and gibs over [spilltarget] floor tiles."
if("sacrifice")
explanation = "We need to sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role], for his blood is the key that will lead our master to this realm. You will need 3 cultists around a Sacrifice rune to perform the ritual."
explanation = "We need to sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role], for their blood is the key that will lead our master to this realm. You will need 3 cultists around a Sacrifice rune to perform the ritual."
for(var/datum/mind/cult_mind in cult)
to_chat(cult_mind.current, "<B>Objective #[current_objective]</B>: [explanation]")
@@ -47,7 +47,7 @@
message_admins("Picking a new Cult objective.")
var/new_objective = "eldergod"
//the idea here is that if the cult performs well, the should get more objectives before they can summon Nar-Sie.
if(cult.len >= 4)//if there are less than 4 remaining cultists, they get a free pass to the summon objective.
if(cult.len >= 4) //if there are less than 4 remaining cultists, they get a free pass to the summon objective.
if(current_objective <= prenarsie_objectives)
var/list/unconvertables = get_unconvertables()
if(unconvertables.len <= (cult.len * 2))//if cultists are getting radically outnumbered, they get a free pass to the summon objective.
@@ -81,12 +81,13 @@
spilltarget = 100 + rand(0,player_list.len * 3)
explanation = "We must prepare this place for [ticker.mode.cultdat.entity_title1]'s coming. Spread blood and gibs over [spilltarget] of the Station's floor tiles."
if("sacrifice")
explanation = "We need to sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role], for his blood is the key that will lead our master to this realm. You will need 3 cultists around a Sacrifice rune to perform the ritual."
explanation = "We need to sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role], for their blood is the key that will lead our master to this realm. You will need 3 cultists around a Sacrifice rune to perform the ritual."
for(var/datum/mind/cult_mind in cult)
to_chat(cult_mind.current, "<span class='cult'>You and your acolytes have completed your task, but this place requires yet more preparation!</span>")
to_chat(cult_mind.current, "<B>Objective #[current_objective]</B>: [explanation]")
cult_mind.memory += "<B>Objective #[current_objective]</B>: [explanation]<BR>"
if(cult_mind)
to_chat(cult_mind.current, "<span class='cult'>You and your acolytes have completed your task, but this place requires yet more preparation!</span>")
to_chat(cult_mind.current, "<B>Objective #[current_objective]</B>: [explanation]")
cult_mind.memory += "<B>Objective #[current_objective]</B>: [explanation]<BR>"
message_admins("New Cult Objective: [new_objective]")
log_admin("New Cult Objective: [new_objective]")
@@ -95,12 +96,13 @@
/datum/game_mode/cult/proc/gtfo_phase()//YOU HAD ONE JOB
var/explanation
objectives +="survive"
objectives += "survive"
explanation = "Our knowledge must live on. Make sure at least [acolytes_needed] acolytes escape on the shuttle to spread their work on an another station."
for(var/datum/mind/cult_mind in cult)
to_chat(cult_mind.current, "<span class='cult'>You and your acolytes suddenly feel the urge to do your best, but survive!</span>")
to_chat(cult_mind.current, "<B>Objective Survive</B>: [explanation]")
cult_mind.memory += "<B>Objective Survive</B>: [explanation]<BR>"
if(cult_mind)
to_chat(cult_mind.current, "<span class='cult'>You and your acolytes suddenly feel the urge to do your best, but survive!</span>")
to_chat(cult_mind.current, "<B>Objective Survive</B>: [explanation]")
cult_mind.memory += "<B>Objective Survive</B>: [explanation]<BR>"
/datum/game_mode/cult/proc/second_phase()
@@ -115,9 +117,10 @@
explanation = "Bring the Slaughter via the rune 'Bring forth the slaughter'. The veil is weak enough in [english_list(summon_spots)] for the ritual to begin."
for(var/datum/mind/cult_mind in cult)
to_chat(cult_mind.current, "<span class='cult'>You and your acolytes have succeeded in preparing the station for the ultimate ritual!</span>")
to_chat(cult_mind.current, "<B>Objective #[current_objective]</B>: [explanation]")
cult_mind.memory += "<B>Objective #[current_objective]</B>: [explanation]<BR>"
if(cult_mind)
to_chat(cult_mind.current, "<span class='cult'>You and your acolytes have succeeded in preparing the station for the ultimate ritual!</span>")
to_chat(cult_mind.current, "<B>Objective #[current_objective]</B>: [explanation]")
cult_mind.memory += "<B>Objective #[current_objective]</B>: [explanation]<BR>"
/datum/game_mode/cult/proc/third_phase()
current_objective++
@@ -132,15 +135,16 @@
switch(last_objective)
if("harvest")
explanation = "[ticker.mode.cultdat.entity_title1] hungers for his first meal of this never-ending day. Offer him [harvest_target] humans in sacrifice."
explanation = "[ticker.mode.cultdat.entity_title1] hungers for their first meal of this never-ending day. Offer them [harvest_target] humans in sacrifice."
if("hijack")
explanation = "[ticker.mode.cultdat.entity_name] wishes for his troops to start the assault on Centcom immediately. Hijack the escape shuttle and don't let a single non-cultist board it."
explanation = "[ticker.mode.cultdat.entity_name] wishes for their troops to start the assault on Centcom immediately. Hijack the escape shuttle and don't let a single non-cultist board it."
if("massacre")
explanation = "[ticker.mode.cultdat.entity_name] wants to watch you as you massacre the remaining humans on the station (until less than [massacre_target] humans are left alive)."
for(var/datum/mind/cult_mind in cult)
to_chat(cult_mind.current, "<B>Objective #[current_objective]</B>: [explanation]")
cult_mind.memory += "<B>Objective #[current_objective]</B>: [explanation]<BR>"
if(cult_mind)
to_chat(cult_mind.current, "<B>Objective #[current_objective]</B>: [explanation]")
cult_mind.memory += "<B>Objective #[current_objective]</B>: [explanation]<BR>"
message_admins("Last Cult Objective: [last_objective]")
log_admin("Last Cult Objective: [last_objective]")
+2 -1
View File
@@ -109,6 +109,7 @@
target cultists!). If this creature has a mind, a soulstone will be created and the creature's soul transported to it. Sacrificing the dead can be done alone, but sacrificing living crew <b>or your cult's target</b> will require 3 cultists. \
Soulstones used on construct shells will move that soul into a powerful construct of your choice.<br><br>"
text += "<font color='red'><b>Rite of Resurrection</b></font><br>This rune requires two corpses. To perform the ritual, place the corpse you wish to revive onto \
the rune and the offering body adjacent to it. When the rune is invoked, the body to be sacrificed will turn to dust, the life force flowing into the revival target. Assuming the target is not moved \
within a few seconds, they will be brought back to life, healed of all ailments.<br><br>"
@@ -293,7 +294,7 @@
var/mob/living/carbon/human/H = user
var/dam_zone = pick("head", "chest", "groin", "l_arm", "l_hand", "r_arm", "r_hand", "l_leg", "l_foot", "r_leg", "r_foot")
var/obj/item/organ/external/affecting = H.get_organ(ran_zone(dam_zone))
user.visible_message("<span class='warning'>[user] cuts open their \The [affecting] and begins writing in their own blood!</span>", "<span class='cult'>You slice open your [affecting] and begin drawing a sigil of [ticker.mode.cultdat.entity_title3].</span>")
user.visible_message("<span class='warning'>[user] cuts open their [affecting] and begins writing in their own blood!</span>", "<span class='cult'>You slice open your [affecting] and begin drawing a sigil of [ticker.mode.cultdat.entity_title3].</span>")
user.apply_damage(initial(rune_to_scribe.scribe_damage), BRUTE , affecting)
if(!do_after(user, initial(rune_to_scribe.scribe_delay)-scribereduct, target = get_turf(user)))
for(var/V in shields)
+61 -15
View File
@@ -36,6 +36,8 @@ To draw a rune, use an arcane tome.
var/req_keyword = 0 //If the rune requires a keyword - go figure amirite
var/keyword //The actual keyword for the rune
var/invoke_damage = 0 //how much damage invokers take when invoking it
/obj/effect/rune/New(loc, set_keyword)
..()
@@ -118,6 +120,8 @@ structure_check() searches for nearby cultist structures required for the invoca
//This proc determines if the rune can be invoked at the time. If there are multiple required cultists, it will find all nearby cultists.
var/list/invokers = list() //people eligible to invoke the rune
var/list/chanters = list() //people who will actually chant the rune when passed to invoke()
if(invisibility == INVISIBILITY_OBSERVER)//hidden rune
return
if(user)
chanters |= user
invokers |= user
@@ -146,11 +150,14 @@ structure_check() searches for nearby cultist structures required for the invoca
/obj/effect/rune/proc/invoke(var/list/invokers)
//This proc contains the effects of the rune as well as things that happen afterwards. If you want it to spawn an object and then delete itself, have both here.
if(invocation)
for(var/M in invokers)
var/mob/living/L = M
for(var/M in invokers)
var/mob/living/L = M
if(invocation)
L.say(invocation)
L.changeNext_move(CLICK_CD_MELEE)//THIS IS WHY WE CAN'T HAVE NICE THINGS
if(invoke_damage)
L.apply_damage(invoke_damage, BRUTE)
to_chat(L, "<span class='cultitalic'>[src] saps your strength!</span>")
do_invoke_glow()
/obj/effect/rune/proc/burn_invokers(var/list/mobstoburn)
@@ -181,13 +188,13 @@ structure_check() searches for nearby cultist structures required for the invoca
cultist_name = "malformed rune"
cultist_desc = "a senseless rune written in gibberish. No good can come from invoking this."
invocation = "Ra'sha yoka!"
invoke_damage = 30
/obj/effect/rune/malformed/invoke(var/list/invokers)
..()
for(var/M in invokers)
var/mob/living/L = M
to_chat(M, "<span class='cultitalic'><b>You feel your life force draining. [ticker.mode.cultdat.entity_title3] is displeased.</b></span>")
L.apply_damage(30, BRUTE)
to_chat(L, "<span class='cultitalic'><b>You feel your life force draining. [ticker.mode.cultdat.entity_title3] is displeased.</b></span>")
qdel(src)
/mob/proc/null_rod_check() //The null rod, if equipped, will protect the holder from the effects of most runes
@@ -252,6 +259,7 @@ var/list/teleport_runes = list()
icon_state = "2"
req_keyword = 1
var/listkey
invoke_damage = 6 //but theres some checks for z-level
/obj/effect/rune/teleport/New(loc, set_keyword)
..()
@@ -316,6 +324,12 @@ var/list/teleport_runes = list()
to_chat(user, "<span class='cult'>You[moveuserlater ? "r vision blurs, and you suddenly appear somewhere else":" send everything above the rune away"].</span>")
if(moveuserlater)
user.forceMove(get_turf(actual_selected_rune))
var/mob/living/carbon/human/H = user
if(user.z != T.z)
H.bleed(5)
user.apply_damage(5, BRUTE)
else
H.bleed(rand(5,10))
else
fail_invoke()
@@ -458,6 +472,8 @@ var/list/teleport_runes = list()
T.gib()
rune_in_use = 0
//Ritual of Dimensional Rending: Calls forth the avatar of Nar-Sie upon the station.
/obj/effect/rune/narsie
cultist_name = "Tear Reality (God)"
@@ -806,6 +822,7 @@ var/list/teleport_runes = list()
cultist_desc = "when invoked, makes an invisible wall to block passage. Can be invoked again to reverse this."
invocation = "Khari'd! Eske'te tannin!"
icon_state = "1"
invoke_damage = 2
/obj/effect/rune/wall/examine(mob/user)
..()
@@ -831,6 +848,8 @@ var/list/teleport_runes = list()
req_cultists = 2
allow_excess_invokers = 1
icon_state = "5"
invoke_damage = 5
var/summontime = 0
/obj/effect/rune/summon/invoke(var/list/invokers)
var/mob/living/user = invokers[1]
@@ -856,13 +875,24 @@ var/list/teleport_runes = list()
fail_invoke()
log_game("Summon Cultist rune failed - target in away mission")
return
cultist_to_summon.visible_message("<span class='warning'>[cultist_to_summon] suddenly disappears in a flash of red light!</span>", \
"<span class='cultitalic'><b>Overwhelming vertigo consumes you as you are hurled through the air!</b></span>")
if((cultist_to_summon.reagents.has_reagent("holywater") || cultist_to_summon.restrained()) && invokers < 3)
to_chat(user, "<span class='cultitalic'>The summoning of [cultist_to_summon] is being blocked somehow! You need 3 chanters to counter it!</span>")
fail_invoke()
new /obj/effect/temp_visual/cult/sparks(get_turf(cultist_to_summon)) //observer warning
log_game("Summon Cultist rune failed - holywater in target")
return
..()
visible_message("<span class='warning'>A foggy shape materializes atop [src] and solidifes into [cultist_to_summon]!</span>")
user.apply_damage(10, BRUTE, "head")
cultist_to_summon.forceMove(get_turf(src))
qdel(src)
if(cultist_to_summon.reagents.has_reagent("holywater") || cultist_to_summon.restrained())
summontime = 20
if(do_after(user, summontime, target = loc))
cultist_to_summon.visible_message("<span class='warning'>[cultist_to_summon] suddenly disappears in a flash of red light!</span>", \
"<span class='cultitalic'><b>Overwhelming vertigo consumes you as you are hurled through the air!</b></span>")
visible_message("<span class='warning'>A foggy shape materializes atop [src] and solidifies into [cultist_to_summon]!</span>")
cultist_to_summon.forceMove(get_turf(src))
qdel(src)
//Rite of Boiling Blood: Deals extremely high amounts of damage to non-cultists nearby
/obj/effect/rune/blood_boil
@@ -872,6 +902,7 @@ var/list/teleport_runes = list()
icon_state = "4"
construct_invoke = 0
req_cultists = 3
invoke_damage = 15
/obj/effect/rune/blood_boil/do_invoke_glow()
return
@@ -889,10 +920,6 @@ var/list/teleport_runes = list()
to_chat(C, "<span class='cultlarge'>Your blood boils in your veins!</span>")
C.take_overall_damage(45,45)
C.Stun(7)
for(var/M in invokers)
var/mob/living/L = M
L.apply_damage(15, BRUTE, pick("l_arm", "r_arm"))
to_chat(L,"<span class='cultitalic'>[src] saps your strength!</span>")
qdel(src)
explosion(T, -1, 0, 1, 5)
@@ -942,6 +969,9 @@ var/list/teleport_runes = list()
construct_invoke = 0
color = rgb(200, 0, 0)
var/list/summoned_guys = list()
var/ghost_limit = 5
var/ghosts = 0
invoke_damage = 10
/obj/effect/rune/manifest/New(loc)
..()
@@ -950,11 +980,21 @@ var/list/teleport_runes = list()
notify_ghosts("Manifest rune created in [get_area(src)].", 'sound/effects/ghost2.ogg', source = src)
/obj/effect/rune/manifest/can_invoke(mob/living/user)
if(ghosts >= ghost_limit)
to_chat(user, "<span class='cultitalic'>You are sustaining too many ghosts to summon more!</span>")
fail_invoke()
log_game("Manifest rune failed - too many summoned ghosts")
return list()
if(!(user in get_turf(src)))
to_chat(user,"<span class='cultitalic'>You must be standing on [src]!</span>")
fail_invoke()
log_game("Manifest rune failed - user not standing on rune")
return list()
if(user.has_status_effect(STATUS_EFFECT_SUMMONEDGHOST))
to_chat(user, "<span class='cult italic'>Ghosts can't summon more ghosts!</span>")
fail_invoke()
log_game("Manifest rune failed - user is a ghost")
return list()
var/list/ghosts_on_rune = list()
for(var/mob/dead/observer/O in get_turf(src))
if(O.client && !jobban_isbanned(O, ROLE_CULTIST) && !jobban_isbanned(O, ROLE_SYNDICATE))
@@ -976,8 +1016,12 @@ var/list/teleport_runes = list()
var/mob/living/carbon/human/new_human = new(get_turf(src))
new_human.real_name = ghost_to_spawn.real_name
new_human.alpha = 150 //Makes them translucent
new_human.equipOutfit(/datum/outfit/ghost_cultist) //give them armor
new_human.apply_status_effect(STATUS_EFFECT_SUMMONEDGHOST) //ghosts can't summon more ghosts
new_human.color = "grey" //heh..cult greytide...litterly...
..()
playsound(src, 'sound/misc/exit_blood.ogg', 50, 1)
visible_message("<span class='warning'>A cloud of red mist forms above [src], and from within steps... a man.</span>")
to_chat(user, "<span class='cultitalic'>Your blood begins flowing into [src]. You must remain in place and conscious to maintain the forms of those summoned. This will hurt you slowly but surely...</span>")
var/obj/machinery/shield/N = new(get_turf(src))
@@ -989,6 +1033,7 @@ var/list/teleport_runes = list()
new_human.key = ghost_to_spawn.key
ticker.mode.add_cultist(new_human.mind, 0)
summoned_guys |= new_human
ghosts++
to_chat(new_human, "<span class='cultitalic'><b>You are a servant of [ticker.mode.cultdat.entity_title3]. You have been made semi-corporeal by the cult of [ticker.mode.cultdat.entity_name], and you are to serve them at all costs.</b></span>")
while(user in get_turf(src))
@@ -1004,6 +1049,7 @@ var/list/teleport_runes = list()
for(var/obj/I in new_human)
new_human.unEquip(I)
summoned_guys -= new_human
ghosts--
new_human.dust()
/obj/effect/rune/manifest/Destroy()
+6
View File
@@ -55,6 +55,7 @@ var/round_start_time = 0
if(pregame_timeleft <= 0)
current_state = GAME_STATE_SETTING_UP
Master.SetRunLevel(RUNLEVEL_SETUP)
while(!setup())
/datum/controller/gameticker/proc/votetimer()
@@ -77,6 +78,7 @@ var/round_start_time = 0
runnable_modes = config.get_runnable_modes()
if(runnable_modes.len==0)
current_state = GAME_STATE_PREGAME
Master.SetRunLevel(RUNLEVEL_LOBBY)
to_chat(world, "<B>Unable to choose playable game mode.</B> Reverting to pre-game lobby.")
return 0
if(secret_force_mode != "secret")
@@ -96,6 +98,7 @@ var/round_start_time = 0
mode = null
current_state = GAME_STATE_PREGAME
job_master.ResetOccupations()
Master.SetRunLevel(RUNLEVEL_LOBBY)
return 0
//Configure mode and assign player to special mode stuff
@@ -108,6 +111,7 @@ var/round_start_time = 0
current_state = GAME_STATE_PREGAME
to_chat(world, "<B>Error setting up [master_mode].</B> Reverting to pre-game lobby.")
job_master.ResetOccupations()
Master.SetRunLevel(RUNLEVEL_LOBBY)
return 0
if(hide_mode)
@@ -125,6 +129,7 @@ var/round_start_time = 0
equip_characters()
data_core.manifest()
current_state = GAME_STATE_PLAYING
Master.SetRunLevel(RUNLEVEL_GAME)
callHook("roundstart")
@@ -385,6 +390,7 @@ var/round_start_time = 0
if((!mode.explosion_in_progress && game_finished) || force_ending)
current_state = GAME_STATE_FINISHED
Master.SetRunLevel(RUNLEVEL_POSTGAME)
auto_toggle_ooc(1) // Turn it on
spawn
declare_completion()
@@ -90,6 +90,7 @@
melee_damage_upper = 15
melee_damage_type = STAMINA
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
obj_damage = 0
environment_smash = 0
attacktext = "shocks"
attack_sound = 'sound/effects/EMPulse.ogg'
@@ -22,6 +22,7 @@
maxHealth = INFINITY //The spirit itself is invincible
health = INFINITY
environment_smash = 0
obj_damage = 40
melee_damage_lower = 15
melee_damage_upper = 15
AIStatus = AI_OFF
@@ -342,8 +343,20 @@
"Lilac" = "#C7A0F6", \
"Orchid" = "#F62CF5")
var/bio_list = list("Rose" = "#F62C6B", \
"Peony" = "#E54750", \
"Lily" = "#F6562C", \
"Daisy" = "#ECCD39", \
"Zinnia" = "#89F62C", \
"Ivy" = "#5DF62C", \
"Iris" = "#2CF6B8", \
"Petunia" = "#51A9D4", \
"Violet" = "#8A347C", \
"Lilac" = "#C7A0F6", \
"Orchid" = "#F62CF5")
var/picked_name
var/picked_color = pick("#FFFFFF","#000000","#808080","#A52A2A","#FF0000","#8B0000","#DC143C","#FFA500","#FFFF00","#008000","#00FF00","#006400","#00FFFF","#0000FF","#000080","#008080","#800080","#4B0082")
// var/picked_color = pick("#FFFFFF","#000000","#808080","#A52A2A","#FF0000","#8B0000","#DC143C","#FFA500","#FFFF00","#008000","#00FF00","#006400","#00FFFF","#0000FF","#000080","#008080","#800080","#4B0082")
switch(theme)
if("magic")
@@ -372,15 +385,18 @@
to_chat(user, "[G.tech_fluff_string].")
G.speak_emote = list("states")
if("bio")
G.name_color = picked_color
G.icon = 'icons/mob/mob.dmi'
color = pick(bio_list) //technically not colors, just using the same flowers as tech currerntly
G.name_color = tech_list[color]
picked_name = pick("brood", "hive", "nest")
to_chat(user, "[G.bio_fluff_string].")
G.name = "[picked_name] swarm"
G.color = picked_color
G.real_name = "[picked_name] swarm"
G.icon_living = "headcrab"
G.icon_state = "headcrab"
G.name = "[color] [picked_name]"
G.real_name = "[color] [picked_name]"
G.icon_living = "[theme][color]"
G.icon_state = "[theme][color]"
G.icon_dead = "[theme][color]"
to_chat(user, "[G.bio_fluff_string].")
G.attacktext = "swarms"
G.speak_emote = list("chitters")
@@ -45,6 +45,8 @@
melee_damage_lower = initial(melee_damage_lower)
melee_damage_upper = initial(melee_damage_upper)
armour_penetration = initial(armour_penetration)
obj_damage = initial(obj_damage)
environment_smash = initial(environment_smash)
alpha = initial(alpha)
if(!forced)
to_chat(src, "<span class='danger'>You exit stealth.</span>")
@@ -61,7 +63,9 @@
melee_damage_lower = 50
melee_damage_upper = 50
armour_penetration = 100
new /obj/effect/temp_visual/guardian/phase(get_turf(src))
obj_damage = 0
environment_smash = ENVIRONMENT_SMASH_NONE
new /obj/effect/temp_visual/guardian/phase/out(get_turf(src))
alpha = 15
if(!forced)
to_chat(src, "<span class='danger'>You enter stealth, empowering your next attack.</span>")
@@ -32,6 +32,8 @@
ranged = 1
melee_damage_lower = 10
melee_damage_upper = 10
obj_damage = initial(obj_damage)
environment_smash = initial(environment_smash)
alpha = 255
range = 13
incorporeal_move = 0
@@ -41,6 +43,8 @@
ranged = 0
melee_damage_lower = 0
melee_damage_upper = 0
obj_damage = 0
environment_smash = ENVIRONMENT_SMASH_NONE
alpha = 60
range = 255
incorporeal_move = 1
@@ -1,6 +1,7 @@
/mob/living/simple_animal/hostile/guardian/punch
melee_damage_lower = 20
melee_damage_upper = 20
obj_damage = 80
damage_transfer = 0.4
playstyle_string = "As a <b>Standard</b> type you have no special abilities, but have a high damage resistance and a powerful attack capable of smashing through walls."
environment_smash = 2
@@ -23,6 +23,7 @@
maxHealth = 150
health = 150
environment_smash = 1
obj_damage = 50
melee_damage_lower = 20
melee_damage_upper = 20
see_in_dark = 8
@@ -29,6 +29,7 @@
health = 200
environment_smash = 1
//universal_understand = 1
obj_damage = 50
melee_damage_lower = 30
melee_damage_upper = 30
see_in_dark = 8
@@ -114,7 +115,7 @@
health = 500
melee_damage_upper = 60
melee_damage_lower = 60
environment_smash = 3 //Smashes through EVERYTHING - r-walls included
environment_smash = ENVIRONMENT_SMASH_RWALLS //Smashes through EVERYTHING - r-walls included
faction = list("cult")
playstyle_string = "<b><span class='userdanger'>You are a Harbringer of the Slaughter.</span> Brought forth by the servants of Nar-Sie, you have a single purpose: slaughter the heretics \
who do not worship your master. You may use the ability 'Blood Crawl' near a pool of blood to enter it and become incorporeal. Using the ability again near a blood pool will allow you \
@@ -400,6 +400,7 @@ var/bomb_set
name = "nuclear authentication disk"
desc = "Better keep this safe."
icon_state = "nucleardisk"
armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 30, bio = 0, rad = 0)
/obj/item/weapon/disk/nuclear/New()
..()
@@ -28,7 +28,7 @@
minbodytemp = 0
maxbodytemp = INFINITY
environment_smash = 3
environment_smash = ENVIRONMENT_SMASH_RWALLS
faction = list("faithless")
+2 -1
View File
@@ -8,6 +8,7 @@
use_power = 1
current_heat_capacity = 1000
layer = 3
armor = list(melee = 0, bullet = 0, laser = 0, energy = 100, bomb = 0, bio = 100, rad = 100)
/obj/machinery/atmospherics/unary/cold_sink/freezer/New()
..()
@@ -159,8 +160,8 @@
var/max_temperature = 0
anchored = 1.0
layer = 3
current_heat_capacity = 1000
armor = list(melee = 0, bullet = 0, laser = 0, energy = 100, bomb = 0, bio = 100, rad = 100)
/obj/machinery/atmospherics/unary/heat_reservoir/heater/New()
..()
+1
View File
@@ -4,6 +4,7 @@
icon_state = "motion3"
layer = 3
anchored = 1.0
armor = list(melee = 50, bullet = 20, laser = 20, energy = 20, bomb = 0, bio = 0, rad = 0)
var/uses = 20
var/disabled = TRUE
var/lethal = 0
+1
View File
@@ -80,6 +80,7 @@
active_power_usage = 8
power_channel = ENVIRON
req_one_access = list(access_atmospherics, access_engine_equip)
armor = list(melee = 0, bullet = 0, laser = 0, energy = 100, bomb = 0, bio = 100, rad = 100)
var/alarm_id = null
var/frequency = 1439
//var/skipprocess = 0 //Experimenting
+2 -2
View File
@@ -129,11 +129,11 @@ obj/machinery/air_sensor
initialize()
..()
atmos_machinery += src
SSair.atmos_machinery += src
set_frequency(frequency)
Destroy()
atmos_machinery -= src
SSair.atmos_machinery -= src
if(radio_controller)
radio_controller.remove_object(src,frequency)
return ..()
@@ -50,6 +50,7 @@ var/datum/canister_icons/canister_icon_container = new()
density = 1
var/health = 100.0
flags = CONDUCT
armor = list(melee = 50, bullet = 50, laser = 50, energy = 100, bomb = 10, bio = 100, rad = 100)
var/menu = 0
//used by nanoui: 0 = main menu, 1 = relabel
+3 -2
View File
@@ -5,6 +5,7 @@
icon_state = "meterX"
var/obj/machinery/atmospherics/pipe/target = null
anchored = 1
armor = list(melee = 0, bullet = 0, laser = 0, energy = 100, bomb = 0, bio = 100, rad = 100)
power_channel = ENVIRON
var/frequency = 0
var/id
@@ -18,14 +19,14 @@
/obj/machinery/meter/New()
..()
atmos_machinery += src
SSair.atmos_machinery += src
target = locate(/obj/machinery/atmospherics/pipe) in loc
if(id && !id_tag)//i'm not dealing with further merge conflicts, fuck it
id_tag = id
return 1
/obj/machinery/meter/Destroy()
atmos_machinery -= src
SSair.atmos_machinery -= src
target = null
return ..()
@@ -1,6 +1,7 @@
/obj/machinery/portable_atmospherics
name = "atmoalter"
use_power = 0
armor = list(melee = 0, bullet = 0, laser = 0, energy = 100, bomb = 0, bio = 100, rad = 100)
var/datum/gas_mixture/air_contents = new
var/obj/machinery/atmospherics/unary/portables_connector/connected_port
@@ -13,7 +14,7 @@
/obj/machinery/portable_atmospherics/New()
..()
atmos_machinery += src
SSair.atmos_machinery += src
air_contents.volume = volume
air_contents.temperature = T20C
@@ -36,7 +37,7 @@
update_icon()
/obj/machinery/portable_atmospherics/Destroy()
atmos_machinery -= src
SSair.atmos_machinery -= src
disconnect()
QDEL_NULL(air_contents)
QDEL_NULL(holding)
+2 -2
View File
@@ -11,10 +11,10 @@
/obj/machinery/zvent/New()
..()
atmos_machinery += src
SSair.atmos_machinery += src
/obj/machinery/zvent/Destroy()
atmos_machinery -= src
SSair.atmos_machinery -= src
return ..()
/obj/machinery/zvent/process_atmos()
+1 -1
View File
@@ -7,7 +7,7 @@
idle_power_usage = 5
active_power_usage = 10
layer = 5
armor = list(melee = 50, bullet = 20, laser = 20, energy = 20, bomb = 0, bio = 0, rad = 0)
var/datum/wires/camera/wires = null // Wires datum
var/list/network = list("SS13")
var/c_tag = null
@@ -332,7 +332,7 @@
// Holographic Items!
/turf/simulated/floor/holofloor/
thermal_conductivity = 0
icon_state = "plating"
/turf/simulated/floor/holofloor/grass
name = "Lush Grass"
icon_state = "grass1"
@@ -441,14 +441,14 @@
icon_state = "sword[item_color]"
hitsound = "sound/weapons/blade1.ogg"
w_class = WEIGHT_CLASS_BULKY
playsound(user, 'sound/weapons/saberon.ogg', 50, 1)
playsound(user, 'sound/weapons/saberon.ogg', 20, 1)
to_chat(user, "<span class='notice'>[src] is now active.</span>")
else
force = 3
icon_state = "sword0"
hitsound = "swing_hit"
w_class = WEIGHT_CLASS_SMALL
playsound(user, 'sound/weapons/saberoff.ogg', 50, 1)
playsound(user, 'sound/weapons/saberoff.ogg', 20, 1)
to_chat(user, "<span class='notice'>[src] can now be concealed.</span>")
if(istype(user,/mob/living/carbon/human))
var/mob/living/carbon/human/H = user
-2
View File
@@ -39,8 +39,6 @@
/obj/machinery/computer/message_monitor/attackby(obj/item/weapon/O as obj, mob/living/user as mob, params)
if(stat & (NOPOWER|BROKEN))
return
if(!istype(user))
return
if(isscrewdriver(O) && emag)
+6
View File
@@ -369,6 +369,7 @@
P.name = "paper - 'Security Record'"
printing = 0
/* Removed due to BYOND issue
else if(href_list["print_p"])
if(!printing)
printing = 1
@@ -377,6 +378,7 @@
if(istype(active1, /datum/data/record) && data_core.general.Find(active1))
create_record_photo(active1)
printing = 0
*/
else if(href_list["printlogs"])
if(cell_logs.len && !printing)
@@ -510,6 +512,8 @@
/obj/machinery/computer/secure_data/proc/setTemp(text, list/buttons = list())
temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0)
/* Proc disabled due to BYOND Issue
/obj/machinery/computer/secure_data/proc/create_record_photo(datum/data/record/R)
// basically copy-pasted from the camera code but different enough that it has to be redone
var/icon/photoimage = get_record_photo(R)
@@ -536,6 +540,8 @@
var/obj/item/weapon/photo/PH = new/obj/item/weapon/photo(loc)
PH.construct(P)
*/
/obj/machinery/computer/secure_data/proc/get_record_photo(datum/data/record/R)
// similar to the code to make a photo, but of course the actual rendering is completely different
var/icon/res = icon('icons/effects/96x96.dmi', "")
+1 -1
View File
@@ -6,7 +6,7 @@
anchored = 1.0
layer = 2.8
interact_offline = 1
armor = list(melee = 0, bullet = 0, laser = 0, energy = 100, bomb = 0, bio = 100, rad = 100)
var/on = 0
var/temperature_archived
var/mob/living/carbon/occupant = null
+458 -177
View File
@@ -24,42 +24,59 @@
#define AIRLOCK_OPENING 4
#define AIRLOCK_DENY 5
#define AIRLOCK_EMAG 6
#define AIRLOCK_SECURITY_NONE 0 //Normal airlock //Wires are not secured
#define AIRLOCK_SECURITY_METAL 1 //Medium security airlock //There is a simple metal over wires (use welder)
#define AIRLOCK_SECURITY_PLASTEEL_I_S 2 //Sliced inner plating (use crowbar), jumps to 0
#define AIRLOCK_SECURITY_PLASTEEL_I 3 //Removed outer plating, second layer here (use welder)
#define AIRLOCK_SECURITY_PLASTEEL_O_S 4 //Sliced outer plating (use crowbar)
#define AIRLOCK_SECURITY_PLASTEEL_O 5 //There is first layer of plasteel (use welder)
#define AIRLOCK_SECURITY_PLASTEEL 6 //Max security airlock //Fully secured wires (use wirecutters to remove grille, that is electrified)
#define AIRLOCK_INTEGRITY_N 300 // Normal airlock integrity
#define AIRLOCK_INTEGRITY_MULTIPLIER 1.5 // How much reinforced doors health increases
#define AIRLOCK_DAMAGE_DEFLECTION_N 21 // Normal airlock damage deflection
#define AIRLOCK_DAMAGE_DEFLECTION_R 30 // Reinforced airlock damage deflection
var/list/airlock_overlays = list()
/obj/machinery/door/airlock
name = "airlock"
icon = 'icons/obj/doors/airlocks/station/public.dmi'
icon_state = "closed"
autoclose = 1
max_integrity = 300
integrity_failure = 70
damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_N
autoclose = TRUE
explosion_block = 1
assemblytype = /obj/structure/door_assembly
normalspeed = 1
var/security_level = 0 //How much are wires secured
var/aiControlDisabled = FALSE //If TRUE, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in.
var/hackProof = FALSE // if TRUE, this door can't be hacked by the AI
var/electrified_until = 0 // World time when the door is no longer electrified. -1 if it is permanently electrified until someone fixes it.
var/main_power_lost_until = 0 //World time when main power is restored.
var/backup_power_lost_until = -1 //World time when backup power is restored.
var/electrified_timer = null
var/main_power_timer = null
var/backup_power_timer = null
var/electrified_timer
var/main_power_timer
var/backup_power_timer
var/spawnPowerRestoreRunning = 0
var/locked = 0
var/lights = 1 // bolt lights show by default
var/datum/wires/airlock/wires = null
var/lights = TRUE // bolt lights show by default
var/datum/wires/airlock/wires
var/aiDisabledIdScanner = 0
var/aiHacking = 0
var/obj/machinery/door/airlock/closeOther = null
var/closeOtherId = null
var/obj/machinery/door/airlock/closeOther
var/closeOtherId
var/lockdownbyai = 0
var/justzap = 0
var/safe = 1
var/obj/item/weapon/airlock_electronics/electronics = null
var/obj/item/weapon/airlock_electronics/electronics
var/hasShocked = 0 //Prevents multiple shocks from happening
var/obj/item/weapon/note //Any papers pinned to the airlock
var/previous_airlock = /obj/structure/door_assembly //what airlock assembly mineral plating was applied to
var/airlock_material = null //material of inner filling; if its an airlock with glass, this should be set to "glass"
var/airlock_material //material of inner filling; if its an airlock with glass, this should be set to "glass"
var/overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi'
var/note_overlay_file = 'icons/obj/doors/airlocks/station/overlays.dmi' //Used for papers and photos pinned to the airlock
var/normal_integrity = AIRLOCK_INTEGRITY_N
var/prying_so_hard = FALSE
var/image/old_frame_overlay //keep those in order to prevent unnecessary updating
var/image/old_filling_overlay
@@ -67,6 +84,7 @@ var/list/airlock_overlays = list()
var/image/old_panel_overlay
var/image/old_weld_overlay
var/image/old_sparks_overlay
var/image/old_dam_overlay
var/image/old_note_overlay
var/doorOpen = 'sound/machines/airlock_open.ogg'
@@ -100,14 +118,25 @@ About the new airlock wires panel:
/obj/machinery/door/airlock/initialize()
. = ..()
if(closeOtherId != null)
for(var/obj/machinery/door/airlock/A in airlocks)
if(A.closeOtherId == closeOtherId && A != src)
closeOther = A
break
addtimer(src, "update_other_id", 5)
if(glass)
airlock_material = "glass"
if(security_level > AIRLOCK_SECURITY_METAL)
obj_integrity = normal_integrity * AIRLOCK_INTEGRITY_MULTIPLIER
max_integrity = normal_integrity * AIRLOCK_INTEGRITY_MULTIPLIER
else
obj_integrity = normal_integrity
max_integrity = normal_integrity
if(damage_deflection == AIRLOCK_DAMAGE_DEFLECTION_N && security_level > AIRLOCK_SECURITY_METAL)
damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_R
update_icon()
/obj/machinery/door/airlock/proc/update_other_id()
for(var/obj/machinery/door/airlock/A in airlocks)
if(A.closeOtherId == closeOtherId && A != src)
closeOther = A
break
/obj/machinery/door/airlock/Destroy()
QDEL_NULL(electronics)
QDEL_NULL(wires)
@@ -299,6 +328,7 @@ About the new airlock wires panel:
var/image/lights_overlay
var/image/panel_overlay
var/image/weld_overlay
var/image/damag_overlay
var/image/sparks_overlay
var/image/note_overlay
var/notetype = note_type()
@@ -311,10 +341,17 @@ About the new airlock wires panel:
else
filling_overlay = get_airlock_overlay("fill_closed", icon)
if(panel_open)
panel_overlay = get_airlock_overlay("panel_closed", overlays_file)
if(security_level)
panel_overlay = get_airlock_overlay("panel_closed_protected", overlays_file)
else
panel_overlay = get_airlock_overlay("panel_closed", overlays_file)
if(welded)
weld_overlay = get_airlock_overlay("welded", overlays_file)
if(lights)
if(obj_integrity <integrity_failure)
damag_overlay = get_airlock_overlay("sparks_broken", overlays_file)
else if(obj_integrity < (0.75 * max_integrity))
damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file)
if(lights && arePowerSystemsOn())
if(locked)
lights_overlay = get_airlock_overlay("lights_bolts", overlays_file)
else if(emergency)
@@ -331,7 +368,14 @@ About the new airlock wires panel:
else
filling_overlay = get_airlock_overlay("fill_closed", icon)
if(panel_open)
panel_overlay = get_airlock_overlay("panel_closed", overlays_file)
if(security_level)
panel_overlay = get_airlock_overlay("panel_closed_protected", overlays_file)
else
panel_overlay = get_airlock_overlay("panel_closed", overlays_file)
if(obj_integrity <integrity_failure)
damag_overlay = get_airlock_overlay("sparks_broken", overlays_file)
else if(obj_integrity < (0.75 * max_integrity))
damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file)
if(welded)
weld_overlay = get_airlock_overlay("welded", overlays_file)
lights_overlay = get_airlock_overlay("lights_denied", overlays_file)
@@ -346,7 +390,14 @@ About the new airlock wires panel:
else
filling_overlay = get_airlock_overlay("fill_closed", icon)
if(panel_open)
panel_overlay = get_airlock_overlay("panel_closed", overlays_file)
if(security_level)
panel_overlay = get_airlock_overlay("panel_closed_protected", overlays_file)
else
panel_overlay = get_airlock_overlay("panel_closed", overlays_file)
if(obj_integrity <integrity_failure)
damag_overlay = get_airlock_overlay("sparks_broken", overlays_file)
else if(obj_integrity < (0.75 * max_integrity))
damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file)
if(welded)
weld_overlay = get_airlock_overlay("welded", overlays_file)
if(note)
@@ -361,7 +412,10 @@ About the new airlock wires panel:
if(lights && arePowerSystemsOn())
lights_overlay = get_airlock_overlay("lights_closing", overlays_file)
if(panel_open)
panel_overlay = get_airlock_overlay("panel_closing", overlays_file)
if(security_level)
panel_overlay = get_airlock_overlay("panel_closing_protected", overlays_file)
else
panel_overlay = get_airlock_overlay("panel_closing", overlays_file)
if(note)
note_overlay = get_airlock_overlay("[notetype]_closing", note_overlay_file)
@@ -372,7 +426,12 @@ About the new airlock wires panel:
else
filling_overlay = get_airlock_overlay("fill_open", icon)
if(panel_open)
panel_overlay = get_airlock_overlay("panel_open", overlays_file)
if(security_level)
panel_overlay = get_airlock_overlay("panel_open_protected", overlays_file)
else
panel_overlay = get_airlock_overlay("panel_open", overlays_file)
if(obj_integrity < (0.75 * max_integrity))
damag_overlay = get_airlock_overlay("sparks_open", overlays_file)
if(note)
note_overlay = get_airlock_overlay("[notetype]_open", note_overlay_file)
@@ -385,7 +444,10 @@ About the new airlock wires panel:
if(lights && arePowerSystemsOn())
lights_overlay = get_airlock_overlay("lights_opening", overlays_file)
if(panel_open)
panel_overlay = get_airlock_overlay("panel_opening", overlays_file)
if(security_level)
panel_overlay = get_airlock_overlay("panel_opening_protected", overlays_file)
else
panel_overlay = get_airlock_overlay("panel_opening", overlays_file)
if(note)
note_overlay = get_airlock_overlay("[notetype]_opening", note_overlay_file)
@@ -414,6 +476,10 @@ About the new airlock wires panel:
overlays -= old_sparks_overlay
overlays += sparks_overlay
old_sparks_overlay = sparks_overlay
if(damag_overlay != old_dam_overlay)
overlays -= old_dam_overlay
overlays += damag_overlay
old_dam_overlay = damag_overlay
if(note_overlay != old_note_overlay)
overlays -= old_note_overlay
overlays += note_overlay
@@ -438,7 +504,6 @@ About the new airlock wires panel:
playsound(src,doorDeni,50,0,3)
sleep(6)
update_icon(AIRLOCK_CLOSED)
icon_state = "closed"
/obj/machinery/door/airlock/examine(mob/user)
..()
@@ -451,6 +516,28 @@ About the new airlock wires panel:
to_chat(user, "There's a [note.name] pinned to the front...")
note.examine(user)
if(panel_open)
switch(security_level)
if(AIRLOCK_SECURITY_NONE)
to_chat(user, "Its wires are exposed!")
if(AIRLOCK_SECURITY_METAL)
to_chat(user, "Its wires are hidden behind a welded metal cover.")
if(AIRLOCK_SECURITY_PLASTEEL_I_S)
to_chat(user, "There is some shredded plasteel inside.")
if(AIRLOCK_SECURITY_PLASTEEL_I)
to_chat(user, "Its wires are behind an inner layer of plasteel.")
if(AIRLOCK_SECURITY_PLASTEEL_O_S)
to_chat(user, "There is some shredded plasteel inside.")
if(AIRLOCK_SECURITY_PLASTEEL_O)
to_chat(user, "There is a welded plasteel cover hiding its wires.")
if(AIRLOCK_SECURITY_PLASTEEL)
to_chat(user, "There is a protective grille over its panel.")
else if(security_level)
if(security_level == AIRLOCK_SECURITY_METAL)
to_chat(user, "It looks a bit stronger.")
else
to_chat(user, "It looks very robust.")
/obj/machinery/door/airlock/attack_ghost(mob/user)
if(panel_open)
wires.Interact(user)
@@ -487,52 +574,51 @@ About the new airlock wires panel:
return data
/obj/machinery/door/airlock/proc/hack(mob/user)
if(aiHacking==0)
aiHacking=1
spawn(20)
//TODO: Make this take a minute
to_chat(user, "Airlock AI control has been blocked. Beginning fault-detection.")
sleep(50)
if(canAIControl())
to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.")
aiHacking=0
return
else if(!canAIHack(user))
to_chat(user, "Connection lost! Unable to hack airlock.")
aiHacking=0
return
to_chat(user, "Fault confirmed: airlock control wire disabled or cut.")
sleep(20)
to_chat(user, "Attempting to hack into airlock. This may take some time.")
sleep(200)
if(canAIControl())
to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.")
aiHacking=0
return
else if(!canAIHack(user))
to_chat(user, "Connection lost! Unable to hack airlock.")
aiHacking=0
return
to_chat(user, "Upload access confirmed. Loading control program into airlock software.")
sleep(170)
if(canAIControl())
to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.")
aiHacking=0
return
else if(!canAIHack(user))
to_chat(user, "Connection lost! Unable to hack airlock.")
aiHacking=0
return
to_chat(user, "Transfer complete. Forcing airlock to execute program.")
sleep(50)
//disable blocked control
aiControlDisabled = 2
to_chat(user, "Receiving control information from airlock.")
sleep(10)
//bring up airlock dialog
aiHacking = 0
if(user)
attack_ai(user)
set waitfor = 0
if(!aiHacking)
aiHacking = TRUE
to_chat(user, "Airlock AI control has been blocked. Beginning fault-detection.")
sleep(50)
if(canAIControl())
to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.")
aiHacking = FALSE
return
else if(!canAIHack(user))
to_chat(user, "Connection lost! Unable to hack airlock.")
aiHacking=0
return
to_chat(user, "Fault confirmed: airlock control wire disabled or cut.")
sleep(20)
to_chat(user, "Attempting to hack into airlock. This may take some time.")
sleep(200)
if(canAIControl())
to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.")
aiHacking = FALSE
return
else if(!canAIHack(user))
to_chat(user, "Connection lost! Unable to hack airlock.")
aiHacking = FALSE
return
to_chat(user, "Upload access confirmed. Loading control program into airlock software.")
sleep(170)
if(canAIControl())
to_chat(user, "Alert cancelled. Airlock control has been restored without our assistance.")
aiHacking = FALSE
return
else if(!canAIHack(user))
to_chat(user, "Connection lost! Unable to hack airlock.")
aiHacking = FALSE
return
to_chat(user, "Transfer complete. Forcing airlock to execute program.")
sleep(50)
//disable blocked control
aiControlDisabled = 2
to_chat(user, "Receiving control information from airlock.")
sleep(10)
//bring up airlock dialog
aiHacking = FALSE
if(user)
attack_ai(user)
/obj/machinery/door/airlock/CanPass(atom/movable/mover, turf/target, height=0)
if(isElectrified() && density && istype(mover, /obj/item))
@@ -565,10 +651,12 @@ About the new airlock wires panel:
return
if(panel_open)
if(security_level)
to_chat(user, "<span class='warning'>Wires are protected!</span>")
return
wires.Interact(user)
else
..(user)
return
..()
/obj/machinery/door/airlock/CanUseTopic(mob/user)
if(!issilicon(user) && !isobserver(user))
@@ -689,26 +777,145 @@ About the new airlock wires panel:
return 1
/obj/machinery/door/airlock/attackby(obj/item/C, mob/user, params)
if(!issilicon(usr))
if(!issilicon(user))
if(isElectrified())
if(shock(user, 75))
return
if(istype(C, /obj/item/device/detective_scanner) || istype(C, /obj/item/taperoll))
return
add_fingerprint(user)
if((iswelder(C) && !operating && density))
var/obj/item/weapon/weldingtool/W = C
if(W.remove_fuel(0,user))
if(!welded)
welded = 1
else
welded = null
update_icon()
return
else
return
else if(isscrewdriver(C))
if(panel_open)
switch(security_level)
if(AIRLOCK_SECURITY_NONE)
if(istype(C, /obj/item/stack/sheet/metal))
var/obj/item/stack/sheet/metal/S = C
if(S.get_amount() < 2)
to_chat(user, "<span class='warning'>You need at least 2 metal sheets to reinforce [src].</span>")
return
to_chat(user, "<span class='notice'>You start reinforcing [src]...</span>")
if(do_after(user, 20, 1, target = src))
if(!panel_open || !S.use(2))
return
user.visible_message("<span class='notice'>[user] reinforce \the [src] with metal.</span>",
"<span class='notice'>You reinforce \the [src] with metal.</span>")
security_level = AIRLOCK_SECURITY_METAL
update_icon()
return
else if(istype(C, /obj/item/stack/sheet/plasteel))
var/obj/item/stack/sheet/plasteel/S = C
if(S.get_amount() < 2)
to_chat(user, "<span class='warning'>You need at least 2 plasteel sheets to reinforce [src].</span>")
return
to_chat(user, "<span class='notice'>You start reinforcing [src]...</span>")
if(do_after(user, 20, 1, target = src))
if(!panel_open || !S.use(2))
return
user.visible_message("<span class='notice'>[user] reinforce \the [src] with plasteel.</span>",
"<span class='notice'>You reinforce \the [src] with plasteel.</span>")
security_level = AIRLOCK_SECURITY_PLASTEEL
modify_max_integrity(normal_integrity * AIRLOCK_INTEGRITY_MULTIPLIER)
damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_R
update_icon()
return
if(AIRLOCK_SECURITY_METAL)
if(iswelder(C))
var/obj/item/weapon/weldingtool/WT = C
if(!WT.remove_fuel(2, user))
return
to_chat(user, "<span class='notice'>You begin cutting the panel's shielding...</span>")
playsound(loc, WT.usesound, 40, 1)
if(do_after(user, 40 * WT.toolspeed, 1, target = src))
if(!panel_open || !WT.isOn())
return
playsound(loc, WT.usesound, 50, 1)
user.visible_message("<span class='notice'>[user] cuts through \the [src]'s shielding.</span>",
"<span class='notice'>You cut through \the [src]'s shielding.</span>",
"<span class='italics'>You hear welding.</span>")
security_level = AIRLOCK_SECURITY_NONE
spawn_atom_to_turf(/obj/item/stack/sheet/metal, user.loc, 2)
update_icon()
return
if(AIRLOCK_SECURITY_PLASTEEL_I_S)
if(iscrowbar(C))
var/obj/item/weapon/crowbar/W = C
to_chat(user, "<span class='notice'>You start removing the inner layer of shielding...</span>")
playsound(src, W.usesound, 100, 1)
if(do_after(user, 40 * W.toolspeed, 1, target = src))
if(!panel_open)
return
if(security_level != AIRLOCK_SECURITY_PLASTEEL_I_S)
return
user.visible_message("<span class='notice'>[user] remove \the [src]'s shielding.</span>",
"<span class='notice'>You remove \the [src]'s inner shielding.</span>")
security_level = AIRLOCK_SECURITY_NONE
modify_max_integrity(normal_integrity)
damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_N
spawn_atom_to_turf(/obj/item/stack/sheet/plasteel, user.loc, 1)
update_icon()
return
if(AIRLOCK_SECURITY_PLASTEEL_I)
if(iswelder(C))
var/obj/item/weapon/weldingtool/WT = C
if(!WT.remove_fuel(2, user))
return
to_chat(user, "<span class='notice'>You begin cutting the inner layer of shielding...</span>")
playsound(loc, WT.usesound, 40, 1)
if(do_after(user, 40 * WT.toolspeed, 1, target = src))
if(!panel_open || !WT.isOn())
return
playsound(loc, WT.usesound, 50, 1)
user.visible_message("<span class='notice'>[user] cuts through \the [src]'s shielding.</span>",
"<span class='notice'>You cut through \the [src]'s shielding.</span>",
"<span class='italics'>You hear welding.</span>")
security_level = AIRLOCK_SECURITY_PLASTEEL_I_S
return
if(AIRLOCK_SECURITY_PLASTEEL_O_S)
if(iscrowbar(C))
var/obj/item/weapon/crowbar/W = C
to_chat(user, "<span class='notice'>You start removing outer layer of shielding...</span>")
playsound(src, W.usesound, 100, 1)
if(do_after(user, 40 * W.toolspeed, 1, target = src))
if(!panel_open)
return
if(security_level != AIRLOCK_SECURITY_PLASTEEL_O_S)
return
user.visible_message("<span class='notice'>[user] remove \the [src]'s shielding.</span>",
"<span class='notice'>You remove \the [src]'s shielding.</span>")
security_level = AIRLOCK_SECURITY_PLASTEEL_I
spawn_atom_to_turf(/obj/item/stack/sheet/plasteel, user.loc, 1)
return
if(AIRLOCK_SECURITY_PLASTEEL_O)
if(iswelder(C))
var/obj/item/weapon/weldingtool/WT = C
if(!WT.remove_fuel(2, user))
return
to_chat(user, "<span class='notice'>You begin cutting the outer layer of shielding...</span>")
playsound(loc, WT.usesound, 40, 1)
if(do_after(user, 40 * WT.toolspeed, 1, target = src))
if(!panel_open || !WT.isOn())
return
playsound(loc, WT.usesound, 50, 1)
user.visible_message("<span class='notice'>[user] cuts through \the [src]'s shielding.</span>",
"<span class='notice'>You cut through \the [src]'s shielding.</span>",
"<span class='italics'>You hear welding.</span>")
security_level = AIRLOCK_SECURITY_PLASTEEL_O_S
return
if(AIRLOCK_SECURITY_PLASTEEL)
if(iswirecutter(C))
var/obj/item/weapon/wirecutters/W = C
if(arePowerSystemsOn() && shock(user, 60)) // Protective grille of wiring is electrified
return
to_chat(user, "<span class='notice'>You start cutting through the outer grille.</span>")
playsound(src, W.usesound, 100, 1)
if(do_after(user, 10 * W.toolspeed, 1, target = src))
if(!panel_open)
return
user.visible_message("<span class='notice'>[user] cut through \the [src]'s outer grille.</span>",
"<span class='notice'>You cut through \the [src]'s outer grille.</span>")
security_level = AIRLOCK_SECURITY_PLASTEEL_O
return
if(isscrewdriver(C))
panel_open = !panel_open
to_chat(user, "<span class='notice'>You [panel_open ? "open":"close"] the maintenance panel of the airlock.</span>")
playsound(loc, C.usesound, 50, 1)
@@ -740,74 +947,105 @@ About the new airlock wires panel:
user.visible_message("<span class='notice'>[user] pins [C] to [src].</span>", "<span class='notice'>You pin [C] to [src].</span>")
note = C
update_icon()
else if(iscrowbar(C) || istype(C, /obj/item/weapon/twohanded/fireaxe))
var/beingcrowbarred = null
if(iscrowbar(C))
beingcrowbarred = 1 //derp, Agouri
else
beingcrowbarred = 0
if(beingcrowbarred && panel_open && (emagged || (density && welded && !operating && !arePowerSystemsOn() && !locked)))
playsound(loc, C.usesound, 100, 1)
user.visible_message("[user] removes the electronics from the airlock assembly.", "You start to remove electronics from the airlock assembly.")
if(do_after(user, 40 * C.toolspeed, target = src))
if(loc)
deconstruct(TRUE, user)
return
if(istype(C, /obj/item/weapon/crowbar/power) && density)
if(isElectrified())
shock(user, 100)//it's like sticking a fork in a power socket
return
if(locked)
to_chat(user, "<span class='warning'>The bolts are down, it won't budge!</span>")
return
if(welded)
to_chat(user, "<span class='warning'>It's welded, it won't budge!</span>")
return
if(arePowerSystemsOn())
var/obj/item/weapon/crowbar/power/P = C
playsound(src, 'sound/machines/airlock_alien_prying.ogg', 100, 1) //is it aliens or just the CE being a dick?
user.visible_message("<span class='notice'>[user] starts forcing [src] with \the [P]...</span>", \
"<span class='notice'>You begin forcing [src] with \the [P]...</span>")
if(do_after(user, P.airlock_open_time, target = src))
user.visible_message("<span class='notice'>[user] forces [src] with \the [P].</span>", \
"<span class='notice'>You force [src] with \the [P].</span>")
open(2)
if(density && !open(2))
to_chat(user, "<span class='warning'>Despite your attempts, the [src] refuses to open.</span>")
return
if(arePowerSystemsOn())
to_chat(user, "<span class='notice'>The airlock's motors resist your efforts to force it.</span>")
else if(locked)
to_chat(user, "<span class='notice'>The airlock's bolts prevent it from being forced.</span>")
else if(!welded && !operating)
if(density)
if(!beingcrowbarred) //being fireaxe'd
var/obj/item/weapon/twohanded/fireaxe/F = C
if(F.wielded)
spawn(0)
open(1)
else
to_chat(user, "<span class='warning'>You need to be wielding \the [C] to do that.</span>")
else
spawn(0)
open(1)
else
if(!beingcrowbarred)
var/obj/item/weapon/twohanded/fireaxe/F = C
if(F.wielded)
spawn(0)
close(1)
else
to_chat(user, "<span class='warning'>You need to be wielding \the [C] to do that.</span>")
else
spawn(0)
close(1)
else
..()
return
return ..()
/obj/machinery/door/airlock/try_to_weld(obj/item/weapon/weldingtool/W, mob/user)
if(!operating && density)
if(user.a_intent != INTENT_HELP)
if(W.remove_fuel(0, user))
user.visible_message("[user] is [welded ? "unwelding":"welding"] the airlock.", \
"<span class='notice'>You begin [welded ? "unwelding":"welding"] the airlock...</span>", \
"<span class='italics'>You hear welding.</span>")
playsound(loc, W.usesound, 40, 1)
if(do_after(user, 40 * W.toolspeed, 1, target = src, extra_checks = CALLBACK(src, .proc/weld_checks, W, user)))
playsound(loc, 'sound/items/welder2.ogg', 50, 1)
welded = !welded
user.visible_message("[user.name] has [welded? "welded shut":"unwelded"] [src].", \
"<span class='notice'>You [welded ? "weld the airlock shut":"unweld the airlock"].</span>")
update_icon()
else
if(obj_integrity < max_integrity)
if(W.remove_fuel(0, user))
user.visible_message("[user] is welding the airlock.", \
"<span class='notice'>You begin repairing the airlock...</span>", \
"<span class='italics'>You hear welding.</span>")
playsound(loc, W.usesound, 40, 1)
if(do_after(user, 40 * W.toolspeed, 1, target = src, extra_checks = CALLBACK(src, .proc/weld_checks, W, user)))
playsound(loc, 'sound/items/welder2.ogg', 50, 1)
obj_integrity = max_integrity
stat &= ~BROKEN
user.visible_message("[user.name] has repaired [src].", \
"<span class='notice'>You finish repairing the airlock.</span>")
update_icon()
else
to_chat(user, "<span class='notice'>The airlock doesn't need repairing.</span>")
/obj/machinery/door/airlock/proc/weld_checks(obj/item/weapon/weldingtool/W, mob/user)
return !operating && density && user && W && W.isOn() && user.loc
/obj/machinery/door/airlock/try_to_crowbar(obj/item/I, mob/living/user)
var/beingcrowbarred = null
if(iscrowbar(I))
beingcrowbarred = 1
else
beingcrowbarred = 0
if(beingcrowbarred && panel_open && ((emagged) || (density && welded && !operating && !arePowerSystemsOn() && !locked)))
playsound(loc, I.usesound, 100, 1)
user.visible_message("[user] removes the electronics from the airlock assembly.", \
"<span class='notice'>You start to remove electronics from the airlock assembly...</span>")
if(do_after(user, 40 * I.toolspeed, target = src))
deconstruct(TRUE, user)
return
else if(arePowerSystemsOn())
to_chat(user, "<span class='warning'>The airlock's motors resist your efforts to force it!</span>")
else if(locked)
to_chat(user, "<span class='warning'>The airlock's bolts prevent it from being forced!</span>")
else if(!welded && !operating)
if(!beingcrowbarred) //being fireaxe'd
var/obj/item/weapon/twohanded/fireaxe/F = I
if(F.wielded)
spawn(0)
if(density)
open(1)
else
close(1)
else
to_chat(user, "<span class='warning'>You need to be wielding the fire axe to do that!</span>")
else
spawn(0)
if(density)
open(1)
else
close(1)
if(istype(I, /obj/item/weapon/crowbar/power))
if(isElectrified())
shock(user, 100)//it's like sticking a forck in a power socket
return
if(!density)//already open
return
if(locked)
to_chat(user, "<span class='warning'>The bolts are down, it won't budge!</span>")
return
if(welded)
to_chat(user, "<span class='warning'>It's welded, it won't budge!</span>")
return
var/time_to_open = 5
if(arePowerSystemsOn() && !prying_so_hard)
time_to_open = 50
playsound(src, 'sound/machines/airlock_alien_prying.ogg', 100, 1) //is it aliens or just the CE being a dick?
prying_so_hard = TRUE
var/result = do_after(user, time_to_open, target = src)
prying_so_hard = FALSE
if(result)
open(1)
if(density && !open(1))
to_chat(user, "<span class='warning'>Despite your attempts, [src] refuses to open.</span>")
/obj/machinery/door/airlock/open(forced=0)
if(operating || welded || locked || emagged)
@@ -824,10 +1062,6 @@ About the new airlock wires panel:
closeOther.close()
if(!density)
return TRUE
if(operating)
return
if(!ticker)
return 0
operating = TRUE
update_icon(AIRLOCK_OPENING, 1)
sleep(1)
@@ -837,7 +1071,7 @@ About the new airlock wires panel:
density = FALSE
air_update_turf(1)
sleep(1)
layer = open_layer
layer = OPEN_DOOR_LAYER
update_icon(AIRLOCK_OPEN, 1)
operating = FALSE
@@ -850,6 +1084,8 @@ About the new airlock wires panel:
/obj/machinery/door/airlock/close(forced=0, override = 0)
if((operating & !override) || welded || locked || emagged)
return
if(density)
return TRUE
if(!forced)
//despite the name, this wire is for general door control.
//Bolts are already covered by the check for locked, above
@@ -857,11 +1093,9 @@ About the new airlock wires panel:
return
if(safe)
for(var/turf/turf in locs)
if(locate(/mob/living) in turf)
// playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0) //THE BUZZING IT NEVER STOPS -Pete
spawn(60)
autoclose()
return
for(var/atom/movable/M in turf)
if(M.density && M != src) //something is blocking the door
addtimer(src, "autoclose", 60)
use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people
if(forced)
@@ -870,13 +1104,11 @@ About the new airlock wires panel:
playsound(loc, doorClose, 30, 1)
var/obj/structure/window/killthis = (locate(/obj/structure/window) in get_turf(src))
if(killthis)
killthis.ex_act(2)//Smashin windows
killthis.ex_act(EXPLODE_HEAVY)//Smashin windows
if(density)
return TRUE
operating = TRUE
update_icon(AIRLOCK_CLOSING, 1)
layer = closed_layer
layer = CLOSED_DOOR_LAYER
if(!override)
sleep(1)
density = TRUE
@@ -892,13 +1124,10 @@ About the new airlock wires panel:
update_icon(AIRLOCK_CLOSED, 1)
operating = FALSE
if(safe)
if(locate(/mob/living) in get_turf(src))
sleep(1)
open()
CheckForMobs()
return TRUE
/obj/machinery/door/airlock/proc/lock(forced=0)
/obj/machinery/door/airlock/lock(forced=0)
if(locked)
return 0
@@ -910,7 +1139,7 @@ About the new airlock wires panel:
update_icon()
return 1
/obj/machinery/door/airlock/proc/unlock(forced=0)
/obj/machinery/door/airlock/unlock(forced=0)
if(!locked)
return
@@ -941,11 +1170,34 @@ About the new airlock wires panel:
return 1
/obj/machinery/door/airlock/emp_act(severity)
..()
if(prob(40/severity))
var/duration = world.time + SecondsToTicks(30 / severity)
if(duration > electrified_until)
electrify(duration)
..()
/obj/machinery/door/airlock/attack_alien(mob/living/carbon/alien/humanoid/user)
add_fingerprint(user)
if(isElectrified())
shock(user, 100) //Mmm, fried xeno!
return
if(!density) //Already open
return
if(locked || welded) //Extremely generic, as aliens only understand the basics of how airlocks work.
to_chat(user, "<span class='warning'>[src] refuses to budge!</span>")
return
user.visible_message("<span class='warning'>[user] begins prying open [src].</span>",\
"<span class='noticealien'>You begin digging your claws into [src] with all your might!</span>",\
"<span class='warning'>You hear groaning metal...</span>")
var/time_to_open = 5
if(arePowerSystemsOn())
time_to_open = 50 //Powered airlocks take longer to open, and are loud.
playsound(src, 'sound/machines/airlock_alien_prying.ogg', 100, 1)
if(do_after(user, time_to_open, target = src))
if(density && !open(1)) //The airlock is still closed, but something prevented it opening. (Another player noticed and bolted/welded the airlock in time!)
to_chat(user, "<span class='warning'>Despite your efforts, [src] managed to resist your attempts to open it!</span>")
/obj/machinery/door/airlock/power_change() //putting this is obj/machinery/door itself makes non-airlock doors turn invisible for some reason
..()
@@ -982,6 +1234,19 @@ About the new airlock wires panel:
open()
safe = TRUE
/obj/machinery/door/airlock/obj_break(damage_flag)
if(!(flags & BROKEN) && can_deconstruct)
stat |= BROKEN
if(!panel_open)
panel_open = TRUE
wires.CutAll()
update_icon()
/obj/machinery/door/airlock/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
. = ..()
if(obj_integrity < (0.75 * max_integrity))
update_icon()
/obj/machinery/door/airlock/deconstruct(disassembled = TRUE, mob/user)
if(can_deconstruct)
var/obj/structure/door_assembly/DA
@@ -998,13 +1263,16 @@ About the new airlock wires panel:
DA.previous_assembly = previous_airlock
DA.update_name()
DA.update_icon()
if(!disassembled)
if(DA)
DA.obj_integrity = DA.max_integrity * 0.5
if(user)
to_chat(user, "<span class='notice'>You remove the airlock electronics.</span>")
var/obj/item/weapon/airlock_electronics/ae
if(!electronics)
ae = new/obj/item/weapon/airlock_electronics(loc)
if(!req_access)
check_access()
check_access()
if(req_access.len)
ae.conf_access = req_access
else if(req_one_access.len)
@@ -1050,3 +1318,16 @@ About the new airlock wires panel:
#undef AIRLOCK_OPENING
#undef AIRLOCK_DENY
#undef AIRLOCK_EMAG
#undef AIRLOCK_SECURITY_NONE
#undef AIRLOCK_SECURITY_METAL
#undef AIRLOCK_SECURITY_PLASTEEL_I_S
#undef AIRLOCK_SECURITY_PLASTEEL_I
#undef AIRLOCK_SECURITY_PLASTEEL_O_S
#undef AIRLOCK_SECURITY_PLASTEEL_O
#undef AIRLOCK_SECURITY_PLASTEEL
#undef AIRLOCK_INTEGRITY_N
#undef AIRLOCK_INTEGRITY_MULTIPLIER
#undef AIRLOCK_DAMAGE_DEFLECTION_N
#undef AIRLOCK_DAMAGE_DEFLECTION_R
+23 -5
View File
@@ -5,10 +5,12 @@
/obj/machinery/door/airlock/command
icon = 'icons/obj/doors/airlocks/station/command.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_com
normal_integrity = 450
/obj/machinery/door/airlock/security
icon = 'icons/obj/doors/airlocks/station/security.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_sec
normal_integrity = 450
/obj/machinery/door/airlock/engineering
icon = 'icons/obj/doors/airlocks/station/engineering.dmi'
@@ -22,6 +24,7 @@
name = "maintenance access"
icon = 'icons/obj/doors/airlocks/station/maintenance.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_mai
normal_integrity = 250
/obj/machinery/door/airlock/maintenance/external
name = "external airlock access"
@@ -63,6 +66,7 @@
/obj/machinery/door/airlock/command/glass
opacity = 0
glass = TRUE
normal_integrity = 400
/obj/machinery/door/airlock/engineering/glass
opacity = 0
@@ -71,6 +75,7 @@
/obj/machinery/door/airlock/security/glass
opacity = 0
glass = TRUE
normal_integrity = 400
/obj/machinery/door/airlock/medical/glass
opacity = 0
@@ -99,6 +104,7 @@
/obj/machinery/door/airlock/maintenance/external/glass
opacity = 0
glass = TRUE
normal_integrity = 200
//////////////////////////////////
/*
@@ -127,9 +133,11 @@
name = "diamond airlock"
icon = 'icons/obj/doors/airlocks/station/diamond.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_diamond
normal_integrity = 1000
explosion_block = 2
/obj/machinery/door/airlock/diamond/glass
normal_integrity = 950
opacity = 0
glass = TRUE
@@ -186,7 +194,8 @@
log_game("Plasma airlock ignited by [key_name(user)] in ([x],[y],[z])")
investigate_log("was <font color='red'><b>ignited</b></font> by [key_name(user)]","atmos")
ignite(is_hot(C))
..()
else
return ..()
/obj/machinery/door/airlock/plasma/BlockSuperconductivity() //we don't stop the heat~
return 0
@@ -239,8 +248,10 @@
assemblytype = /obj/structure/door_assembly/door_assembly_titanium
icon = 'icons/obj/doors/airlocks/shuttle/shuttle.dmi'
overlays_file = 'icons/obj/doors/airlocks/shuttle/overlays.dmi'
normal_integrity = 400
/obj/machinery/door/airlock/titanium/glass
normal_integrity = 350
opacity = 0
glass = TRUE
@@ -287,6 +298,8 @@
opacity = 0
explosion_block = 2
assemblytype = /obj/structure/door_assembly/door_assembly_centcom
normal_integrity = 1000
security_level = 6
//////////////////////////////////
/*
@@ -299,6 +312,8 @@
overlays_file = 'icons/obj/doors/airlocks/vault/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_vault
explosion_block = 2
normal_integrity = 400 // reverse engieneerd: 400 * 1.5 (sec lvl 6) = 600 = original
security_level = 6
//////////////////////////////////
/*
@@ -363,6 +378,9 @@
overlays_file = 'icons/obj/doors/airlocks/highsec/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_highsecurity
explosion_block = 2
normal_integrity = 500
security_level = 1
damage_deflection = 30
/obj/machinery/door/airlock/highsecurity/red
name = "secure armory airlock"
@@ -381,10 +399,7 @@
if((iswelder(C) && !operating && density))
var/obj/item/weapon/weldingtool/W = C
if(W.remove_fuel(0,user))
if(!welded)
welded = 1
else
welded = null
welded = !welded
update_icon()
return
else
@@ -412,9 +427,12 @@
overlays_file = 'icons/obj/doors/airlocks/abductor/overlays.dmi'
note_overlay_file = 'icons/obj/doors/airlocks/external/overlays.dmi'
assemblytype = /obj/structure/door_assembly/door_assembly_abductor
damage_deflection = 30
explosion_block = 3
hackProof = TRUE
aiControlDisabled = 1
normal_integrity = 700
security_level = 1
//////////////////////////////////
/*
+175 -86
View File
@@ -3,41 +3,53 @@
desc = "It opens and closes."
icon = 'icons/obj/doors/Doorint.dmi'
icon_state = "door1"
anchored = 1
anchored = TRUE
opacity = 1
density = 1
density = TRUE
layer = OPEN_DOOR_LAYER
power_channel = ENVIRON
var/open_layer = OPEN_DOOR_LAYER
var/closed_layer = CLOSED_DOOR_LAYER
max_integrity = 350
armor = list(melee = 30, bullet = 30, laser = 20, energy = 20, bomb = 10, bio = 100, rad = 100)
var/closingLayer = CLOSED_DOOR_LAYER
var/visible = 1
var/operating = 0
var/operating = FALSE
var/autoclose = 0
var/autoclose_timer
var/glass = 0
var/safe = TRUE //whether the door detects things and mobs in its way and reopen or crushes them.
var/locked = FALSE //whether the door is bolted or not.
var/glass = FALSE
var/welded = FALSE
var/normalspeed = 1
var/auto_close_time = 150
var/auto_close_time_dangerous = 5
var/assemblytype //the type of door frame to drop during deconstruction
var/heat_proof = 0 // For rglass-windowed airlocks and firedoors
var/emergency = 0
var/air_properties_vary_with_direction = 0
var/block_air_zones = 1 //If set, air zones cannot merge across the door even when it is opened.
var/datum/effect_system/spark_spread/spark_system
var/damage_deflection = 10
var/real_explosion_block //ignore this, just use explosion_block
var/heat_proof = FALSE // For rglass-windowed airlocks and firedoors
var/emergency = FALSE
//Multi-tile doors
var/width = 1
/obj/machinery/door/New()
. = ..()
if(density)
layer = closed_layer //Above most items if closed
else
layer = open_layer //Under all objects if opened. 2.7 due to tables being at 2.6
..()
set_init_door_layer()
update_dir()
update_freelook_sight()
airlocks += src
spark_system = new /datum/effect_system/spark_spread
spark_system.set_up(2, 1, src)
//doors only block while dense though so we have to use the proc
real_explosion_block = explosion_block
explosion_block = EXPLOSION_BLOCK_PROC
/obj/machinery/door/proc/set_init_door_layer()
if(density)
layer = closingLayer
else
layer = initial(layer)
/obj/machinery/door/setDir(newdir)
..()
@@ -64,34 +76,43 @@
if(autoclose_timer)
deltimer(autoclose_timer)
autoclose_timer = 0
if(spark_system)
QDEL_NULL(spark_system)
return ..()
/obj/machinery/door/Bumped(atom/AM)
if(panel_open || operating || emagged)
if(operating || emagged)
return
if(isliving(AM))
var/mob/living/M = AM
if(world.time - M.last_bumped <= 10)
return //Can bump-open one airlock per second. This is to prevent shock spam.
M.last_bumped = world.time
if(!M.restrained())
if(ismob(AM))
var/mob/B = AM
if((isrobot(B)) && B.stat)
return
if(isliving(AM))
var/mob/living/M = AM
if(world.time - M.last_bumped <= 10)
return //Can bump-open one airlock per second. This is to prevent shock spam.
M.last_bumped = world.time
if(M.restrained() && !check_access(null))
return
if(M.mob_size > MOB_SIZE_SMALL)
bumpopen(M)
else if(ispet(M))
var/mob/living/simple_animal/A = AM
if(A.collar)
bumpopen(M)
return
return
if(istype(AM, /obj/mecha))
if(ismecha(AM))
var/obj/mecha/mecha = AM
if(density)
if(mecha.occupant && (allowed(mecha.occupant) || check_access_list(mecha.operation_req_access) || emergency == 1))
if(mecha.occupant)
if(world.time - mecha.occupant.last_bumped <= 10)
return
if(mecha.occupant && allowed(mecha.occupant) || check_access_list(mecha.operation_req_access))
open()
else
do_animate("deny")
return
return
/obj/machinery/door/Move(new_loc, new_dir)
var/turf/T = loc
@@ -122,14 +143,13 @@
user = null
if(density && !emagged)
if(allowed(user) || emergency == 1)
if(allowed(user))
open()
if(istype(user, /mob/living/simple_animal/bot))
if(isbot(user))
var/mob/living/simple_animal/bot/B = user
B.door_opened(src)
else
do_animate("deny")
return
/obj/machinery/door/attack_ai(mob/user)
return attack_hand(user)
@@ -139,37 +159,74 @@
return attack_hand(user)
/obj/machinery/door/attack_hand(mob/user)
return attackby(user, user)
return try_to_activate_door(user)
/obj/machinery/door/attack_tk(mob/user)
if(requiresID() && !allowed(null))
return
..()
/obj/machinery/door/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/device/detective_scanner))
return
if(isrobot(user))
return //borgs can't attack doors open because it conflicts with their AI-like interaction with them.
/obj/machinery/door/proc/try_to_activate_door(mob/user)
add_fingerprint(user)
if(operating || emagged)
return
if(density && (istype(I, /obj/item/weapon/card/emag) || istype(I, /obj/item/weapon/melee/energy/blade)))
emag_act(user)
return 1
if(allowed(user) || emergency == 1 || user.can_advanced_admin_interact())
if(!requiresID())
user = null //so allowed(user) always succeeds
if(allowed(user) || user.can_advanced_admin_interact())
if(density)
open()
else
close()
return
if(density)
do_animate("deny")
/obj/machinery/door/allowed(mob/M)
if(emergency)
return TRUE
return ..()
/obj/machinery/door/proc/try_to_weld(obj/item/weapon/weldingtool/W, mob/user)
return
/obj/machinery/door/proc/try_to_crowbar(obj/item/I, mob/user)
return
/obj/machinery/door/attackby(obj/item/I, mob/user, params)
if(user.a_intent != INTENT_HARM && (iscrowbar(I) || istype(I, /obj/item/weapon/twohanded/fireaxe)))
try_to_crowbar(I, user)
return 1
else if(iswelder(I))
try_to_weld(I, user)
return 1
else if(!(I.flags & NOBLUDGEON) && user.a_intent != INTENT_HARM)
try_to_activate_door(user)
return 1
return ..()
/obj/machinery/door/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir)
if(damage_flag == "melee" && damage_amount < damage_deflection)
return 0
. = ..()
/obj/machinery/door/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
. = ..()
if(. && obj_integrity > 0)
if(damage_amount >= 10 && prob(30))
spark_system.start()
/obj/machinery/door/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
switch(damage_type)
if(BRUTE)
if(glass)
playsound(loc, 'sound/effects/glasshit.ogg', 90, 1)
else if(damage_amount)
playsound(loc, 'sound/weapons/smash.ogg', 50, 1)
else
playsound(loc, 'sound/weapons/tap.ogg', 50, 1)
if(BURN)
playsound(loc, 'sound/items/welder.ogg', 100, 1)
/obj/machinery/door/emag_act(mob/user)
if(density)
flick("door_spark", src)
@@ -178,37 +235,17 @@
emagged = 1
return 1
/obj/machinery/door/blob_act()
if(prob(40))
qdel(src)
return
/obj/machinery/door/emp_act(severity)
if(prob(20/severity) && (istype(src,/obj/machinery/door/airlock) || istype(src,/obj/machinery/door/window)) )
spawn(0)
open()
..()
/obj/machinery/door/ex_act(severity)
switch(severity)
if(1)
qdel(src)
if(2)
if(prob(25))
qdel(src)
if(3)
if(prob(80))
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
s.set_up(2, 1, src)
s.start()
return
/obj/machinery/door/update_icon()
if(density)
icon_state = "door1"
else
icon_state = "door0"
return
/obj/machinery/door/proc/do_animate(animation)
switch(animation)
@@ -223,27 +260,24 @@
else
flick("doorc1", src)
if("deny")
flick("door_deny", src)
return
if(!stat)
flick("door_deny", src)
/obj/machinery/door/proc/open()
if(!density)
return 1
return TRUE
if(operating)
return
if(!ticker)
return 0
operating = 1
operating = TRUE
do_animate("opening")
set_opacity(0)
sleep(5)
density = 0
density = FALSE
sleep(5)
layer = open_layer
layer = initial(layer)
update_icon()
set_opacity(0)
operating = 0
operating = FALSE
air_update_turf(1)
update_freelook_sight()
@@ -251,34 +285,51 @@
if(autoclose)
autoclose_timer = addtimer(src, "autoclose", normalspeed ? auto_close_time : auto_close_time_dangerous, unique = 1)
return 1
return TRUE
/obj/machinery/door/proc/close()
if(density)
return 1
if(operating)
return TRUE
if(operating || welded)
return
operating = 1
if(safe)
for(var/atom/movable/M in get_turf(src))
if(M.density && M != src) //something is blocking the door
if(autoclose)
addtimer(src, "autoclose", 60)
return
operating = TRUE
if(autoclose_timer)
deltimer(autoclose_timer)
autoclose_timer = 0
do_animate("closing")
layer = closed_layer
layer = closingLayer
sleep(5)
density = 1
density = TRUE
sleep(5)
update_icon()
if(visible && !glass)
set_opacity(1) //caaaaarn!
set_opacity(1)
operating = 0
air_update_turf(1)
update_freelook_sight()
return
if(safe)
CheckForMobs()
else
crush()
return TRUE
/obj/machinery/door/proc/CheckForMobs()
if(locate(/mob/living) in get_turf(src))
sleep(1)
open()
/obj/machinery/door/proc/crush()
for(var/mob/living/L in get_turf(src))
L.visible_message("<span class='warning'>[src] closes on [L], crushing them!</span>", "<span class='userdanger'>[src] closes on you and crushes you!</span>")
if(isalien(L)) //For xenos
L.adjustBruteLoss(DOOR_CRUSH_DAMAGE * 1.5) //Xenos go into crit after aproximately the same amount of crushes as humans.
L.emote("roar")
@@ -291,19 +342,21 @@
L.adjustBruteLoss(DOOR_CRUSH_DAMAGE)
var/turf/location = get_turf(src)
L.add_splatter_floor(location)
for(var/obj/mecha/M in get_turf(src))
M.take_damage(DOOR_CRUSH_DAMAGE)
/obj/machinery/door/proc/requiresID()
return 1
/obj/machinery/door/proc/hasPower()
return !(stat & NOPOWER)
/obj/machinery/door/proc/autoclose()
autoclose_timer = 0
if(!qdeleted(src) && !density && !operating && autoclose)
if(!qdeleted(src) && !density && !operating && !locked && !welded && autoclose)
close()
return
/obj/machinery/door/proc/update_freelook_sight()
// Glass door glass = 1
// don't check then?
if(!glass && cameranet)
cameranet.updateVisibility(src, 0)
@@ -315,6 +368,12 @@
/obj/machinery/door/morgue
icon = 'icons/obj/doors/doormorgue.dmi'
/obj/machinery/door/proc/lock()
return
/obj/machinery/door/proc/unlock()
return
/obj/machinery/door/proc/hostile_lockdown(mob/origin)
if(!stat) //So that only powered doors are closed.
close() //Close ALL the doors!
@@ -322,3 +381,33 @@
/obj/machinery/door/proc/disable_lockdown()
if(!stat) //Opens only powered doors.
open() //Open everything!
/obj/machinery/door/blob_act(obj/structure/blob/B)
if(isturf(loc))
var/turf/T = loc
if(T.intact && level == 1) //the blob doesn't destroy thing below the floor
return
take_damage(400, BRUTE, "melee", 0, get_dir(src, B))
/obj/machinery/door/ex_act(severity, target)
if(severity)
severity = max(1, severity - 1)
else
severity = 0
if(resistance_flags & INDESTRUCTIBLE)
return
if(target == src)
obj_integrity = 0
qdel(src)
return
switch(severity)
if(1)
obj_integrity = 0
qdel(src)
if(2)
take_damage(rand(100, 250), BRUTE, "bomb", 0)
if(3)
take_damage(rand(10, 90), BRUTE, "bomb", 0)
/obj/machinery/door/GetExplosionBlock()
return density ? real_explosion_block : 0
+120 -103
View File
@@ -4,8 +4,8 @@
#define CONSTRUCTION_GUTTED 3 //Wires are removed, circuit ready to remove
#define CONSTRUCTION_NOCIRCUIT 4 //Circuit board removed, can safely weld apart
/var/const/OPEN = 1
/var/const/CLOSED = 2
/var/const/FD_OPEN = 1
/var/const/FD_CLOSED = 2
/obj/machinery/door/firedoor
name = "firelock"
@@ -14,11 +14,17 @@
icon_state = "door_open"
opacity = 0
density = FALSE
burn_state = FIRE_PROOF
max_integrity = 300
heat_proof = TRUE
glass = TRUE
closed_layer = 3.11
explosion_block = 1
safe = FALSE
layer = BELOW_OPEN_DOOR_LAYER
closingLayer = CLOSED_FIREDOOR_LAYER
auto_close_time = 50
assemblytype = /obj/structure/firelock_frame
armor = list("melee" = 30, "bullet" = 30, "laser" = 20, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 100)
var/can_force = TRUE
var/force_open_time = 300
var/can_crush = TRUE
@@ -26,6 +32,22 @@
var/boltslocked = TRUE
var/active_alarm = FALSE
/obj/machinery/door/firedoor/examine(mob/user)
..()
if(!density)
to_chat(user, "<span class='notice'>It is open, but could be <b>pried</b> closed.</span>")
else if(!welded)
to_chat(user, "<span class='notice'>It is closed, but could be <i>pried</i> open. Deconstruction would require it to be <b>welded</b> shut.</span>")
else if(boltslocked)
to_chat(user, "<span class='notice'>It is <i>welded</i> shut. The floor bolts have been locked by <b>screws</b>.</span>")
else
to_chat(user, "<span class='notice'>The bolt locks have been <i>unscrewed</i>, but the bolts themselves are still <b>wrenched</b> to the floor.</span>")
/obj/machinery/door/firedoor/closed
icon_state = "door_closed"
opacity = TRUE
density = TRUE
/obj/machinery/door/firedoor/Bumped(atom/AM)
if(panel_open || operating)
return
@@ -33,14 +55,6 @@
return ..()
return 0
/obj/machinery/door/firedoor/ex_act(severity)
switch(severity)
if(1)
qdel(src)
if(2)
if(prob(50))
qdel(src)
/obj/machinery/door/firedoor/power_change()
if(powered(power_channel))
stat &= ~NOPOWER
@@ -49,60 +63,6 @@
stat |= NOPOWER
update_icon()
/obj/machinery/door/firedoor/attackby(obj/item/weapon/C, mob/user, params)
add_fingerprint(user)
if(operating)
return
if(iswelder(C))
if(!density)
return
var/obj/item/weapon/weldingtool/W = C
if(W.remove_fuel(0, user))
welded = !welded
to_chat(user, "<span class='danger'>You [welded ? "welded" : "unwelded"] \the [src]</span>")
update_icon()
return
if(welded)
if(iswrench(C))
if(boltslocked)
to_chat(user, "<span class='notice'>There are screws locking the bolts in place!</span>")
return
playsound(get_turf(src), C.usesound, 50, 1)
user.visible_message("<span class='notice'>[user] starts undoing [src]'s bolts...</span>", \
"<span class='notice'>You start unfastening [src]'s floor bolts...</span>")
if(!do_after(user, 50 * C.toolspeed, target = src))
return
playsound(get_turf(src), C.usesound, 50, 1)
user.visible_message("<span class='notice'>[user] unfastens [src]'s bolts.</span>", \
"<span class='notice'>You undo [src]'s floor bolts.</span>")
deconstruct(TRUE)
return
else if(isscrewdriver(C))
user.visible_message("<span class='notice'>[user] [boltslocked ? "unlocks" : "locks"] [src]'s bolts...</span>", \
"<span class='notice'>You [boltslocked ? "unlock" : "lock"] [src]'s floor bolts...</span>")
playsound(get_turf(src), C.usesound, 50, 1)
boltslocked = !boltslocked
return
else
to_chat(user, "<span class='warning'>\The [src] is welded solid!</span>")
return
if(iscrowbar(C) || istype(C, /obj/item/weapon/twohanded/fireaxe))
if(istype(C, /obj/item/weapon/twohanded/fireaxe))
var/obj/item/weapon/twohanded/fireaxe/F = C
if(!F.wielded)
return
user.visible_message("[user] forces \the [src] with [C].",
"You force \the [src] with [C].")
if(density)
open()
else
close()
/obj/machinery/door/firedoor/attack_hand(mob/user)
if(operating || !density)
return
@@ -122,6 +82,57 @@
"<span class='warning'>You bang on \the [src].</span>")
playsound(get_turf(src), 'sound/effects/Glassknock.ogg', 10, 1)
/obj/machinery/door/firedoor/attackby(obj/item/weapon/C, mob/user, params)
add_fingerprint(user)
if(operating)
return
if(welded)
if(iswrench(C))
if(boltslocked)
to_chat(user, "<span class='notice'>There are screws locking the bolts in place!</span>")
return
playsound(get_turf(src), C.usesound, 50, 1)
user.visible_message("<span class='notice'>[user] starts undoing [src]'s bolts...</span>", \
"<span class='notice'>You start unfastening [src]'s floor bolts...</span>")
if(!do_after(user, 50 * C.toolspeed, target = src))
return
playsound(get_turf(src), C.usesound, 50, 1)
user.visible_message("<span class='notice'>[user] unfastens [src]'s bolts.</span>", \
"<span class='notice'>You undo [src]'s floor bolts.</span>")
deconstruct(TRUE)
return
if(isscrewdriver(C))
user.visible_message("<span class='notice'>[user] [boltslocked ? "unlocks" : "locks"] [src]'s bolts.</span>", \
"<span class='notice'>You [boltslocked ? "unlock" : "lock"] [src]'s floor bolts.</span>")
playsound(get_turf(src), C.usesound, 50, 1)
boltslocked = !boltslocked
return
return ..()
/obj/machinery/door/firedoor/try_to_activate_door(mob/user)
return
/obj/machinery/door/firedoor/try_to_weld(obj/item/weapon/weldingtool/W, mob/user)
if(W.remove_fuel(0, user))
playsound(get_turf(src), W.usesound, 50, 1)
user.visible_message("<span class='notice'>[user] starts [welded ? "unwelding" : "welding"] [src].</span>", "<span class='notice'>You start welding [src].</span>")
if(do_after(user, 40 * W.toolspeed, 1, target=src))
welded = !welded
to_chat(user, "<span class='danger'>[user] [welded ? "welds" : "unwelds"] [src].</span>", "<span class='notice'>You [welded ? "weld" : "unweld"] [src].</span>")
update_icon()
/obj/machinery/door/firedoor/try_to_crowbar(obj/item/I, mob/user)
if(welded || operating)
return
if(density)
open()
else
close()
/obj/machinery/door/firedoor/attack_ai(mob/user)
forcetoggle()
@@ -147,7 +158,7 @@
/obj/machinery/door/firedoor/update_icon()
overlays.Cut()
if(active_alarm && !(stat & NOPOWER))
if(active_alarm && hasPower())
overlays += image('icons/obj/doors/Doorfire.dmi', "alarmlights")
if(density)
icon_state = "door_closed"
@@ -177,8 +188,6 @@
/obj/machinery/door/firedoor/close()
. = ..()
if(can_crush)
crush()
latetoggle()
/obj/machinery/door/firedoor/autoclose()
@@ -186,18 +195,18 @@
. = ..()
/obj/machinery/door/firedoor/proc/latetoggle(auto_close = TRUE)
if(operating || stat & NOPOWER || !nextstate)
if(operating || !hasPower() || !nextstate)
return
switch(nextstate)
if(OPEN)
if(FD_OPEN)
nextstate = null
open(auto_close)
if(CLOSED)
if(FD_CLOSED)
nextstate = null
close()
/obj/machinery/door/firedoor/proc/forcetoggle(magic = FALSE, auto_close = TRUE)
if(!magic && (operating || stat & NOPOWER))
if(!magic && (operating || !hasPower()))
return
if(density)
open(auto_close)
@@ -211,6 +220,7 @@
F.constructionStep = CONSTRUCTION_PANEL_OPEN
else
F.constructionStep = CONSTRUCTION_WIRES_EXPOSED
F.obj_integrity = F.max_integrity * 0.5
F.update_icon()
qdel(src)
@@ -219,6 +229,11 @@
flags = ON_BORDER
can_crush = FALSE
/obj/machinery/door/firedoor/border_only/closed
icon_state = "door_closed"
opacity = TRUE
density = TRUE
/obj/machinery/door/firedoor/border_only/CanPass(atom/movable/mover, turf/target, height=0)
if(istype(mover) && mover.checkpass(PASSGLASS))
return 1
@@ -246,13 +261,10 @@
icon = 'icons/obj/doors/Doorfire.dmi'
glass = FALSE
opacity = 1
explosion_block = 2
assemblytype = /obj/structure/firelock_frame/heavy
can_force = FALSE
/obj/machinery/door/firedoor/heavy/ex_act(severity)
switch(severity)
if(1)
qdel(src)
max_integrity = 550
/obj/item/weapon/firelock_electronics
name = "firelock electronics"
@@ -270,28 +282,25 @@
desc = "A partially completed firelock."
icon = 'icons/obj/doors/Doorfire.dmi'
icon_state = "frame1"
anchored = 0
density = 1
anchored = FALSE
density = TRUE
max_integrity = 300
var/constructionStep = CONSTRUCTION_NOCIRCUIT
var/reinforced = 0
/obj/structure/firelock_frame/heavy
name = "heavy firelock frame"
reinforced = 1
/obj/structure/firelock_frame/examine(mob/user)
..()
switch(constructionStep)
if(CONSTRUCTION_PANEL_OPEN)
to_chat(user, "There is a small metal plate covering the wires.")
to_chat(user, "<span class='notice'>It is <i>unbolted</i> from the floor. A small <b>loosely connected</b> metal plate is covering the wires.</span>")
if(!reinforced)
to_chat(user, "<span class='notice'>It could be reinforced with plasteel.</span>")
if(CONSTRUCTION_WIRES_EXPOSED)
to_chat(user, "Wires are trailing from the maintenance panel.")
to_chat(user, "<span class='notice'>The maintenance plate has been <i>pried away</i>, and <b>wires</b> are trailing.</span>")
if(CONSTRUCTION_GUTTED)
to_chat(user, "The circuit board is visible.")
to_chat(user, "<span class='notice'>The maintenance panel is missing <i>wires</i> and the circuit board is <b>loosely connected</b>.</span>")
if(CONSTRUCTION_NOCIRCUIT)
to_chat(user, "There are no electronics in the frame.")
if(reinforced)
to_chat(user, "The frame is reinforced.")
to_chat(user, "<span class='notice'>There are no <i>firelock electronics</i> in the frame. The frame could be <b>cut</b> apart.</span>")
/obj/structure/firelock_frame/update_icon()
..()
@@ -371,19 +380,17 @@
constructionStep = CONSTRUCTION_GUTTED
update_icon()
return
if(iswelder(C))
var/obj/item/weapon/weldingtool/W = C
if(W.remove_fuel(1, user))
playsound(get_turf(src), C.usesound, 50, 1)
user.visible_message("<span class='notice'>[user] starts welding a metal plate into [src]...</span>", \
"<span class='notice'>You begin welding the cover plate back onto [src]...</span>")
if(!do_after(user, 80 * C.toolspeed, target = src))
return
if(constructionStep != CONSTRUCTION_WIRES_EXPOSED)
return
playsound(get_turf(src), C.usesound, 50, 1)
user.visible_message("<span class='notice'>[user] welds the metal plate into [src].</span>", \
"<span class='notice'>You weld [src]'s cover plate into place, hiding the wires.</span>")
if(iscrowbar(C))
playsound(get_turf(src), C.usesound, 50, 1)
user.visible_message("<span class='notice'>[user] starts prying a metal plate into [src]...</span>", \
"<span class='notice'>You begin prying the cover plate back onto [src]...</span>")
if(!do_after(user, 80 * C.toolspeed, target = src))
return
if(constructionStep != CONSTRUCTION_WIRES_EXPOSED)
return
playsound(get_turf(src), C.usesound, 50, 1)
user.visible_message("<span class='notice'>[user] pries the metal plate into [src].</span>", \
"<span class='notice'>You pry [src]'s cover plate into place, hiding the wires.</span>")
constructionStep = CONSTRUCTION_PANEL_OPEN
update_icon()
return
@@ -428,7 +435,7 @@
playsound(get_turf(src), W.usesound, 50, 1)
user.visible_message("<span class='notice'>[user] begins cutting apart [src]'s frame...</span>", \
"<span class='notice'>You begin slicing [src] apart...</span>")
if(!do_after(user, 80 * W.toolspeed, target = src))
if(!do_after(user, 40 * W.toolspeed, target = src))
return
if(constructionStep != CONSTRUCTION_NOCIRCUIT)
return
@@ -458,3 +465,13 @@
update_icon()
return
return ..()
/obj/structure/firelock_frame/heavy
name = "heavy firelock frame"
reinforced = 1
#undef CONSTRUCTION_COMPLETE
#undef CONSTRUCTION_PANEL_OPEN
#undef CONSTRUCTION_WIRES_EXPOSED
#undef CONSTRUCTION_GUTTED
#undef CONSTRUCTION_NOCIRCUIT
+35 -80
View File
@@ -1,107 +1,62 @@
/obj/machinery/door/poddoor
name = "blast door"
desc = "That looks like it doesn't open easily."
icon = 'icons/obj/doors/rapid_pdoor.dmi'
icon_state = "pdoor1"
desc = "A heavy duty blast door that opens mechanically."
icon = 'icons/obj/doors/blastdoor.dmi'
icon_state = "closed"
layer = BLASTDOOR_LAYER
closingLayer = CLOSED_BLASTDOOR_LAYER
explosion_block = 3
heat_proof = 1
heat_proof = TRUE
safe = FALSE
armor = list(melee = 50, bullet = 100, laser = 100, energy = 100, bomb = 50, bio = 100, rad = 100)
burn_state = FIRE_PROOF
damage_deflection = 70
var/id_tag = 1.0
var/protected = 1
/obj/machinery/door/poddoor/preopen
icon_state = "pdoor0"
density = 0
icon_state = "open"
density = FALSE
opacity = 0
/obj/machinery/door/poddoor/Bumped(atom/AM)
if(!density)
return ..()
if(density)
return
else
return 0
//"BLAST" doors are obviously stronger than regular doors when it comes to BLASTS.
/obj/machinery/door/poddoor/ex_act(severity, target)
switch(severity)
if(1)
if(prob(80))
qdel(src)
else
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
s.set_up(2, 1, src)
s.start()
if(2)
if(prob(20))
qdel(src)
else
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
s.set_up(2, 1, src)
s.start()
if(3)
if(prob(80))
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
s.set_up(2, 1, src)
s.start()
/obj/machinery/door/poddoor/attackby(obj/item/weapon/C, mob/user, params)
add_fingerprint(user)
if(!(iscrowbar(C) || (istype(C, /obj/item/weapon/twohanded/fireaxe) && C:wielded == 1)))
if(severity == 3)
return
if((density && (stat & NOPOWER) && !operating))
spawn(0)
operating = 1
flick("pdoorc0", src)
icon_state = "pdoor0"
set_opacity(0)
sleep(15)
density = 0
operating = 0
return
return
..()
/obj/machinery/door/poddoor/open()
if(operating || emagged) //doors can still open when emag-disabled
return
if(!ticker)
return 0
if(!operating) //in case of emag
operating = 1
flick("pdoorc0", src)
icon_state = "pdoor0"
set_opacity(0)
sleep(5)
density = 0
sleep(5)
air_update_turf(1)
update_freelook_sight()
/obj/machinery/door/poddoor/do_animate(animation)
switch(animation)
if("opening")
flick("opening", src)
playsound(src, 'sound/machines/blastdoor.ogg', 30, 1)
if("closing")
flick("closing", src)
playsound(src, 'sound/machines/blastdoor.ogg', 30, 1)
if(operating) //emag again
operating = 0
if(autoclose)
spawn(150)
autoclose()
return 1
/obj/machinery/door/poddoor/update_icon()
if(density)
icon_state = "closed"
else
icon_state = "open"
/obj/machinery/door/poddoor/close()
if(operating)
return
operating = 1
flick("pdoorc1", src)
icon_state = "pdoor1"
set_opacity(initial(opacity))
air_update_turf(1)
update_freelook_sight()
sleep(5)
crush()
density = 1
sleep(5)
/obj/machinery/door/poddoor/try_to_activate_door(mob/user)
return
operating = 0
return
/obj/machinery/door/poddoor/try_to_crowbar(obj/item/I, mob/user)
if(!hasPower())
open()
/obj/machinery/door/poddoor/multi_tile // Whoever wrote the old code for multi-tile spesspod doors needs to burn in hell.
name = "large pod door"
layer = CLOSED_DOOR_LAYER
closingLayer = CLOSED_DOOR_LAYER
/obj/machinery/door/poddoor/multi_tile/four_tile_ver/
icon = 'icons/obj/doors/1x4blast_vert.dmi'
+8 -63
View File
@@ -1,69 +1,14 @@
/obj/machinery/door/poddoor/shutters
gender = PLURAL
name = "shutters"
desc = "Heavy duty metal shutters that opens mechanically."
icon = 'icons/obj/doors/rapid_pdoor.dmi'
icon_state = "shutter1"
desc = "Heavy duty metal shutters that open mechanically."
icon = 'icons/obj/doors/shutters.dmi'
layer = SHUTTER_LAYER
closingLayer = SHUTTER_LAYER
damage_deflection = 20
dir = EAST
/obj/machinery/door/poddoor/shutters/New()
..()
layer = 3.1
/obj/machinery/door/poddoor/shutters/preopen
icon_state = "shutter0"
density = 0
icon_state = "open"
density = FALSE
opacity = 0
/obj/machinery/door/poddoor/shutters/attackby(obj/item/weapon/C, mob/user, params)
add_fingerprint(user)
if(!(iscrowbar(C) || (istype(C, /obj/item/weapon/twohanded/fireaxe) && C:wielded == 1)))
return
if(density && (stat & NOPOWER) && !operating)
operating = 1
spawn(-1)
flick("shutterc0", src)
icon_state = "shutter0"
sleep(15)
density = 0
set_opacity(0)
operating = 0
return
return
/obj/machinery/door/poddoor/shutters/open()
if(operating || emagged) //doors can still open when emag-disabled
return
if(!ticker)
return 0
if(!operating) //in case of emag
operating = 1
flick("shutterc0", src)
icon_state = "shutter0"
sleep(10)
density = 0
set_opacity(0)
air_update_turf(1)
update_freelook_sight()
if(operating) //emag again
operating = 0
if(autoclose)
spawn(150)
autoclose() //TODO: note to self: look into this ~Carn
return 1
/obj/machinery/door/poddoor/shutters/close()
if(operating)
return
operating = 1
flick("shutterc1", src)
icon_state = "shutter1"
density = 1
if(visible)
set_opacity(1)
air_update_turf(1)
update_freelook_sight()
sleep(10)
operating = 0
return
+5 -5
View File
@@ -1,7 +1,5 @@
/obj/machinery/door/unpowered
autoclose = 0
explosion_block = 1
var/locked = 0
/obj/machinery/door/unpowered/Bumped(atom/AM)
if(locked)
@@ -9,11 +7,13 @@
..()
/obj/machinery/door/unpowered/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/weapon/card/emag) || istype(I, /obj/item/weapon/melee/energy/blade))
return
if(locked)
return
return ..()
else
return ..()
/obj/machinery/door/unpowered/emag_act()
return
/obj/machinery/door/unpowered/shuttle
icon = 'icons/turf/shuttle.dmi'
+180 -198
View File
@@ -3,32 +3,58 @@
desc = "A strong door."
icon = 'icons/obj/doors/windoor.dmi'
icon_state = "left"
visible = 0.0
layer = ABOVE_WINDOW_LAYER
closingLayer = ABOVE_WINDOW_LAYER
visible = 0
flags = ON_BORDER
opacity = 0
dir = EAST
var/obj/item/weapon/airlock_electronics/electronics = null
max_integrity = 150 //If you change this, consider changing ../door/window/brigdoor/ max_integrity at the bottom of this .dm file
integrity_failure = 0
armor = list(melee = 20, bullet = 50, laser = 50, energy = 50, bomb = 10, bio = 100, rad = 100)
unacidable = 1
var/obj/item/weapon/airlock_electronics/electronics
var/base_state = "left"
var/health = 150.0 //If you change this, consider changing ../door/window/brigdoor/ health at the bottom of this .dm file
var/reinf = 0
var/shards = 2
var/rods = 2
var/cable = 1
var/list/debris = list()
/obj/machinery/door/window/New()
..()
if(req_access && req_access.len)
icon_state = "[icon_state]"
base_state = icon_state
if(!color)
color = color_windows(src)
return
for(var/i in 1 to shards)
debris += new /obj/item/weapon/shard(src)
if(rods)
debris += new /obj/item/stack/rods(src, rods)
if(cable)
debris += new /obj/item/stack/cable_coil(src, cable)
/obj/machinery/door/window/Destroy()
density = 0
if(health == 0)
density = FALSE
for(var/I in debris)
qdel(I)
if(obj_integrity == 0)
playsound(src, "shatter", 70, 1)
QDEL_NULL(electronics)
return ..()
/obj/machinery/door/window/update_icon()
if(density)
icon_state = base_state
else
icon_state = "[base_state]open"
/obj/machinery/door/window/examine(mob/user)
..()
if(emagged)
to_chat(user, "<span class='warning'>Its access panel is smoking slightly.</span>")
/obj/machinery/door/window/proc/open_and_close()
open()
if(check_access(null))
@@ -41,19 +67,18 @@
if(operating || !density)
return
if(!ismob(AM))
if(istype(AM, /obj/mecha))
if(ismecha(AM))
var/obj/mecha/mecha = AM
if(mecha.occupant && allowed(mecha.occupant))
open_and_close()
else
flick(text("[]deny", base_state), src)
do_animate("deny")
return
if(!ticker)
return
var/mob/living/M = AM
if(!M.restrained() && M.mob_size > MOB_SIZE_SMALL)
if(!M.restrained() && M.mob_size > MOB_SIZE_SMALL && (!(isrobot(M) && M.stat)))
bumpopen(M)
return
/obj/machinery/door/window/bumpopen(mob/user)
if(operating || !density)
@@ -65,8 +90,7 @@
if(allowed(user))
open_and_close()
else
flick(text("[]deny", base_state), src)
return
do_animate("deny")
/obj/machinery/door/window/CanPass(atom/movable/mover, turf/target, height=0)
if(istype(mover) && mover.checkpass(PASSGLASS))
@@ -84,7 +108,7 @@
//used in the AStar algorithm to determinate if the turf the door is on is passable
/obj/machinery/door/window/CanAStarPass(obj/item/weapon/card/id/ID, to_dir)
return !density || (dir != to_dir) || (check_access(ID) && !(stat & NOPOWER))
return !density || (dir != to_dir) || (check_access(ID) && hasPower())
/obj/machinery/door/window/CheckExit(atom/movable/mover, turf/target)
if(istype(mover) && mover.checkpass(PASSGLASS))
@@ -97,41 +121,39 @@
/obj/machinery/door/window/open(forced=0)
if(operating) //doors can still open when emag-disabled
return 0
if(!ticker)
return 0
if(!forced)
if(stat & NOPOWER)
if(!hasPower())
return 0
if(forced < 2)
if(emagged)
return 0
if(!operating) //in case of emag
operating = 1
flick(text("[]opening", base_state), src)
do_animate("opening")
playsound(loc, 'sound/machines/windowdoor.ogg', 100, 1)
icon_state = text("[]open", base_state)
icon_state ="[base_state]open"
sleep(10)
density = 0
density = FALSE
// sd_set_opacity(0) //TODO: why is this here? Opaque windoors? ~Carn
air_update_turf(1)
update_freelook_sight()
if(operating) //emag again
operating = 0
operating = FALSE
return 1
/obj/machinery/door/window/close(forced=0)
if(operating)
return 0
if(!forced)
if(stat & NOPOWER)
if(!hasPower())
return 0
if(forced < 2)
if(emagged)
return 0
operating = 1
flick(text("[]closing", base_state), src)
do_animate("closing")
playsound(loc, 'sound/machines/windowdoor.ogg', 100, 1)
icon_state = base_state
@@ -145,47 +167,30 @@
operating = 0
return 1
/obj/machinery/door/window/take_damage(damage)
health = max(0, health - damage)
if(health <= 0)
var/debris = list(
new /obj/item/weapon/shard(loc),
new /obj/item/weapon/shard(loc),
new /obj/item/stack/rods(loc, 2),
new /obj/item/stack/cable_coil(loc, 2)
)
/obj/machinery/door/window/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
switch(damage_type)
if(BRUTE)
playsound(loc, 'sound/effects/glasshit.ogg', 90, 1)
if(BURN)
playsound(loc, 'sound/items/welder.ogg', 100, 1)
/obj/machinery/door/window/deconstruct(disassembled = TRUE)
if(can_deconstruct && !disassembled)
for(var/obj/fragment in debris)
fragment.forceMove(get_turf(src))
transfer_fingerprints_to(fragment)
density = 0
qdel(src)
return
debris -= fragment
qdel(src)
/obj/machinery/door/window/bullet_act(obj/item/projectile/Proj)
if(Proj.damage)
if((Proj.damage_type == BRUTE || Proj.damage_type == BURN))
take_damage(round(Proj.damage / 2))
/obj/machinery/door/window/narsie_act()
color = NARSIE_WINDOW_COLOUR
/obj/machinery/door/window/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
if(exposed_temperature > T0C + (reinf ? 1600 : 800))
take_damage(round(exposed_volume / 200), BURN, 0, 0)
..()
//When an object is thrown at the window
/obj/machinery/door/window/hitby(atom/movable/AM)
..()
var/tforce = 0
if(ismob(AM))
tforce = 40
else if(isobj(AM))
var/obj/O = AM
tforce = O.throwforce
playsound(loc, 'sound/effects/Glasshit.ogg', 100, 1)
take_damage(tforce)
/obj/machinery/door/window/mech_melee_attack(obj/mecha/M)
if(M.damtype == "brute")
playsound(loc, 'sound/effects/Glasshit.ogg', 75, 1)
M.occupant_message("<span class='danger'>You hit [src].</span>")
visible_message("<span class='danger'>[src] has been hit by [M.name].</span>")
take_damage(M.force)
return
/obj/machinery/door/window/attack_ai(mob/user)
return attack_hand(user)
@@ -193,56 +198,18 @@
if(user.can_advanced_admin_interact())
return attack_hand(user)
/obj/machinery/door/window/proc/attack_generic(mob/user, damage = 0)
if(operating)
return
user.changeNext_move(CLICK_CD_MELEE)
user.do_attack_animation(src)
playsound(loc, 'sound/effects/Glasshit.ogg', 75, 1)
user.visible_message("<span class='danger'>[user] smashes against the [name].</span>", \
"<span class='userdanger'>[user] smashes against the [name].</span>")
take_damage(damage)
/obj/machinery/door/window/attack_alien(mob/living/user)
if(islarva(user))
return
attack_generic(user, 25)
/obj/machinery/door/window/attack_animal(mob/living/user)
if(!isanimal(user))
return
var/mob/living/simple_animal/M = user
if(M.melee_damage_upper > 0 && (M.melee_damage_type == BRUTE || M.melee_damage_type == BURN))
attack_generic(M, M.melee_damage_upper)
/obj/machinery/door/window/attack_slime(mob/living/carbon/slime/user)
if(!user.is_adult)
return
attack_generic(user, 25)
/obj/machinery/door/window/attack_hand(mob/user)
return attackby(user, user)
/obj/machinery/door/window/emag_act(mob/user, obj/weapon)
if(!operating && density && !emagged)
emagged = TRUE
operating = 1
flick("[base_state]spark", src)
playsound(src, "sparks", 75, 1)
sleep(6)
operating = 0
desc += "<BR><span class='warning'>Its access panel is smoking slightly.</span>"
if(istype(weapon, /obj/item/weapon/melee/energy/blade))
var/obj/item/weapon/melee/energy/blade/B
var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread()
spark_system.set_up(5, 0, loc)
spark_system.start()
playsound(loc, "sparks", 50, 1)
playsound(loc, B.usesound, 50, 1)
visible_message("<span class='warning'> The glass door was sliced open by [user]!</span>")
open(2)
emagged = 1
return 1
open()
emagged = 1
operating = FALSE
open(2)
return 1
/obj/machinery/door/window/attackby(obj/item/weapon/I, mob/living/user, params)
@@ -253,117 +220,100 @@
add_fingerprint(user)
//Ninja swords? You may pass.
if(density && (istype(I, /obj/item/weapon/card/emag) || istype(I, /obj/item/weapon/melee/energy/blade)))
emag_act(user,I)
return 1
if(isscrewdriver(I))
if(density || operating)
to_chat(user, "<span class='warning'>You need to open the door to access the maintenance panel.</span>")
if(can_deconstruct)
if(isscrewdriver(I))
if(density || operating)
to_chat(user, "<span class='warning'>You need to open the door to access the maintenance panel!</span>")
return
playsound(src.loc, I.usesound, 50, 1)
panel_open = !panel_open
to_chat(user, "<span class='notice'>You [panel_open ? "open":"close"] the maintenance panel of the [src.name].</span>")
return
playsound(loc, I.usesound, 50, 1)
panel_open = !panel_open
to_chat(user, "<span class='notice'>You [panel_open ? "open":"close"] the maintenance panel of the [name].</span>")
return
if(iscrowbar(I))
if(panel_open && !density && !operating)
playsound(loc, I.usesound, 100, 1)
user.visible_message("<span class='warning'>[user] removes the electronics from the [name].</span>", \
"You start to remove electronics from the [name].")
if(do_after(user, 40 * I.toolspeed, target = src))
if(panel_open && !density && !operating && loc)
var/obj/structure/windoor_assembly/WA = new /obj/structure/windoor_assembly(loc)
switch(base_state)
if("left")
WA.facing = "l"
if("right")
WA.facing = "r"
if("leftsecure")
WA.facing = "l"
WA.secure = 1
if("rightsecure")
WA.facing = "r"
WA.secure = 1
WA.anchored = 1
WA.state= "02"
WA.dir = dir
WA.ini_dir = dir
WA.update_icon()
WA.created_name = name
if(iscrowbar(I))
if(panel_open && !density && !operating)
playsound(loc, I.usesound, 100, 1)
user.visible_message("<span class='warning'>[user] removes the electronics from the [name].</span>", \
"You start to remove electronics from the [name]...")
if(do_after(user, 40 * I.toolspeed, target = src))
if(panel_open && !density && !operating && loc)
var/obj/structure/windoor_assembly/WA = new /obj/structure/windoor_assembly(loc)
switch(base_state)
if("left")
WA.facing = "l"
if("right")
WA.facing = "r"
if("leftsecure")
WA.facing = "l"
WA.secure = TRUE
if("rightsecure")
WA.facing = "r"
WA.secure = TRUE
WA.anchored = TRUE
WA.state= "02"
WA.setDir(dir)
WA.ini_dir = dir
WA.update_icon()
WA.created_name = name
if(emagged)
to_chat(user, "<span class='warning'>You discard the damaged electronics.</span>")
qdel(src)
return
to_chat(user, "<span class='notice'>You remove the airlock electronics.</span>")
var/obj/item/weapon/airlock_electronics/ae
if(!electronics)
ae = new/obj/item/weapon/airlock_electronics(loc)
if(!req_access)
check_access()
if(req_access.len)
ae.conf_access = req_access
else if(req_one_access.len)
ae.conf_access = req_one_access
ae.one_access = 1
else
ae = electronics
electronics = null
ae.forceMove(loc)
if(emagged)
to_chat(user, "<span class='warning'>You discard the damaged electronics.</span>")
qdel(src)
return
return
return ..()
to_chat(user, "<span class='notice'>You removed the airlock electronics!</span>")
var/obj/item/weapon/airlock_electronics/ae
if(!electronics)
ae = new/obj/item/weapon/airlock_electronics(loc)
if(!req_access)
check_access()
if(req_access.len)
ae.conf_access = req_access
else if(req_one_access.len)
ae.conf_access = req_one_access
ae.one_access = 1
else
ae = electronics
electronics = null
ae.loc = loc
qdel(src)
return
//If windoor is unpowered, crowbar, fireaxe and armblade can force it.
if(iscrowbar(I) || istype(I, /obj/item/weapon/twohanded/fireaxe))
if(stat & NOPOWER)
if(density)
open(2)
else
close(2)
return
//If it's a weapon, smash windoor. Unless it's an id card, agent card, ect.. then ignore it (Cards really shouldnt damage a door anyway)
if(density && istype(I, /obj/item/weapon) && !istype(I, /obj/item/weapon/card))
user.changeNext_move(CLICK_CD_MELEE)
user.do_attack_animation(src)
if((I.flags&NOBLUDGEON) || !I.force)
return
var/aforce = I.force
playsound(loc, 'sound/effects/Glasshit.ogg', 75, 1)
visible_message("<span class='danger'>\The [src] has been hit by [user] with [I].</span>")
if(I.damtype == BURN || I.damtype == BRUTE)
take_damage(aforce)
return
if(!requiresID())
//don't care who they are or what they have, act as if they're NOTHING
user = null
if(allowed(user) || user.can_advanced_admin_interact())
/obj/machinery/door/window/try_to_crowbar(obj/item/I, mob/user)
if(!hasPower())
if(density)
open()
open(2)
else
close()
close(2)
else
to_chat(user, "<span class='warning'>The door's motors resist your efforts to force it!</span>")
else if(density)
flick(text("[]deny", base_state), src)
return
/obj/machinery/door/window/do_animate(animation)
switch(animation)
if("opening")
flick("[base_state]opening", src)
if("closing")
flick("[base_state]closing", src)
if("deny")
flick("[base_state]deny", src)
/obj/machinery/door/window/brigdoor
name = "secure door"
icon = 'icons/obj/doors/windoor.dmi'
icon_state = "leftsecure"
base_state = "leftsecure"
health = 300.0 //Stronger doors for prison (regular window door health is 200)
max_integrity = 300 //Stronger doors for prison (regular window door health is 200)
reinf = 1
explosion_block = 1
var/id = null
/obj/machinery/door/window/brigdoor/security/cell
name = "cell door"
desc = "For keeping in criminal scum."
req_access = list(access_brig)
/obj/machinery/door/window/northleft
dir = NORTH
@@ -427,3 +377,35 @@
dir = SOUTH
icon_state = "rightsecure"
base_state = "rightsecure"
/obj/machinery/door/window/brigdoor/security/cell/northleft
dir = NORTH
/obj/machinery/door/window/brigdoor/security/cell/eastleft
dir = EAST
/obj/machinery/door/window/brigdoor/security/cell/westleft
dir = WEST
/obj/machinery/door/window/brigdoor/security/cell/southleft
dir = SOUTH
/obj/machinery/door/window/brigdoor/security/cell/northright
dir = NORTH
icon_state = "rightsecure"
base_state = "rightsecure"
/obj/machinery/door/window/brigdoor/security/cell/eastright
dir = EAST
icon_state = "rightsecure"
base_state = "rightsecure"
/obj/machinery/door/window/brigdoor/security/cell/westright
dir = WEST
icon_state = "rightsecure"
base_state = "rightsecure"
/obj/machinery/door/window/brigdoor/security/cell/southright
dir = SOUTH
icon_state = "rightsecure"
base_state = "rightsecure"
+1
View File
@@ -12,6 +12,7 @@ FIRE ALARM
var/timing = 0.0
var/lockdownbyai = 0
anchored = 1.0
armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 100)
use_power = 1
idle_power_usage = 2
active_power_usage = 6
+1 -1
View File
@@ -44,7 +44,7 @@ var/list/holopads = list()
idle_power_usage = 5
active_power_usage = 100
layer = TURF_LAYER+0.1 //Preventing mice and drones from sneaking under them.
armor = list(melee = 50, bullet = 20, laser = 20, energy = 20, bomb = 0, bio = 0, rad = 0)
var/list/masters = list()//List of living mobs that use the holopad
var/list/holorays = list()//Holoray-mob link.
var/last_request = 0 //to prevent request spam. ~Carn
+1
View File
@@ -3,6 +3,7 @@
desc = "It's useful for igniting plasma."
icon = 'icons/obj/stationobjs.dmi'
icon_state = "igniter1"
armor = list(melee = 50, bullet = 30, laser = 70, energy = 50, bomb = 20, bio = 0, rad = 0)
var/id = null
var/on = 1.0
anchored = 1.0

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