Merge remote-tracking branch 'citadel/master' into storage_rework

This commit is contained in:
kevinz000
2020-04-06 18:40:58 -07:00
2864 changed files with 782707 additions and 267358 deletions
+244 -1
View File
@@ -48,6 +48,8 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
icon_state = "space_near"
dynamic_lighting = DYNAMIC_LIGHTING_IFSTARLIGHT
/area/space/station_ruins //Paint this area where you want station ruins to be allowed to spawn
/area/start
name = "start area"
icon_state = "start"
@@ -259,6 +261,9 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
//Hallway
/area/hallway
nightshift_public_area = NIGHTSHIFT_AREA_PUBLIC
/area/hallway/primary/aft
name = "Aft Primary Hallway"
icon_state = "hallA"
@@ -319,6 +324,11 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "Service Hallway"
icon_state = "hall_service"
/area/hallway/secondary/civilian
name = "Civilian Wing"
icon_state = "hallFS"
//Command
/area/bridge
@@ -404,14 +414,17 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "Dormitories"
icon_state = "Sleep"
safe = TRUE
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/dorms/male
name = "Male Dorm"
icon_state = "Sleep"
nightshift_public_area = NIGHTSHIFT_AREA_NONE
/area/crew_quarters/dorms/female
name = "Female Dorm"
icon_state = "Sleep"
nightshift_public_area = NIGHTSHIFT_AREA_NONE
/area/crew_quarters/rehab_dome
name = "Rehabilitation Dome"
@@ -448,26 +461,32 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/crew_quarters/locker
name = "Locker Room"
icon_state = "locker"
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/lounge
name = "Lounge"
icon_state = "yellow"
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/fitness
name = "Fitness Room"
icon_state = "fitness"
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/fitness/recreation
name = "Recreation Area"
icon_state = "fitness"
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/cafeteria
name = "Cafeteria"
icon_state = "cafeteria"
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/cafeteria/lunchroom
name = "Lunchroom"
icon_state = "cafeteria"
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/kitchen
name = "Kitchen"
@@ -480,6 +499,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/crew_quarters/bar
name = "Bar"
icon_state = "bar"
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/bar/atrium
name = "Atrium"
@@ -518,6 +538,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "Library"
icon_state = "library"
flags_1 = NONE
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/library/lounge
name = "Library Lounge"
@@ -527,6 +548,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "Abandoned Library"
icon_state = "library"
flags_1 = NONE
nightshift_public_area = NIGHTSHIFT_AREA_NONE
/area/chapel
icon_state = "chapel"
@@ -534,12 +556,14 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
flags_1 = NONE
clockwork_warp_allowed = FALSE
clockwork_warp_fail = "The consecration here prevents you from warping in."
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/chapel/main
name = "Chapel"
/area/chapel/main/monastery
name = "Monastery"
nightshift_public_area = NIGHTSHIFT_AREA_NONE
/area/chapel/office
name = "Chapel Office"
@@ -832,6 +856,10 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "Medbay Treatment Center"
icon_state = "exam_room"
/area/medical/paramedic
name = "Paramedic Station"
icon_state = "paramedic"
//Security
@@ -1023,7 +1051,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "Monastery Garden"
icon_state = "hydro"
//Science
/area/science
@@ -1228,6 +1255,22 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
icon_state = "storage_wing"
// Vacant Rooms
/area/vacant_room
name = "Vacant Room"
ambientsounds = MAINTENANCE
icon_state = "vacant_room"
/area/vacant_room/office
name = "Vacant Office"
icon_state = "vacant_room"
/area/vacant_room/commissary
name = "Vacant Commissary"
icon_state = "vacant_room"
//AI
/area/ai_monitored/security/armory
@@ -1350,3 +1393,203 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/tcommsat/lounge
name = "Telecommunications Satellite Lounge"
icon_state = "tcomsatlounge"
/area/crew_quarters/fitness/pool
name = "Pool Area"
icon_state = "pool"
/area/crew_quarters/dorms/blue
name = "Blue Dorms"
icon_state = "Sleep"
nightshift_public_area = NIGHTSHIFT_AREA_NONE
/area/crew_quarters/dorms/purple
name = "Purple Dorms"
icon_state = "Sleep"
nightshift_public_area = NIGHTSHIFT_AREA_NONE
/area/crew_quarters/lounge/jazz
name = "Jazz Lounge"
icon_state = "yellow"
ambientsounds = list('sound/ambience/ambidet1.ogg','sound/ambience/ambidet2.ogg')
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/fitness/recreation
name = "Recreation Area"
icon_state = "fitness"
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/fitness/cogpool
name = "Pool"
icon_state = "fitness"
clockwork_warp_fail = "Pool's closed."
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/barbershop
name = "Barbershop"
icon_state = "blue"
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/observatory
name = "Observatory"
icon_state = "Sleep"
//Engineering
/area/engine/teg_hot
name = "Hot Loop"
icon_state = "red"
/area/engine/teg_cold
name = "Cold Loop"
icon_state = "blue"
/area/engine/workshop
name = "Engineering Workshop"
icon_state = "engine"
/area/security/courtroom/jury
name = "Jury Room"
icon_state = "courtroom"
/area/quartermaster/miningdock/airless
name = "Mining Dock"
icon_state = "mining"
has_gravity = STANDARD_GRAVITY
valid_territory = FALSE
outdoors = TRUE
ambientsounds = SPACE
blob_allowed = FALSE //While part of the station, what good will it do you?
/area/quartermaster/miningdock/airless/no_grav
name = "Mining Dock"
icon_state = "mining"
dynamic_lighting = DYNAMIC_LIGHTING_IFSTARLIGHT
power_light = FALSE
power_equip = FALSE
power_environ = FALSE
valid_territory = FALSE
outdoors = TRUE
ambientsounds = SPACE
blob_allowed = FALSE //While part of the station, what good will it do you?
/area/janitor/aux
name = "Auxiliary Custodial Closet"
icon_state = "janitor"
flags_1 = NONE
/area/hydroponics/lobby
name = "Hydroponics Lobby"
icon_state = "hydro"
//Storage
/area/storage/emergency/generic
name = "Emergency Storage"
icon_state = "emergencystorage"
//Construction
/area/construction
name = "Construction Area"
icon_state = "yellow"
ambientsounds = ENGINEERING
/area/construction/secondary
name = "Secondary Construction Area"
icon_state = "yellow"
/area/construction/minisat_exterior
name = "Minisat Exterior"
icon_state = "yellow"
/area/construction/mining/aux_base
name = "Auxiliary Base Construction"
icon_state = "yellow"
/area/construction/mining/aux_base/closet
name = "Auxiliary Closet Construction"
icon_state = "yellow"
/area/construction/supplyshuttle
name = "Supply Shuttle"
icon_state = "yellow"
/area/construction/quarters
name = "Engineers' Quarters"
icon_state = "yellow"
/area/construction/qmaint
name = "Maintenance"
icon_state = "yellow"
/area/construction/hallway
name = "Hallway"
icon_state = "yellow"
/area/construction/solars
name = "Solar Panels"
icon_state = "yellow"
/area/construction/solarscontrol
name = "Solar Panel Control"
icon_state = "yellow"
/area/construction/storage
name = "Construction Site Storage"
icon_state = "yellow"
/area/construction/storage/wing
name = "Storage Wing"
icon_state = "storage_wing"
//Routers
/area/router
name = "Router"
icon_state = "yellow"
ambientsounds = ENGINEERING
/area/router/service
name = "Service Router"
icon_state = "green"
/area/router/public
name = "Public Router"
icon_state = "yellow"
/area/router/sec
name = "Security Router"
icon_state = "blue"
/area/router/medsci
name = "MedSci Router"
icon_state = "yellow"
/area/router/eva
name = "EVA Router"
icon_state = "yellow"
/area/router/air
name = "Airbridge Router"
icon_state = "red"
/area/router/eng
name = "Engineering Router"
icon_state = "yellow"
/area/router/aux
name = "Routing System"
icon_state = "yellow"
requires_power = FALSE
has_gravity = STANDARD_GRAVITY
always_unpowered = TRUE
dynamic_lighting = DYNAMIC_LIGHTING_IFSTARLIGHT
power_light = FALSE
power_equip = FALSE
power_environ = FALSE
valid_territory = FALSE
outdoors = TRUE
ambientsounds = SPACE
blob_allowed = FALSE //While part of the station, what good will it do you?
+102 -68
View File
@@ -63,6 +63,21 @@
var/xenobiology_compatible = FALSE //Can the Xenobio management console transverse this area by default?
var/list/canSmoothWithAreas //typecache to limit the areas that atoms in this area can smooth with
/// Color on minimaps, if it's null (which is default) it makes one at random.
var/minimap_color
/**
* These two vars allow for multiple unique areas to be linked to a master area
* and share some functionalities such as APC powernet nodes, fire alarms etc, without sacrificing
* their own flags, statuses, variables and more snowflakes.
* Friendly reminder: no map edited areas.
*/
var/list/area/sub_areas //list of typepaths of the areas you wish to link here, will be replaced with a list of references on mapload.
var/area/base_area //The area we wish to use in place of src for certain actions such as APC area linking.
var/nightshift_public_area = NIGHTSHIFT_AREA_NONE //considered a public area for nightshift
/*Adding a wizard area teleport list because motherfucking lag -- Urist*/
/*I am far too lazy to make it a proper list of areas so I'll just make it run the usual telepot routine at the start of the game*/
GLOBAL_LIST_EMPTY(teleportlocs)
@@ -85,7 +100,14 @@ GLOBAL_LIST_EMPTY(teleportlocs)
// ===
/area/New()
// This interacts with the map loader, so it needs to be set immediately
if(!minimap_color) // goes in New() because otherwise it doesn't fucking work
// generate one using the icon_state
if(icon_state && icon_state != "unknown")
var/icon/I = new(icon, icon_state, dir)
I.Scale(1,1)
minimap_color = I.GetPixel(1,1)
else // no icon state? use random.
minimap_color = rgb(rand(50,70),rand(50,70),rand(50,70)) // This interacts with the map loader, so it needs to be set immediately
// rather than waiting for atoms to initialize.
if (unique)
GLOB.areas_by_type[type] = src
@@ -121,10 +143,35 @@ GLOBAL_LIST_EMPTY(teleportlocs)
reg_in_areas_in_z()
//so far I'm only implementing it on mapped unique areas, it's easier this way.
if(unique && sub_areas)
if(type in sub_areas)
WARNING("\"[src]\" typepath found inside its own sub-areas list, please make sure it doesn't share its parent type initial sub-areas value.")
sub_areas = null
else
var/paths = sub_areas.Copy()
sub_areas = null
for(var/type in paths)
var/area/A = GLOB.areas_by_type[type]
if(!A) //By chance an area not loaded in the current world, no warning report.
continue
if(A == src)
WARNING("\"[src]\" area a attempted to link with itself.")
continue
if(A.base_area)
WARNING("[src] attempted to link with [A] while the latter is already linked to another area ([A.base_area]).")
continue
LAZYADD(sub_areas, A)
A.base_area = src
else if(LAZYLEN(sub_areas))
WARNING("sub-areas are currently not supported for non-unique areas such as [src].")
sub_areas = null
return INITIALIZE_HINT_LATELOAD
/area/LateInitialize()
power_change() // all machines set to current power level, also updates icon
if(!base_area) //we don't want to run it twice.
power_change() // all machines set to current power level, also updates icon
/area/proc/reg_in_areas_in_z()
if(contents.len)
@@ -147,6 +194,19 @@ GLOBAL_LIST_EMPTY(teleportlocs)
/area/Destroy()
if(GLOB.areas_by_type[type] == src)
GLOB.areas_by_type[type] = null
if(base_area)
LAZYREMOVE(base_area, src)
base_area = null
if(sub_areas)
for(var/i in sub_areas)
var/area/A = i
A.base_area = null
sub_areas -= A
if(A.requires_power)
A.power_light = FALSE
A.power_equip = FALSE
A.power_environ = FALSE
INVOKE_ASYNC(A, .proc/power_change)
STOP_PROCESSING(SSobj, src)
return ..()
@@ -212,9 +272,12 @@ GLOBAL_LIST_EMPTY(teleportlocs)
var/datum/computer_file/program/alarm_monitor/p = item
p.cancelAlarm("Atmosphere", src, source)
src.atmosalm = danger_level
return 1
return 0
atmosalm = danger_level
for(var/i in sub_areas)
var/area/A = i
A.atmosalm = danger_level
return TRUE
return FALSE
/area/proc/ModifyFiredoors(opening)
if(firedoors)
@@ -239,11 +302,8 @@ GLOBAL_LIST_EMPTY(teleportlocs)
return
if (!fire)
set_fire_alarm_effect()
set_fire_alarm_effects(TRUE)
ModifyFiredoors(FALSE)
for(var/item in firealarms)
var/obj/machinery/firealarm/F = item
F.update_icon()
for (var/item in GLOB.alert_consoles)
var/obj/machinery/computer/station_alert/a = item
@@ -262,11 +322,8 @@ GLOBAL_LIST_EMPTY(teleportlocs)
/area/proc/firereset(obj/source)
if (fire)
unset_fire_alarm_effects()
set_fire_alarm_effects(FALSE)
ModifyFiredoors(TRUE)
for(var/item in firealarms)
var/obj/machinery/firealarm/F = item
F.update_icon()
for (var/item in GLOB.silicon_mobs)
var/mob/living/silicon/aiPlayer = item
@@ -298,9 +355,9 @@ GLOBAL_LIST_EMPTY(teleportlocs)
return
//Trigger alarm effect
set_fire_alarm_effect()
set_fire_alarm_effects(TRUE)
//Lockdown airlocks
for(var/obj/machinery/door/DOOR in src)
for(var/obj/machinery/door/DOOR in get_sub_areas_contents(src))
close_and_lock_door(DOOR)
for (var/i in GLOB.silicon_mobs)
@@ -309,23 +366,20 @@ GLOBAL_LIST_EMPTY(teleportlocs)
//Cancel silicon alert after 1 minute
addtimer(CALLBACK(SILICON, /mob/living/silicon.proc/cancelAlarm,"Burglar",src,trigger), 600)
/area/proc/set_fire_alarm_effect()
fire = TRUE
/area/proc/set_fire_alarm_effects(boolean)
fire = boolean
for(var/i in sub_areas)
var/area/A = i
A.fire = boolean
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
for(var/alarm in firealarms)
var/obj/machinery/firealarm/F = alarm
F.update_fire_light(fire)
for(var/obj/machinery/light/L in src)
F.update_icon()
for(var/obj/machinery/light/L in get_sub_areas_contents(src))
L.update()
/area/proc/unset_fire_alarm_effects()
fire = FALSE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
for(var/alarm in firealarms)
var/obj/machinery/firealarm/F = alarm
F.update_fire_light(fire)
for(var/obj/machinery/light/L in src)
L.update()
/area/proc/updateicon()
/**
* Update the icon state of the area
*
@@ -378,26 +432,35 @@ GLOBAL_LIST_EMPTY(teleportlocs)
/area/proc/power_change()
for(var/obj/machinery/M in src) // for each machine in the area
M.power_change() // reverify power status (to update icons etc.)
if(sub_areas)
for(var/i in sub_areas)
var/area/A = i
A.power_light = power_light
A.power_equip = power_equip
A.power_environ = power_environ
INVOKE_ASYNC(A, .proc/power_change)
update_icon()
/area/proc/usage(chan)
var/used = 0
switch(chan)
if(LIGHT)
used += used_light
. += used_light
if(EQUIP)
used += used_equip
. += used_equip
if(ENVIRON)
used += used_environ
. += used_environ
if(TOTAL)
used += used_light + used_equip + used_environ
. += used_light + used_equip + used_environ
if(STATIC_EQUIP)
used += static_equip
. += static_equip
if(STATIC_LIGHT)
used += static_light
. += static_light
if(STATIC_ENVIRON)
used += static_environ
return used
. += static_environ
if(sub_areas)
for(var/i in sub_areas)
var/area/A = i
. += A.usage(chan)
/area/proc/addStaticPower(value, powerchannel)
switch(powerchannel)
@@ -412,6 +475,10 @@ GLOBAL_LIST_EMPTY(teleportlocs)
used_equip = 0
used_light = 0
used_environ = 0
if(sub_areas)
for(var/i in sub_areas)
var/area/A = i
A.clear_usage()
/area/proc/use_power(amount, chan)
@@ -458,39 +525,6 @@ GLOBAL_LIST_EMPTY(teleportlocs)
/client/proc/ResetAmbiencePlayed()
played = FALSE
/atom/proc/has_gravity(turf/T)
if(!T || !isturf(T))
T = get_turf(src)
if(!T)
return 0
var/list/forced_gravity = list()
SEND_SIGNAL(src, COMSIG_ATOM_HAS_GRAVITY, T, forced_gravity)
if(!forced_gravity.len)
SEND_SIGNAL(T, COMSIG_TURF_HAS_GRAVITY, src, forced_gravity)
if(forced_gravity.len)
var/max_grav
for(var/i in forced_gravity)
max_grav = max(max_grav, i)
if(max_grav)
return max_grav
if(isspaceturf(T)) // Turf never has gravity
return 0
var/area/A = get_area(T)
if(A.has_gravity) // Areas which always has gravity
return A.has_gravity
else
// There's a gravity generator on our z level
if(GLOB.gravity_generators["[T.z]"])
var/max_grav = 0
for(var/obj/machinery/gravity_generator/main/G in GLOB.gravity_generators["[T.z]"])
max_grav = max(G.setting,max_grav)
return max_grav
return SSmapping.level_trait(T.z, ZTRAIT_GRAVITY)
/area/proc/setup(a_name)
name = a_name
power_equip = FALSE
+3
View File
@@ -99,6 +99,9 @@
/area/holodeck/rec_center/winterwonderland
name = "Holodeck - Winter Wonderland"
/area/holodeck/rec_center/wrestlingarena
name = "Holodeck - Wrestling Arena"
// Bad programs
/area/holodeck/rec_center/burn
+2
View File
@@ -3,6 +3,8 @@
/area/ruin/space
has_gravity = FALSE
blob_allowed = FALSE //Nope, no winning in space as a blob. Gotta eat the station.
outdoors = TRUE
ambientsounds = SPACE
/area/ruin/space/has_grav
has_gravity = STANDARD_GRAVITY
+171 -18
View File
@@ -28,7 +28,6 @@
var/list/managed_overlays
var/datum/proximity_monitor/proximity_monitor
var/buckle_message_cooldown = 0
var/fingerprintslast
var/list/filter_data //For handling persistent filters
@@ -38,6 +37,13 @@
var/rad_flags = NONE // Will move to flags_1 when i can be arsed to
var/rad_insulation = RAD_NO_INSULATION
///The custom materials this atom is made of, used by a lot of things like furniture, walls, and floors (if I finish the functionality, that is.)
var/list/custom_materials
///Bitfield for how the atom handles materials.
var/material_flags = NONE
///Modifier that raises/lowers the effect of the amount of a material, prevents small and easy to get items from being death machines.
var/material_modifier = 1
var/icon/blood_splatter_icon
var/list/fingerprints
var/list/fingerprintshidden
@@ -89,6 +95,12 @@
if (canSmoothWith)
canSmoothWith = typelist("canSmoothWith", canSmoothWith)
var/temp_list = list()
for(var/i in custom_materials)
temp_list[SSmaterials.GetMaterialRef(i)] = custom_materials[i] //Get the proper instanced version
custom_materials = null //Null the list to prepare for applying the materials properly
set_custom_materials(temp_list)
ComponentInitialize()
return INITIALIZE_HINT_NORMAL
@@ -290,6 +302,11 @@
if(desc)
. += desc
if(custom_materials)
for(var/i in custom_materials)
var/datum/material/M = i
. += "<u>It is made out of [M.name]</u>."
if(reagents)
if(reagents.reagents_holder_flags & TRANSPARENT)
. += "It contains:"
@@ -316,9 +333,11 @@
/atom/proc/update_icon()
// I expect we're going to need more return flags and options in this proc
var/signalOut = SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_ICON)
. = FALSE
if(!(signalOut & COMSIG_ATOM_NO_UPDATE_ICON_STATE))
update_icon_state()
. = TRUE
if(!(signalOut & COMSIG_ATOM_NO_UPDATE_OVERLAYS))
var/list/new_overlays = update_overlays()
@@ -328,6 +347,9 @@
if(length(new_overlays))
managed_overlays = new_overlays
add_overlay(new_overlays)
. = TRUE
SEND_SIGNAL(src, COMSIG_ATOM_UPDATED_ICON, signalOut, .)
/// Updates the icon state of the atom
/atom/proc/update_icon_state()
@@ -338,11 +360,12 @@
. = list()
SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_OVERLAYS, .)
/atom/proc/relaymove(mob/user)
if(buckle_message_cooldown <= world.time)
buckle_message_cooldown = world.time + 50
/atom/proc/relaymove(mob/living/user)
if(!istype(user))
return //why are you buckling nonliving mobs to atoms?
if(user.buckle_message_cooldown <= world.time)
user.buckle_message_cooldown = world.time + 50
to_chat(user, "<span class='warning'>You can't move while buckled to [src]!</span>")
return
/atom/proc/contents_explosion(severity, target)
return //For handling the effects of explosions on contents that would not normally be effected
@@ -360,7 +383,7 @@
SEND_SIGNAL(src, COMSIG_ATOM_FIRE_ACT, exposed_temperature, exposed_volume)
return
/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked)
/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...).
addtimer(CALLBACK(src, .proc/hitby_react, AM), 2)
@@ -505,9 +528,8 @@
return final_rgb
/atom/proc/clean_blood()
if(islist(blood_DNA))
blood_DNA = null
return TRUE
. = blood_DNA? TRUE : FALSE
blood_DNA = null
/atom/proc/wash_cream()
return TRUE
@@ -672,14 +694,64 @@
/atom/vv_get_dropdown()
. = ..()
. += "---"
var/turf/curturf = get_turf(src)
if (curturf)
.["Jump to"] = "?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[curturf.x];Y=[curturf.y];Z=[curturf.z]"
.["Modify Transform"] = "?_src_=vars;[HrefToken()];modtransform=[REF(src)]"
.["Add reagent"] = "?_src_=vars;[HrefToken()];addreagent=[REF(src)]"
.["Trigger EM pulse"] = "?_src_=vars;[HrefToken()];emp=[REF(src)]"
.["Trigger explosion"] = "?_src_=vars;[HrefToken()];explode=[REF(src)]"
VV_DROPDOWN_OPTION("", "---------")
if(!ismovableatom(src))
var/turf/curturf = get_turf(src)
if(curturf)
. += "<option value='?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[curturf.x];Y=[curturf.y];Z=[curturf.z]'>Jump To</option>"
VV_DROPDOWN_OPTION(VV_HK_MODIFY_TRANSFORM, "Modify Transform")
VV_DROPDOWN_OPTION(VV_HK_ADD_REAGENT, "Add Reagent")
VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EMP, "EMP Pulse")
VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EXPLOSION, "Explosion")
/atom/vv_do_topic(list/href_list)
. = ..()
if(href_list[VV_HK_ADD_REAGENT] && check_rights(R_VAREDIT))
if(!reagents)
var/amount = input(usr, "Specify the reagent size of [src]", "Set Reagent Size", 50) as num
if(amount)
create_reagents(amount)
if(reagents)
var/chosen_id = choose_reagent_id(usr)
if(chosen_id)
var/amount = input(usr, "Choose the amount to add.", "Choose the amount.", reagents.maximum_volume) as num
if(amount)
reagents.add_reagent(chosen_id, amount)
log_admin("[key_name(usr)] has added [amount] units of [chosen_id] to [src]")
message_admins("<span class='notice'>[key_name(usr)] has added [amount] units of [chosen_id] to [src]</span>")
if(href_list[VV_HK_TRIGGER_EXPLOSION] && check_rights(R_FUN))
usr.client.cmd_admin_explosion(src)
if(href_list[VV_HK_TRIGGER_EMP] && check_rights(R_FUN))
usr.client.cmd_admin_emp(src)
if(href_list[VV_HK_MODIFY_TRANSFORM] && check_rights(R_VAREDIT))
var/result = input(usr, "Choose the transformation to apply","Transform Mod") as null|anything in list("Scale","Translate","Rotate")
var/matrix/M = transform
switch(result)
if("Scale")
var/x = input(usr, "Choose x mod","Transform Mod") as null|num
var/y = input(usr, "Choose y mod","Transform Mod") as null|num
if(!isnull(x) && !isnull(y))
transform = M.Scale(x,y)
if("Translate")
var/x = input(usr, "Choose x mod","Transform Mod") as null|num
var/y = input(usr, "Choose y mod","Transform Mod") as null|num
if(!isnull(x) && !isnull(y))
transform = M.Translate(x,y)
if("Rotate")
var/angle = input(usr, "Choose angle to rotate","Transform Mod") as null|num
if(!isnull(angle))
transform = M.Turn(angle)
if(href_list[VV_HK_AUTO_RENAME] && check_rights(R_VAREDIT))
var/newname = input(usr, "What do you want to rename this to?", "Automatic Rename") as null|text
if(newname)
vv_auto_rename(newname)
/atom/vv_get_header()
. = ..()
var/refid = REF(src)
. += "[VV_HREF_TARGETREF(refid, VV_HK_AUTO_RENAME, "<b id='name'>[src]</b>")]"
. += "<br><font size='1'><a href='?_src_=vars;[HrefToken()];rotatedatum=[refid];rotatedir=left'><<</a> <a href='?_src_=vars;[HrefToken()];datumedit=[refid];varnameedit=dir' id='dir'>[dir2text(dir) || dir]</a> <a href='?_src_=vars;[HrefToken()];rotatedatum=[refid];rotatedir=right'>>></a></font>"
/atom/proc/drop_location()
var/atom/L = loc
@@ -687,6 +759,9 @@
return null
return L.AllowDrop() ? L : L.drop_location()
/atom/proc/vv_auto_rename(newname)
name = newname
/atom/Entered(atom/movable/AM, atom/oldLoc)
SEND_SIGNAL(src, COMSIG_ATOM_ENTERED, AM, oldLoc)
@@ -728,6 +803,13 @@
/atom/proc/multitool_act(mob/living/user, obj/item/I)
return
/atom/proc/multitool_check_buffer(user, obj/item/I, silent = FALSE)
if(!istype(I, /obj/item/multitool))
if(user && !silent)
to_chat(user, "<span class='warning'>[I] has no data buffer!</span>")
return FALSE
return TRUE
/atom/proc/screwdriver_act(mob/living/user, obj/item/I)
SEND_SIGNAL(src, COMSIG_ATOM_SCREWDRIVER_ACT, user, I)
@@ -761,6 +843,8 @@
log_whisper(log_text)
if(LOG_EMOTE)
log_emote(log_text)
if(LOG_SUBTLER)
log_subtler(log_text)
if(LOG_DSAY)
log_dsay(log_text)
if(LOG_PDA)
@@ -863,4 +947,73 @@ Proc for attack log creation, because really why not
return TRUE
/atom/proc/intercept_zImpact(atom/movable/AM, levels = 1)
. |= SEND_SIGNAL(src, COMSIG_ATOM_INTERCEPT_Z_FALL, AM, levels)
. |= SEND_SIGNAL(src, COMSIG_ATOM_INTERCEPT_Z_FALL, AM, levels)
///Sets the custom materials for an item.
/atom/proc/set_custom_materials(var/list/materials, multiplier = 1)
if(!materials)
materials = custom_materials
if(custom_materials) //Only runs if custom materials existed at first. Should usually be the case but check anyways
for(var/i in custom_materials)
var/datum/material/custom_material = SSmaterials.GetMaterialRef(i)
custom_material.on_removed(src, material_flags) //Remove the current materials
if(!length(materials))
return
custom_materials = list() //Reset the list
for(var/x in materials)
var/datum/material/custom_material = SSmaterials.GetMaterialRef(x)
if(!(material_flags & MATERIAL_NO_EFFECTS))
custom_material.on_applied(src, materials[custom_material] * multiplier * material_modifier, material_flags)
custom_materials[custom_material] += materials[x] * multiplier
/**
* Returns true if this atom has gravity for the passed in turf
*
* Sends signals COMSIG_ATOM_HAS_GRAVITY and COMSIG_TURF_HAS_GRAVITY, both can force gravity with
* the forced gravity var
*
* Gravity situations:
* * No gravity if you're not in a turf
* * No gravity if this atom is in is a space turf
* * Gravity if the area it's in always has gravity
* * Gravity if there's a gravity generator on the z level
* * Gravity if the Z level has an SSMappingTrait for ZTRAIT_GRAVITY
* * otherwise no gravity
*/
/atom/proc/has_gravity(turf/T)
if(!T || !isturf(T))
T = get_turf(src)
if(!T)
return 0
var/list/forced_gravity = list()
SEND_SIGNAL(src, COMSIG_ATOM_HAS_GRAVITY, T, forced_gravity)
if(!forced_gravity.len)
SEND_SIGNAL(T, COMSIG_TURF_HAS_GRAVITY, src, forced_gravity)
if(forced_gravity.len)
var/max_grav
for(var/i in forced_gravity)
max_grav = max(max_grav, i)
return max_grav
if(isspaceturf(T)) // Turf never has gravity
return 0
var/area/A = get_area(T)
if(A.has_gravity) // Areas which always has gravity
return A.has_gravity
else
// There's a gravity generator on our z level
if(GLOB.gravity_generators["[T.z]"])
var/max_grav = 0
for(var/obj/machinery/gravity_generator/main/G in GLOB.gravity_generators["[T.z]"])
max_grav = max(G.setting,max_grav)
return max_grav
return SSmapping.level_trait(T.z, ZTRAIT_GRAVITY)
+79 -322
View File
@@ -3,6 +3,9 @@
var/last_move = null
var/last_move_time = 0
var/anchored = FALSE
var/move_resist = MOVE_RESIST_DEFAULT
var/move_force = MOVE_FORCE_DEFAULT
var/pull_force = PULL_FORCE_DEFAULT
var/datum/thrownthing/throwing = null
var/throw_speed = 2 //How many tiles to move per ds when being thrown. Float values are fully supported
var/throw_range = 7
@@ -36,6 +39,37 @@
var/zfalling = FALSE
/// Either FALSE, [EMISSIVE_BLOCK_GENERIC], or [EMISSIVE_BLOCK_UNIQUE]
var/blocks_emissive = FALSE
///Internal holder for emissive blocker object, do not use directly use blocks_emissive
var/atom/movable/emissive_blocker/em_block
/atom/movable/Initialize(mapload)
. = ..()
switch(blocks_emissive)
if(EMISSIVE_BLOCK_GENERIC)
update_emissive_block()
if(EMISSIVE_BLOCK_UNIQUE)
render_target = ref(src)
em_block = new(src, render_target)
vis_contents += em_block
/atom/movable/Destroy()
QDEL_NULL(em_block)
return ..()
/atom/movable/proc/update_emissive_block()
if(blocks_emissive != EMISSIVE_BLOCK_GENERIC)
return
if(length(managed_vis_overlays))
for(var/a in managed_vis_overlays)
var/obj/effect/overlay/vis/vs
if(vs.plane == EMISSIVE_BLOCKER_PLANE)
SSvis_overlays.remove_vis_overlay(src, list(vs))
break
SSvis_overlays.add_vis_overlay(src, icon, icon_state, EMISSIVE_BLOCKER_LAYER, EMISSIVE_BLOCKER_PLANE)
/atom/movable/proc/can_zFall(turf/source, levels = 1, turf/target, direction)
if(!direction)
direction = DOWN
@@ -115,20 +149,20 @@
return FALSE
return ..()
/atom/movable/proc/start_pulling(atom/movable/AM,gs)
/atom/movable/proc/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE)
if(QDELETED(AM))
return FALSE
if(!(AM.can_be_pulled(src)))
if(!(AM.can_be_pulled(src, state, force)))
return FALSE
// If we're pulling something then drop what we're currently pulling and pull this instead.
if(pulling)
if(gs==0)
if(state == 0)
stop_pulling()
return FALSE
// Are we trying to pull something we are already pulling? Then enter grab cycle and end.
if(AM == pulling)
grab_state = gs
setGrabState(state)
if(istype(AM,/mob/living))
var/mob/living/AMob = AM
AMob.grabbedby(src)
@@ -139,11 +173,12 @@
AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once.
pulling = AM
AM.pulledby = src
grab_state = gs
setGrabState(state)
if(ismob(AM))
var/mob/M = AM
log_combat(src, M, "grabbed", addition="passive grab")
visible_message("<span class='warning'>[src] has grabbed [M] passively!</span>")
if(!supress_message)
visible_message("<span class='warning'>[src] has grabbed [M] passively!</span>")
return TRUE
/atom/movable/proc/stop_pulling()
@@ -151,15 +186,15 @@
pulling.pulledby = null
var/mob/living/ex_pulled = pulling
pulling = null
grab_state = 0
setGrabState(0)
if(isliving(ex_pulled))
var/mob/living/L = ex_pulled
L.update_canmove()// mob gets up if it was lyng down in a chokehold
L.update_mobility()// mob gets up if it was lyng down in a chokehold
/atom/movable/proc/Move_Pulled(atom/A)
if(!pulling)
return
if(pulling.anchored || !pulling.Adjacent(src))
if(pulling.anchored || pulling.move_resist > move_force || !pulling.Adjacent(src))
stop_pulling()
return
if(isliving(pulling))
@@ -186,164 +221,10 @@
log_game("DEBUG:[src]'s pull on [pullee] wasn't broken despite [pullee] being in [pullee.loc]. Pull stopped manually.")
stop_pulling()
return
if(pulling.anchored)
if(pulling.anchored || pulling.move_resist > move_force)
stop_pulling()
return
////////////////////////////////////////
// Here's where we rewrite how byond handles movement except slightly different
// To be removed on step_ conversion
// All this work to prevent a second bump
/atom/movable/Move(atom/newloc, direct=0)
. = FALSE
if(!newloc || newloc == loc)
return
if(!direct)
direct = get_dir(src, newloc)
setDir(direct)
if(!loc.Exit(src, newloc))
return
if(!newloc.Enter(src, src.loc))
return
// Past this is the point of no return
var/atom/oldloc = loc
var/area/oldarea = get_area(oldloc)
var/area/newarea = get_area(newloc)
loc = newloc
. = TRUE
oldloc.Exited(src, newloc)
if(oldarea != newarea)
oldarea.Exited(src, newloc)
for(var/i in oldloc)
if(i == src) // Multi tile objects
continue
var/atom/movable/thing = i
thing.Uncrossed(src)
newloc.Entered(src, oldloc)
if(oldarea != newarea)
newarea.Entered(src, oldloc)
for(var/i in loc)
if(i == src) // Multi tile objects
continue
var/atom/movable/thing = i
thing.Crossed(src)
//
////////////////////////////////////////
/atom/movable/Move(atom/newloc, direct)
var/atom/movable/pullee = pulling
var/turf/T = loc
if(pulling)
if(pullee && get_dist(src, pullee) > 1)
stop_pulling()
if(pullee && pullee.loc != loc && !isturf(pullee.loc) ) //to be removed once all code that changes an object's loc uses forceMove().
log_game("DEBUG:[src]'s pull on [pullee] wasn't broken despite [pullee] being in [pullee.loc]. Pull stopped manually.")
stop_pulling()
if(!loc || !newloc)
return FALSE
var/atom/oldloc = loc
if(loc != newloc)
if (!(direct & (direct - 1))) //Cardinal move
. = ..()
else //Diagonal move, split it into cardinal moves
moving_diagonally = FIRST_DIAG_STEP
var/first_step_dir
// The `&& moving_diagonally` checks are so that a forceMove taking
// place due to a Crossed, Bumped, etc. call will interrupt
// the second half of the diagonal movement, or the second attempt
// at a first half if step() fails because we hit something.
if (direct & NORTH)
if (direct & EAST)
if (step(src, NORTH) && moving_diagonally)
first_step_dir = NORTH
moving_diagonally = SECOND_DIAG_STEP
. = step(src, EAST)
else if (moving_diagonally && step(src, EAST))
first_step_dir = EAST
moving_diagonally = SECOND_DIAG_STEP
. = step(src, NORTH)
else if (direct & WEST)
if (step(src, NORTH) && moving_diagonally)
first_step_dir = NORTH
moving_diagonally = SECOND_DIAG_STEP
. = step(src, WEST)
else if (moving_diagonally && step(src, WEST))
first_step_dir = WEST
moving_diagonally = SECOND_DIAG_STEP
. = step(src, NORTH)
else if (direct & SOUTH)
if (direct & EAST)
if (step(src, SOUTH) && moving_diagonally)
first_step_dir = SOUTH
moving_diagonally = SECOND_DIAG_STEP
. = step(src, EAST)
else if (moving_diagonally && step(src, EAST))
first_step_dir = EAST
moving_diagonally = SECOND_DIAG_STEP
. = step(src, SOUTH)
else if (direct & WEST)
if (step(src, SOUTH) && moving_diagonally)
first_step_dir = SOUTH
moving_diagonally = SECOND_DIAG_STEP
. = step(src, WEST)
else if (moving_diagonally && step(src, WEST))
first_step_dir = WEST
moving_diagonally = SECOND_DIAG_STEP
. = step(src, SOUTH)
if(moving_diagonally == SECOND_DIAG_STEP)
if(!.)
setDir(first_step_dir)
else if (!inertia_moving)
inertia_next_move = world.time + inertia_move_delay
newtonian_move(direct)
moving_diagonally = 0
return
if(!loc || (loc == oldloc && oldloc != newloc))
last_move = 0
return
if(.)
Moved(oldloc, direct)
if(. && pulling && pulling == pullee) //we were pulling a thing and didn't lose it during our move.
if(pulling.anchored)
stop_pulling()
else
var/pull_dir = get_dir(src, pulling)
//puller and pullee more than one tile away or in diagonal position
if(get_dist(src, pulling) > 1 || (moving_diagonally != SECOND_DIAG_STEP && ((pull_dir - 1) & pull_dir)))
pulling.Move(T, get_dir(pulling, T)) //the pullee tries to reach our previous position
if(pulling && get_dist(src, pulling) > 1) //the pullee couldn't keep up
stop_pulling()
if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1)//separated from our puller and not in the middle of a diagonal move.
pulledby.stop_pulling()
last_move = direct
setDir(direct)
if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc,direct)) //movement failed due to buckled mob(s)
return FALSE
//Called after a successful Move(). By this point, we've already moved
/atom/movable/proc/Moved(atom/OldLoc, Dir, Forced = FALSE)
SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, OldLoc, Dir, Forced)
if (!inertia_moving)
inertia_next_move = world.time + inertia_move_delay
newtonian_move(Dir)
if (length(client_mobs_in_contents))
update_parallax_contents()
return TRUE
/atom/movable/Destroy(force)
QDEL_NULL(proximity_monitor)
QDEL_NULL(language_holder)
@@ -368,157 +249,22 @@
orbiting.end_orbit(src)
orbiting = null
// Make sure you know what you're doing if you call this, this is intended to only be called by byond directly.
// You probably want CanPass()
/atom/movable/Cross(atom/movable/AM)
. = TRUE
SEND_SIGNAL(src, COMSIG_MOVABLE_CROSS, AM)
return CanPass(AM, AM.loc, TRUE)
//oldloc = old location on atom, inserted when forceMove is called and ONLY when forceMove is called!
/atom/movable/Crossed(atom/movable/AM, oldloc)
SEND_SIGNAL(src, COMSIG_MOVABLE_CROSSED, AM)
/atom/movable/Uncross(atom/movable/AM, atom/newloc)
. = ..()
if(SEND_SIGNAL(src, COMSIG_MOVABLE_UNCROSS, AM) & COMPONENT_MOVABLE_BLOCK_UNCROSS)
return FALSE
if(isturf(newloc) && !CheckExit(AM, newloc))
return FALSE
/atom/movable/Uncrossed(atom/movable/AM)
SEND_SIGNAL(src, COMSIG_MOVABLE_UNCROSSED, AM)
/atom/movable/Bump(atom/A)
if(!A)
CRASH("Bump was called with no argument.")
SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, A)
. = ..()
if(!QDELETED(throwing))
throwing.hit_atom(A)
. = TRUE
if(QDELETED(A))
return
A.Bumped(src)
/atom/movable/proc/forceMove(atom/destination)
. = FALSE
if(destination)
. = doMove(destination)
else
CRASH("No valid destination passed into forceMove")
/atom/movable/proc/moveToNullspace()
return doMove(null)
/atom/movable/proc/doMove(atom/destination)
. = FALSE
if(destination)
if(pulledby)
pulledby.stop_pulling()
var/atom/oldloc = loc
var/same_loc = oldloc == destination
var/area/old_area = get_area(oldloc)
var/area/destarea = get_area(destination)
loc = destination
moving_diagonally = 0
if(!same_loc)
if(oldloc)
oldloc.Exited(src, destination)
if(old_area && old_area != destarea)
old_area.Exited(src, destination)
for(var/atom/movable/AM in oldloc)
AM.Uncrossed(src)
var/turf/oldturf = get_turf(oldloc)
var/turf/destturf = get_turf(destination)
var/old_z = (oldturf ? oldturf.z : null)
var/dest_z = (destturf ? destturf.z : null)
if (old_z != dest_z)
onTransitZ(old_z, dest_z)
destination.Entered(src, oldloc)
if(destarea && old_area != destarea)
destarea.Entered(src, oldloc)
for(var/atom/movable/AM in destination)
if(AM == src)
continue
AM.Crossed(src, oldloc)
Moved(oldloc, NONE, TRUE)
. = TRUE
//If no destination, move the atom into nullspace (don't do this unless you know what you're doing)
else
. = TRUE
if (loc)
var/atom/oldloc = loc
var/area/old_area = get_area(oldloc)
oldloc.Exited(src, null)
if(old_area)
old_area.Exited(src, null)
loc = null
/atom/movable/proc/onTransitZ(old_z,new_z)
SEND_SIGNAL(src, COMSIG_MOVABLE_Z_CHANGED, old_z, new_z)
for (var/item in src) // Notify contents of Z-transition. This can be overridden IF we know the items contents do not care.
var/atom/movable/AM = item
AM.onTransitZ(old_z,new_z)
/atom/movable/proc/setMovetype(newval)
movement_type = newval
//Called whenever an object moves and by mobs when they attempt to move themselves through space
//And when an object or action applies a force on src, see newtonian_move() below
//Return 0 to have src start/keep drifting in a no-grav area and 1 to stop/not start drifting
//Mobs should return 1 if they should be able to move of their own volition, see client/Move() in mob_movement.dm
//movement_dir == 0 when stopping or any dir when trying to move
/atom/movable/proc/Process_Spacemove(movement_dir = 0)
if(has_gravity(src))
return 1
if(pulledby)
return 1
if(throwing)
return 1
if(!isturf(loc))
return 1
if(locate(/obj/structure/lattice) in range(1, get_turf(src))) //Not realistic but makes pushing things in space easier
return 1
return 0
/atom/movable/proc/newtonian_move(direction) //Only moves the object if it's under no gravity
if(!loc || Process_Spacemove(0))
inertia_dir = 0
return 0
inertia_dir = direction
if(!direction)
return 1
inertia_last_loc = loc
SSspacedrift.processing[src] = src
return 1
/atom/movable/proc/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
set waitfor = 0
SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum)
return hit_atom.hitby(src)
return hit_atom.hitby(src, throwingdatum=throwingdatum)
/atom/movable/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked)
if(!anchored && hitpush)
/atom/movable/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked, datum/thrownthing/throwingdatum)
if(!anchored && hitpush && (!throwingdatum || (throwingdatum.force >= (move_resist * MOVE_FORCE_PUSH_RATIO))))
step(src, AM.dir)
..()
/atom/movable/proc/safe_throw_at(atom/target, range, speed, mob/thrower, spin=TRUE, diagonals_first = FALSE, var/datum/callback/callback, messy_throw = TRUE)
return throw_at(target, range, speed, thrower, spin, diagonals_first, callback, messy_throw)
/atom/movable/proc/safe_throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = INFINITY, messy_throw = TRUE)
if((force < (move_resist * MOVE_FORCE_THROW_RATIO)) || (move_resist == INFINITY))
return
return throw_at(target, range, speed, thrower, spin, diagonals_first, callback, force, messy_throw)
/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin=TRUE, diagonals_first = FALSE, var/datum/callback/callback, messy_throw = TRUE) //If this returns FALSE then callback will not be called.
/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = INFINITY, messy_throw = TRUE) //If this returns FALSE then callback will not be called.
. = FALSE
if (!target || speed <= 0)
return
@@ -564,6 +310,7 @@
TT.speed = speed
TT.thrower = thrower
TT.diagonals_first = diagonals_first
TT.force = force
TT.callback = callback
if(!QDELETED(thrower))
TT.target_zone = thrower.zone_selected
@@ -603,20 +350,25 @@
SSthrowing.currentrun[src] = TT
TT.tick()
/atom/movable/proc/handle_buckled_mob_movement(newloc,direct)
for(var/m in buckled_mobs)
var/mob/living/buckled_mob = m
if(!buckled_mob.Move(newloc, direct))
forceMove(buckled_mob.loc)
last_move = buckled_mob.last_move
inertia_dir = last_move
buckled_mob.inertia_dir = last_move
return 0
return 1
/atom/movable/proc/force_pushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction)
return FALSE
/atom/movable/proc/force_push(atom/movable/AM, force = move_force, direction, silent = FALSE)
. = AM.force_pushed(src, force, direction)
if(!silent && .)
visible_message("<span class='warning'>[src] forcefully pushes against [AM]!</span>", "<span class='warning'>You forcefully push against [AM]!</span>")
/atom/movable/proc/move_crush(atom/movable/AM, force = move_force, direction, silent = FALSE)
. = AM.move_crushed(src, force, direction)
if(!silent && .)
visible_message("<span class='danger'>[src] crushes past [AM]!</span>", "<span class='danger'>You crush [AM]!</span>")
/atom/movable/proc/move_crushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction)
return FALSE
/atom/movable/CanPass(atom/movable/mover, turf/target)
if(mover in buckled_mobs)
return 1
return TRUE
return ..()
// called when this atom is removed from a storage item, which is passed on as S. The loc variable is already set to the new destination before this is called.
@@ -716,9 +468,8 @@
/atom/movable/vv_get_dropdown()
. = ..()
. -= "Jump to"
.["Follow"] = "?_src_=holder;[HrefToken()];adminplayerobservefollow=[REF(src)]"
.["Get"] = "?_src_=holder;[HrefToken()];admingetmovable=[REF(src)]"
. += "<option value='?_src_=holder;[HrefToken()];adminplayerobservefollow=[REF(src)]'>Follow</option>"
. += "<option value='?_src_=holder;[HrefToken()];admingetmovable=[REF(src)]'>Get</option>"
/atom/movable/proc/ex_check(ex_id)
if(!ex_id)
@@ -833,13 +584,19 @@
/atom/movable/proc/get_cell()
return
/atom/movable/proc/can_be_pulled(user)
/atom/movable/proc/can_be_pulled(user, grab_state, force)
if(src == user || !isturf(loc))
return FALSE
if(anchored || throwing)
return FALSE
if(force < (move_resist * MOVE_FORCE_PULL_RATIO))
return FALSE
return TRUE
/// Updates the grab state of the movable
/// This exists to act as a hook for behaviour
/atom/movable/proc/setGrabState(newstate)
grab_state = newstate
/obj/item/proc/do_pickup_animation(atom/target)
set waitfor = FALSE
+307
View File
@@ -0,0 +1,307 @@
// File for movement procs for atom/movable
////////////////////////////////////////
// Here's where we rewrite how byond handles movement except slightly different
// To be removed on step_ conversion
// All this work to prevent a second bump
/atom/movable/Move(atom/newloc, direct=0)
. = FALSE
if(!newloc || newloc == loc)
return
if(!direct)
direct = get_dir(src, newloc)
setDir(direct)
if(!loc.Exit(src, newloc))
return
if(!newloc.Enter(src, src.loc))
return
// Past this is the point of no return
var/atom/oldloc = loc
var/area/oldarea = get_area(oldloc)
var/area/newarea = get_area(newloc)
loc = newloc
. = TRUE
oldloc.Exited(src, newloc)
if(oldarea != newarea)
oldarea.Exited(src, newloc)
for(var/i in oldloc)
if(i == src) // Multi tile objects
continue
var/atom/movable/thing = i
thing.Uncrossed(src)
newloc.Entered(src, oldloc)
if(oldarea != newarea)
newarea.Entered(src, oldloc)
for(var/i in loc)
if(i == src) // Multi tile objects
continue
var/atom/movable/thing = i
thing.Crossed(src)
//
////////////////////////////////////////
/atom/movable/Move(atom/newloc, direct)
var/atom/movable/pullee = pulling
var/turf/T = loc
if(pulling)
if(pullee && get_dist(src, pullee) > 1)
stop_pulling()
if(pullee && pullee.loc != loc && !isturf(pullee.loc) ) //to be removed once all code that changes an object's loc uses forceMove().
log_game("DEBUG:[src]'s pull on [pullee] wasn't broken despite [pullee] being in [pullee.loc]. Pull stopped manually.")
stop_pulling()
if(!loc || !newloc)
return FALSE
var/atom/oldloc = loc
if(loc != newloc)
if (!(direct & (direct - 1))) //Cardinal move
. = ..()
else //Diagonal move, split it into cardinal moves
moving_diagonally = FIRST_DIAG_STEP
var/first_step_dir
// The `&& moving_diagonally` checks are so that a forceMove taking
// place due to a Crossed, Bumped, etc. call will interrupt
// the second half of the diagonal movement, or the second attempt
// at a first half if step() fails because we hit something.
if (direct & NORTH)
if (direct & EAST)
if (step(src, NORTH) && moving_diagonally)
first_step_dir = NORTH
moving_diagonally = SECOND_DIAG_STEP
. = step(src, EAST)
else if (moving_diagonally && step(src, EAST))
first_step_dir = EAST
moving_diagonally = SECOND_DIAG_STEP
. = step(src, NORTH)
else if (direct & WEST)
if (step(src, NORTH) && moving_diagonally)
first_step_dir = NORTH
moving_diagonally = SECOND_DIAG_STEP
. = step(src, WEST)
else if (moving_diagonally && step(src, WEST))
first_step_dir = WEST
moving_diagonally = SECOND_DIAG_STEP
. = step(src, NORTH)
else if (direct & SOUTH)
if (direct & EAST)
if (step(src, SOUTH) && moving_diagonally)
first_step_dir = SOUTH
moving_diagonally = SECOND_DIAG_STEP
. = step(src, EAST)
else if (moving_diagonally && step(src, EAST))
first_step_dir = EAST
moving_diagonally = SECOND_DIAG_STEP
. = step(src, SOUTH)
else if (direct & WEST)
if (step(src, SOUTH) && moving_diagonally)
first_step_dir = SOUTH
moving_diagonally = SECOND_DIAG_STEP
. = step(src, WEST)
else if (moving_diagonally && step(src, WEST))
first_step_dir = WEST
moving_diagonally = SECOND_DIAG_STEP
. = step(src, SOUTH)
if(moving_diagonally == SECOND_DIAG_STEP)
if(!.)
setDir(first_step_dir)
else if (!inertia_moving)
inertia_next_move = world.time + inertia_move_delay
newtonian_move(direct)
moving_diagonally = 0
return
if(!loc || (loc == oldloc && oldloc != newloc))
last_move = NONE
return
if(.)
last_move = direct
setDir(direct)
if(has_buckled_mobs() && !handle_buckled_mob_movement(loc,direct)) //movement failed due to buckled mob(s)
return FALSE
if(pulling && pulling == pullee) //we were pulling a thing and didn't lose it during our move.
if(pulling.anchored)
stop_pulling()
else
var/pull_dir = get_dir(src, pulling)
//puller and pullee more than one tile away or in diagonal position
if(get_dist(src, pulling) > 1 || (moving_diagonally != SECOND_DIAG_STEP && ((pull_dir - 1) & pull_dir)))
pulling.Move(T, get_dir(pulling, T)) //the pullee tries to reach our previous position
if(pulling && get_dist(src, pulling) > 1) //the pullee couldn't keep up
stop_pulling()
if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1)//separated from our puller and not in the middle of a diagonal move.
pulledby.stop_pulling()
Moved(oldloc, direct)
/atom/movable/proc/handle_buckled_mob_movement(newloc,direct)
for(var/m in buckled_mobs)
var/mob/living/buckled_mob = m
if(!buckled_mob.Move(newloc, direct))
forceMove(buckled_mob.loc)
last_move = buckled_mob.last_move
inertia_dir = last_move
buckled_mob.inertia_dir = last_move
return FALSE
return TRUE
//Called after a successful Move(). By this point, we've already moved
/atom/movable/proc/Moved(atom/OldLoc, Dir, Forced = FALSE)
SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, OldLoc, Dir, Forced)
if (!inertia_moving)
inertia_next_move = world.time + inertia_move_delay
newtonian_move(Dir)
if (length(client_mobs_in_contents))
update_parallax_contents()
return TRUE
// Make sure you know what you're doing if you call this, this is intended to only be called by byond directly.
// You probably want CanPass()
/atom/movable/Cross(atom/movable/AM)
. = TRUE
SEND_SIGNAL(src, COMSIG_MOVABLE_CROSS, AM)
return CanPass(AM, AM.loc, TRUE)
//oldloc = old location on atom, inserted when forceMove is called and ONLY when forceMove is called!
/atom/movable/Crossed(atom/movable/AM, oldloc)
SEND_SIGNAL(src, COMSIG_MOVABLE_CROSSED, AM)
/atom/movable/Uncross(atom/movable/AM, atom/newloc)
. = ..()
if(SEND_SIGNAL(src, COMSIG_MOVABLE_UNCROSS, AM) & COMPONENT_MOVABLE_BLOCK_UNCROSS)
return FALSE
if(isturf(newloc) && !CheckExit(AM, newloc))
return FALSE
/atom/movable/Uncrossed(atom/movable/AM)
SEND_SIGNAL(src, COMSIG_MOVABLE_UNCROSSED, AM)
/atom/movable/Bump(atom/A)
if(!A)
CRASH("Bump was called with no argument.")
SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, A)
. = ..()
if(!QDELETED(throwing))
throwing.hit_atom(A)
. = TRUE
if(QDELETED(A))
return
A.Bumped(src)
/atom/movable/proc/onTransitZ(old_z,new_z)
SEND_SIGNAL(src, COMSIG_MOVABLE_Z_CHANGED, old_z, new_z)
for (var/item in src) // Notify contents of Z-transition. This can be overridden IF we know the items contents do not care.
var/atom/movable/AM = item
AM.onTransitZ(old_z,new_z)
/atom/movable/proc/setMovetype(newval)
movement_type = newval
///////////// FORCED MOVEMENT /////////////
/atom/movable/proc/forceMove(atom/destination)
. = FALSE
if(destination)
. = doMove(destination)
else
CRASH("No valid destination passed into forceMove")
/atom/movable/proc/moveToNullspace()
return doMove(null)
/atom/movable/proc/doMove(atom/destination)
. = FALSE
if(destination)
if(pulledby)
pulledby.stop_pulling()
var/atom/oldloc = loc
var/same_loc = oldloc == destination
var/area/old_area = get_area(oldloc)
var/area/destarea = get_area(destination)
loc = destination
moving_diagonally = 0
if(!same_loc)
if(oldloc)
oldloc.Exited(src, destination)
if(old_area && old_area != destarea)
old_area.Exited(src, destination)
for(var/atom/movable/AM in oldloc)
AM.Uncrossed(src)
var/turf/oldturf = get_turf(oldloc)
var/turf/destturf = get_turf(destination)
var/old_z = (oldturf ? oldturf.z : null)
var/dest_z = (destturf ? destturf.z : null)
if (old_z != dest_z)
onTransitZ(old_z, dest_z)
destination.Entered(src, oldloc)
if(destarea && old_area != destarea)
destarea.Entered(src, oldloc)
for(var/atom/movable/AM in destination)
if(AM == src)
continue
AM.Crossed(src, oldloc)
Moved(oldloc, NONE, TRUE)
. = TRUE
//If no destination, move the atom into nullspace (don't do this unless you know what you're doing)
else
. = TRUE
if (loc)
var/atom/oldloc = loc
var/area/old_area = get_area(oldloc)
oldloc.Exited(src, null)
if(old_area)
old_area.Exited(src, null)
loc = null
//Called whenever an object moves and by mobs when they attempt to move themselves through space
//And when an object or action applies a force on src, see newtonian_move() below
//Return 0 to have src start/keep drifting in a no-grav area and 1 to stop/not start drifting
//Mobs should return 1 if they should be able to move of their own volition, see client/Move() in mob_movement.dm
//movement_dir == 0 when stopping or any dir when trying to move
/atom/movable/proc/Process_Spacemove(movement_dir = 0)
if(has_gravity(src))
return 1
if(pulledby)
return 1
if(throwing)
return 1
if(!isturf(loc))
return 1
if(locate(/obj/structure/lattice) in range(1, get_turf(src))) //Not realistic but makes pushing things in space easier
return 1
return 0
/atom/movable/proc/newtonian_move(direction) //Only moves the object if it's under no gravity
if(!loc || Process_Spacemove(0))
inertia_dir = 0
return 0
inertia_dir = direction
if(!direction)
return 1
inertia_last_loc = loc
SSspacedrift.processing[src] = src
return 1
@@ -7,7 +7,7 @@
// LISTS //
var/list/vassal_allowed_antags = list(/datum/antagonist/brother, /datum/antagonist/traitor, /datum/antagonist/traitor/internal_affairs, /datum/antagonist/survivalist, \
/datum/antagonist/rev, /datum/antagonist/nukeop, /datum/antagonist/pirate, /datum/antagonist/cult, /datum/antagonist/abductee)
/datum/antagonist/rev, /datum/antagonist/nukeop, /datum/antagonist/pirate, /datum/antagonist/cult, /datum/antagonist/abductee, /datum/antagonist/valentine, /datum/antagonist/heartbreaker,)
// The antags you're allowed to be if turning Vassal.
/proc/isvamp(mob/living/M)
return istype(M) && M.mind && M.mind.has_antag_datum(/datum/antagonist/bloodsucker)
@@ -19,7 +19,7 @@
antag_flag = ROLE_BLOODSUCKER
false_report_weight = 1
restricted_jobs = list("AI","Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
protected_jobs = list("Chaplain", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
required_players = 20
required_enemies = 2
recommended_enemies = 4
@@ -5,6 +5,7 @@
/datum/game_mode/traitor/bros
name = "traitor+brothers"
config_tag = "traitorbro"
required_players = 25
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
+2 -2
View File
@@ -88,13 +88,13 @@ GLOBAL_VAR(changeling_team_objective_type) //If this is not null, we hand our th
user.underwear = chosen_prof.underwear
user.undie_color = chosen_prof.undie_color
user.undershirt = chosen_prof.undershirt
user.shirt_color =chosen_prof.shirt_color
user.shirt_color = chosen_prof.shirt_color
user.socks = chosen_prof.socks
user.socks_color =chosen_prof.socks_color
chosen_dna.transfer_identity(user, 1)
user.updateappearance(mutcolor_update=1)
user.update_body()
user.update_body(TRUE)
user.domutcheck()
//vars hackery. not pretty, but better than the alternative.
+2 -2
View File
@@ -269,7 +269,7 @@ Credit where due:
//Servant of Ratvar outfit
/datum/outfit/servant_of_ratvar
name = "Servant of Ratvar"
uniform = /obj/item/clothing/under/rank/engineer //no more chameleon suit for them, as requested
uniform = /obj/item/clothing/under/rank/engineering/engineer //no more chameleon suit for them, as requested
shoes = /obj/item/clothing/shoes/sneakers/black
back = /obj/item/storage/backpack
ears = /obj/item/radio/headset
@@ -351,5 +351,5 @@ Credit where due:
/obj/item/paper/servant_primer/oui_getcontent(mob/target)
if(!is_servant_of_ratvar(target) && !isobserver(target))
return "<HTML><HEAD><TITLE>[name]</TITLE></HEAD><BODY>[stars(info)]<HR>[stamps]</BODY></HTML>"
return "<HTML><HEAD><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><TITLE>[name]</TITLE></HEAD><BODY>[stars(info)]<HR>[stamps]</BODY></HTML>"
return ..()
+11 -17
View File
@@ -4,23 +4,17 @@
icon = 'icons/obj/machines/nuke.dmi'
icon_state = "bananiumbomb_base"
/obj/machinery/nuclearbomb/syndicate/bananium/update_icon()
if(deconstruction_state == NUKESTATE_INTACT)
switch(get_nuke_state())
if(NUKE_OFF_LOCKED, NUKE_OFF_UNLOCKED)
icon_state = "bananiumbomb_base"
update_icon_interior()
update_icon_lights()
if(NUKE_ON_TIMING)
cut_overlays()
icon_state = "bananiumbomb_timing"
if(NUKE_ON_EXPLODING)
cut_overlays()
icon_state = "bananiumbomb_exploding"
else
/obj/machinery/nuclearbomb/syndicate/bananium/update_icon_state()
if(deconstruction_state != NUKESTATE_INTACT)
icon_state = "bananiumbomb_base"
update_icon_interior()
update_icon_lights()
return
switch(get_nuke_state())
if(NUKE_OFF_LOCKED, NUKE_OFF_UNLOCKED)
icon_state = "bananiumbomb_base"
if(NUKE_ON_TIMING)
icon_state = "bananiumbomb_timing"
if(NUKE_ON_EXPLODING)
icon_state = "bananiumbomb_exploding"
/obj/machinery/nuclearbomb/syndicate/bananium/get_cinematic_type(off_station)
switch(off_station)
@@ -41,7 +35,7 @@
H.Stun(10)
var/obj/item/clothing/C
if(!H.w_uniform || H.dropItemToGround(H.w_uniform))
C = new /obj/item/clothing/under/rank/clown(H)
C = new /obj/item/clothing/under/rank/civilian/clown(H)
ADD_TRAIT(C, TRAIT_NODROP, CLOWN_NUKE_TRAIT)
H.equip_to_slot_or_del(C, SLOT_W_UNIFORM)
+43 -33
View File
@@ -40,14 +40,14 @@
/obj/item/clothing/shoes/clown_shoes/banana_shoes/combat/Initialize()
. = ..()
var/datum/component/material_container/bananium = GetComponent(/datum/component/material_container)
bananium.insert_amount(max_recharge, MAT_BANANIUM)
bananium.insert_amount_mat(max_recharge, /datum/material/bananium)
START_PROCESSING(SSobj, src)
/obj/item/clothing/shoes/clown_shoes/banana_shoes/combat/process()
var/datum/component/material_container/bananium = GetComponent(/datum/component/material_container)
var/bananium_amount = bananium.amount(MAT_BANANIUM)
var/bananium_amount = bananium.get_material_amount(/datum/material/bananium)
if(bananium_amount < max_recharge)
bananium.insert_amount(min(recharge_rate, max_recharge - bananium_amount), MAT_BANANIUM)
bananium.insert_amount_mat(min(recharge_rate, max_recharge - bananium_amount), /datum/material/bananium)
/obj/item/clothing/shoes/clown_shoes/banana_shoes/combat/attack_self(mob/user)
ui_action_click(user)
@@ -57,10 +57,8 @@
/obj/item/melee/transforming/energy/sword/bananium
name = "bananium sword"
desc = "An elegant weapon, for a more civilized age."
force = 0
throwforce = 0
force_on = 0
throwforce_on = 0
force_on = 15
throwforce_on = 15
hitsound = null
attack_verb_on = list("slipped")
clumsy_check = FALSE
@@ -69,24 +67,26 @@
heat = 0
light_color = "#ffff00"
var/next_trombone_allowed = 0
var/datum/component/slippery/slipper
/obj/item/melee/transforming/energy/sword/bananium/Initialize()
. = ..()
AddComponent(/datum/component/slippery, 60, GALOSHES_DONT_HELP)
var/datum/component/slippery/slipper = GetComponent(/datum/component/slippery)
slipper = LoadComponent(/datum/component/slippery, 81, GALOSHES_DONT_HELP)
slipper.signal_enabled = active
/obj/item/melee/transforming/energy/sword/bananium/attack(mob/living/M, mob/living/user)
..()
if(active)
var/datum/component/slippery/slipper = GetComponent(/datum/component/slippery)
slipper.Slip(M)
slipper.lube_flags |= FLYING_DOESNT_HELP|SLIP_WHEN_CRAWLING
slipper.Slip(src, M)
slipper.lube_flags &= ~(FLYING_DOESNT_HELP|SLIP_WHEN_CRAWLING)
/obj/item/melee/transforming/energy/sword/bananium/throw_impact(atom/hit_atom, throwingdatum)
. = ..()
if(active)
var/datum/component/slippery/slipper = GetComponent(/datum/component/slippery)
slipper.Slip(hit_atom)
slipper.lube_flags |= FLYING_DOESNT_HELP|SLIP_WHEN_CRAWLING
slipper.Slip(src, hit_atom)
slipper.lube_flags &= ~(FLYING_DOESNT_HELP|SLIP_WHEN_CRAWLING)
/obj/item/melee/transforming/energy/sword/bananium/attackby(obj/item/I, mob/living/user, params)
if((world.time > next_trombone_allowed) && istype(I, /obj/item/melee/transforming/energy/sword/bananium))
@@ -98,7 +98,6 @@
/obj/item/melee/transforming/energy/sword/bananium/transform_weapon(mob/living/user, supress_message_text)
..()
var/datum/component/slippery/slipper = GetComponent(/datum/component/slippery)
slipper.signal_enabled = active
/obj/item/melee/transforming/energy/sword/bananium/ignition_effect(atom/A, mob/user)
@@ -108,8 +107,9 @@
if(!active)
transform_weapon(user, TRUE)
user.visible_message("<span class='suicide'>[user] is [pick("slitting [user.p_their()] stomach open with", "falling on")] [src]! It looks like [user.p_theyre()] trying to commit seppuku, but the blade slips off of [user.p_them()] harmlessly!</span>")
var/datum/component/slippery/slipper = GetComponent(/datum/component/slippery)
slipper.Slip(user)
slipper.lube_flags |= FLYING_DOESNT_HELP|SLIP_WHEN_CRAWLING
slipper.Slip(src, user)
slipper.lube_flags &= ~(FLYING_DOESNT_HELP|SLIP_WHEN_CRAWLING)
return SHAME
//BANANIUM SHIELD
@@ -126,16 +126,15 @@
on_force = 0
on_throwforce = 0
on_throw_speed = 1
var/datum/component/slippery/slipper
/obj/item/shield/energy/bananium/Initialize()
. = ..()
AddComponent(/datum/component/slippery, 60, GALOSHES_DONT_HELP)
var/datum/component/slippery/slipper = GetComponent(/datum/component/slippery)
slipper = LoadComponent(/datum/component/slippery, 81, GALOSHES_DONT_HELP)
slipper.signal_enabled = active
/obj/item/shield/energy/bananium/attack_self(mob/living/carbon/human/user)
..()
var/datum/component/slippery/slipper = GetComponent(/datum/component/slippery)
slipper.signal_enabled = active
/obj/item/shield/energy/bananium/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
@@ -145,12 +144,13 @@
C.throw_mode_on() //so they can catch it on the return.
return ..()
/obj/item/shield/energy/bananium/throw_impact(atom/hit_atom)
/obj/item/shield/energy/bananium/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(active)
var/caught = hit_atom.hitby(src, 0, 0)
var/caught = hit_atom.hitby(src, FALSE, FALSE, throwingdatum=throwingdatum)
if(iscarbon(hit_atom) && !caught)//if they are a carbon and they didn't catch it
var/datum/component/slippery/slipper = GetComponent(/datum/component/slippery)
slipper.Slip(hit_atom)
slipper.lube_flags |= FLYING_DOESNT_HELP|SLIP_WHEN_CRAWLING
slipper.Slip(src, hit_atom)
slipper.lube_flags &= ~(FLYING_DOESNT_HELP|SLIP_WHEN_CRAWLING)
if(thrownby && !caught)
throw_at(thrownby, throw_range+2, throw_speed, null, 1)
else
@@ -203,24 +203,35 @@
clumsy_check = GRENADE_NONCLUMSY_FUMBLE
/obj/item/grenade/chem_grenade/teargas/moustache/prime()
var/myloc = get_turf(src)
var/list/check_later = list()
for(var/mob/living/carbon/C in get_turf(src))
check_later += C
. = ..()
for(var/mob/living/carbon/M in view(6, myloc))
if(!istype(M.wear_mask, /obj/item/clothing/mask/gas/clown_hat) && !istype(M.wear_mask, /obj/item/clothing/mask/gas/mime) )
if(!M.wear_mask || M.dropItemToGround(M.wear_mask))
if(!.) //grenade did not properly prime.
return
for(var/M in check_later)
var/mob/living/carbon/C = M
if(!istype(C.wear_mask, /obj/item/clothing/mask/gas/clown_hat) && !istype(C.wear_mask, /obj/item/clothing/mask/gas/mime))
if(!C.wear_mask || C.dropItemToGround(C.wear_mask))
var/obj/item/clothing/mask/fakemoustache/sticky/the_stash = new /obj/item/clothing/mask/fakemoustache/sticky()
M.equip_to_slot_or_del(the_stash, SLOT_WEAR_MASK, TRUE, TRUE, TRUE, TRUE)
C.equip_to_slot_or_del(the_stash, SLOT_WEAR_MASK, TRUE, TRUE, TRUE, TRUE)
/obj/item/clothing/mask/fakemoustache/sticky
var/unstick_time = 600
var/unstick_time = 2 MINUTES
/obj/item/clothing/mask/fakemoustache/sticky/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, STICKY_MOUSTACHE_TRAIT)
addtimer(CALLBACK(src, .proc/unstick), unstick_time)
addtimer(TRAIT_CALLBACK_REMOVE(src, TRAIT_NODROP, STICKY_MOUSTACHE_TRAIT), unstick_time)
/obj/item/clothing/mask/fakemoustache/sticky/proc/unstick()
ADD_TRAIT(src, TRAIT_NODROP, STICKY_MOUSTACHE_TRAIT)
/obj/item/clothing/mask/fakemoustache/sticky/equipped(mob/user, slot)
. = ..()
if(slot == SLOT_WEAR_MASK)
ADD_TRAIT(user, TRAIT_NO_INTERNALS, STICKY_MOUSTACHE_TRAIT)
/obj/item/clothing/mask/fakemoustache/sticky/dropped(mob/user)
. = ..()
REMOVE_TRAIT(user, TRAIT_NO_INTERNALS, STICKY_MOUSTACHE_TRAIT)
//DARK H.O.N.K. AND CLOWN MECH WEAPONS
@@ -268,7 +279,6 @@
internals_req_access = list(ACCESS_SYNDICATE)
wreckage = /obj/structure/mecha_wreckage/honker/dark
max_equip = 3
spawn_tracked = FALSE
/obj/mecha/combat/honker/dark/GrantActions(mob/living/user, human_occupant = 0)
..()
+57 -53
View File
@@ -41,7 +41,7 @@ GLOBAL_LIST_EMPTY(dynamic_forced_roundstart_ruleset)
// Forced threat level, setting this to zero or higher forces the roundstart threat to the value.
GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
/datum/game_mode/dynamic
name = "dynamic mode"
@@ -54,16 +54,16 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
// Current storyteller
var/datum/dynamic_storyteller/storyteller = null
// Threat logging vars
/// The "threat cap", threat shouldn't normally go above this and is used in ruleset calculations
/// Target threat level right now. Events and antags will try to keep the round at this level.
var/threat_level = 0
/// Set at the beginning of the round. Spent by the mode to "purchase" rules.
var/threat = 0
/// The current antag threat. Recalculated every time a ruletype starts or ends.
var/threat = 0
/// Starting threat level, for things that increase it but can bring it back down.
var/initial_threat_level = 0
/// Things that cause a rolling threat adjustment to be displayed at roundend.
var/list/threat_tallies = list()
/// Running information about the threat. Can store text or datum entries.
var/list/threat_log = list()
var/list/threat_log = list()
/// As above, but with info such as refunds.
var/list/threat_log_verbose = list()
/// List of roundstart rules used for selecting the rules.
@@ -127,6 +127,8 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
var/only_ruleset_executed = FALSE
/// Antags rolled by rules so far, to keep track of and discourage scaling past a certain ratio of crew/antags especially on lowpop.
var/antags_rolled = 0
// Arbitrary threat addition, for fudging purposes.
var/added_threat = 50
/datum/game_mode/dynamic/New() // i have NO IDEA if this is the proper way to do this.
..()
@@ -150,11 +152,11 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
GLOB.dynamic_first_midround_delay_max = CONFIG_GET(number/dynamic_first_midround_delay_max)*600
/datum/game_mode/dynamic/admin_panel()
var/list/dat = list("<html><head><title>Game Mode Panel</title></head><body><h1><B>Game Mode Panel</B></h1>")
var/list/dat = list("<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>Game Mode Panel</title></head><body><h1><B>Game Mode Panel</B></h1>")
dat += "Dynamic Mode <a href='?_src_=vars;[HrefToken()];Vars=[REF(src)]'>\[VV\]</A><a href='?src=\ref[src];[HrefToken()]'>\[Refresh\]</A><BR>"
dat += "Threat Level: <b>[threat_level]</b><br/>"
dat += "Target threat: <b>[threat_level]</b><br/>"
dat += "Threat to Spend: <b>[threat]</b> <a href='?src=\ref[src];[HrefToken()];adjustthreat=1'>\[Adjust\]</A> <a href='?src=\ref[src];[HrefToken()];threatlog=1'>\[View Log\]</a><br/>"
dat += "Current threat: <b>[threat]</b> <a href='?src=\ref[src];[HrefToken()];adjustthreat=1'>\[Adjust\]</A> <a href='?src=\ref[src];[HrefToken()];threatlog=1'>\[View Log\]</a><br/>"
dat += "<br/>"
dat += "Storyteller: <b>[storyteller.name]</b><br/>"
dat += "Parameters: centre = [GLOB.dynamic_curve_centre] ; width = [GLOB.dynamic_curve_width].<br/>"
@@ -172,9 +174,9 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
else
dat += "none.<br>"
dat += "<br>Injection Timers: (<b>[storyteller.get_injection_chance(TRUE)]%</b> chance)<BR>"
dat += "Latejoin: [(latejoin_injection_cooldown-world.time)>60*10 ? "[round((latejoin_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(latejoin_injection_cooldown-world.time)] seconds"] <a href='?src=\ref[src];[HrefToken()];injectlate=1'>\[Now!\]</a><BR>"
dat += "Midround: [(midround_injection_cooldown-world.time)>60*10 ? "[round((midround_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(midround_injection_cooldown-world.time)] seconds"] <a href='?src=\ref[src];[HrefToken()];injectmid=1'>\[Now!\]</a><BR>"
dat += "Event: [(event_injection_cooldown-world.time)>60*10 ? "[round((event_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(event_injection_cooldown-world.time)] seconds"] <a href='?src=\ref[src];[HrefToken()];forceevent=1'>\[Now!\]</a><BR>"
dat += "Latejoin: [(latejoin_injection_cooldown-world.time)>60*10 ? "[round((latejoin_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(latejoin_injection_cooldown-world.time)/10] seconds"] <a href='?src=\ref[src];[HrefToken()];injectlate=1'>\[Now!\]</a><BR>"
dat += "Midround: [(midround_injection_cooldown-world.time)>60*10 ? "[round((midround_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(midround_injection_cooldown-world.time)/10] seconds"] <a href='?src=\ref[src];[HrefToken()];injectmid=1'>\[Now!\]</a><BR>"
dat += "Event: [(event_injection_cooldown-world.time)>60*10 ? "[round((event_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(event_injection_cooldown-world.time)/10] seconds"] <a href='?src=\ref[src];[HrefToken()];forceevent=1'>\[Now!\]</a><BR>"
usr << browse(dat.Join(), "window=gamemode_panel;size=500x500")
/datum/game_mode/dynamic/Topic(href, href_list)
@@ -239,12 +241,20 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
. += "<b>Peaceful Waypoint</b></center><BR>"
. += "Your station orbits deep within controlled, core-sector systems and serves as a waypoint for routine traffic through Nanotrasen's trade empire. Due to the combination of high security, interstellar traffic, and low strategic value, it makes any direct threat of violence unlikely. Your primary enemies will be incompetence and bored crewmen: try to organize team-building events to keep staffers interested and productive. However, even deep in our territory there may be subversive elements, especially for such a high-value target as your station. Keep an eye out, but don't expect much trouble."
set_security_level(SEC_LEVEL_GREEN)
for(var/T in subtypesof(/datum/station_goal))
var/datum/station_goal/G = new T
if(!(G in station_goals))
station_goals += G
if(21 to 79)
var/perc_green = 100-round(100*((threat_level-21)/(79-21)))
if(prob(perc_green))
. += "<b>Core Territory</b></center><BR>"
. += "Your station orbits within reliably mundane, secure space. Although Nanotrasen has a firm grip on security in your region, the valuable resources and strategic position aboard your station make it a potential target for infiltrations. Monitor crew for non-loyal behavior, but expect a relatively tame shift free of large-scale destruction. We expect great things from your station."
set_security_level(SEC_LEVEL_GREEN)
for(var/T in subtypesof(/datum/station_goal))
var/datum/station_goal/G = new T
if(!(G in station_goals))
station_goals += G
else if(prob(perc_green))
. += "<b>Contested System</b></center><BR>"
. += "Your station's orbit passes along the edge of Nanotrasen's sphere of influence. While subversive elements remain the most likely threat against your station, hostile organizations are bolder here, where our grip is weaker. Exercise increased caution against elite Syndicate strike forces, or Executives forbid, some kind of ill-conceived unionizing attempt."
@@ -253,11 +263,11 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
. += "<b>Uncharted Space</b></center><BR>"
. += "Congratulations and thank you for participating in the NT 'Frontier' space program! Your station is actively orbiting a high value system far from the nearest support stations. Little is known about your region of space, and the opportunity to encounter the unknown invites greater glory. You are encouraged to elevate security as necessary to protect Nanotrasen assets."
set_security_level(SEC_LEVEL_BLUE)
if(80 to 99)
if(80 to 95)
. += "<b>Black Orbit</b></center><BR>"
. += "As part of a mandatory security protocol, we are required to inform you that as a result of your orbital pattern directly behind an astrological body (oriented from our nearest observatory), your station will be under decreased monitoring and support. It is anticipated that your extreme location and decreased surveillance could pose security risks. Avoid unnecessary risks and attempt to keep your station in one piece."
set_security_level(SEC_LEVEL_AMBER)
if(100)
if(96 to 100)
. += "<b>Impending Doom</b></center><BR>"
. += "Your station is somehow in the middle of hostile territory, in clear view of any enemy of the corporation. Your likelihood to survive is low, and station destruction is expected and almost inevitable. Secure any sensitive material and neutralize any enemy you will come across. It is important that you at least try to maintain the station.<BR>"
. += "Good luck."
@@ -273,7 +283,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
if(GLOB.security_level >= SEC_LEVEL_BLUE)
priority_announce("A summary has been copied and printed to all communications consoles.", "Security level elevated.", "intercept")
else
priority_announce("Thanks to the tireless efforts of our security and intelligence divisions, there are currently no likely threats to [station_name()]. Have a secure shift!", "Security Report", "commandreport")
priority_announce("Thanks to the tireless efforts of our security and intelligence divisions, there are currently no likely threats to [station_name()]. All station construction projects have been authorized. Have a secure shift!", "Security Report", "commandreport")
// Yes, this is copy pasted from game_mode
/datum/game_mode/dynamic/check_finished(force_ending)
@@ -322,7 +332,6 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
peaceful_percentage = round(LORENTZ_CUMULATIVE_DISTRIBUTION(relative_threat, GLOB.dynamic_curve_centre, GLOB.dynamic_curve_width), 0.01)*100
threat = threat_level
SSblackbox.record_feedback("tally","dynamic_threat",threat_level,"Initial threat level")
SSblackbox.record_feedback("tally","dynamic_threat",GLOB.dynamic_curve_centre,"Curve centre")
SSblackbox.record_feedback("tally","dynamic_threat",GLOB.dynamic_curve_width,"Curve width")
@@ -332,9 +341,10 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
storyteller = new GLOB.dynamic_storyteller_type // this is where all the initialization happens
storyteller.on_start()
SSblackbox.record_feedback("text","dynamic_storyteller",1,storyteller.name)
message_admins("Dynamic mode parameters for the round:")
message_admins("Centre is [GLOB.dynamic_curve_centre], Width is [GLOB.dynamic_curve_width], Forced extended is [GLOB.dynamic_forced_extended ? "Enabled" : "Disabled"], No stacking is [GLOB.dynamic_no_stacking ? "Enabled" : "Disabled"].")
message_admins("Stacking limit is [GLOB.dynamic_stacking_limit], Classic secret is [GLOB.dynamic_classic_secret ? "Enabled" : "Disabled"], High population limit is [GLOB.dynamic_high_pop_limit].")
message_admins("Dynamic mode parameters for the round:\n\
Storyteller is [storyteller.name].\n\
Centre is [GLOB.dynamic_curve_centre], Width is [GLOB.dynamic_curve_width], Forced extended is [GLOB.dynamic_forced_extended ? "Enabled" : "Disabled"], No stacking is [GLOB.dynamic_no_stacking ? "Enabled" : "Disabled"].\n\
Stacking limit is [GLOB.dynamic_stacking_limit], Classic secret is [GLOB.dynamic_classic_secret ? "Enabled" : "Disabled"], High population limit is [GLOB.dynamic_high_pop_limit].")
log_game("DYNAMIC: Dynamic mode parameters for the round:")
log_game("DYNAMIC: Centre is [GLOB.dynamic_curve_centre], Width is [GLOB.dynamic_curve_width], Forced extended is [GLOB.dynamic_forced_extended ? "Enabled" : "Disabled"], No stacking is [GLOB.dynamic_no_stacking ? "Enabled" : "Disabled"].")
log_game("DYNAMIC: Stacking limit is [GLOB.dynamic_stacking_limit], Classic secret is [GLOB.dynamic_classic_secret ? "Enabled" : "Disabled"], High population limit is [GLOB.dynamic_high_pop_limit].")
@@ -346,7 +356,8 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
generate_threat()
storyteller.start_injection_cooldowns()
SSevents.frequency_lower = storyteller.event_frequency_lower // 6 minutes by default
SSevents.frequency_upper = storyteller.event_frequency_upper // 20 minutes by default
log_game("DYNAMIC: Dynamic Mode initialized with a Threat Level of... [threat_level]!")
initial_threat_level = threat_level
return TRUE
@@ -454,7 +465,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
log_game("DYNAMIC: Additional ruleset picked successfully, now [executed_rules.len] picked. [extra_rulesets_amount] remaining.")
else
if(threat >= 50)
if(threat_level >= 50)
message_admins("DYNAMIC: Picking first roundstart ruleset failed. You should report this.")
log_game("DYNAMIC: Picking first roundstart ruleset failed. drafted_rules.len = [drafted_rules.len] and threat = [threat]/[threat_level]")
return FALSE
@@ -500,10 +511,9 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
drafted_rules -= starting_rule
starting_rule.trim_candidates()
var/added_threat = starting_rule.scale_up(extra_rulesets_amount, threat)
starting_rule.scale_up(extra_rulesets_amount, threat_level)
if (starting_rule.pre_execute())
spend_threat(starting_rule.cost + added_threat)
log_threat("[starting_rule.ruletype] - <b>[starting_rule.name]</b> -[starting_rule.cost + starting_rule.scaled_times * starting_rule.scaling_cost] threat", verbose = TRUE)
log_threat("[starting_rule.ruletype] - <b>[starting_rule.name]</b> [starting_rule.cost + starting_rule.scaled_times * starting_rule.scaling_cost] threat", verbose = TRUE)
if(starting_rule.flags & HIGHLANDER_RULESET)
highlander_executed = TRUE
else if(starting_rule.flags & ONLY_RULESET)
@@ -525,8 +535,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
/datum/game_mode/dynamic/proc/execute_roundstart_rule(sent_rule)
var/datum/dynamic_ruleset/rule = sent_rule
if(rule.execute())
if(rule.persistent)
current_rules += rule
current_rules += rule
SSblackbox.record_feedback("associative","dynamic_rulesets",1,rule.get_blackbox_info())
return TRUE
rule.clean_up() // Refund threat, delete teams and so on.
@@ -598,8 +607,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
if ((forced || (new_rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && new_rule.cost <= threat)))
new_rule.trim_candidates()
if (new_rule.ready(forced))
spend_threat(new_rule.cost)
log_threat("[new_rule.ruletype] - <b>[new_rule.name]</b> -[new_rule.cost] threat", verbose = TRUE)
log_threat("[new_rule.ruletype] - <b>[new_rule.name]</b> [new_rule.cost] threat", verbose = TRUE)
if (new_rule.execute()) // This should never fail since ready() returned 1
if(new_rule.flags & HIGHLANDER_RULESET)
highlander_executed = TRUE
@@ -608,8 +616,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
log_game("DYNAMIC: Making a call to a specific ruleset...[new_rule.name]!")
SSblackbox.record_feedback("associative","dynamic_rulesets",1,new_rule.get_blackbox_info())
executed_rules += new_rule
if (new_rule.persistent)
current_rules += new_rule
current_rules += new_rule
return TRUE
else if (forced)
log_game("DYNAMIC: The ruleset [new_rule.name] couldn't be executed due to lack of eligible players.")
@@ -620,8 +627,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
var/datum/dynamic_ruleset/rule = sent_rule
if (rule.execute())
log_game("DYNAMIC: Injected a [rule.ruletype == "latejoin" ? "latejoin" : "midround"] ruleset [rule.name].")
spend_threat(rule.cost)
log_threat("[rule.ruletype] [rule.name] spent [rule.cost]", verbose = TRUE)
log_threat("[rule.ruletype] [rule.name] added [rule.cost]", verbose = TRUE)
if(rule.flags & HIGHLANDER_RULESET)
highlander_executed = TRUE
else if(rule.flags & ONLY_RULESET)
@@ -640,7 +646,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
return FALSE
/datum/game_mode/dynamic/process()
if (pop_last_updated < world.time - (60 SECONDS))
if (pop_last_updated < world.time - (120 SECONDS))
pop_last_updated = world.time
update_playercounts()
@@ -649,9 +655,9 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
current_rules -= rule
SSblackbox.record_feedback("tally","dynamic",1,"Rulesets finished")
SSblackbox.record_feedback("associative","dynamic_rulesets_finished",1,rule.get_blackbox_info())
storyteller.do_process()
if (midround_injection_cooldown < world.time)
if (GLOB.dynamic_forced_extended)
return
@@ -663,7 +669,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
// Time to inject some threat into the round
if(EMERGENCY_ESCAPED_OR_ENDGAMED) // Unless the shuttle is gone
return
if((world.realtime - SSshuttle.realtimeofstart) > SSshuttle.auto_call) // no rules after shuttle is auto-called
if(SSshuttle.endvote_passed) // no rules after shuttle call is voted
return
message_admins("DYNAMIC: Checking for midround injection.")
log_game("DYNAMIC: Checking for midround injection.")
@@ -708,7 +714,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
current_players[CURRENT_OBSERVERS].Add(M)
continue
current_players[CURRENT_DEAD_PLAYERS].Add(M) // Players who actually died (and admins who ghosted, would be nice to avoid counting them somehow)
threat = storyteller.calculate_threat() + added_threat
/// Removes type from the list
/datum/game_mode/dynamic/proc/remove_from_list(list/type_list, type)
for(var/I in type_list)
@@ -739,7 +745,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
return
if(EMERGENCY_ESCAPED_OR_ENDGAMED) // No more rules after the shuttle has left
return
if((world.realtime - SSshuttle.realtimeofstart) > SSshuttle.auto_call) // no rules after shuttle is auto-called
if(SSshuttle.endvote_passed) // no rules after shuttle is auto-called
return
update_playercounts()
@@ -758,25 +764,23 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
SSblackbox.record_feedback("tally","dynamic",1,"Successful latejoin injections")
latejoin_injection_cooldown = storyteller.get_latejoin_cooldown() + world.time
/// Refund threat, but no more than threat_level.
/datum/game_mode/dynamic/proc/refund_threat(regain)
threat = min(threat_level,threat+regain)
SSblackbox.record_feedback("tally","dynamic_threat",regain,"Refunded threat")
log_threat("[regain] refunded. Threat is now [threat].", verbose = TRUE)
/// Generate threat and increase the threat_level if it goes beyond, capped at 100
/// Increase the threat level.
/datum/game_mode/dynamic/proc/create_threat(gain)
threat = min(100, threat+gain)
if(threat > threat_level)
threat_level = threat
SSblackbox.record_feedback("tally","dynamic_threat",gain,"Created threat")
log_threat("[gain] created. Threat is now [threat] and threat level is now [threat_level].", verbose = TRUE)
threat_level += gain
SSblackbox.record_feedback("tally","dynamic_threat",gain,"Created threat level")
log_threat("[gain] created. Threat level is now [threat_level].", verbose = TRUE)
/// Expend threat, can't fall under 0.
/// Decrease the threat level.
/datum/game_mode/dynamic/proc/remove_threat(loss)
threat_level -= loss
SSblackbox.record_feedback("tally","dynamic_threat",loss,"Removed threat level")
log_threat("[loss] removed. Threat level is now [threat_level].", verbose = TRUE)
/// Fill up more of the threat level.
/datum/game_mode/dynamic/proc/spend_threat(cost)
threat = max(threat-cost,0)
SSblackbox.record_feedback("tally","dynamic_threat",cost,"Threat spent")
log_threat("[cost] spent. Threat is now [threat].", verbose = TRUE)
added_threat += cost
SSblackbox.record_feedback("tally","dynamic_threat",cost,"Threat added")
log_threat("[cost] added. Threat is now [threat].", verbose = TRUE)
/// Turns the value generated by lorentz distribution to threat value between 0 and 100.
/datum/game_mode/dynamic/proc/lorentz_to_threat(x)
@@ -174,8 +174,8 @@
/// This is called if persistent variable is true everytime SSTicker ticks.
/datum/dynamic_ruleset/proc/rule_process()
return
return TRUE
/// Called on game mode pre_setup for roundstart rulesets.
/// Do everything you need to do before job is assigned here.
/// IMPORTANT: ASSIGN special_role HERE
@@ -201,8 +201,7 @@
/// Runs from gamemode process() if ruleset fails to start, like delayed rulesets not getting valid candidates.
/// This one only handles refunding the threat, override in ruleset to clean up the rest.
/datum/dynamic_ruleset/proc/clean_up()
mode.refund_threat(cost + (scaled_times * scaling_cost))
mode.log_threat("[ruletype] [name] refunded [cost + (scaled_times * scaling_cost)]",verbose=TRUE)
return
/// Gets weight of the ruleset
/// Note that this decreases weight if repeatable is TRUE and repeatable_weight_decrease is higher than 0
@@ -2,6 +2,7 @@
ruletype = "Event"
var/typepath // typepath of the event
var/triggering
var/earliest_start = 20 MINUTES
/datum/dynamic_ruleset/event/get_blackbox_info()
var/list/ruleset_data = list()
@@ -25,8 +26,10 @@
return E
/datum/dynamic_ruleset/event/ready(forced = FALSE) // same as midround cause we're still using enemy system
/datum/dynamic_ruleset/event/ready(forced = FALSE)
if (!forced)
if(earliest_start >= world.time-SSticker.round_start_time)
return FALSE
var/job_check = 0
if (enemy_roles.len > 0)
for (var/mob/M in mode.current_players[CURRENT_LIVING_PLAYERS])
@@ -56,6 +59,7 @@
required_enemies = list(2,2,1,1,0,0,0,0,0,0)
weight = 5
cost = 10
earliest_start = 30 MINUTES
blocking_rules = list(/datum/dynamic_ruleset/roundstart/nuclear,/datum/dynamic_ruleset/midround/from_ghosts/nuclear)
requirements = list(70,60,50,50,40,40,40,30,20,15)
property_weights = list("story_potential" = 1, "trust" = 1, "chaos" = 1)
@@ -157,17 +161,18 @@
required_enemies = list(3,3,3,3,3,3,3,3,3,3)
cost = 15
weight = 3
earliest_start = 25 MINUTES
repeatable_weight_decrease = 2
requirements = list(60,50,40,30,30,30,30,30,30,30)
high_population_requirement = 30
property_weights = list("extended" = -2)
/datum/dynamic_ruleset/event/meteor_wave/ready()
if(mode.threat_level > 40 && mode.threat >= 25 && prob(20))
if(world.time-SSticker.round_start_time > 35 MINUTES && mode.threat_level > 40 && mode.threat >= 25 && prob(30))
name = "Meteor Wave: Threatening"
cost = 25
typepath = /datum/round_event/meteor_wave/threatening
else if(mode.threat_level > 50 && mode.threat >= 40 && prob(30))
else if(world.time-SSticker.round_start_time > 45 MINUTES && mode.threat_level > 50 && mode.threat >= 40 && prob(30))
name = "Meteor Wave: Catastrophic"
cost = 40
typepath = /datum/round_event/meteor_wave/catastrophic
@@ -280,6 +285,7 @@
cost = 4
requirements = list(10,10,10,10,10,10,10,10,10,10)
high_population_requirement = 10
earliest_start = 10 MINUTES
repeatable = TRUE
property_weights = list("extended" = 1)
@@ -324,6 +330,7 @@
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
earliest_start = 0 MINUTES
property_weights = list("extended" = 1)
always_max_weight = TRUE
@@ -381,3 +388,67 @@
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
property_weights = list("extended" = 1,"chaos" = 1)
/datum/dynamic_ruleset/event/portal_storm_syndicate
name = "Portal Storm"
config_tag = "portal_storm"
typepath = /datum/round_event/portal_storm/syndicate_shocktroop
cost = 10
weight = 1
enemy_roles = list("Head of Security","Security Officer","AI","Captain","Shaft Miner")
required_enemies = list(2,2,2,2,2,2,2,2,2,2)
requirements = list(101,101,101,30,30,30,30,30,30,30)
high_population_requirement = 30
earliest_start = 30 MINUTES
property_weights = list("teamwork" = 1,"chaos" = 1, "extended" = -1)
/datum/dynamic_ruleset/event/wormholes
name = "Wormholes"
config_tag = "wormhole"
typepath = /datum/round_event/wormholes
cost = 3
weight = 4
enemy_roles = list("AI","Medical Doctor","Station Engineer","Head of Personnel","Captain")
required_enemies = list(2,2,2,2,2,2,2,2,2,2)
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
property_weights = list("extended" = 1)
/datum/dynamic_ruleset/event/swarmers
name = "Swarmers"
config_tag = "swarmer"
typepath = /datum/round_event/spawn_swarmer
cost = 10
weight = 1
earliest_start = 30 MINUTES
enemy_roles = list("AI","Security Officer","Head of Security","Captain","Station Engineer","Atmos Technician","Chief Engineer")
required_enemies = list(4,4,4,4,3,3,2,2,1,1)
requirements = list(101,101,101,101,101,101,101,101,101,101)
high_population_requirement = 5
property_weights = list("extended" = -2)
/datum/dynamic_ruleset/event/sentient_disease
name = "Sentient Disease"
config_tag = "sentient_disease"
typepath = /datum/round_event/ghost_role/sentient_disease
enemy_roles = list("Virologist","Chief Medical Officer","Captain","Chemist")
required_enemies = list(2,1,1,1,0,0,0,0,0,0)
required_candidates = 1
weight = 4
cost = 5
requirements = list(30,30,20,20,15,10,10,10,10,5) // yes, it can even happen in "extended"!
property_weights = list("story_potential" = 1, "extended" = 1, "valid" = -2)
high_population_requirement = 5
/datum/dynamic_ruleset/event/revenant
name = "Revenant"
config_tag = "revenant"
typepath = /datum/round_event/ghost_role/revenant
enemy_roles = list("Chief Engineer","Station Engineer","Captain","Chaplain","AI")
required_enemies = list(2,1,1,1,0,0,0,0,0,0)
required_candidates = 1
weight = 4
cost = 5
requirements = list(30,30,30,30,20,15,15,15,15,15)
high_population_requirement = 15
property_weights = list("story_potential" = -2, "extended" = -1)
@@ -47,6 +47,8 @@
assigned += M.mind
M.mind.special_role = antag_flag
M.mind.add_antag_datum(antag_datum)
log_admin("[M.name] was made into a [name] by dynamic.")
message_admins("[M.name] was made into a [name] by dynamic.")
return TRUE
//////////////////////////////////////////////
@@ -72,12 +74,6 @@
property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 1)
always_max_weight = TRUE
/datum/dynamic_ruleset/latejoin/infiltrator/execute()
. = ..()
for(var/datum/mind/M in assigned)
log_admin("[M.name] was made into a traitor by dynamic.")
message_admins("[M.name] was made into a traitor by dynamic.")
//////////////////////////////////////////////
// //
// REVOLUTIONARY PROVOCATEUR //
@@ -225,3 +221,25 @@
log_admin("[M.name] was made into a bloodsucker by dynamic.")
message_admins("[M.name] was made into a bloodsucker by dynamic.")
return TRUE
//////////////////////////////////////////////
// //
// COLLECTOR //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/latejoin/collector
name = "Contraband Collector"
config_tag = "latejoin_collector"
antag_datum = /datum/antagonist/collector
antag_flag = ROLE_MINOR_ANTAG
restricted_roles = list("AI", "Cyborg")
protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
required_candidates = 1
weight = 5
cost = 1
requirements = list(10,10,10,10,10,10,10,10,10,10)
high_population_requirement = 10
repeatable = TRUE
flags = TRAITOR_RULESET | MINOR_RULESET
property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 2)
@@ -34,7 +34,7 @@
living_players = trim_list(mode.current_players[CURRENT_LIVING_PLAYERS])
living_antags = trim_list(mode.current_players[CURRENT_LIVING_ANTAGS])
list_observers = trim_list(mode.current_players[CURRENT_OBSERVERS])
var/datum/element/ghost_role_eligibility/eligibility = SSdcs.GetElement(/datum/element/ghost_role_eligibility)
var/datum/element/ghost_role_eligibility/eligibility = SSdcs.GetElement(list(/datum/element/ghost_role_eligibility))
ghost_eligible = trim_list(eligibility.get_all_ghost_role_eligible())
/datum/dynamic_ruleset/midround/proc/trim_list(list/L = list())
@@ -43,7 +43,7 @@
if (!istype(M, required_type))
trimmed_list.Remove(M)
continue
if (M.GetComponent(/datum/component/virtual_reality))
if (HAS_TRAIT(M, TRAIT_NO_MIDROUND_ANTAG))
trimmed_list.Remove(M)
continue
if (!M.client) // Are they connected?
@@ -61,7 +61,7 @@
trimmed_list.Remove(M)
continue
if (M.mind)
if (restrict_ghost_roles && M.mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL]) // Are they playing a ghost role?
if (restrict_ghost_roles && (M.mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL])) // Are they playing a ghost role?
trimmed_list.Remove(M)
continue
if (M.mind.assigned_role in restricted_roles) // Does their job allow it?
@@ -106,7 +106,7 @@
if (M.mind && M.mind.assigned_role && (M.mind.assigned_role in enemy_roles) && (!(M in candidates) || (M.mind.assigned_role in restricted_roles)))
job_check++ // Checking for "enemies" (such as sec officers). To be counters, they must either not be candidates to that rule, or have a job that restricts them from it
var/threat = round(mode.threat_level/10)
var/threat = CLAMP(round(mode.threat_level/10),1,10)
if (job_check < required_enemies[threat])
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough enemy roles")
return FALSE
@@ -216,7 +216,7 @@
var/player_count = mode.current_players[CURRENT_LIVING_PLAYERS].len
var/antag_count = mode.current_players[CURRENT_LIVING_ANTAGS].len
var/max_traitors = round(player_count / 10) + 1
if ((antag_count < max_traitors) && prob(mode.threat_level))//adding traitors if the antag population is getting low
if ((antag_count < max_traitors) && prob(min(100,mode.threat_level)))//adding traitors if the antag population is getting low
return ..()
else
return FALSE
@@ -273,10 +273,14 @@
var/ion_announce = 33
var/removeDontImproveChance = 10
/datum/dynamic_ruleset/midround/malf/ready()
if(!candidates || !candidates.len)
return FALSE
return ..()
/datum/dynamic_ruleset/midround/malf/trim_candidates()
..()
living_players = candidates[CURRENT_LIVING_PLAYERS]
for(var/mob/living/player in candidates)
for(var/mob/living/player in living_players)
if(!isAI(player))
candidates -= player
continue
@@ -287,8 +291,6 @@
candidates -= player
/datum/dynamic_ruleset/midround/malf/execute()
if(!candidates || !candidates.len)
return FALSE
var/mob/living/silicon/ai/M = pick_n_take(candidates)
assigned += M.mind
var/datum/antagonist/traitor/AI = new
@@ -313,7 +315,6 @@
/datum/dynamic_ruleset/midround/from_ghosts/wizard
name = "Wizard"
config_tag = "midround_wizard"
persistent = TRUE
antag_datum = /datum/antagonist/wizard
antag_flag = ROLE_WIZARD
enemy_roles = list("Security Officer","Detective","Head of Security", "Captain")
@@ -337,11 +338,11 @@
/datum/dynamic_ruleset/midround/from_ghosts/wizard/finish_setup(mob/new_character, index)
..()
new_character.forceMove(pick(GLOB.wizardstart))
wizard = new_character.mind
/datum/dynamic_ruleset/midround/from_ghosts/wizard/rule_process() // i can literally copy this from are_special_antags_dead it's great
if(isliving(wizard.current) && wizard.current.stat!=DEAD)
return FALSE
for(var/obj/item/phylactery/P in GLOB.poi_list) //TODO : IsProperlyDead()
if(P.mind && P.mind.has_antag_datum(/datum/antagonist/wizard))
return FALSE
@@ -414,7 +415,7 @@
required_candidates = 1
blocking_rules = list(/datum/dynamic_ruleset/roundstart/clockcult)
weight = 4
cost = 10
cost = 20
requirements = list(101,101,101,80,60,50,50,50,50,50)
high_population_requirement = 50
repeatable = TRUE
@@ -447,9 +448,7 @@
property_weights = list("story_potential" = -1, "trust" = 1, "chaos" = 2, "extended" = -2, "valid" = 2)
var/list/vents = list()
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/execute()
// 50% chance of being incremented by one
required_candidates += prob(50)
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/ready()
for(var/obj/machinery/atmospherics/components/unary/vent_pump/temp_vent in GLOB.machines)
if(QDELETED(temp_vent))
continue
@@ -463,6 +462,12 @@
vents += temp_vent
if(!vents.len)
return FALSE
return ..()
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/execute()
// 50% chance of being incremented by one
required_candidates += prob(50)
. = ..()
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/generate_ruleset_body(mob/applicant)
@@ -494,10 +499,10 @@
high_population_requirement = 50
repeatable_weight_decrease = 2
repeatable = TRUE
property_weights = list("story_potential" = 1, "trust" = 1, "extended" = 1, "valid" = 2, "integrity" = 2)
property_weights = list("story_potential" = 1, "trust" = 1, "extended" = 1, "valid" = 2, "integrity" = 1)
var/list/spawn_locs = list()
/datum/dynamic_ruleset/midround/from_ghosts/nightmare/execute()
/datum/dynamic_ruleset/midround/from_ghosts/nightmare/ready()
for(var/X in GLOB.xeno_spawn)
var/turf/T = X
var/light_amount = T.get_lumcount()
@@ -505,7 +510,7 @@
spawn_locs += T
if(!spawn_locs.len)
return FALSE
. = ..()
return ..()
/datum/dynamic_ruleset/midround/from_ghosts/nightmare/generate_ruleset_body(mob/applicant)
var/datum/mind/player_mind = new /datum/mind(applicant.key)
@@ -523,88 +528,6 @@
log_game("DYNAMIC: [key_name(S)] was spawned as a Nightmare by the midround ruleset.")
return S
//////////////////////////////////////////////
// //
// SENTIENT DISEASE //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/midround/from_ghosts/sentient_disease
name = "Sentient Disease"
config_tag = "sentient_disease"
antag_flag = ROLE_ALIEN
enemy_roles = list("Virologist","Chief Medical Officer","Captain","Chemist")
required_enemies = list(2,1,1,1,0,0,0,0,0,0)
required_candidates = 1
weight = 4
cost = 5
requirements = list(30,30,20,20,15,10,10,10,10,5) // yes, it can even happen in "extended"!
property_weights = list("story_potential" = 1, "extended" = 1, "valid" = -2)
high_population_requirement = 5
/datum/dynamic_ruleset/midround/from_ghosts/sentient_disease/generate_ruleset_body(mob/applicant)
var/mob/camera/disease/virus = new /mob/camera/disease(SSmapping.get_station_center())
applicant.transfer_ckey(virus, FALSE)
INVOKE_ASYNC(virus, /mob/camera/disease/proc/pick_name)
message_admins("[ADMIN_LOOKUPFLW(virus)] has been made into a sentient disease by the midround ruleset.")
log_game("[key_name(virus)] was spawned as a sentient disease by the midround ruleset.")
return virus
//////////////////////////////////////////////
// //
// REVENANT //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/midround/from_ghosts/revenant
name = "Revenant"
config_tag = "revenant"
antag_flag = ROLE_REVENANT
enemy_roles = list("Chief Engineer","Station Engineer","Captain","Chaplain","AI")
required_enemies = list(2,1,1,1,0,0,0,0,0,0)
required_candidates = 1
weight = 4
cost = 5
requirements = list(30,30,30,30,20,15,15,15,15,15)
high_population_requirement = 15
property_weights = list("story_potential" = -2, "extended" = -1)
var/list/spawn_locs = list()
/datum/dynamic_ruleset/midround/from_ghosts/revenant/acceptable(population = 0,threat = 0)
var/deadMobs = 0
for(var/mob/M in GLOB.dead_mob_list)
deadMobs++
if(deadMobs < REVENANT_SPAWN_THRESHOLD)
return FALSE
return ..()
/datum/dynamic_ruleset/midround/from_ghosts/revenant/ready(forced = FALSE)
for(var/mob/living/L in GLOB.dead_mob_list) //look for any dead bodies
var/turf/T = get_turf(L)
if(T && is_station_level(T.z))
spawn_locs += T
if(!spawn_locs.len || spawn_locs.len < 15) //look for any morgue trays, crematoriums, ect if there weren't alot of dead bodies on the station to pick from
for(var/obj/structure/bodycontainer/bc in GLOB.bodycontainers)
var/turf/T = get_turf(bc)
if(T && is_station_level(T.z))
spawn_locs += T
if(!spawn_locs.len) //If we can't find any valid spawnpoints, try the carp spawns
for(var/obj/effect/landmark/carpspawn/L in GLOB.landmarks_list)
if(isturf(L.loc))
spawn_locs += L.loc
if(!spawn_locs.len) //If we can't find THAT, then just give up and cry
return FALSE
return ..()
/datum/dynamic_ruleset/midround/from_ghosts/revenant/generate_ruleset_body(mob/applicant)
var/mob/living/simple_animal/revenant/revvie = new(pick(spawn_locs))
applicant.transfer_ckey(revvie, FALSE)
message_admins("[ADMIN_LOOKUPFLW(revvie)] has been made into a revenant by the midround ruleset.")
log_game("[key_name(revvie)] was spawned as a revenant by the midround ruleset.")
return revvie
//////////////////////////////////////////////
// //
// SLAUGHTER DEMON //
@@ -668,11 +591,11 @@
required_candidates = 2
weight = 8
cost = 10
requirements = list(101,101,70,50,40,30,30,20,15,15)
requirements = list(101,101,70,50,40,30,30,30,30,30)
blocking_rules = list(/datum/dynamic_ruleset/roundstart/nuclear,/datum/dynamic_ruleset/midround/from_ghosts/nuclear)
high_population_requirement = 15
var/datum/team/abductor_team/team
property_weights = list("story_potential" = 1, "extended" = -2, "valid" = 1, "trust" = -1, "chaos" = 2)
property_weights = list("extended" = -2, "valid" = 1, "trust" = -1, "chaos" = 2)
repeatable_weight_decrease = 4
repeatable = TRUE
@@ -741,16 +664,17 @@
Mind.transfer_to(Ninja)
var/datum/antagonist/ninja/ninjadatum = new
ninjadatum.helping_station = pick(TRUE,FALSE)
if(ninjadatum.helping_station)
mode.refund_threat(5)
Mind.add_antag_datum(ninjadatum)
if(Ninja.mind != Mind) //something has gone wrong!
throw EXCEPTION("Ninja created with incorrect mind")
stack_trace("Ninja created with incorrect mind")
message_admins("[ADMIN_LOOKUPFLW(Ninja)] has been made into a ninja by dynamic.")
log_game("[key_name(Ninja)] was spawned as a ninja by dynamic.")
return Ninja
/datum/dynamic_ruleset/midround/from_ghosts/ninja/finish_setup(mob/new_character, index)
return
#undef ABDUCTOR_MAX_TEAMS
#undef REVENANT_SPAWN_THRESHOLD
@@ -623,8 +623,7 @@
config_tag = "clownops"
antag_datum = /datum/antagonist/nukeop/clownop
antag_leader_datum = /datum/antagonist/nukeop/leader/clownop
requirements = list(101,101,101,101,101,101,101,101,101,101)
high_population_requirement = 101
weight = 1
property_weights = list("trust" = 2, "chaos" = 2, "extended" = -2, "story_potential" = 2, "valid" = 2)
@@ -803,11 +802,10 @@
/datum/dynamic_ruleset/roundstart/bloodsucker
name = "Bloodsuckers"
config_tag = "bloodsucker"
persistent = TRUE
antag_flag = ROLE_BLOODSUCKER
antag_datum = ANTAG_DATUM_BLOODSUCKER
minimum_required_age = 0
protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
protected_roles = list("Chaplain", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
restricted_roles = list("Cyborg", "AI")
required_candidates = 1
weight = 2
@@ -1,13 +1,24 @@
/datum/dynamic_storyteller
var/name = "none"
var/desc = "A coder's idiocy."
var/list/property_weights = list()
var/curve_centre = 0
var/curve_width = 1.8
var/forced_threat_level = -1
var/name = "none" // Name for voting.
var/config_tag = null // Config tag for config weights.
var/desc = "A coder's idiocy." // Description for voting.
var/list/property_weights = list() // See below.
var/curve_centre = 0 // As GLOB.dynamic_curve_centre.
var/curve_width = 1.8 // As GLOB.dynamic_curve_width.
var/forced_threat_level = -1 // As GLOB.dynamic_forced_threat_level
/*
NO_ASSASSIN: Will not have permanent assassination targets.
WAROPS_ALWAYS_ALLOWED: Can always do warops, regardless of threat level.
USE_PREF_WEIGHTS: Will use peoples' preferences to change the threat centre.
FORCE_IF_WON: If this mode won the vote, forces it
*/
var/flags = 0
var/weight = 3 // how many rounds need to have been recently played for this storyteller to be left out of the vote
var/datum/game_mode/dynamic/mode = null
var/dead_player_weight = 1 // How much dead players matter for threat calculation
var/weight = 3 // Weights for randomly picking storyteller. Multiplied by score after voting.
var/event_frequency_lower = 6 MINUTES // How rare events will be, at least.
var/event_frequency_upper = 20 MINUTES // How rare events will be, at most.
var/min_players = -1 // How many players are required for this one to start.
var/datum/game_mode/dynamic/mode = null // Cached as soon as it's made, by dynamic.
/**
Property weights are:
@@ -20,14 +31,6 @@ Property weights are:
"conversion" -- Basically a bool. Conversion antags, well, convert. It's its own class for a good reason.
*/
/datum/dynamic_storyteller/New()
..()
if (istype(SSticker.mode, /datum/game_mode/dynamic))
mode = SSticker.mode
GLOB.dynamic_curve_centre = curve_centre
GLOB.dynamic_curve_width = curve_width
GLOB.dynamic_forced_threat_level = forced_threat_level
/datum/dynamic_storyteller/proc/start_injection_cooldowns()
var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_latejoin_delay_max + GLOB.dynamic_first_latejoin_delay_min)
mode.latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_first_latejoin_delay_min, GLOB.dynamic_first_latejoin_delay_max)) + world.time
@@ -38,11 +41,58 @@ Property weights are:
var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
mode.event_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time)
/datum/dynamic_storyteller/proc/calculate_threat()
var/threat = 0
for(var/datum/antagonist/A in GLOB.antagonists)
if(A?.owner?.current && A.owner.current.stat != DEAD)
threat += A.threat()
for(var/r in SSevents.running)
var/datum/round_event/R = r
threat += R.threat()
for(var/mob/living/simple_animal/hostile/H in GLOB.mob_living_list)
var/turf/T = get_turf(H)
if(H.stat != DEAD && is_station_level(T.z) && !("Station" in H.faction))
threat += H.threat()
for(var/obj/item/phylactery/P in GLOB.poi_list)
threat += 25 // can't be giving them too much of a break
for (var/mob/M in mode.current_players[CURRENT_LIVING_PLAYERS])
if (M?.mind?.assigned_role && M.stat != DEAD)
var/datum/job/J = SSjob.GetJob(M.mind.assigned_role)
if(J)
if(length(M.mind.antag_datums))
threat += J.GetThreat()
else
threat -= J.GetThreat()
threat += (mode.current_players[CURRENT_DEAD_PLAYERS].len)*dead_player_weight
return round(threat,0.1)
/datum/dynamic_storyteller/proc/do_process()
return
/datum/dynamic_storyteller/proc/on_start()
return
if (istype(SSticker.mode, /datum/game_mode/dynamic))
mode = SSticker.mode
GLOB.dynamic_curve_centre = curve_centre
GLOB.dynamic_curve_width = curve_width
if(flags & USE_PREF_WEIGHTS)
var/voters = 0
var/mean = 0
for(var/client/c in GLOB.clients)
var/vote = c.prefs.preferred_chaos
if(vote)
voters += 1
switch(vote)
if(CHAOS_NONE)
mean -= 5
if(CHAOS_LOW)
mean -= 2.5
if(CHAOS_HIGH)
mean += 2.5
if(CHAOS_MAX)
mean += 5
if(voters)
GLOB.dynamic_curve_centre += (mean/voters)
GLOB.dynamic_forced_threat_level = forced_threat_level
/datum/dynamic_storyteller/proc/get_midround_cooldown()
var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_midround_delay_max + GLOB.dynamic_midround_delay_min)
@@ -60,30 +110,14 @@ Property weights are:
if(mode.forced_injection)
mode.forced_injection = !dry_run
return 100
var/chance = 0
// If the high pop override is in effect, we reduce the impact of population on the antag injection chance
var/high_pop_factor = (mode.current_players[CURRENT_LIVING_PLAYERS].len >= GLOB.dynamic_high_pop_limit)
var/max_pop_per_antag = max(5,15 - round(mode.threat_level/10) - round(mode.current_players[CURRENT_LIVING_PLAYERS].len/(high_pop_factor ? 10 : 5)))
if (!mode.current_players[CURRENT_LIVING_ANTAGS].len)
chance += 80 // No antags at all? let's boost those odds!
else
var/current_pop_per_antag = mode.current_players[CURRENT_LIVING_PLAYERS].len / mode.current_players[CURRENT_LIVING_ANTAGS].len
if (current_pop_per_antag > max_pop_per_antag)
chance += min(50, 25+10*(current_pop_per_antag-max_pop_per_antag))
else
chance += 25-10*(max_pop_per_antag-current_pop_per_antag)
if (mode.current_players[CURRENT_DEAD_PLAYERS].len > mode.current_players[CURRENT_LIVING_PLAYERS].len)
chance -= 30 // More than half the crew died? ew, let's calm down on antags
if (mode.threat > 70)
chance += 15
if (mode.threat < 30)
chance -= 15
return round(max(0,chance))
var/threat_perc = mode.threat/mode.threat_level
return round(max(0,100*(1-(threat_perc*threat_perc*threat_perc))))
/datum/dynamic_storyteller/proc/roundstart_draft()
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/roundstart/rule in mode.roundstart_rules)
if (rule.acceptable(mode.roundstart_pop_ready, mode.threat_level) && mode.threat >= rule.cost) // If we got the population and threat required
if (rule.acceptable(mode.roundstart_pop_ready, mode.threat_level)) // If we got the population and threat required
rule.candidates = mode.candidates.Copy()
rule.trim_candidates()
if (rule.ready() && rule.candidates.len > 0)
@@ -91,32 +125,43 @@ Property weights are:
for(var/property in property_weights)
if(property in rule.property_weights) // just treat it as 0 if it's not in there
property_weight += rule.property_weights[property] * property_weights[property]
drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
drafted_rules[rule] = (rule.get_weight() * property_weight)*rule.weight_mult
return drafted_rules
/datum/dynamic_storyteller/proc/midround_draft()
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/midround/rule in mode.midround_rules)
// if there are antags OR the rule is an antag rule, antag_acceptable will be true.
if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost)
if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level))
// Classic secret : only autotraitor/minor roles
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
continue
rule.trim_candidates()
var/threat_weight = 1
if(!(rule.flags & MINOR_RULESET)) // makes the traitor rulesets always possible anyway
var/cost_difference = abs(rule.cost-(mode.threat_level-mode.threat))
/* Basically, the closer the cost is to the current threat-level-away-from-threat, the more likely it is to
pick this particular ruleset.
Let's use a toy example: there's 60 threat level and 10 threat spent.
We want to pick a ruleset that's close to that, so we run the below equation, on two rulesets.
Ruleset 1 has 30 cost, ruleset 2 has 5 cost.
When we do the math, ruleset 1's threat_weight is 0.538, and ruleset 2's is 0.238, meaning ruleset 1
is 2.26 times as likely to be picked, all other things considered.
Of course, we don't want it to GUARANTEE the closest, that's no fun, so it's just a weight.
*/
threat_weight = 1-abs(1-LOGISTIC_FUNCTION(2,0.05,cost_difference,0))
if (rule.ready())
var/property_weight = 0
for(var/property in property_weights)
if(property in rule.property_weights)
property_weight += rule.property_weights[property] * property_weights[property]
drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
else if(mode.threat < rule.cost)
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
drafted_rules[rule] = round(((rule.get_weight() * property_weight)*rule.weight_mult*threat_weight)*1000,1)
return drafted_rules
/datum/dynamic_storyteller/proc/latejoin_draft(mob/living/carbon/human/newPlayer)
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/latejoin/rule in mode.latejoin_rules)
if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost)
if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level - mode.threat))
// Classic secret : only autotraitor/minor roles
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
continue
@@ -127,58 +172,64 @@ Property weights are:
rule.candidates = list(newPlayer)
rule.trim_candidates()
var/threat_weight = 1
if(!(rule.flags & MINOR_RULESET))
var/cost_difference = abs(rule.cost-(mode.threat_level-mode.threat))
threat_weight = 1-abs(1-(LOGISTIC_FUNCTION(2,0.05,cost_difference,0)))
if (rule.ready())
var/property_weight = 0
for(var/property in property_weights)
if(property in rule.property_weights)
property_weight += rule.property_weights[property] * property_weights[property]
drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
else if(mode.threat < rule.cost)
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
drafted_rules[rule] = round(((rule.get_weight() * property_weight)*rule.weight_mult*threat_weight)*1000,1)
return drafted_rules
/datum/dynamic_storyteller/proc/event_draft()
var/list/drafted_rules = list()
for(var/datum/dynamic_ruleset/event/rule in mode.events)
if(rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost)
if(rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && (mode.threat_level - mode.threat) >= rule.cost)
if(rule.ready())
var/property_weight = 0
for(var/property in property_weights)
if(property in rule.property_weights)
property_weight += rule.property_weights[property] * property_weights[property]
drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
else if(mode.threat < rule.cost)
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
return drafted_rules
/datum/dynamic_storyteller/cowabunga
/datum/dynamic_storyteller/chaotic
name = "Chaotic"
config_tag = "chaotic"
curve_centre = 10
desc = "Chaos: high. Variation: high. Likely antags: clock cult, revs, wizard."
property_weights = list("extended" = -1, "chaos" = 10)
weight = 2
desc = "High chaos modes. Revs, wizard, clock cult. Multiple antags at once. Chaos is kept up all round."
property_weights = list("extended" = -1, "chaos" = 1)
weight = 1
event_frequency_lower = 2 MINUTES
event_frequency_upper = 10 MINUTES
flags = WAROPS_ALWAYS_ALLOWED
var/refund_cooldown
min_players = 40
var/refund_cooldown = 0
/datum/dynamic_storyteller/cowabunga/get_midround_cooldown()
return ..() / 4
/datum/dynamic_storyteller/cowabunga/get_latejoin_cooldown()
return ..() / 4
/datum/dynamic_storyteller/cowabunga/do_process()
/datum/dynamic_storyteller/chaotic/do_process()
if(refund_cooldown < world.time)
mode.refund_threat(10)
mode.log_threat("Cowabunga it is. Refunded 10 threat. Threat is now [mode.threat].")
refund_cooldown = world.time + 300 SECONDS
mode.create_threat(20)
mode.log_threat("Chaotic storyteller ramped up the chaos. Threat level is now [mode.threat_level].")
refund_cooldown = world.time + 20 MINUTES
/datum/dynamic_storyteller/chaotic/get_midround_cooldown()
return ..() / 4
/datum/dynamic_storyteller/chaotic/get_latejoin_cooldown()
return ..() / 4
/datum/dynamic_storyteller/team
name = "Teamwork"
desc = "Chaos: high. Variation: low. Likely antags: nukies, clockwork cult, wizard, blob, xenomorph."
config_tag = "teamwork"
desc = "Modes where the crew must band together. Nukies, xenos, blob. Only one antag threat at once."
curve_centre = 2
curve_width = 1.5
weight = 2
min_players = 30
flags = WAROPS_ALWAYS_ALLOWED
property_weights = list("valid" = 3, "trust" = 5)
@@ -187,49 +238,150 @@ Property weights are:
/datum/dynamic_storyteller/conversion
name = "Conversion"
desc = "Chaos: high. Variation: medium. Likely antags: cults, bloodsuckers, revs."
config_tag = "conversion"
desc = "Conversion antags. Cults, revs."
curve_centre = 3
curve_width = 1
weight = 2
weight = 0
flags = WAROPS_ALWAYS_ALLOWED
property_weights = list("valid" = 1, "conversion" = 20)
/datum/dynamic_storyteller/classic
/datum/dynamic_storyteller/random
name = "Random"
desc = "Chaos: varies. Variation: highest. No special weights attached."
weight = 6
curve_width = 4
config_tag = "random"
weight = 1
desc = "No weighting at all; every ruleset has the same chance of happening. Cooldowns vary wildly. As random as it gets."
/datum/dynamic_storyteller/memes
/datum/dynamic_storyteller/random/on_start()
..()
GLOB.dynamic_forced_threat_level = rand(0,100)
/datum/dynamic_storyteller/random/get_midround_cooldown()
return rand(GLOB.dynamic_midround_delay_min/2, GLOB.dynamic_midround_delay_max*2)
/datum/dynamic_storyteller/random/get_event_cooldown()
return rand(GLOB.dynamic_event_delay_min/2, GLOB.dynamic_event_delay_max*2)
/datum/dynamic_storyteller/random/get_latejoin_cooldown()
return rand(GLOB.dynamic_latejoin_delay_min/2, GLOB.dynamic_latejoin_delay_max*2)
/datum/dynamic_storyteller/random/get_injection_chance()
return 50 // i would do rand(0,100) but it's actually the same thing when you do the math
/datum/dynamic_storyteller/random/calculate_threat()
return 0 // what IS threat
/datum/dynamic_storyteller/random/roundstart_draft()
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/roundstart/rule in mode.roundstart_rules)
if (rule.acceptable(mode.roundstart_pop_ready, mode.threat_level)) // If we got the population and threat required
rule.candidates = mode.candidates.Copy()
rule.trim_candidates()
if (rule.ready() && rule.candidates.len > 0)
drafted_rules[rule] = 1
return drafted_rules
/datum/dynamic_storyteller/random/midround_draft()
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/midround/rule in mode.midround_rules)
if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level))
// Classic secret : only autotraitor/minor roles
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
continue
rule.trim_candidates()
if (rule.ready())
drafted_rules[rule] = 1
return drafted_rules
/datum/dynamic_storyteller/random/latejoin_draft(mob/living/carbon/human/newPlayer)
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/latejoin/rule in mode.latejoin_rules)
if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level))
// Classic secret : only autotraitor/minor roles
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
continue
// No stacking : only one round-ender, unless threat level > stacking_limit.
if (mode.threat_level > GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking)
if(rule.flags & HIGHLANDER_RULESET && mode.highlander_executed)
continue
rule.candidates = list(newPlayer)
rule.trim_candidates()
if (rule.ready())
drafted_rules[rule] = 1
return drafted_rules
/datum/dynamic_storyteller/random/event_draft()
var/list/drafted_rules = list()
for(var/datum/dynamic_ruleset/event/rule in mode.events)
if(rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level))
if(rule.ready())
drafted_rules[rule] = 1
return drafted_rules
/datum/dynamic_storyteller/story
name = "Story"
desc = "Chaos: varies. Variation: high. Likely antags: abductors, nukies, wizard, traitor."
curve_width = 4
property_weights = list("story_potential" = 10)
config_tag = "story"
desc = "Antags with options for loadouts and gimmicks. Traitor, wizard, nukies. Has a buildup-climax-falling action threat curve."
weight = 2
curve_width = 2
property_weights = list("story_potential" = 2)
/datum/dynamic_storyteller/story/calculate_threat()
var/current_time = (world.time / SSautotransfer.targettime)*180
mode.threat_level = round(mode.initial_threat_level*(sin(current_time)+0.25),0.1)
..()
/datum/dynamic_storyteller/classic
name = "Classic"
config_tag = "classic"
desc = "No special antagonist weights. Good variety, but not like random. Uses your chaos preference to weight."
flags = USE_PREF_WEIGHTS
/datum/dynamic_storyteller/suspicion
name = "Intrigue"
desc = "Chaos: low. Variation: high. Likely antags: traitor, bloodsucker. Rare: revs, blood cult."
curve_width = 4
property_weights = list("trust" = -5)
config_tag = "intrigue"
desc = "Antags that instill distrust in the crew. Traitors, bloodsuckers."
weight = 2
curve_width = 2
dead_player_weight = 2
property_weights = list("trust" = -3)
/datum/dynamic_storyteller/liteextended
name = "Calm"
desc = "Chaos: low. Variation: medium. Likely antags: bloodsuckers, traitors, sentient disease, revenant."
config_tag = "calm"
desc = "Low-chaos round. Few antags. No conversion."
curve_centre = -3
curve_width = 0.5
flags = NO_ASSASSIN | FORCE_IF_WON
weight = 1
dead_player_weight = 5
property_weights = list("extended" = 2, "chaos" = -1, "valid" = -1, "story_potential" = 1, "conversion" = -10)
/datum/dynamic_storyteller/no_antag
name = "Extended"
config_tag = "semiextended"
desc = "No standard antags. Threatening events may still spawn."
curve_centre = -5
curve_width = 0.5
flags = NO_ASSASSIN
weight = 2
property_weights = list("extended" = 1, "chaos" = -1, "valid" = -1, "story_potential" = 1, "conversion" = -10)
flags = NO_ASSASSIN | FORCE_IF_WON
weight = 1
property_weights = list("extended" = 2)
/datum/dynamic_storyteller/liteextended/get_injection_chance(dry_run = FALSE)
return ..()/2
/datum/dynamic_storyteller/no_antag/roundstart_draft()
return list()
/datum/dynamic_storyteller/no_antag/get_injection_chance(dry_run)
return 0
/datum/dynamic_storyteller/extended
name = "Extended"
desc = "Chaos: none. Variation: none. Likely antags: none."
name = "Super Extended"
config_tag = "extended"
desc = "No antags. No dangerous events."
curve_centre = -20
weight = 2
weight = 0
curve_width = 0.5
/datum/dynamic_storyteller/extended/on_start()
..()
GLOB.dynamic_forced_extended = TRUE
+3 -2
View File
@@ -12,7 +12,7 @@
var/list/skipped_areas = list(/area/engine/engineering, /area/engine/supermatter, /area/engine/atmospherics_engine, /area/ai_monitored/turret_protected/ai)
for(var/area/A in world)
if( !A.requires_power || A.always_unpowered )
if( !A.requires_power || A.always_unpowered || A.base_area)
continue
var/skip = 0
@@ -61,8 +61,9 @@
S.output_attempt = 1
S.update_icon()
S.power_change()
for(var/area/A in world)
if(!istype(A, /area/space) && !istype(A, /area/shuttle) && !istype(A, /area/arrival))
if(!istype(A, /area/space) && !istype(A, /area/shuttle) && !istype(A, /area/arrival) && !A.always_unpowered && !A.base_area)
A.power_light = TRUE
A.power_equip = TRUE
A.power_environ = TRUE
+3 -3
View File
@@ -323,7 +323,7 @@
var/free_tickets = CONFIG_GET(number/default_antag_tickets)
//Max extra tickets you can use
var/additional_tickets = CONFIG_GET(number/max_tickets_per_roll)
var/list/ckey_to_mind = list() //this is admittedly shitcode but I'm webediting
var/list/prev_tickets = SSpersistence.antag_rep //cache for hyper-speed in theory. how many tickets someone has stored
var/list/curr_tickets = list() //how many tickets someone has for *this* antag roll, so with the free tickets
@@ -337,7 +337,7 @@
continue
curr_tickets[mind_ckey] = amount
ckey_to_mind[mind_ckey] = M //make sure we can look them up after picking
if(!return_list) //return a single guy
var/ckey
if(length(curr_tickets))
@@ -584,7 +584,7 @@
//By default nuke just ends the round
/datum/game_mode/proc/OnNukeExplosion(off_station)
nuke_off_station = off_station
if(off_station < 2)
if(!off_station)
station_was_nuked = TRUE //Will end the round on next check.
//Additional report section in roundend report
+3 -3
View File
@@ -13,7 +13,7 @@
anchored = TRUE
layer = HIGH_OBJ_LAYER
max_integrity = 300
integrity_failure = 100
integrity_failure = 0.33
armor = list("melee" = 20, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 10, "acid" = 70)
var/datum/team/gang/gang
var/operating = FALSE //false=standby or broken, true=takeover
@@ -101,7 +101,7 @@
playsound(loc, 'sound/items/timer.ogg', 10, 0)
if(!warned && (time_remaining < 180))
warned = TRUE
var/area/domloc = get_area(loc)
var/area/domloc = get_base_area(loc)
gang.message_gangtools("Less than 3 minutes remains in hostile takeover. Defend your dominator at [domloc.map_name]!")
for(var/G in GLOB.gangs)
var/datum/team/gang/tempgang = G
@@ -179,7 +179,7 @@
if((tempgang.domination_time != NOT_DOMINATING) || !tempgang.dom_attempts || !in_range(src, user) || !isturf(loc))
return 0
var/area/A = get_area(loc)
var/area/A = get_base_area(loc)
var/locname = A.map_name
gang = tempgang
+3 -1
View File
@@ -4,6 +4,7 @@
can_coexist_with_others = FALSE
job_rank = ROLE_GANG
antagpanel_category = "Gang"
threat = 2
var/hud_type = "gangster"
var/message_name = "Gangster"
var/datum/team/gang/gang
@@ -167,6 +168,7 @@
name = "Gang boss"
hud_type = "gang_boss"
message_name = "Leader"
threat = 10
/datum/antagonist/gang/boss/on_gain()
..()
@@ -474,4 +476,4 @@
#undef MAXIMUM_RECALLS
#undef INFLUENCE_INTERVAL
#undef INFLUENCE_INTERVAL
+6 -6
View File
@@ -21,7 +21,7 @@
/datum/team/gang/zerog
name = "Zero-G"
color = "#C0C0C0"
inner_outfits = list(/obj/item/clothing/under/suit_jacket/white)
inner_outfits = list(/obj/item/clothing/under/suit/white)
outer_outfits = list(/obj/item/clothing/suit/hooded/wintercoat)
/datum/team/gang/max
@@ -33,13 +33,13 @@
/datum/team/gang/blasto
name = "Blasto"
color = "#000080"
inner_outfits = list(/obj/item/clothing/under/suit_jacket/navy)
inner_outfits = list(/obj/item/clothing/under/suit/navy)
outer_outfits = list(/obj/item/clothing/suit/jacket/miljacket)
/datum/team/gang/waffle
name = "Waffle"
color = "#808000" //shared color with cyber, but they can keep brown cause waffles.
inner_outfits = list(/obj/item/clothing/under/suit_jacket/green)
inner_outfits = list(/obj/item/clothing/under/suit/green)
outer_outfits = list(/obj/item/clothing/suit/poncho)
/datum/team/gang/north
@@ -87,7 +87,7 @@
/datum/team/gang/tunnel
name = "Tunnel"
color = "#FF00FF" //Gave the leather jacket to the tunnel gang over diablo.
inner_outfits = list(/obj/item/clothing/under/villain)
inner_outfits = list(/obj/item/clothing/under/costume/villain)
outer_outfits = list(/obj/item/clothing/suit/jacket/leather)
/datum/team/gang/diablo
@@ -123,13 +123,13 @@
/datum/team/gang/h
name = "H"
color = "#993333"
inner_outfits = list(/obj/item/clothing/under/jabroni) //Why not?
inner_outfits = list(/obj/item/clothing/under/costume/jabroni) //Why not?
outer_outfits = list(/obj/item/clothing/suit/toggle/owlwings)
/datum/team/gang/rigatonifamily
name = "Rigatoni family"
color = "#cc9900" // p a s t a colored
inner_outfits = list(/obj/item/clothing/under/rank/chef)
inner_outfits = list(/obj/item/clothing/under/rank/civilian/chef)
outer_outfits = list(/obj/item/clothing/suit/apron/chef)
/datum/team/gang/weed
+2 -2
View File
@@ -19,7 +19,7 @@
return INITIALIZE_HINT_QDEL
gang = G
var/newcolor = G.color
var/area/territory = get_area(src)
var/area/territory = get_base_area(src)
icon_state = G.name
G.new_territories |= list(territory.type = territory.name)
//If this isn't tagged by a specific gangster there's no bonus income.
@@ -27,7 +27,7 @@
/obj/effect/decal/cleanable/crayon/gang/Destroy()
if(gang)
var/area/territory = get_area(src)
var/area/territory = get_base_area(src)
gang.territories -= territory.type
gang.new_territories -= territory.type
gang.lost_territories |= list(territory.type = territory.name)
+4 -10
View File
@@ -220,11 +220,11 @@ datum/gang_item/clothing/shades //Addition: Why not have cool shades on a gang m
cost = 15
item_path = /obj/item/ammo_box/magazine/sniper_rounds
/datum/gang_item/weapon/ammo/sleeper_ammo
/*/datum/gang_item/weapon/ammo/sleeper_ammo //no. absolutely no.
name = "Illicit Soporific Cartridges"
id = "sniper_ammo"
cost = 15
item_path = /obj/item/ammo_box/magazine/sniper_rounds/soporific
cost = 15 //who the fuck thought a ONE-HIT K.O. for 15 gbp IN AN ENVIRONMENT WHERE WE'RE GETTING RID OF HARDSTUNS is a GOOD IDEA
item_path = /obj/item/ammo_box/magazine/sniper_rounds/soporific*/
/datum/gang_item/weapon/machinegun
name = "Mounted Machine Gun"
@@ -290,12 +290,6 @@ datum/gang_item/clothing/shades //Addition: Why not have cool shades on a gang m
cost = 5
item_path = /obj/item/grenade/syndieminibomb/concussion/frag
/datum/gang_item/equipment/stimpack
name = "Black Market Stimulants"
id = "stimpack"
cost = 12
item_path = /obj/item/reagent_containers/syringe/stimulants
/datum/gang_item/equipment/implant_breaker
name = "Implant Breaker"
id = "implant_breaker"
@@ -401,7 +395,7 @@ datum/gang_item/equipment/gangsheild
return "This device requires a 5x5 area clear of walls to FUNCTION. (Estimated Takeover Time: [round(gang.determine_domination_time()/60,0.1)] minutes)"
/datum/gang_item/equipment/dominator/purchase(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
var/area/userarea = get_area(user)
var/area/userarea = get_base_area(user)
if(!(userarea.type in gang.territories|gang.new_territories))
to_chat(user,"<span class='warning'>The <b>dominator</b> can be spawned only on territory controlled by your gang!</span>")
return FALSE
+1 -1
View File
@@ -54,6 +54,6 @@
return
var/mob/living/carbon/human/H = gangster_mind.current // we are sure the dude's human cause it's checked in attack()
H.silent = max(H.silent, 5)
H.Knockdown(100)
H.DefaultCombatKnockdown(100)
gangster_mind.add_antag_datum(/datum/antagonist/gang, gang)
return TRUE
+2 -1
View File
@@ -6,7 +6,8 @@ GLOBAL_LIST_EMPTY(gangs)
name = "gang war"
config_tag = "gang"
antag_flag = ROLE_GANG
restricted_jobs = list("Security Officer", "Warden", "Detective", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security")
restricted_jobs = list("AI", "Cyborg")
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
required_players = 15
required_enemies = 0
recommended_enemies = 2
+4 -4
View File
@@ -21,16 +21,16 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
//Meteor spawning global procs
///////////////////////////////
/proc/spawn_meteors(number = 10, list/meteortypes)
/proc/spawn_meteors(number = 10, list/meteortypes, dir)
for(var/i = 0; i < number; i++)
spawn_meteor(meteortypes)
spawn_meteor(meteortypes, dir)
/proc/spawn_meteor(list/meteortypes)
/proc/spawn_meteor(list/meteortypes, dir)
var/turf/pickedstart
var/turf/pickedgoal
var/max_i = 10//number of tries to spawn meteor.
while(!isspaceturf(pickedstart))
var/startSide = pick(GLOB.cardinals)
var/startSide = dir || pick(GLOB.cardinals)
var/startZ = pick(SSmapping.levels_by_trait(ZTRAIT_STATION))
pickedstart = spaceDebrisStartLoc(startSide, startZ)
pickedgoal = spaceDebrisFinishLoc(startSide, startZ)
+2 -2
View File
@@ -165,7 +165,7 @@
/datum/outfit/syndicate/full
name = "Syndicate Operative - Full Kit"
glasses = /obj/item/clothing/glasses/night
glasses = /obj/item/clothing/glasses/night/syndicate
mask = /obj/item/clothing/mask/gas/syndicate
suit = /obj/item/clothing/suit/space/hardsuit/syndi
r_pocket = /obj/item/tank/internals/emergency_oxygen/engi
@@ -180,7 +180,7 @@
/datum/outfit/syndicate/lone
name = "Syndicate Operative - Lone"
glasses = /obj/item/clothing/glasses/night
glasses = /obj/item/clothing/glasses/night/syndicate
mask = /obj/item/clothing/mask/gas/syndicate
suit = /obj/item/clothing/suit/space/syndicate/black/red
head = /obj/item/clothing/head/helmet/space/syndicate/black/red
+225 -42
View File
@@ -9,9 +9,10 @@ GLOBAL_LIST_EMPTY(objectives)
var/explanation_text = "Nothing" //What that person is supposed to do.
var/team_explanation_text //For when there are multiple owners.
var/datum/mind/target = null //If they are focused on a particular person.
var/target_amount = 0 //If they are focused on a particular number. Steal objectives have their own counter.
var/completed = 0 //currently only used for custom objectives.
var/martyr_compatible = 0 //If the objective is compatible with martyr objective, i.e. if you can still do it while dead.
var/target_amount = FALSE //If they are focused on a particular number. Steal objectives have their own counter.
var/completed = FALSE //currently only used for custom objectives.
var/completable = TRUE //Whether this objective shows greentext when completed
var/martyr_compatible = FALSE //If the objective is compatible with martyr objective, i.e. if you can still do it while dead.
/datum/objective/New(var/text)
GLOB.objectives += src // CITADEL EDIT FOR CRYOPODS
@@ -85,8 +86,10 @@ GLOBAL_LIST_EMPTY(objectives)
if(M)
. += M
/datum/objective/proc/find_target()
/datum/objective/proc/find_target(dupe_search_range, blacklist)
var/list/datum/mind/owners = get_owners()
if(!dupe_search_range)
dupe_search_range = get_owners()
var/list/possible_targets = list()
var/try_target_late_joiners = FALSE
for(var/I in owners)
@@ -95,7 +98,8 @@ GLOBAL_LIST_EMPTY(objectives)
try_target_late_joiners = TRUE
for(var/datum/mind/possible_target in get_crewmember_minds())
if(!(possible_target in owners) && ishuman(possible_target.current) && (possible_target.current.stat != DEAD) && is_unique_objective(possible_target))
possible_targets += possible_target
if(!(possible_target in blacklist))
possible_targets += possible_target
if(try_target_late_joiners)
var/list/all_possible_targets = possible_targets.Copy()
for(var/I in all_possible_targets)
@@ -172,6 +176,26 @@ GLOBAL_LIST_EMPTY(objectives)
/datum/objective/assassinate/admin_edit(mob/admin)
admin_simple_target_pick(admin)
/datum/objective/assassinate/once
name = "kill once"
var/won = FALSE
/datum/objective/assassinate/once/update_explanation_text()
..()
if(target && target.current)
explanation_text = "Kill [target.name], the [!target_role_type ? target.assigned_role : target.special_role]. You only need to kill them once; if they come back, you've still succeeded."
START_PROCESSING(SSprocessing,src)
else
explanation_text = "Free Objective"
/datum/objective/assassinate/once/check_completion()
return won || ..()
/datum/objective/assassinate/once/process()
won = check_completion()
if(won)
STOP_PROCESSING(SSprocessing,src)
/datum/objective/assassinate/internal
var/stolen = 0 //Have we already eliminated this target?
@@ -293,9 +317,11 @@ GLOBAL_LIST_EMPTY(objectives)
/datum/objective/hijack
name = "hijack"
explanation_text = "Hijack the shuttle to ensure no loyalist Nanotrasen crew escape alive and out of custody."
team_explanation_text = "Hijack the shuttle to ensure no loyalist Nanotrasen crew escape alive and out of custody. Leave no team member behind."
explanation_text = "Hijack the emergency shuttle by hacking its navigational protocols through the control console (alt click emergency shuttle console)."
team_explanation_text = "Hijack the emergency shuttle by hacking its navigational protocols through the control console (alt click emergency shuttle console). Leave no team member behind."
martyr_compatible = 0 //Technically you won't get both anyway.
/// Overrides the hijack speed of any antagonist datum it is on ONLY, no other datums are impacted.
var/hijack_speed_override = 1
/datum/objective/hijack/check_completion() // Requires all owners to escape.
if(SSshuttle.emergency.mode != SHUTTLE_ENDGAME)
@@ -365,6 +391,28 @@ GLOBAL_LIST_EMPTY(objectives)
return FALSE
return TRUE
/datum/objective/breakout
name = "breakout"
martyr_compatible = 1
var/target_role_type = 0
var/human_check = TRUE
/datum/objective/breakout/check_completion()
return !target || considered_escaped(target)
/datum/objective/breakout/find_target_by_role(role, role_type=0, invert=0)
if(!invert)
target_role_type = role_type
..()
return target
/datum/objective/breakout/update_explanation_text()
..()
if(target && target.current)
explanation_text = "Make sure [target.name], the [!target_role_type ? target.assigned_role : target.special_role] escapes on the shuttle or an escape pod alive and without being in custody."
else
explanation_text = "Free Objective"
/datum/objective/escape/escape_with_identity
name = "escape with identity"
var/target_real_name // Has to be stored because the target's real_name can change over the course of the round
@@ -527,7 +575,7 @@ GLOBAL_LIST_EMPTY(possible_items)
else if(targetinfo.check_special_completion(I))//Returns 1 by default. Items with special checks will return 1 if the conditions are fulfilled.
return TRUE
if(targetinfo && I.type in targetinfo.altitems) //Ok, so you don't have the item. Do you have an alternative, at least?
if(targetinfo && (I.type in targetinfo.altitems)) //Ok, so you don't have the item. Do you have an alternative, at least?
if(targetinfo.check_special_completion(I))//Yeah, we do! Don't return 0 if we don't though - then you could fail if you had 1 item that didn't pass and got checked first!
return TRUE
return FALSE
@@ -582,7 +630,6 @@ GLOBAL_LIST_EMPTY(possible_items_special)
explanation_text = "Do not give up or lose [targetinfo.name]."
steal_target = targetinfo.targetitem
/datum/objective/download
name = "download"
@@ -616,7 +663,7 @@ GLOBAL_LIST_EMPTY(possible_items_special)
if(count)
target_amount = count
update_explanation_text()
/*
/datum/objective/capture
name = "capture"
var/captured_amount = 0
@@ -662,7 +709,7 @@ GLOBAL_LIST_EMPTY(possible_items_special)
if(count)
target_amount = count
update_explanation_text()
*/
//Changeling Objectives
/datum/objective/absorb
@@ -786,40 +833,10 @@ GLOBAL_LIST_EMPTY(possible_items_special)
/datum/objective/destroy/internal
var/stolen = FALSE //Have we already eliminated this target?
/datum/objective/steal_five_of_type
name = "steal five of"
explanation_text = "Steal at least five items!"
var/list/wanted_items = list(/obj/item)
/datum/objective/steal_five_of_type/New()
..()
wanted_items = typecacheof(wanted_items)
/datum/objective/steal_five_of_type/summon_guns
name = "steal guns"
explanation_text = "Steal at least five guns!"
wanted_items = list(/obj/item/gun)
/datum/objective/steal_five_of_type/summon_magic
name = "steal magic"
explanation_text = "Steal at least five magical artefacts!"
wanted_items = list(/obj/item/spellbook, /obj/item/gun/magic, /obj/item/clothing/suit/space/hardsuit/wizard, /obj/item/scrying, /obj/item/antag_spawner/contract, /obj/item/necromantic_stone)
/datum/objective/steal_five_of_type/check_completion()
var/list/datum/mind/owners = get_owners()
var/stolen_count = 0
for(var/datum/mind/M in owners)
if(!isliving(M.current))
continue
var/list/all_items = M.current.GetAllContents() //this should get things in cheesewheels, books, etc.
for(var/obj/I in all_items) //Check for wanted items
if(is_type_in_typecache(I, wanted_items))
stolen_count++
return stolen_count >= 5
//Created by admin tools
/datum/objective/custom
name = "custom"
completable = FALSE
/datum/objective/custom/admin_edit(mob/admin)
var/expl = stripped_input(admin, "Custom objective:", "Objective", explanation_text)
@@ -997,4 +1014,170 @@ GLOBAL_LIST_EMPTY(possible_items_special)
command_staff_only = TRUE
/datum/objective/hoard
name = "hoard"
var/obj/item/hoarded_item = null
/datum/objective/hoard/get_target()
return hoarded_item
/datum/objective/hoard/proc/set_target(obj/item/I)
if(I)
hoarded_item = I
explanation_text = "Keep [I] on your person at all times."
return hoarded_item
else
explanation_text = "Free objective"
return
/datum/objective/hoard/check_completion()
var/list/datum/mind/owners = get_owners()
if(!hoarded_item)
return TRUE
for(var/datum/mind/M in owners)
if(!isliving(M.current))
continue
var/list/all_items = M.current.GetAllContents() //this should get things in cheesewheels, books, etc.
for(var/obj/I in all_items) //Check for items
if(I == hoarded_item)
return TRUE
return FALSE
/datum/objective/hoard/heirloom
name = "steal heirloom"
/datum/objective/hoard/heirloom/find_target()
set_target(pick(GLOB.family_heirlooms))
GLOBAL_LIST_EMPTY(traitor_contraband)
GLOBAL_LIST_EMPTY(cult_contraband)
/datum/objective/hoard/collector
name = "Hoard contraband"
/datum/objective/collector/New()
..()
if(!GLOB.traitor_contraband.len)//Only need to fill the list when it's needed.
GLOB.traitor_contraband = list(/obj/item/card/emag/empty,/obj/item/clothing/glasses/phantomthief,/obj/item/clothing/gloves/chameleon/broken)
if(!GLOB.cult_contraband.len)
GLOB.cult_contraband = list(/obj/item/clockwork/slab,/obj/item/clockwork/component/belligerent_eye,/obj/item/clockwork/component/belligerent_eye/lens_gem,/obj/item/shuttle_curse,/obj/item/cult_shift)
/datum/objective/hoard/collector/find_target()
var/obj/item/I
var/I_type
if(prob(50))
I_type = pick_n_take(GLOB.traitor_contraband) // always unique unless it's run out, in which case we refill it anyway
else
I_type = pick_n_take(GLOB.cult_contraband)
I = new I_type
I.forceMove(get_turf(owner))
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
H.equip_in_one_of_slots(I, list("backpack" = SLOT_IN_BACKPACK))
hoarded_item = I
GLOBAL_LIST_EMPTY(possible_sabotages)
// For saboteurs. Go in and cause some trouble somewhere. Not necessarily breaking things, just sufficiently troublemaking.
/datum/objective/sabotage
name = "sabotage"
var/datum/sabotage_objective/targetinfo = null //composition > inheritance.
/datum/objective/sabotage/get_target()
return targetinfo.sabotage_type
/datum/objective/sabotage/New()
..()
if(!GLOB.possible_sabotages.len)//Only need to fill the list when it's needed.
for(var/I in subtypesof(/datum/sabotage_objective))
new I
/datum/objective/sabotage/find_target()
var/list/datum/mind/owners = get_owners()
var/approved_targets = list()
check_sabotages:
for(var/datum/sabotage_objective/possible_sabotage in GLOB.possible_sabotages)
if(!is_unique_objective(possible_sabotage.sabotage_type) || possible_sabotage.check_conditions() || !possible_sabotage.can_run())
continue
for(var/datum/mind/M in owners)
if(M.current.mind.assigned_role in possible_sabotage.excludefromjob)
continue check_sabotages
approved_targets += possible_sabotage
return set_target(safepick(approved_targets))
/datum/objective/sabotage/proc/set_target(datum/sabotage_objective/sabo)
if(sabo)
targetinfo = sabo
explanation_text = "[targetinfo.name]"
give_special_equipment(targetinfo.special_equipment)
return sabo
else
explanation_text = "Free objective"
return
/datum/objective/sabotage/check_completion()
return targetinfo.check_conditions()
/datum/objective/flavor
name = "flavor"
completable = FALSE
var/flavor_file
/datum/objective/flavor/proc/get_flavor_list()
return world.file2list(flavor_file)
/datum/objective/flavor/proc/forge_objective()
var/flavor_list = get_flavor_list()
explanation_text = pick(flavor_list)
/datum/objective/flavor/traitor
name = "traitor flavor"
flavor_file = "strings/flavor_objectives/traitor.txt"
/datum/objective/flavor/traitor/get_flavor_list()
. = ..()
switch(owner.assigned_role)
if("Station Engineer", "Atmospheric Technician")
. += world.file2list("strings/flavor_objectives/traitor/engineering.txt")
if("Medical Doctor","Chemist","Virologist","Geneticist")
. += world.file2list("strings/flavor_objectives/traitor/medical.txt")
if("Scientist","Roboticist","Geneticist")
. += world.file2list("strings/flavor_objectives/traitor/science.txt")
if("Assistant")
. += world.file2list("strings/flavor_objectives/traitor/assistant.txt")
/datum/objective/flavor/ninja_helping
flavor_file = "strings/flavor_objectives/ninja_helping.txt"
/datum/objective/flavor/ninja_syndie
flavor_file = "strings/flavor_objectives/ninja_syndie.txt"
/datum/objective/flavor/wizard
flavor_file = "strings/flavor_objectives/wizard.txt"
/datum/objective/contract
var/payout = 0
var/payout_bonus = 0
var/area/dropoff = null
var/static/list/blacklisted_areas = typecacheof(list(/area/ai_monitored/turret_protected,
/area/solar/,
/area/ruin/, //thank you station space ruins
/area/science/test_area/,
/area/shuttle/))
/datum/objective/contract/proc/generate_dropoff() // Generate a random valid area on the station that the dropoff will happen.
var/found = FALSE
while(!found)
var/area/dropoff_area = pick(GLOB.sortedAreas)
if(dropoff_area && is_station_level(dropoff_area.z) && !dropoff_area.outdoors && !is_type_in_typecache(dropoff_area, blacklisted_areas))
dropoff = dropoff_area
found = TRUE
/datum/objective/contract/proc/dropoff_check(mob/user, mob/target) // Check if both the contractor and contract target are at the dropoff point.
var/area/user_area = get_area(user)
var/area/target_area = get_area(target)
return (istype(user_area, dropoff) && istype(target_area, dropoff))
+9 -4
View File
@@ -205,13 +205,18 @@
difficulty = 10
/datum/objective_item/special/boh
name = "a bag of holding."
name = "a type of bag of holding."
targetitem = /obj/item/storage/backpack/holding
difficulty = 10
/datum/objective_item/special/hypercell
name = "a hyper-capacity power cell."
targetitem = /obj/item/stock_parts/cell/hyper
/datum/objective_item/special/adv_surgical_drapes
name = "a set of smart surgical drapes."
targetitem = /obj/item/surgical_drapes/advanced
difficulty = 10 //would be 15 but cmo rarely have it on themselfs and leave it in their lockers...
/datum/objective_item/special/bluespace
name = "a bluespace power cell."
targetitem = /obj/item/stock_parts/cell/bluespace
difficulty = 5
/datum/objective_item/special/laserpointer
+109
View File
@@ -0,0 +1,109 @@
/datum/sabotage_objective
var/name = "Free Objective"
var/sabotage_type = "nothing"
var/special_equipment = list()
var/list/excludefromjob = list()
/datum/sabotage_objective/New()
..()
if(sabotage_type!="nothing")
GLOB.possible_sabotages += src
/datum/sabotage_objective/proc/check_conditions()
return TRUE
/datum/sabotage_objective/proc/can_run()
return TRUE
/datum/sabotage_objective/processing
var/won = FALSE
/datum/sabotage_objective/processing/New()
..()
START_PROCESSING(SSprocessing, src)
/datum/sabotage_objective/processing/proc/check_condition_processing()
return 1
/datum/sabotage_objective/processing/process()
check_condition_processing()
if(won >= 1)
STOP_PROCESSING(SSprocessing,src)
/datum/sabotage_objective/processing/check_conditions()
return won
/datum/sabotage_objective/processing/power_sink
name = "Drain at least 100 megajoules of power using a power sink."
sabotage_type = "powersink"
special_equipment = list(/obj/item/sbeacondrop/powersink)
var/sink_found = FALSE
var/count = 0
/datum/sabotage_objective/processing/power_sink/check_condition_processing()
for(var/s in GLOB.power_sinks)
var/obj/item/powersink/sink = s
won = max(won,sink.power_drained/1e8)
/obj/item/paper/guides/antag/supermatter_sabotage
info = "Ways to sabotage a supermatter:<br>\
<ul>\
<li>Set the air alarm's operating mode to anything that isn't 'draught' (yes, anything, though 'off' works best). Or just smash the air alarm, that works too.</li>\
<li>Wrench a pipe (the junction to the cold loop is most effective, but some setups will robust through this no issue; best to try for multiple)</li>\
<li>Pump in as much carbon dioxide, oxygen, plasma or tritium as you can find (this will likely also cause a singularity or tesla delamination, so watch out!)</li>\
<li>Unset the filters on the cooling loop, or, perhaps more insidious, set them to oxygen/plasma.</li>\
<li>Deactivate the digital valve that sends the exhaust gases to space (note: only works on box station; others you must unwrench).</li>\
<li>There are many other ways; be creative!</li>\
</ul>"
/datum/sabotage_objective/processing/supermatter
name = "Sabotage the supermatter so that it goes under 50% integrity. If it is delaminated, you will fail."
sabotage_type = "supermatter"
special_equipment = list(/obj/item/paper/guides/antag/supermatter_sabotage)
var/list/supermatters = list()
excludefromjob = list("Chief Engineer", "Station Engineer", "Atmospheric Technician")
/datum/sabotage_objective/processing/supermatter/check_condition_processing()
if(!supermatters.len)
supermatters = list()
for(var/obj/machinery/power/supermatter_crystal/S in GLOB.machines)
// Delaminating, not within coverage, not on a tile.
if (!isturf(S.loc) || !(is_station_level(S.z) || is_mining_level(S.z)))
continue
supermatters.Add(S)
for(var/obj/machinery/power/supermatter_crystal/S in supermatters) // you can win this with a wishgranter... lol.
won = max(1-((S.get_integrity()-50)/50),won)
return FALSE
/datum/sabotage_objective/processing/supermatter/can_run()
return (locate(/obj/machinery/power/supermatter_crystal) in GLOB.machines)
/*
/datum/sabotage_objective/station_integrity
name = "Make sure the station is at less than 80% integrity by the end. Smash walls, windows etc. to reach this goal."
sabotage_type = "integrity"
/datum/sabotage_objective/station_integrity/check_conditions()
return 5-(max(SSticker.station_integrity*4,320)/80)
*/
/datum/sabotage_objective/cloner
name = "Destroy all Nanotrasen cloning machines."
sabotage_type = "cloner"
/datum/sabotage_objective/cloner/check_conditions()
return !(locate(/obj/machinery/clonepod) in GLOB.machines)
/datum/sabotage_objective/ai_law
name = "Upload a hacked law to the AI."
sabotage_type = "ailaw"
special_equipment = list(/obj/item/aiModule/syndicate)
excludefromjob = list("Chief Engineer","Research Director","Head of Personnel","Captain","Chief Medical Officer","Head Of Security")
/datum/sabotage_objective/ai_law/can_run()
return length(active_ais())
/datum/sabotage_objective/ai_law/check_conditions()
for (var/i in GLOB.ai_list)
var/mob/living/silicon/ai/aiPlayer = i
if(aiPlayer.mind && length(aiPlayer.laws.hacked))
return TRUE
return FALSE
+1 -1
View File
@@ -28,7 +28,7 @@
update_icon()
// update the icon_state
/obj/machinery/bluespace_beacon/update_icon()
/obj/machinery/bluespace_beacon/update_icon_state()
var/state="floor_beacon"
if(invisibility)
+10 -5
View File
@@ -9,16 +9,12 @@
var/list/colorlist = list()
/obj/machinery/pdapainter/update_icon()
cut_overlays()
/obj/machinery/pdapainter/update_icon_state()
if(stat & BROKEN)
icon_state = "[initial(icon_state)]-broken"
return
if(storedpda)
add_overlay("[initial(icon_state)]-closed")
if(powered())
icon_state = initial(icon_state)
else
@@ -26,6 +22,15 @@
return
/obj/machinery/pdapainter/update_overlays()
. = ..()
if(stat & BROKEN)
return
if(storedpda)
. += "[initial(icon_state)]-closed"
/obj/machinery/pdapainter/Initialize()
. = ..()
var/list/blocked = list(
+35 -25
View File
@@ -71,7 +71,7 @@
reagents.maximum_volume = (500*E)
/obj/machinery/sleeper/update_icon()
/obj/machinery/sleeper/update_icon_state()
icon_state = initial(icon_state)
if(state_open)
icon_state += "-open"
@@ -182,7 +182,7 @@
/obj/machinery/sleeper/AltClick(mob/user)
. = ..()
if(!user.canUseTopic(src, !issilicon(user)))
if(!user.canUseTopic(src, !hasSiliconAccessInArea(user)))
return
if(state_open)
close_machine()
@@ -207,11 +207,14 @@
/obj/machinery/sleeper/ui_data()
var/list/data = list()
var/chemical_list = list()
var/blood_percent = 0
data["occupied"] = occupant ? 1 : 0
data["open"] = state_open
data["efficiency"] = efficiency
data["current_vol"] = reagents.total_volume
data["tot_capacity"] = reagents.maximum_volume
data["blood_levels"] = blood_percent
data["blood_status"] = "Patient either has no blood, or does not require it to function."
data["chemical_list"] = chemical_list
data["chems"] = list()
for(var/chem in available_chems)
@@ -247,10 +250,13 @@
data["occupant"]["fireLoss"] = mob_occupant.getFireLoss()
data["occupant"]["cloneLoss"] = mob_occupant.getCloneLoss()
data["occupant"]["brainLoss"] = mob_occupant.getOrganLoss(ORGAN_SLOT_BRAIN)
data["occupant"]["reagents"] = list()
if(mob_occupant.reagents && mob_occupant.reagents.reagent_list.len)
if(mob_occupant.reagents.reagent_list.len)
for(var/datum/reagent/R in mob_occupant.reagents.reagent_list)
data["occupant"]["reagents"] += list(list("name" = R.name, "volume" = R.volume))
chemical_list += list(list("name" = R.name, "volume" = R.volume))
else
chemical_list = "Patient has no reagents."
data["occupant"]["failing_organs"] = list()
var/mob/living/carbon/C = mob_occupant
if(C)
@@ -259,21 +265,25 @@
continue
data["occupant"]["failing_organs"] += list(list("name" = Or.name))
if(mob_occupant.has_dna()) // Blood-stuff is mostly a copy-paste from the healthscanner.
var/blood_id = C.get_blood_id()
if(blood_id)
data["occupant"]["blood"] = list() // We can start populating this list.
var/blood_type = C.dna.blood_type
if(blood_id != "blood") // special blood substance
var/datum/reagent/R = GLOB.chemical_reagents_list[blood_id]
if(R)
blood_type = R.name
else
blood_type = blood_id
data["occupant"]["blood"]["maxBloodVolume"] = (BLOOD_VOLUME_NORMAL*C.blood_ratio)
data["occupant"]["blood"]["currentBloodVolume"] = C.blood_volume
data["occupant"]["blood"]["dangerBloodVolume"] = BLOOD_VOLUME_SAFE
data["occupant"]["blood"]["bloodType"] = blood_type
if(istype(C)) //Non-carbons shouldn't be able to enter sleepers, but this is to prevent runtimes if something ever breaks
if(mob_occupant.has_dna()) // Blood-stuff is mostly a copy-paste from the healthscanner.
blood_percent = round((C.blood_volume / BLOOD_VOLUME_NORMAL)*100)
var/blood_id = C.get_blood_id()
var/blood_warning = ""
if(blood_percent < 80)
blood_warning = "Patient has low blood levels."
if(blood_percent < 60)
blood_warning = "Patient has DANGEROUSLY low blood levels."
if(blood_id)
var/blood_type = C.dna.blood_type
if(!(blood_id in GLOB.blood_reagent_types)) // special blood substance
var/datum/reagent/R = GLOB.chemical_reagents_list[blood_id]
if(R)
blood_type = R.name
else
blood_type = blood_id
data["blood_status"] = "Patient has [blood_type] type blood. [blood_warning]"
data["blood_levels"] = blood_percent
return data
/obj/machinery/sleeper/ui_act(action, params)
@@ -309,14 +319,14 @@
if(allowed(usr))
if(!is_operational())
return
reagents.remove_reagent(chem, 10)
reagents.remove_reagent(chem, 1000)
return
if(chem in available_chems)
if(!is_operational())
return
/*var/datum/reagent/R = reagents.has_reagent(chem) //For when purity effects are in
if(R.purity < 0.8)*/
reagents.remove_reagent(chem, 10)
reagents.remove_reagent(chem, 1000)
else
visible_message("<span class='warning'>Access Denied.</span>")
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
+7 -2
View File
@@ -113,6 +113,11 @@ Class Procs:
var/atom/movable/occupant = null
var/speed_process = FALSE // Process as fast as possible?
var/obj/item/circuitboard/circuit // Circuit to be created and inserted when the machinery is created
// For storing and overriding ui id and dimensions
var/tgui_id // ID of TGUI interface
var/ui_style // ID of custom TGUI style (optional)
var/ui_x
var/ui_y
var/interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON | INTERACT_MACHINE_SET_MACHINE
@@ -180,7 +185,7 @@ Class Procs:
A.forceMove(T)
if(isliving(A))
var/mob/living/L = A
L.update_canmove()
L.update_mobility()
if(occupant)
SEND_SIGNAL(src, COMSIG_MACHINE_EJECT_OCCUPANT, occupant)
occupant = null
@@ -224,7 +229,7 @@ Class Procs:
return !(stat & (NOPOWER|BROKEN|MAINT))
/obj/machinery/can_interact(mob/user)
var/silicon = issiliconoradminghost(user)
var/silicon = hasSiliconAccessInArea(user) || IsAdminGhost(user)
if((stat & (NOPOWER|BROKEN)) && !(interaction_flags_machine & INTERACT_MACHINE_OFFLINE))
return FALSE
if(panel_open && !(interaction_flags_machine & INTERACT_MACHINE_OPEN))
+1 -1
View File
@@ -113,7 +113,7 @@
id_tag = INCINERATOR_SYNDICATELAVA_AIRLOCK_SENSOR
master_tag = INCINERATOR_SYNDICATELAVA_AIRLOCK_CONTROLLER
/obj/machinery/airlock_sensor/update_icon()
/obj/machinery/airlock_sensor/update_icon_state()
if(on)
if(alert)
icon_state = "airlock_sensor_alert"
+12 -12
View File
@@ -29,25 +29,25 @@ GLOBAL_LIST_EMPTY(announcement_systems)
/obj/machinery/announcement_system/Initialize()
. = ..()
GLOB.announcement_systems += src
radio = new /obj/item/radio/headset/ai(src)
radio = new /obj/item/radio/headset/silicon/ai(src)
update_icon()
/obj/machinery/announcement_system/update_icon()
/obj/machinery/announcement_system/update_icon_state()
if(is_operational())
icon_state = (panel_open ? "AAS_On_Open" : "AAS_On")
else
icon_state = (panel_open ? "AAS_Off_Open" : "AAS_Off")
cut_overlays()
/obj/machinery/announcement_system/update_overlays()
. =..()
if(arrivalToggle)
add_overlay(greenlight)
. += greenlight
if(newheadToggle)
add_overlay(pinklight)
. += pinklight
if(stat & BROKEN)
add_overlay(errorlight)
. += errorlight
/obj/machinery/announcement_system/Destroy()
QDEL_NULL(radio)
@@ -105,7 +105,7 @@ GLOBAL_LIST_EMPTY(announcement_systems)
/obj/machinery/announcement_system/ui_interact(mob/user)
. = ..()
if(!user.canUseTopic(src, !issilicon(user)))
if(!user.canUseTopic(src, !hasSiliconAccessInArea(user)))
return
if(stat & BROKEN)
visible_message("<span class='warning'>[src] buzzes.</span>", "<span class='italics'>You hear a faint buzz.</span>")
@@ -123,7 +123,7 @@ GLOBAL_LIST_EMPTY(announcement_systems)
/obj/machinery/announcement_system/Topic(href, href_list)
if(..())
return
if(!usr.canUseTopic(src, !issilicon(usr)))
if(!usr.canUseTopic(src, !hasSiliconAccessInArea(usr)))
return
if(stat & BROKEN)
visible_message("<span class='warning'>[src] buzzes.</span>", "<span class='italics'>You hear a faint buzz.</span>")
@@ -132,13 +132,13 @@ GLOBAL_LIST_EMPTY(announcement_systems)
if(href_list["ArrivalTopic"])
var/NewMessage = stripped_input(usr, "Enter in the arrivals announcement configuration.", "Arrivals Announcement Config", arrival)
if(!usr.canUseTopic(src, !issilicon(usr)))
if(!usr.canUseTopic(src, !hasSiliconAccessInArea(usr)))
return
if(NewMessage)
arrival = NewMessage
else if(href_list["NewheadTopic"])
var/NewMessage = stripped_input(usr, "Enter in the departmental head announcement configuration.", "Head Departmental Announcement Config", newhead)
if(!usr.canUseTopic(src, !issilicon(usr)))
if(!usr.canUseTopic(src, !hasSiliconAccessInArea(usr)))
return
if(NewMessage)
newhead = NewMessage
@@ -157,7 +157,7 @@ GLOBAL_LIST_EMPTY(announcement_systems)
. = attack_ai(user)
/obj/machinery/announcement_system/attack_ai(mob/user)
if(!user.canUseTopic(src, !issilicon(user)))
if(!user.canUseTopic(src, !hasSiliconAccessInArea(user)))
return
if(stat & BROKEN)
to_chat(user, "<span class='warning'>[src]'s firmware appears to be malfunctioning!</span>")
+6 -6
View File
@@ -19,21 +19,21 @@
initial_icon_state = initial(icon_state)
return ..()
/obj/machinery/aug_manipulator/update_icon()
cut_overlays()
/obj/machinery/aug_manipulator/update_icon_state()
if(stat & BROKEN)
icon_state = "[initial_icon_state]-broken"
return
if(storedpart)
add_overlay("[initial_icon_state]-closed")
if(powered())
icon_state = initial_icon_state
else
icon_state = "[initial_icon_state]-off"
/obj/machinery/aug_manipulator/update_overlays()
. = ..()
if(storedpart)
. += "[initial_icon_state]-closed"
/obj/machinery/aug_manipulator/Destroy()
QDEL_NULL(storedpart)
return ..()
+97 -34
View File
@@ -17,6 +17,7 @@
var/list/L = list()
var/list/LL = list()
var/hacked = FALSE
var/hackable = TRUE
var/disabled = 0
var/shocked = FALSE
var/hack_wire
@@ -46,9 +47,24 @@
)
/obj/machinery/autolathe/Initialize()
AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS), 0, TRUE, null, null, CALLBACK(src, .proc/AfterMaterialInsert))
var/static/list/allowed_types = list(
/datum/material/iron,
/datum/material/glass,
/datum/material/gold,
/datum/material/silver,
/datum/material/diamond,
/datum/material/uranium,
/datum/material/plasma,
/datum/material/bluespace,
/datum/material/bananium,
/datum/material/titanium,
/datum/material/runite,
/datum/material/plastic,
/datum/material/adamantine,
/datum/material/mythril
)
AddComponent(/datum/component/material_container, allowed_types, _show_on_examine=TRUE, _after_insert=CALLBACK(src, .proc/AfterMaterialInsert))
. = ..()
wires = new /datum/wires/autolathe(src)
stored_research = new /datum/techweb/specialized/autounlocking/autolathe
matching_designs = list()
@@ -120,15 +136,14 @@
return ..()
/obj/machinery/autolathe/proc/AfterMaterialInsert(type_inserted, id_inserted, amount_inserted)
if(ispath(type_inserted, /obj/item/stack/ore/bluespace_crystal))
/obj/machinery/autolathe/proc/AfterMaterialInsert(obj/item/item_inserted, id_inserted, amount_inserted)
if(istype(item_inserted, /obj/item/stack/ore/bluespace_crystal))
use_power(MINERAL_MATERIAL_AMOUNT / 10)
else if(item_inserted.custom_materials?.len && item_inserted.custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)])
flick("autolathe_r",src)//plays glass insertion animation by default otherwise
else
switch(id_inserted)
if (MAT_METAL)
flick("autolathe_o",src)//plays metal insertion animation
if (MAT_GLASS)
flick("autolathe_r",src)//plays glass insertion animation
flick("autolathe_o",src)//plays metal insertion animation
use_power(min(1000, amount_inserted / 100))
updateUsrDialog()
@@ -159,18 +174,42 @@
/////////////////
var/coeff = (is_stack ? 1 : prod_coeff) //stacks are unaffected by production coefficient
var/metal_cost = being_built.materials[MAT_METAL]
var/glass_cost = being_built.materials[MAT_GLASS]
var/total_amount = 0
var/power = max(2000, (metal_cost+glass_cost)*multiplier/5)
for(var/MAT in being_built.materials)
total_amount += being_built.materials[MAT]
var/power = max(2000, (total_amount)*multiplier/5) //Change this to use all materials
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
if((materials.amount(MAT_METAL) >= metal_cost*multiplier*coeff) && (materials.amount(MAT_GLASS) >= glass_cost*multiplier*coeff))
var/list/materials_used = list()
var/list/custom_materials = list() //These will apply their material effect, This should usually only be one.
for(var/MAT in being_built.materials)
var/datum/material/used_material = MAT
var/amount_needed = being_built.materials[MAT] * coeff * multiplier
if(istext(used_material)) //This means its a category
var/list/list_to_show = list()
for(var/i in SSmaterials.materials_by_category[used_material])
if(materials.materials[i] > 0)
list_to_show += i
used_material = input("Choose [used_material]", "Custom Material") as null|anything in list_to_show
if(!used_material)
return //Didn't pick any material, so you can't build shit either.
custom_materials[used_material] += amount_needed
materials_used[used_material] = amount_needed
if(materials.has_materials(materials_used))
busy = TRUE
use_power(power)
icon_state = "autolathe_n"
var/time = is_stack ? 32 : 32*coeff*multiplier
addtimer(CALLBACK(src, .proc/make_item, power, metal_cost, glass_cost, multiplier, coeff, is_stack), time)
addtimer(CALLBACK(src, .proc/make_item, power, materials_used, custom_materials, multiplier, coeff, is_stack), time)
else
to_chat(usr, "<span class=\"alert\">Not enough materials for this operation.</span>")
if(href_list["search"])
matching_designs.Cut()
@@ -187,12 +226,11 @@
return
/obj/machinery/autolathe/proc/make_item(power, metal_cost, glass_cost, multiplier, coeff, is_stack)
/obj/machinery/autolathe/proc/make_item(power, var/list/materials_used, var/list/picked_materials, multiplier, coeff, is_stack)
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
var/atom/A = drop_location()
use_power(power)
var/list/materials_used = list(MAT_METAL=metal_cost*coeff*multiplier, MAT_GLASS=glass_cost*coeff*multiplier)
materials.use_amount(materials_used)
materials.use_materials(materials_used)
if(is_stack)
var/obj/item/stack/N = new being_built.build_path(A, multiplier)
@@ -201,10 +239,11 @@
else
for(var/i=1, i<=multiplier, i++)
var/obj/item/new_item = new being_built.build_path(A)
new_item.materials = new_item.materials.Copy()
for(var/mat in materials_used)
new_item.materials[mat] = materials_used[mat] / multiplier
new_item.autolathe_crafted(src)
if(length(picked_materials))
new_item.set_custom_materials(picked_materials, 1 / multiplier) //Ensure we get the non multiplied amount
icon_state = "autolathe"
busy = FALSE
updateDialog()
@@ -220,6 +259,12 @@
T -= M.rating*0.2
prod_coeff = min(1,max(0,T)) // Coeff going 1 -> 0,8 -> 0,6 -> 0,4
/obj/machinery/autolathe/examine(mob/user)
. += ..()
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
if(in_range(user, src) || isobserver(user))
. += "<span class='notice'>The status display reads: Storing up to <b>[materials.max_amount]</b> material units.<br>Material consumption at <b>[prod_coeff*100]%</b>.</span>"
/obj/machinery/autolathe/proc/main_win(mob/user)
var/dat = "<div class='statusDisplay'><h3>Autolathe Menu:</h3><br>"
dat += materials_printout()
@@ -263,7 +308,9 @@
if(ispath(D.build_path, /obj/item/stack))
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
var/max_multiplier = min(D.maxstack, D.materials[MAT_METAL] ?round(materials.amount(MAT_METAL)/D.materials[MAT_METAL]):INFINITY,D.materials[MAT_GLASS]?round(materials.amount(MAT_GLASS)/D.materials[MAT_GLASS]):INFINITY)
var/max_multiplier
for(var/datum/material/mat in D.materials)
max_multiplier = min(D.maxstack, round(materials.get_material_amount(mat)/D.materials[mat]))
if (max_multiplier>10 && !disabled)
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=10'>x10</a>"
if (max_multiplier>25 && !disabled)
@@ -295,7 +342,9 @@
if(ispath(D.build_path, /obj/item/stack))
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
var/max_multiplier = min(D.maxstack, D.materials[MAT_METAL] ?round(materials.amount(MAT_METAL)/D.materials[MAT_METAL]):INFINITY,D.materials[MAT_GLASS]?round(materials.amount(MAT_GLASS)/D.materials[MAT_GLASS]):INFINITY)
var/max_multiplier
for(var/datum/material/mat in D.materials)
max_multiplier = min(D.maxstack, round(materials.get_material_amount(mat)/D.materials[mat]))
if (max_multiplier>10 && !disabled)
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=10'>x10</a>"
if (max_multiplier>25 && !disabled)
@@ -312,8 +361,10 @@
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
var/dat = "<b>Total amount:</b> [materials.total_amount] / [materials.max_amount] cm<sup>3</sup><br>"
for(var/mat_id in materials.materials)
var/datum/material/M = materials.materials[mat_id]
dat += "<b>[M.name] amount:</b> [M.amount] cm<sup>3</sup><br>"
var/datum/material/M = mat_id
var/mineral_amount = materials.materials[mat_id]
if(mineral_amount > 0)
dat += "<b>[M.name] amount:</b> [mineral_amount] cm<sup>3</sup><br>"
return dat
/obj/machinery/autolathe/proc/can_build(datum/design/D, amount = 1)
@@ -322,20 +373,24 @@
var/coeff = (ispath(D.build_path, /obj/item/stack) ? 1 : prod_coeff)
var/list/required_materials = list()
for(var/i in D.materials)
required_materials[i] = D.materials[i] * coeff * amount
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
if(D.materials[MAT_METAL] && (materials.amount(MAT_METAL) < (D.materials[MAT_METAL] * coeff * amount)))
return FALSE
if(D.materials[MAT_GLASS] && (materials.amount(MAT_GLASS) < (D.materials[MAT_GLASS] * coeff * amount)))
return FALSE
return TRUE
return materials.has_materials(required_materials)
/obj/machinery/autolathe/proc/get_design_cost(datum/design/D)
var/coeff = (ispath(D.build_path, /obj/item/stack) ? 1 : prod_coeff)
var/dat
if(D.materials[MAT_METAL])
dat += "[D.materials[MAT_METAL] * coeff] metal "
if(D.materials[MAT_GLASS])
dat += "[D.materials[MAT_GLASS] * coeff] glass"
for(var/i in D.materials)
if(istext(i)) //Category handling
dat += "[D.materials[i] * coeff] [i]"
else
var/datum/material/M = i
dat += "[D.materials[i] * coeff] [M.name] "
return dat
/obj/machinery/autolathe/proc/reset(wire)
@@ -365,6 +420,8 @@
/obj/machinery/autolathe/proc/adjust_hacked(state)
hacked = state
if(!hackable && hacked)
return
for(var/id in SSresearch.techweb_designs)
var/datum/design/D = SSresearch.techweb_design_by_id(id)
if((D.build_type & AUTOLATHE) && ("hacked" in D.category))
@@ -377,7 +434,13 @@
. = ..()
adjust_hacked(TRUE)
/obj/machinery/autolathe/secure
name = "secured autolathe"
desc = "An autolathe reprogrammed with security protocols to prevent hacking."
hackable = FALSE
circuit = /obj/item/circuitboard/machine/autolathe/secure
//Called when the object is constructed by an autolathe
//Has a reference to the autolathe so you can do !!FUN!! things with hacked lathes
/obj/item/proc/autolathe_crafted(obj/machinery/autolathe/A)
return
return
+28 -25
View File
@@ -46,34 +46,37 @@
new /obj/item/stack/spacecash/c200(drop_location()) // will autostack
playsound(src.loc, 'sound/items/poster_being_created.ogg', 100, 1)
SSshuttle.points -= 200
if(next_warning < world.time && prob(15))
var/area/A = get_area(loc)
var/message = "Unauthorized credit withdrawal underway in [A.map_name]!!"
radio.talk_into(src, message, radio_channel)
next_warning = world.time + minimum_time_between_warnings
if(next_warning < world.time && prob(15))
var/area/A = get_area(loc)
var/message = "Unauthorized credit withdrawal underway in [A.map_name]!!"
radio.talk_into(src, message, radio_channel)
next_warning = world.time + minimum_time_between_warnings
/obj/machinery/computer/bank_machine/ui_interact(mob/user)
. = ..()
var/dat = "[station_name()] secure vault. Authorized personnel only.<br>"
dat += "Current Balance: [SSshuttle.points] credits.<br>"
if(!siphoning)
dat += "<A href='?src=[REF(src)];siphon=1'>Siphon Credits</A><br>"
else
dat += "<A href='?src=[REF(src)];halt=1'>Halt Credit Siphon</A><br>"
/obj/machinery/computer/bank_machine/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "bank_machine", name, 320, 165, master_ui, state)
ui.open()
dat += "<a href='?src=[REF(user)];mach_close=computer'>Close</a>"
/obj/machinery/computer/bank_machine/ui_data(mob/user)
var/list/data = list()
data["current_balance"] = SSshuttle.points
data["siphoning"] = siphoning
data["station_name"] = station_name()
var/datum/browser/popup = new(user, "computer", "Bank Vault", 300, 200)
popup.set_content("<center>[dat]</center>")
popup.set_title_image(usr.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
return data
/obj/machinery/computer/bank_machine/Topic(href, href_list)
/obj/machinery/computer/bank_machine/ui_act(action, params)
if(..())
return
if(href_list["siphon"])
say("Siphon of station credits has begun!")
siphoning = TRUE
if(href_list["halt"])
say("Station credit withdrawal halted.")
siphoning = FALSE
switch(action)
if("siphon")
say("Siphon of station credits has begun!")
siphoning = TRUE
. = TRUE
if("halt")
say("Station credit withdrawal halted.")
siphoning = FALSE
. = TRUE
+20 -20
View File
@@ -32,7 +32,7 @@
if(bag)
. += "<span class='notice'>It has \a [bag.name] hooked to its <b>input</b> slot. The counter reads: \"Current Capacity: [bag.reagents.total_volume] of [bag.reagents.maximum_volume]\"</span>"
if(outbag)
. += "<span class='notice'>It has \a [bag.name] hooked to its <b>output</b> slot. The counter reads: \"Current Capacity: [outbag.reagents.total_volume] of [outbag.reagents.maximum_volume]\"</span>"
. += "<span class='notice'>It has \a [outbag.name] hooked to its <b>output</b> slot. The counter reads: \"Current Capacity: [outbag.reagents.total_volume] of [outbag.reagents.maximum_volume]\"</span>"
/obj/machinery/bloodbankgen/handle_atom_del(atom/A)
@@ -54,16 +54,17 @@
efficiency = E
productivity = P
/obj/machinery/bloodbankgen/update_icon()
cut_overlays()
/obj/machinery/bloodbankgen/update_icon_state()
if(is_operational())
icon_state = "bloodbank-on"
icon_state = "bloodbank-[is_operational() ? "on" : "off"]"
/obj/machinery/bloodbankgen/update_overlays()
. = ..()
if(panel_open)
add_overlay("bloodbank-panel")
. += "bloodbank-panel"
if(src.bag)
add_overlay("bloodbag-input")
if(bag)
. += "bloodbag-input"
if(bag.reagents.total_volume)
var/mutable_appearance/filling_overlay = mutable_appearance(icon, "input-reagent")
@@ -85,10 +86,10 @@
filling_overlay.icon_state = "input-reagent100"
filling_overlay.color = list(mix_color_from_reagents(bag.reagents.reagent_list))
add_overlay(filling_overlay)
. += filling_overlay
if(src.outbag)
add_overlay("bloodbag-output")
if(outbag)
. += "bloodbag-output"
if(outbag.reagents.total_volume)
var/mutable_appearance/filling_overlay = mutable_appearance(icon, "output-reagent")
@@ -110,8 +111,7 @@
filling_overlay.icon_state = "output-reagent100"
filling_overlay.color = list(mix_color_from_reagents(outbag.reagents.reagent_list))
add_overlay(filling_overlay)
return
. += filling_overlay
/obj/machinery/bloodbankgen/process()
if(!is_operational())
@@ -274,20 +274,20 @@
return TRUE
/obj/machinery/bloodbankgen/proc/detachinput()
/obj/machinery/bloodbankgen/proc/detachinput(mob/user)
if(bag)
bag.forceMove(drop_location())
if(usr && Adjacent(usr) && !issiliconoradminghost(usr))
usr.put_in_hands(bag)
if(user && Adjacent(usr) && user.can_hold_items())
user.put_in_hands(bag)
bag = null
draining = null
update_icon()
/obj/machinery/bloodbankgen/proc/detachoutput()
/obj/machinery/bloodbankgen/proc/detachoutput(mob/user)
if(outbag)
outbag.forceMove(drop_location())
if(usr && Adjacent(usr) && !issiliconoradminghost(usr))
usr.put_in_hands(outbag)
if(user && Adjacent(user) && user.can_hold_items())
user.put_in_hands(outbag)
outbag = null
filling = null
update_icon()
@@ -325,12 +325,12 @@
activateinput()
else if(href_list["detachinput"])
detachinput()
detachinput(usr)
else if(href_list["activateoutput"])
activateoutput()
else if(href_list["detachoutput"])
detachoutput()
detachoutput(usr)
updateUsrDialog()
+1 -1
View File
@@ -265,4 +265,4 @@
desc = "Used for building buttons."
icon_state = "button"
result_path = /obj/machinery/button
materials = list(MAT_METAL=MINERAL_MATERIAL_AMOUNT)
custom_materials = list(/datum/material/iron = MINERAL_MATERIAL_AMOUNT)
+5 -4
View File
@@ -17,7 +17,7 @@
armor = list("melee" = 50, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 50)
max_integrity = 100
integrity_failure = 50
integrity_failure = 0.5
var/list/network = list("ss13")
var/c_tag = null
var/status = TRUE
@@ -240,10 +240,10 @@
to_chat(AI, "<b>[U]</b> holds <a href='?_src_=usr;show_paper=1;'>\a [itemname]</a> up to one of your cameras ...")
else
to_chat(AI, "<b><a href='?src=[REF(AI)];track=[html_encode(U.name)]'>[U]</a></b> holds <a href='?_src_=usr;show_paper=1;'>\a [itemname]</a> up to one of your cameras ...")
AI.last_paper_seen = "<HTML><HEAD><TITLE>[itemname]</TITLE></HEAD><BODY><TT>[info]</TT></BODY></HTML>"
AI.last_paper_seen = "<HTML><HEAD><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><TITLE>[itemname]</TITLE></HEAD><BODY><TT>[info]</TT></BODY></HTML>"
else if (O.client && O.client.eye == src)
to_chat(O, "[U] holds \a [itemname] up to one of the cameras ...")
O << browse(text("<HTML><HEAD><TITLE>[]</TITLE></HEAD><BODY><TT>[]</TT></BODY></HTML>", itemname, info), text("window=[]", itemname))
O << browse(text("<HTML><HEAD<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>><TITLE>[]</TITLE></HEAD><BODY><TT>[]</TT></BODY></HTML>", itemname, info), text("window=[]", itemname))
return
else if(istype(I, /obj/item/camera_bug))
@@ -319,7 +319,8 @@
if(status)
change_msg = "reactivates"
triggerCameraAlarm()
addtimer(CALLBACK(src, .proc/cancelCameraAlarm), 100)
if(!QDELETED(src)) //We'll be doing it anyway in destroy
addtimer(CALLBACK(src, .proc/cancelCameraAlarm), 100)
if(displaymessage)
if(user)
visible_message("<span class='danger'>[user] [change_msg] [src]!</span>")
@@ -3,7 +3,7 @@
desc = "The basic construction for Nanotrasen-Always-Watching-You cameras."
icon = 'icons/obj/machines/camera.dmi'
icon_state = "cameracase"
materials = list(MAT_METAL=400, MAT_GLASS=250)
custom_materials = list(/datum/material/iron=400, /datum/material/glass=250)
result_path = /obj/structure/camera_assembly
+14 -13
View File
@@ -10,24 +10,27 @@
circuit = /obj/item/circuitboard/machine/cell_charger
pass_flags = PASSTABLE
var/obj/item/stock_parts/cell/charging = null
var/chargelevel = -1
var/charge_rate = 500
/obj/machinery/cell_charger/update_icon()
cut_overlays()
if(charging)
add_overlay(image(charging.icon, charging.icon_state))
add_overlay("ccharger-on")
if(!(stat & (BROKEN|NOPOWER)))
var/newlevel = round(charging.percent() * 4 / 100)
chargelevel = newlevel
add_overlay("ccharger-o[newlevel]")
/obj/machinery/cell_charger/update_overlays()
. += ..()
if(!charging)
return
. += mutable_appearance(charging.icon, charging.icon_state)
. += "ccharger-on"
if(!(stat & (BROKEN|NOPOWER)))
var/newlevel = round(charging.percent() * 4 / 100)
. += "ccharger-o[newlevel]"
/obj/machinery/cell_charger/examine(mob/user)
. = ..()
. += "There's [charging ? "a" : "no"] cell in the charger."
if(charging)
. += "Current charge: [round(charging.percent(), 1)]%."
if(in_range(user, src) || isobserver(user))
. += "<span class='notice'>The status display reads: Charge rate at <b>[charge_rate]J</b> per cycle.</span>"
/obj/machinery/cell_charger/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/stock_parts/cell) && !panel_open)
@@ -44,7 +47,7 @@
var/area/a = loc.loc // Gets our locations location, like a dream within a dream
if(!isarea(a))
return
if(a.power_equip == 0) // There's no APC in this area, don't try to cheat power!
if(!a.powered(EQUIP)) // There's no APC in this area, don't try to cheat power!
to_chat(user, "<span class='warning'>[src] blinks red as you try to insert the cell!</span>")
return
if(!user.transferItemToLoc(W,src))
@@ -52,7 +55,6 @@
charging = W
user.visible_message("[user] inserts a cell into [src].", "<span class='notice'>You insert a cell into [src].</span>")
chargelevel = -1
update_icon()
else
if(!charging && default_deconstruction_screwdriver(user, icon_state, icon_state, W))
@@ -75,7 +77,6 @@
/obj/machinery/cell_charger/proc/removecell()
charging.update_icon()
charging = null
chargelevel = -1
update_icon()
/obj/machinery/cell_charger/attack_hand(mob/user)
+57 -52
View File
@@ -4,7 +4,7 @@
//Potential replacement for genetics revives or something I dunno (?)
#define CLONE_INITIAL_DAMAGE 150 //Clones in clonepods start with 150 cloneloss damage and 150 brainloss damage, thats just logical
#define MINIMUM_HEAL_LEVEL 40
#define MINIMUM_HEAL_LEVEL 20
#define SPEAK(message) radio.talk_into(src, message, radio_channel)
@@ -67,12 +67,9 @@
for(var/obj/item/stock_parts/scanning_module/S in component_parts)
efficiency += S.rating
for(var/obj/item/stock_parts/manipulator/P in component_parts)
speed_coeff += P.rating
heal_level = (efficiency * 15) + 10
if(heal_level < MINIMUM_HEAL_LEVEL)
heal_level = MINIMUM_HEAL_LEVEL
if(heal_level > 100)
heal_level = 100
speed_coeff += (P.rating / 2)
speed_coeff = max(1, speed_coeff)
heal_level = CLAMP((efficiency * 10) + 10, MINIMUM_HEAL_LEVEL, 100)
//The return of data disks?? Just for transferring between genetics machine/cloning machine.
//TO-DO: Make the genetics machine accept them.
@@ -80,6 +77,8 @@
name = "cloning data disk"
icon_state = "datadisk0" //Gosh I hope syndies don't mistake them for the nuke disk.
var/list/fields = list()
var/list/mutations = list()
var/max_mutations = 6
var/read_only = 0 //Well,it's still a floppy disk
//Disk stuff.
@@ -102,8 +101,13 @@
/obj/machinery/clonepod/examine(mob/user)
. = ..()
var/mob/living/mob_occupant = occupant
. += "<span class='notice'>The <i>linking</i> device can be <i>scanned<i> with a multitool.</span>"
if(mess)
. += "It's filled with blood and viscera. You swear you can see it moving..."
if(in_range(user, src) || isobserver(user))
. += "<span class='notice'>The status display reads: Cloning speed at <b>[speed_coeff*50]%</b>.<br>Predicted amount of cellular damage: <b>[100-heal_level]%</b>.</span>"
if(efficiency > 5)
to_chat(user, "<span class='notice'>Pod has been upgraded to support autoprocessing.<span>")
if(is_operational() && mob_occupant)
if(mob_occupant.stat != DEAD)
. += "Current clone cycle is [round(get_completion())]% complete."
@@ -127,7 +131,7 @@
return examine(user)
//Start growing a human clone in the pod!
/obj/machinery/clonepod/proc/growclone(ckey, clonename, ui, se, mindref, datum/species/mrace, list/features, factions, list/quirks)
/obj/machinery/clonepod/proc/growclone(ckey, clonename, ui, mutation_index, mindref, datum/species/mrace, list/features, factions, list/quirks)
if(panel_open)
return FALSE
if(mess || attempting)
@@ -163,10 +167,9 @@
var/mob/living/carbon/human/H = new /mob/living/carbon/human(src)
H.hardset_dna(ui, se, H.real_name, null, mrace, features)
H.hardset_dna(ui, mutation_index, H.real_name, null, mrace, features)
if(prob(50 - efficiency*10)) //Chance to give a bad mutation.
H.randmutb() //100% bad mutation. Can be cured with mutadone.
H.easy_randmut(NEGATIVE+MINOR_NEGATIVE) //100% bad mutation. Can be cured with mutadone.
H.silent = 20 //Prevents an extreme edge case where clones could speak if they said something at exactly the right moment.
occupant = H
@@ -177,6 +180,7 @@
//Get the clone body ready
maim_clone(H)
ADD_TRAIT(H, TRAIT_MUTATION_STASIS, CLONING_POD_TRAIT)
ADD_TRAIT(H, TRAIT_STABLEHEART, CLONING_POD_TRAIT)
ADD_TRAIT(H, TRAIT_STABLELIVER, CLONING_POD_TRAIT)
ADD_TRAIT(H, TRAIT_EMOTEMUTE, CLONING_POD_TRAIT)
@@ -216,7 +220,7 @@
if(!is_operational()) //Autoeject if power is lost
if(mob_occupant)
go_out()
mob_occupant.apply_vore_prefs()
mob_occupant.copy_from_prefs_vr()
connected_message("Clone Ejected: Loss of power.")
else if(mob_occupant && (mob_occupant.loc == src))
@@ -226,7 +230,7 @@
SPEAK("The cloning has been \
aborted due to unrecoverable tissue failure.")
go_out()
mob_occupant.apply_vore_prefs()
mob_occupant.copy_from_prefs_vr()
else if(mob_occupant.cloneloss > (100 - heal_level))
mob_occupant.Unconscious(80)
@@ -272,7 +276,7 @@
BP.attach_limb(mob_occupant)
go_out()
mob_occupant.apply_vore_prefs()
mob_occupant.copy_from_prefs_vr()
else if (!mob_occupant || mob_occupant.loc != src)
occupant = null
@@ -320,7 +324,7 @@
SPEAK("An emergency ejection of the current clone has occurred. Survival not guaranteed.")
to_chat(user, "<span class='notice'>You force an emergency ejection. </span>")
go_out()
mob_occupant.apply_vore_prefs()
mob_occupant.copy_from_prefs_vr()
else
return ..()
@@ -366,6 +370,7 @@
return
REMOVE_TRAIT(mob_occupant, TRAIT_STABLEHEART, CLONING_POD_TRAIT)
REMOVE_TRAIT(mob_occupant, TRAIT_MUTATION_STASIS, CLONING_POD_TRAIT)
REMOVE_TRAIT(mob_occupant, TRAIT_STABLELIVER, CLONING_POD_TRAIT)
REMOVE_TRAIT(mob_occupant, TRAIT_EMOTEMUTE, CLONING_POD_TRAIT)
REMOVE_TRAIT(mob_occupant, TRAIT_MUTE, CLONING_POD_TRAIT)
@@ -418,7 +423,7 @@
if(mob_occupant && prob(100/(severity*efficiency)))
connected_message(Gibberish("EMP-caused Accidental Ejection", 0))
SPEAK(Gibberish("Exposure to electromagnetic fields has caused the ejection of, ERROR: John Doe, prematurely." ,0))
mob_occupant.apply_vore_prefs()
mob_occupant.copy_from_prefs_vr()
go_out()
/obj/machinery/clonepod/ex_act(severity, target)
@@ -471,57 +476,57 @@
if(!istype(organ) || (organ.organ_flags & ORGAN_VITAL))
continue
organ.organ_flags |= ORGAN_FROZEN
organ.Remove(H, special=TRUE)
organ.Remove(TRUE)
organ.forceMove(src)
unattached_flesh += organ
flesh_number = unattached_flesh.len
/obj/machinery/clonepod/update_icon()
cut_overlays()
/obj/machinery/clonepod/update_icon_state()
if(mess)
icon_state = "pod_g"
var/image/gib1 = image(CRYOMOBS, "gibup")
var/image/gib2 = image(CRYOMOBS, "gibdown")
gib1.pixel_y = 27 + round(sin(world.time) * 3)
gib1.pixel_x = round(sin(world.time * 3))
gib2.pixel_y = 27 + round(cos(world.time) * 3)
gib2.pixel_x = round(cos(world.time * 3))
add_overlay(gib2)
add_overlay(gib1)
add_overlay("cover-on")
else if(occupant)
icon_state = "pod_1"
var/image/occupant_overlay
var/completion = (flesh_number - unattached_flesh.len) / flesh_number
if(unattached_flesh.len <= 0)
occupant_overlay = image(occupant.icon, occupant.icon_state)
occupant_overlay.copy_overlays(occupant)
else
occupant_overlay = image(CRYOMOBS, "clone_meat")
var/matrix/tform = matrix()
tform.Scale(completion)
tform.Turn(cos(world.time * 2) * 3)
occupant_overlay.transform = tform
occupant_overlay.appearance_flags = 0
occupant_overlay.dir = SOUTH
occupant_overlay.pixel_y = 27 + round(sin(world.time) * 3)
occupant_overlay.pixel_x = round(sin(world.time * 3))
add_overlay(occupant_overlay)
add_overlay("cover-on")
else
icon_state = "pod_0"
if(panel_open)
icon_state = "pod_0_maintenance"
add_overlay("panel")
/obj/machinery/clonepod/update_overlays()
. = ..()
if(mess)
var/mutable_appearance/gib1 = mutable_appearance(CRYOMOBS, "gibup")
var/mutable_appearance/gib2 = mutable_appearance(CRYOMOBS, "gibdown")
gib1.pixel_y = 27 + round(sin(world.time) * 3)
gib1.pixel_x = round(sin(world.time * 3))
gib2.pixel_y = 27 + round(cos(world.time) * 3)
gib2.pixel_x = round(cos(world.time * 3))
. += gib2
. += gib1
else if(occupant)
var/mutable_appearance/occupant_overlay
var/completion = (flesh_number - unattached_flesh.len) / flesh_number
if(unattached_flesh.len <= 0)
occupant_overlay = mutable_appearance(occupant.icon, occupant.icon_state)
occupant_overlay.copy_overlays(occupant)
. += "cover-on"
else
occupant_overlay = mutable_appearance(CRYOMOBS, "clone_meat")
var/matrix/tform = matrix()
tform.Scale(completion)
tform.Turn(cos(world.time * 2) * 3)
occupant_overlay.transform = tform
occupant_overlay.appearance_flags = NONE
occupant_overlay.dir = SOUTH
occupant_overlay.pixel_y = 27 + round(sin(world.time) * 3)
occupant_overlay.pixel_x = round(sin(world.time * 3))
. += occupant_overlay
. += "cover-on"
. += "panel"
/*
* Manual -- A big ol' manual.
+3 -8
View File
@@ -11,7 +11,6 @@
var/obj/structure/table/optable/table
var/list/advanced_surgeries = list()
var/datum/techweb/linked_techweb
var/menu = MENU_OPERATION
light_color = LIGHT_COLOR_BLUE
/obj/machinery/computer/operating/Initialize()
@@ -54,8 +53,6 @@
var/list/data = list()
data["table"] = table
if(table)
data["menu"] = menu
var/list/surgeries = list()
for(var/X in advanced_surgeries)
var/datum/surgery/S = X
@@ -64,9 +61,8 @@
surgery["desc"] = initial(S.desc)
surgeries += list(surgery)
data["surgeries"] = surgeries
data["patient"] = list()
if(table.check_patient())
data["patient"] = list()
patient = table.patient
switch(patient.stat)
if(CONSCIOUS)
@@ -110,15 +106,14 @@
"alternative_step" = alternative_step,
"alt_chems_needed" = alt_chems_needed
))
else
data["patient"] = null
return data
/obj/machinery/computer/operating/ui_act(action, params)
if(..())
return
switch(action)
if("change_menu")
menu = text2num(params["menu"])
. = TRUE
if("sync")
sync_surgeries()
. = TRUE
+6 -6
View File
@@ -7,7 +7,7 @@
idle_power_usage = 300
active_power_usage = 300
max_integrity = 200
integrity_failure = 100
integrity_failure = 0.5
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 20)
var/brightness_on = 1
var/icon_keyboard = "generic_key"
@@ -48,13 +48,13 @@
icon_state = initial(icon_state)
update_icon()
/obj/machinery/computer/update_icon()
cut_overlays()
/obj/machinery/computer/update_overlays()
. = ..()
SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
if(stat & NOPOWER)
add_overlay("[icon_keyboard]_off")
. += "[icon_keyboard]_off"
return
add_overlay(icon_keyboard)
. += icon_keyboard
// This whole block lets screens ignore lighting and be visible even in the darkest room
// We can't do this for many things that emit light unfortunately because it layers over things that would be on top of it
@@ -62,7 +62,7 @@
if(stat & BROKEN)
overlay_state = "[icon_state]_broken"
SSvis_overlays.add_vis_overlay(src, icon, overlay_state, layer, plane, dir)
SSvis_overlays.add_vis_overlay(src, icon, overlay_state, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir, alpha=128)
SSvis_overlays.add_vis_overlay(src, icon, overlay_state, EMISSIVE_LAYER, EMISSIVE_PLANE, dir, alpha=128)
/obj/machinery/computer/power_change()
..()
+11 -12
View File
@@ -103,21 +103,20 @@
add_fingerprint(usr)
updateUsrDialog()
/obj/machinery/computer/aifixer/update_icon()
..()
/obj/machinery/computer/aifixer/update_overlays()
. = ..()
if(stat & (NOPOWER|BROKEN))
return
if(active)
. += "ai-fixer-on"
if (occupier)
switch (occupier.stat)
if (0)
. += "ai-fixer-full"
if (2)
. += "ai-fixer-404"
else
if(active)
add_overlay("ai-fixer-on")
if (occupier)
switch (occupier.stat)
if (0)
add_overlay("ai-fixer-full")
if (2)
add_overlay("ai-fixer-404")
else
add_overlay("ai-fixer-empty")
. += "ai-fixer-empty"
/obj/machinery/computer/aifixer/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card)
if(!..())
+1 -1
View File
@@ -198,7 +198,7 @@
/obj/machinery/computer/apc_control/proc/log_activity(log_text)
var/op_string = operator && !(obj_flags & EMAGGED) ? operator : "\[NULL OPERATOR\]"
LAZYADD(logs, "<b>([STATION_TIME_TIMESTAMP("hh:mm:ss")])</b> [op_string] [log_text]")
LAZYADD(logs, "<b>([STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)])</b> [op_string] [log_text]")
/mob/proc/using_power_flow_console()
for(var/obj/machinery/computer/apc_control/A in range(1, src))
@@ -159,6 +159,8 @@
if(CHECK_BITFIELD(obj_flags, EMAGGED) && !exploding_hell)
exploding_hell = TRUE
explode_EVERYTHING()
if(QDELETED(src))
return
if(mine_sound)
switch(rand(1,3)) //Play every time a mine is hit
if(1)
@@ -304,12 +306,12 @@
/obj/machinery/computer/arcade/minesweeper/proc/custom_generation(mob/user)
playsound(loc, 'sound/arcade/minesweeper_menuselect.ogg', 50, 0, extrarange = -3, falloff = 10) //Entered into the menu so ping sound
var/new_rows = input(user, "How many rows do you want? (Minimum: 4, Maximum: 30)", "Minesweeper Rows") as null|num
if(!new_rows || !user.canUseTopic(src, !issilicon(user)))
if(!new_rows || !user.canUseTopic(src, !hasSiliconAccessInArea(user)))
return FALSE
new_rows = CLAMP(new_rows + 1, 4, 30)
playsound(loc, 'sound/arcade/minesweeper_menuselect.ogg', 50, 0, extrarange = -3, falloff = 10)
var/new_columns = input(user, "How many columns do you want? (Minimum: 4, Maximum: 50)", "Minesweeper Squares") as null|num
if(!new_columns || !user.canUseTopic(src, !issilicon(user)))
if(!new_columns || !user.canUseTopic(src, !hasSiliconAccessInArea(user)))
return FALSE
new_columns = CLAMP(new_columns + 1, 4, 50)
playsound(loc, 'sound/arcade/minesweeper_menuselect.ogg', 50, 0, extrarange = -3, falloff = 10)
@@ -317,7 +319,7 @@
var/lower_limit = round(grid_area*0.156)
var/upper_limit = round(grid_area*0.85)
var/new_mine_limit = input(user, "How many mines do you want? (Minimum: [lower_limit], Maximum: [upper_limit])", "Minesweeper Mines") as null|num
if(!new_mine_limit || !user.canUseTopic(src, !issilicon(user)))
if(!new_mine_limit || !user.canUseTopic(src, !hasSiliconAccessInArea(user)))
return FALSE
playsound(loc, 'sound/arcade/minesweeper_menuselect.ogg', 50, 0, extrarange = -3, falloff = 10)
rows = new_rows
@@ -367,10 +369,10 @@
var/row_limit = rows-1
var/column_limit = columns-1
var/mine_limit_v2 = mine_limit
if(rows > 11)
row_limit = 10
if(columns > 11)
column_limit = 10
if(rows > 21)
row_limit = 20
if(columns > 21)
column_limit = 20
if(mine_limit > (rows*columns) * 0.25)
mine_limit_v2 = 24
message_admins("[key_name_admin(user)] failed an emagged Minesweeper arcade and has unleashed an explosion armageddon of size [row_limit],[column_limit] around [ADMIN_LOOKUPFLW(user.loc)]!")
@@ -378,15 +380,38 @@
explosion(loc, 2, 5, 10, 15) //Thought you could survive by putting as few mines as possible, huh??
else
explosion(loc, 1, 3, rand(1,5), rand(1,10))
for(var/y69=y-row_limit;y69<y+row_limit;y69++) //Create a shitton of explosions in irl turfs if we lose, it will probably kill us
for(var/x69=x-column_limit;x69<x+column_limit;x69++)
if(prob(mine_limit_v2)) //Probability of explosion happening, according to how many mines were on the board... up to a limit
var/explosionloc
explosionloc = locate(y69,x69,z)
explosion(explosionloc, 0, rand(1,2),rand(1,5),rand(3,10), adminlog = FALSE)
var/list/targets = list()
var/cur_y = y - round(row_limit * 0.5, 1)
var/starting_row = 1
if(cur_y < 1)
starting_row -= cur_y - 1
cur_y = 1
var/start_x = x - round(column_limit * 0.5, 1)
var/starting_column = 1
if(start_x < 1)
starting_column -= start_x - 1
start_x = 1
for(var/row in starting_row to length(table)) //translate the mines locations into actual turf coordinates.
if(!locate(cur_y, start_x, z))
break
var/cur_x = start_x
for(var/column in starting_column to length(table[row]))
var/coord_value = table[row][column]
if(coord_value == 10 || coord_value == 0) //there is a mine in here.
var/turf/T = locate(cur_y, cur_x, z)
if(!T)
break
targets += T
cur_x++
cur_y++
var/num_explosions = 0
for(var/T in shuffle(targets)) //Create a shitton of explosions in irl turfs if we lose, it will probably kill us
addtimer(CALLBACK(GLOBAL_PROC, /proc/explosion, T, 0, rand(1,2),rand(1,5),rand(3,10), FALSE), 15 * ++num_explosions)
if(num_explosions == mine_limit_v2)
return
#undef MINESWEEPERIMG
#undef MINESWEEPER_GAME_MAIN_MENU
#undef MINESWEEPER_GAME_PLAYING
#undef MINESWEEPER_GAME_LOST
#undef MINESWEEPER_GAME_WON
#undef MINESWEEPER_GAME_WON
@@ -217,7 +217,7 @@
M.vomit(10, distance = 5)
if(ORION_TRAIL_FLUX)
if(prob(75))
M.Knockdown(60)
M.DefaultCombatKnockdown(60)
say("A sudden gust of powerful wind slams [M] into the floor!")
M.take_bodypart_damage(25)
playsound(loc, 'sound/weapons/genhit.ogg', 100, 1)
+6 -9
View File
@@ -78,23 +78,20 @@
update_icon()
return
/obj/machinery/computer/atmos_alert/update_icon()
..()
cut_overlays()
/obj/machinery/computer/atmos_alert/update_overlays()
. = ..()
SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
var/overlay_state = icon_screen
if(stat & (NOPOWER|BROKEN))
add_overlay("[icon_keyboard]_off")
. |= "[icon_keyboard]_off"
return
add_overlay(icon_keyboard)
. |= icon_keyboard
if(priority_alarms.len)
overlay_state = "alert:2"
add_overlay("alert:2")
else if(minor_alarms.len)
overlay_state = "alert:1"
add_overlay("alert:1")
else
overlay_state = "alert:0"
add_overlay("alert:0")
. |= overlay_state
SSvis_overlays.add_vis_overlay(src, icon, overlay_state, layer, plane, dir)
SSvis_overlays.add_vis_overlay(src, icon, overlay_state, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir, alpha=128)
SSvis_overlays.add_vis_overlay(src, icon, overlay_state, EMISSIVE_LAYER, EMISSIVE_PLANE, dir, alpha=128)
+32 -13
View File
@@ -41,7 +41,7 @@
name = "incinerator chamber gas sensor"
id_tag = ATMOS_GAS_MONITOR_SENSOR_INCINERATOR
/obj/machinery/air_sensor/update_icon()
/obj/machinery/air_sensor/update_icon_state()
icon_state = "gsensor[on]"
/obj/machinery/air_sensor/process_atmos()
@@ -91,6 +91,8 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers)
icon_screen = "tank"
icon_keyboard = "atmos_key"
circuit = /obj/item/circuitboard/computer/atmos_control
ui_x = 400
ui_y = 925
var/frequency = FREQ_ATMOS_STORAGE
var/list/sensors = list(
@@ -125,7 +127,7 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers)
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "atmos_control", name, 400, 925, master_ui, state)
ui = new(user, src, ui_key, "atmos_control", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/atmos_control/ui_data(mob/user)
@@ -161,6 +163,20 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers)
frequency = new_frequency
radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA)
//Incinerator sensor only
/obj/machinery/computer/atmos_control/incinerator
name = "Incinerator Air Control"
sensors = list(ATMOS_GAS_MONITOR_SENSOR_INCINERATOR = "Incinerator Chamber")
ui_x = 400
ui_y = 300
//Toxins mix sensor only
/obj/machinery/computer/atmos_control/toxinsmix
name = "Toxins Mixing Air Control"
sensors = list(ATMOS_GAS_MONITOR_SENSOR_TOXINS_LAB = "Toxins Mixing Chamber")
ui_x = 400
ui_y = 300
/////////////////////////////////////////////////////////////
// LARGE TANK CONTROL
/////////////////////////////////////////////////////////////
@@ -174,6 +190,9 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers)
var/list/input_info
var/list/output_info
ui_x = 500
ui_y = 315
/obj/machinery/computer/atmos_control/tank/oxygen_tank
name = "Oxygen Supply Control"
input_tag = ATMOS_GAS_MONITOR_INPUT_O2
@@ -216,12 +235,6 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers)
output_tag = ATMOS_GAS_MONITOR_OUTPUT_CO2
sensors = list(ATMOS_GAS_MONITOR_SENSOR_CO2 = "Carbon Dioxide Tank")
/obj/machinery/computer/atmos_control/tank/incinerator
name = "Incinerator Air Control"
input_tag = ATMOS_GAS_MONITOR_INPUT_INCINERATOR
output_tag = ATMOS_GAS_MONITOR_OUTPUT_INCINERATOR
sensors = list(ATMOS_GAS_MONITOR_SENSOR_INCINERATOR = "Incinerator Chamber")
// This hacky madness is the evidence of the fact that a lot of machines were never meant to be constructable, im so sorry you had to see this
/obj/machinery/computer/atmos_control/tank/proc/reconnect(mob/user)
var/list/IO = list()
@@ -235,7 +248,7 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers)
IO |= text[1]
if(!IO.len)
to_chat(user, "<span class='alert'>No machinery detected.</span>")
var/S = input("Select the device set: ", "Selection", IO[1]) as anything in IO
var/S = input("Select the device set: ", "Selection", IO[1]) as anything in sortList(IO)
if(src)
src.input_tag = "[S]_in"
src.output_tag = "[S]_out"
@@ -256,7 +269,7 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers)
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "atmos_control", name, 500, 305, master_ui, state)
ui = new(user, src, ui_key, "atmos_control", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/atmos_control/tank/ui_data(mob/user)
@@ -280,13 +293,19 @@ GLOBAL_LIST_EMPTY(atmos_air_controllers)
if("input")
signal.data += list("tag" = input_tag, "power_toggle" = TRUE)
. = TRUE
if("rate")
var/target = text2num(params["rate"])
if(!isnull(target))
target = CLAMP(target, 0, MAX_TRANSFER_RATE)
signal.data += list("tag" = input_tag, "set_volume_rate" = target)
. = TRUE
if("output")
signal.data += list("tag" = output_tag, "power_toggle" = TRUE)
. = TRUE
if("pressure")
var/target = input("New target pressure:", name, output_info ? output_info["internal"] : 0) as num|null
if(!isnull(target) && !..())
target = CLAMP(target, 0, 50 * ONE_ATMOSPHERE)
var/target = text2num(params["pressure"])
if(!isnull(target))
target = CLAMP(target, 0, MAX_OUTPUT_PRESSURE)
signal.data += list("tag" = output_tag, "set_internal_pressure" = target)
. = TRUE
radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)
+2 -3
View File
@@ -36,7 +36,7 @@
return ..()
/obj/machinery/computer/security/can_interact(mob/user)
if((!issilicon(user) && !Adjacent(user)) || is_blind(user) || !in_view_range(user, src))
if((!hasSiliconAccessInArea(user) && !Adjacent(user)) || is_blind(user) || !in_view_range(user, src))
return FALSE
return ..()
@@ -175,11 +175,10 @@
clockwork = TRUE //it'd look very weird
light_power = 0
/obj/machinery/computer/security/telescreen/update_icon()
/obj/machinery/computer/security/telescreen/update_icon_state()
icon_state = initial(icon_state)
if(stat & BROKEN)
icon_state += "b"
return
/obj/machinery/computer/security/telescreen/entertainment
name = "entertainment monitor"
@@ -47,7 +47,10 @@
jump_action.Grant(user)
actions += jump_action
/obj/machinery/computer/camera_advanced/proc/remove_eye_control(mob/living/user)
/obj/machinery/proc/remove_eye_control(mob/living/user)
CRASH("[type] does not implement ai eye handling")
/obj/machinery/computer/camera_advanced/remove_eye_control(mob/living/user)
if(!user)
return
for(var/V in actions)
@@ -69,7 +72,7 @@
playsound(src, 'sound/machines/terminal_off.ogg', 25, 0)
/obj/machinery/computer/camera_advanced/check_eye(mob/user)
if( (stat & (NOPOWER|BROKEN)) || (!Adjacent(user) && !user.has_unlimited_silicon_privilege) || user.eye_blind || user.incapacitated() )
if( (stat & (NOPOWER|BROKEN)) || (!Adjacent(user) && hasSiliconAccessInArea(user)) || user.eye_blind || user.incapacitated() )
user.unset_machine()
/obj/machinery/computer/camera_advanced/Destroy()
@@ -157,7 +160,7 @@
var/cooldown = 0
var/acceleration = 1
var/mob/living/eye_user = null
var/obj/machinery/computer/camera_advanced/origin
var/obj/machinery/origin
var/eye_initialized = 0
var/visible_icon = 0
var/image/user_image = null
@@ -170,7 +173,7 @@
/mob/camera/aiEye/remote/Destroy()
if(origin && eye_user)
origin.remove_eye_control(eye_user)
origin.remove_eye_control(eye_user,src)
origin = null
. = ..()
eye_user = null
+5 -5
View File
@@ -171,7 +171,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
/obj/machinery/computer/card/AltClick(mob/user)
. = ..()
if(!user.canUseTopic(src, !issilicon(user)) || !is_operational())
if(!user.canUseTopic(src, !hasSiliconAccessInArea(user)) || !is_operational())
return
if(inserted_modify_id)
if(id_eject(user, inserted_modify_id))
@@ -360,7 +360,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
if(..())
return
if(!usr.canUseTopic(src, !issilicon(usr)) || !is_operational())
if(!usr.canUseTopic(src, !hasSiliconAccessInArea(usr)) || !is_operational())
usr.unset_machine()
usr << browse(null, "window=id_com")
return
@@ -392,7 +392,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
inserted_scan_id = id_to_insert
updateUsrDialog()
if ("auth")
if ((!( authenticated ) && (inserted_scan_id || issilicon(usr)) || mode))
if ((!( authenticated ) && (inserted_scan_id || hasSiliconAccessInArea(usr)) || mode))
if (check_access(inserted_scan_id))
region_access = list()
head_subordinates = list()
@@ -426,7 +426,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
get_subordinates("Quartermaster")
if(region_access)
authenticated = 1
else if ((!( authenticated ) && issilicon(usr)) && (!inserted_modify_id))
else if ((!( authenticated ) && hasSiliconAccessInArea(usr)) && (!inserted_modify_id))
to_chat(usr, "<span class='warning'>You can't modify an ID without an ID inserted to modify! Once one is in the modify slot on the computer, you can log in.</span>")
if ("logout")
region_access = null
@@ -481,7 +481,7 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
if ("reg")
if (authenticated)
var/t2 = inserted_modify_id
if ((authenticated && inserted_modify_id == t2 && (in_range(src, usr) || issilicon(usr)) && isturf(loc)))
if ((authenticated && inserted_modify_id == t2 && (in_range(src, usr) || hasSiliconAccessInArea(usr)) && isturf(loc)))
var/newName = reject_bad_name(href_list["reg"])
if(newName)
inserted_modify_id.registered_name = newName
+2 -2
View File
@@ -482,10 +482,10 @@
R.fields["ckey"] = mob_occupant.ckey
R.fields["name"] = mob_occupant.real_name
R.fields["id"] = copytext(md5(mob_occupant.real_name), 2, 6)
R.fields["id"] = copytext_char(md5(mob_occupant.real_name), 2, 6)
R.fields["UE"] = dna.unique_enzymes
R.fields["UI"] = dna.uni_identity
R.fields["SE"] = dna.struc_enzymes
R.fields["SE"] = dna.mutation_index
R.fields["blood_type"] = dna.blood_type
R.fields["features"] = dna.features
R.fields["factions"] = mob_occupant.faction
+13 -8
View File
@@ -15,6 +15,7 @@
var/message_cooldown = 0
var/ai_message_cooldown = 0
var/tmp_alertlevel = 0
var/static/security_level_cd // used to stop mass spam.
var/const/STATE_DEFAULT = 1
var/const/STATE_CALLSHUTTLE = 2
var/const/STATE_CANCELSHUTTLE = 3
@@ -94,16 +95,18 @@
I = pda.id
if (I && istype(I))
if(ACCESS_CAPTAIN in I.access)
if(security_level_cd > world.time)
to_chat(usr, "<span class='warning'>Security level protocols are currently on cooldown. Please stand by.</span>")
return
var/old_level = GLOB.security_level
if(!tmp_alertlevel)
tmp_alertlevel = SEC_LEVEL_GREEN
if(tmp_alertlevel < SEC_LEVEL_GREEN)
tmp_alertlevel = SEC_LEVEL_GREEN
if(tmp_alertlevel == SEC_LEVEL_BLUE)
tmp_alertlevel = SEC_LEVEL_BLUE
if(tmp_alertlevel > SEC_LEVEL_AMBER)
tmp_alertlevel = SEC_LEVEL_AMBER //Cannot engage delta with this
set_security_level(tmp_alertlevel)
security_level_cd = world.time + 15 SECONDS
if(GLOB.security_level != old_level)
to_chat(usr, "<span class='notice'>Authorization confirmed. Modifying security level.</span>")
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
@@ -194,7 +197,7 @@
state = STATE_CANCELSHUTTLE
if("cancelshuttle2")
if(authenticated)
if((world.realtime - SSshuttle.realtimeofstart) > SSshuttle.auto_call) //Citadel Edit Removing auto_call caused recall.
if(SSshuttle.endvote_passed) //Citadel Edit - endvote passing = no recalls
say("Warning: Emergency shuttle recalls have been blocked by Central Command due to ongoing crew transfer procedures.")
else
SSshuttle.cancelEvac(usr)
@@ -376,19 +379,21 @@
if("ai-announce")
make_announcement(usr, 1)
if("ai-securitylevel")
if(security_level_cd > world.time)
to_chat(usr, "<span class='warning'>Security level protocols are currently on cooldown. Please stand by.</span>")
return
tmp_alertlevel = text2num( href_list["newalertlevel"] )
if(!tmp_alertlevel)
tmp_alertlevel = 0
tmp_alertlevel = SEC_LEVEL_GREEN
var/old_level = GLOB.security_level
if(!tmp_alertlevel)
tmp_alertlevel = SEC_LEVEL_GREEN
if(tmp_alertlevel < SEC_LEVEL_GREEN)
tmp_alertlevel = SEC_LEVEL_GREEN
if(tmp_alertlevel == SEC_LEVEL_BLUE)
tmp_alertlevel = SEC_LEVEL_BLUE
if(tmp_alertlevel > SEC_LEVEL_AMBER)
tmp_alertlevel = SEC_LEVEL_AMBER //Cannot engage delta with this
set_security_level(tmp_alertlevel)
security_level_cd = world.time + 15 SECONDS
if(GLOB.security_level != old_level)
//Only notify people if an actual change happened
var/security_level = get_security_level()
@@ -443,13 +448,13 @@
var/dat = ""
if(SSshuttle.emergency.mode == SHUTTLE_CALL)
var/timeleft = SSshuttle.emergency.timeLeft()
dat += "<B>Emergency shuttle</B>\n<BR>\nETA: [timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]"
dat += "<B>Emergency shuttle</B>\n<BR>\nETA: [timeleft / 60 % 60]:[add_leading(num2text(timeleft % 60), 2, "0")]"
var/datum/browser/popup = new(user, "communications", "Communications Console", 400, 500)
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
if(issilicon(user))
if(issilicon(user) || (hasSiliconAccessInArea(user) && !in_range(user,src)))
var/dat2 = interact_ai(user) // give the AI a different interact proc to limit its access
if(dat2)
dat += dat2
+2 -1
View File
@@ -40,6 +40,7 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new)
jobs["Geneticist"] = 22
jobs["Virologist"] = 23
jobs["Medical Doctor"] = 24
jobs["Paramedic"] = 25
jobs["Research Director"] = 30
jobs["Scientist"] = 31
jobs["Roboticist"] = 32
@@ -207,4 +208,4 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new)
if ("select_person")
AI.ai_camera_track(params["name"])
#undef SENSORS_UPDATE_PERIOD
#undef SENSORS_UPDATE_PERIOD
+599 -139
View File
@@ -1,14 +1,16 @@
#define INJECTOR_TIMEOUT 100
#define REJUVENATORS_INJECT 15
#define REJUVENATORS_MAX 90
#define NUMBER_OF_BUFFERS 3
#define SCRAMBLE_TIMEOUT 600
#define JOKER_TIMEOUT 12000 //20 minutes
#define JOKER_UPGRADE 1800
#define RADIATION_STRENGTH_MAX 15
#define RADIATION_STRENGTH_MULTIPLIER 1 //larger has a more range
#define RADIATION_STRENGTH_MULTIPLIER 1 //larger has more range
#define RADIATION_DURATION_MAX 30
#define RADIATION_ACCURACY_MULTIPLIER 3 //larger is less accurate
#define RADIATION_IRRADIATION_MULTIPLIER 1 //multiplier for how much radiation a test subject receives
#define SCANNER_ACTION_SE 1
@@ -23,22 +25,44 @@
icon_keyboard = "med_key"
density = TRUE
circuit = /obj/item/circuitboard/computer/scan_consolenew
var/radduration = 2
var/radstrength = 1
var/list/buffer[NUMBER_OF_BUFFERS]
var/injectorready = 0 //world timer cooldown var
var/current_screen = "mainmenu"
var/obj/machinery/dna_scannernew/connected = null
var/obj/item/disk/data/diskette = null
var/list/delayed_action = null
use_power = IDLE_POWER_USE
idle_power_usage = 10
active_power_usage = 400
light_color = LIGHT_COLOR_BLUE
var/datum/techweb/stored_research
var/max_storage = 6
var/combine
var/radduration = 2
var/radstrength = 1
var/max_chromosomes = 6
///Amount of mutations we can store
var/list/buffer[NUMBER_OF_BUFFERS]
///mutations we have stored
var/list/stored_mutations = list()
///chromosomes we have stored
var/list/stored_chromosomes = list()
///combinations of injectors for the 'injector selection'. format is list("Elsa" = list(Cryokinesis, Geladikinesis), "The Hulk" = list(Hulk, Gigantism), etc) Glowy and the gang being an initialized datum
var/list/injector_selection = list()
///max amount of selections you can make
var/max_injector_selections = 2
///hard-cap on the advanced dna injector
var/max_injector_mutations = 10
///the max instability of the advanced injector.
var/max_injector_instability = 50
var/injectorready = 0 //world timer cooldown var
var/jokerready = 0
var/scrambleready = 0
var/current_screen = "mainmenu"
var/current_mutation //what block are we inspecting? only used when screen = "info"
var/current_storage //what storage block are we looking at?
var/obj/machinery/dna_scannernew/connected = null
var/obj/item/disk/data/diskette = null
var/list/delayed_action = null
/obj/machinery/computer/scan_consolenew/attackby(obj/item/I, mob/user, params)
if (istype(I, /obj/item/disk/data)) //INSERT SOME DISKETTES
if (!src.diskette)
@@ -48,6 +72,28 @@
to_chat(user, "<span class='notice'>You insert [I].</span>")
src.updateUsrDialog()
return
if (istype(I, /obj/item/chromosome))
if(LAZYLEN(stored_chromosomes) < max_chromosomes)
I.forceMove(src)
stored_chromosomes += I
to_chat(user, "<span class='notice'>You insert [I].</span>")
else
to_chat(user, "<span class='warning'>You cannot store any more chromosomes!</span>")
return
if(istype(I, /obj/item/dnainjector/activator))
var/obj/item/dnainjector/activator/A = I
if(A.used)
to_chat(user,"<span class='notice'>Recycled [I].</span>")
if(A.research)
var/c_typepath = generate_chromosome()
var/obj/item/chromosome/CM = new c_typepath (drop_location())
to_chat(user,"<span class='notice'>Recycled [I].</span>")
if((LAZYLEN(stored_chromosomes) < max_chromosomes) && prob(60))
CM.forceMove(src)
stored_chromosomes += CM
to_chat(user,"<span class='notice'>[capitalize(CM.name)] added to storage.</span>")
qdel(I)
return
else
return ..()
@@ -58,13 +104,27 @@
if(!isnull(connected))
break
injectorready = world.time + INJECTOR_TIMEOUT
scrambleready = world.time + SCRAMBLE_TIMEOUT
jokerready = world.time + JOKER_TIMEOUT
stored_research = SSresearch.science_tech
/obj/machinery/computer/scan_consolenew/examine(mob/user)
. = ..()
if(jokerready < world.time)
. += "<span class='notice'>JOKER algorithm available.</span>"
else
. += "<span class='notice'>JOKER algorithm available in about [round(0.00166666667 * (jokerready - world.time))] minutes."
/obj/machinery/computer/scan_consolenew/ui_interact(mob/user, last_change)
. = ..()
if(!user)
return
var/datum/browser/popup = new(user, "scannernew", "DNA Modifier Console", 800, 630) // Set up the popup browser window
if(!(in_range(src, user) || issilicon(user)))
if(user.client)
var/datum/asset/simple/assets = get_asset_datum(/datum/asset/simple/genetics)
assets.send(user.client)
if(!(in_range(src, user) || hasSiliconAccessInArea(user)))
popup.close()
return
popup.add_stylesheet("scannernew", 'html/browser/scannernew.css')
@@ -76,7 +136,7 @@
if(connected && connected.is_operational())
if(connected.occupant) //set occupant_status message
viable_occupant = connected.occupant
if(viable_occupant.has_dna() && !HAS_TRAIT(viable_occupant, TRAIT_RADIMMUNE) && !HAS_TRAIT(viable_occupant, TRAIT_NOCLONE) || (connected.scan_level == 3)) //occupant is viable for dna modification
if(viable_occupant.has_dna() && !HAS_TRAIT_NOT_FROM(viable_occupant, TRAIT_RADIMMUNE,BLOODSUCKER_TRAIT) && !HAS_TRAIT(viable_occupant, TRAIT_NOCLONE) || (connected.scan_level == 3)) //occupant is viable for dna modification
occupant_status += "[viable_occupant.name] => "
switch(viable_occupant.stat)
if(CONSCIOUS)
@@ -88,8 +148,6 @@
occupant_status += "</div></div>"
occupant_status += "<div class='line'><div class='statusLabel'>Health:</div><div class='progressBar'><div style='width: [viable_occupant.health]%;' class='progressFill good'></div></div><div class='statusValue'>[viable_occupant.health] %</div></div>"
occupant_status += "<div class='line'><div class='statusLabel'>Radiation Level:</div><div class='progressBar'><div style='width: [viable_occupant.radiation/(RAD_MOB_SAFE/100)]%;' class='progressFill bad'></div></div><div class='statusValue'>[viable_occupant.radiation/(RAD_MOB_SAFE/100)] %</div></div>"
var/rejuvenators = viable_occupant.reagents.get_reagent_amount(/datum/reagent/medicine/potass_iodide)
occupant_status += "<div class='line'><div class='statusLabel'>Rejuvenators:</div><div class='progressBar'><div style='width: [round((rejuvenators / REJUVENATORS_MAX) * 100)]%;' class='progressFill highlight'></div></div><div class='statusValue'>[rejuvenators] units</div></div>"
occupant_status += "<div class='line'><div class='statusLabel'>Unique Enzymes :</div><div class='statusValue'><span class='highlight'>[viable_occupant.dna.unique_enzymes]</span></div></div>"
occupant_status += "<div class='line'><div class='statusLabel'>Last Operation:</div><div class='statusValue'>[last_change ? last_change : "----"]</div></div>"
else
@@ -147,28 +205,61 @@
buttons += "<a href='?src=[REF(src)];task=togglelock;'>[connected.locked ? "Unlock" : "Lock"] Scanner</a>"
else
buttons += "<span class='linkOff'>Open Scanner</span> <span class='linkOff'>Lock Scanner</span>"
if(viable_occupant)
buttons += "<a href='?src=[REF(src)];task=rejuv'>Inject Rejuvenators</a>"
if(viable_occupant && (scrambleready < world.time))
buttons += "<a href='?src=[REF(src)];task=scramble'>Scramble DNA</a>"
else
buttons += "<span class='linkOff'>Inject Rejuvenators</span>"
buttons += "<span class='linkOff'>Scramble DNA</span>"
if(diskette)
buttons += "<a href='?src=[REF(src)];task=ejectdisk'>Eject Disk</a>"
buttons += "<a href='?src=[REF(src)];task=screen;text=disk;'>Disk</a>"
else
buttons += "<span class='linkOff'>Eject Disk</span>"
if(current_screen == "buffer")
buttons += "<a href='?src=[REF(src)];task=screen;text=mainmenu;'>Radiation Emitter Menu</a>"
buttons += "<span class='linkOff'>Disk</span>"
if(current_screen == "mutations")
buttons += "<span class='linkOff'>Mutations</span>"
else
buttons += "<a href='?src=[REF(src)];task=screen;text=buffer;'>Buffer Menu</a>"
buttons += "<a href='?src=[REF(src)];task=screen;text=mutations;'>Mutations</a>"
if((current_screen == "mainmenu") || !current_screen)
buttons += "<span class='linkOff'>Genetic Sequencer</span>"
else
buttons += "<a href='?src=[REF(src)];task=screen;text=mainmenu;'>Genetic Sequencer</a>"
if(current_screen == "ui")
buttons += "<span class='linkOff'>Unique Identifiers</span>"
else
buttons += "<a href='?src=[REF(src)];task=screen;text=ui;'>Unique Identifiers</a>"
if(current_screen == "advinjector")
buttons += "<span class='linkOff'>Adv. Injectors</span>"
else
buttons += "<a href='?src=[REF(src)];task=screen;text=advinjector;'>Adv. Injectors</a>"
switch(current_screen)
if("working")
temp_html += status
temp_html += "<h1>System Busy</h1>"
temp_html += "Working ... Please wait ([DisplayTimeText(radduration*10)])"
if("buffer")
if("ui")
temp_html += status
temp_html += buttons
temp_html += "<h1>Buffer Menu</h1>"
temp_html += "<h1>Unique Identifiers</h1>"
temp_html += "<a href='?src=[REF(src)];task=setstrength;num=[radstrength-1];'>--</a> <a href='?src=[REF(src)];task=setstrength;'>Output Level</a> <a href='?src=[REF(src)];task=setstrength;num=[radstrength+1];'>++</a>"
temp_html += "<br><a href='?src=[REF(src)];task=setduration;num=[radduration-1];'>--</a> <a href='?src=[REF(src)];task=setduration;'>Pulse Duration</a> <a href='?src=[REF(src)];task=setduration;num=[radduration+1];'>++</a>"
temp_html += "<h3>Irradiate Subject</h3>"
temp_html += "<div class='line'><div class='statusLabel'>Unique Identifier:</div><div class='statusValue'><div class='clearBoth'>"
var/max_line_len = 7*DNA_BLOCK_SIZE
if(viable_occupant)
temp_html += "<div class='dnaBlockNumber'>1</div>"
var/char = ""
var/ui_text = viable_occupant.dna.uni_identity
var/len_byte = length(ui_text)
var/char_it = 0
for(var/byte_it = 1, byte_it <= len_byte, byte_it += length(char))
char_it++
char = ui_text[byte_it]
temp_html += "<a class='dnaBlock' href='?src=[REF(src)];task=pulseui;num=[char_it];'>[char]</a>"
if((char_it % max_line_len) == 0)
temp_html += "</div><div class='clearBoth'>"
if((char_it % DNA_BLOCK_SIZE) == 0 && byte_it < len_byte)
temp_html += "<div class='dnaBlockNumber'>[(char_it / DNA_BLOCK_SIZE) + 1]</div>"
else
temp_html += "---------"
temp_html += "</div></div><br><h1>Buffer Menu</h1>"
if(istype(buffer))
for(var/i=1, i<=buffer.len, i++)
@@ -188,7 +279,6 @@
temp_html += "<span class='linkOff'>Save to Disk</span>"
else
var/ui = buffer_slot["UI"]
var/se = buffer_slot["SE"]
var/ue = buffer_slot["UE"]
var/name = buffer_slot["name"]
var/label = buffer_slot["label"]
@@ -234,19 +324,6 @@
temp_html += "<a href='?src=[REF(src)];task=injector;num=[i];text=mixed'>UI+UE Injector</a>"
else
temp_html += "<span class='linkOff'>UI+UE Injector</span>"
if(se)
temp_html += "<br>\tSE: [se] "
if(viable_occupant)
temp_html += "<a href='?src=[REF(src)];task=transferbuffer;num=[i];text=se'>Occupant</a>"
else
temp_html += "<span class='linkOff'>Occupant</span>"
temp_html += "<a href='?src=[REF(src)];task=setdelayed;num=[i];delayaction=[SCANNER_ACTION_SE]'>Occupant:Delayed</a>"
if(injectorready < world.time )
temp_html += "<a href='?src=[REF(src)];task=injector;num=[i];text=se'>Injector</a>"
else
temp_html += "<span class='linkOff'>Injector</span>"
else
temp_html += "<br>\tSE: No Data"
if(viable_occupant)
temp_html += "<br><a href='?src=[REF(src)];task=setbuffer;num=[i];'>Save to Buffer</a>"
else
@@ -260,55 +337,232 @@
temp_html += "<a href='?src=[REF(src)];task=savedisk;num=[i];'>Save to Disk</a>"
else
temp_html += "<span class='linkOff'>Save to Disk</span>"
if("disk")
temp_html += status
temp_html += buttons
if(diskette)
temp_html += "<h3>[diskette.name]</h3><br>"
temp_html += "<a href='?src=[REF(src)];task=ejectdisk'>Eject Disk</a><br>"
if(LAZYLEN(diskette.mutations))
temp_html += "<table>"
for(var/datum/mutation/human/A in diskette.mutations)
temp_html += "<tr><td><span class='linkOff'>[A.name]</span></td>"
temp_html += "<td><a href='?src=[REF(src)];task=deletediskmut;num=[diskette.mutations.Find(A)];'>Delete</a></td>"
if(LAZYLEN(stored_mutations) < max_storage)
temp_html += "<td><a href='?src=[REF(src)];task=importdiskmut;num=[diskette.mutations.Find(A)];'>Import</a></td>"
else
temp_html += "<td><td><span class='linkOff'>Import</span></td>"
temp_html += "</tr>"
temp_html += "</table>"
else
temp_html += "<br>Load diskette to start ----------"
if("info")
if(LAZYLEN(stored_mutations))
if(LAZYLEN(stored_mutations) >= current_storage)
var/datum/mutation/human/HM = stored_mutations[current_storage]
if(HM)
temp_html += display_sequence(HM.type, current_storage)
else
current_screen = "mainmenu"
if("mutations")
temp_html += status
temp_html += buttons
temp_html += "<h3>Mutation Storage:<br></h3>"
temp_html += "<table>"
for(var/datum/mutation/human/HM in stored_mutations)
var/i = stored_mutations.Find(HM)
temp_html += "<tr><td><a href='?src=[REF(src)];task=inspectstorage;num=[i]'>[HM.name]</a></td>"
temp_html += "<td><a href='?src=[REF(src)];task=exportdiskmut;path=[HM.type]'>Export</a></td>"
temp_html += "<td><a href='?src=[REF(src)];task=deletemut;num=[i]'>Delete</a></td>"
if(combine == HM.type)
temp_html += "<td><span class='linkOff'>Combine</span></td></tr>"
else
temp_html += "<td><a href='?src=[REF(src)];task=combine;num=[i]'>Combine</a></td></tr>"
temp_html += "</table><br>"
temp_html += "<h3>Chromosome Storage:<br></h3>"
temp_html += "<table>"
for(var/i in 1 to stored_chromosomes.len)
var/obj/item/chromosome/CM = stored_chromosomes[i]
temp_html += "<td><a href='?src=[REF(src)];task=ejectchromosome;num=[i]'>[CM.name]</a></td><br>"
temp_html += "</table>"
if("advinjector")
temp_html += status
temp_html += buttons
temp_html += "<div class='line'><div class='statusLabel'><b>Advanced Injectors:</b></div></div><br>"
temp_html += "<div class='statusLine'><a href='?src=[REF(src)];task=add_advinjector;'>New Selection</a></div>"
for(var/A in injector_selection)
temp_html += "<div class='statusDisplay'><b>[A]</b>"
var/list/true_selection = injector_selection[A]
temp_html += "<br>"
for(var/B in true_selection)
var/datum/mutation/human/HM = B
var/mutcolor
switch(HM.quality)
if(POSITIVE)
mutcolor = "good"
if(MINOR_NEGATIVE)
mutcolor = "average"
if(NEGATIVE)
mutcolor = "bad"
temp_html += "<div class='statusLine'><span class='[mutcolor]'>[HM.name] </span>"
temp_html += "<a href='?src=[REF(src)];task=remove_from_advinjector;injector=[A];path=[HM.type];'>Remove</a></div>"
if (injectorready < world.time)
temp_html += "<div class='statusLine'> <a href='?src=[REF(src)];task=advinjector;injector=[A];'>Print Advanced Injector</a>"
else
temp_html += "<div class='statusLine'> <span class='linkOff'>Print Advanced Injector</span>"
temp_html += "<a href='?src=[REF(src)];task=remove_advinjector;injector=[A];'>Remove Injector</a></div>"
temp_html += "<br></div>"
else
temp_html += status
temp_html += buttons
temp_html += "<h1>Radiation Emitter Menu</h1>"
temp_html += "<a href='?src=[REF(src)];task=setstrength;num=[radstrength-1];'>--</a> <a href='?src=[REF(src)];task=setstrength;'>Output Level</a> <a href='?src=[REF(src)];task=setstrength;num=[radstrength+1];'>++</a>"
temp_html += "<br><a href='?src=[REF(src)];task=setduration;num=[radduration-1];'>--</a> <a href='?src=[REF(src)];task=setduration;'>Pulse Duration</a> <a href='?src=[REF(src)];task=setduration;num=[radduration+1];'>++</a>"
temp_html += "<h3>Irradiate Subject</h3>"
temp_html += "<div class='line'><div class='statusLabel'>Unique Identifier:</div><div class='statusValue'><div class='clearBoth'>"
var/max_line_len = 7*DNA_BLOCK_SIZE
temp_html += "<div class='line'><div class='statusLabel'>Genetic Sequence:</div><br>"
if(viable_occupant)
temp_html += "<div class='dnaBlockNumber'>1</div>"
var/len = length(viable_occupant.dna.uni_identity)
for(var/i=1, i<=len, i++)
temp_html += "<a class='dnaBlock' href='?src=[REF(src)];task=pulseui;num=[i];'>[copytext(viable_occupant.dna.uni_identity,i,i+1)]</a>"
if ((i % max_line_len) == 0)
temp_html += "</div><div class='clearBoth'>"
if((i % DNA_BLOCK_SIZE) == 0 && i < len)
temp_html += "<div class='dnaBlockNumber'>[(i / DNA_BLOCK_SIZE) + 1]</div>"
if(viable_occupant)
for(var/A in get_mutation_list())
temp_html += display_inactive_sequence(A)
temp_html += "<br>"
else
temp_html += "----"
if(viable_occupant && (current_mutation in get_mutation_list(viable_occupant)))
temp_html += display_sequence(current_mutation)
temp_html += "</div><br>"
else
temp_html += "----"
temp_html += "</div></div></div><br>"
temp_html += "<br><div class='line'><div class='statusLabel'>Structural Enzymes:</div><div class='statusValue'><div class='clearBoth'>"
if(viable_occupant)
temp_html += "<div class='dnaBlockNumber'>1</div>"
var/len = length(viable_occupant.dna.struc_enzymes)
for(var/i=1, i<=len, i++)
temp_html += "<a class='dnaBlock' href='?src=[REF(src)];task=pulsese;num=[i];'>[copytext(viable_occupant.dna.struc_enzymes,i,i+1)]</a>"
if ((i % max_line_len) == 0)
temp_html += "</div><div class='clearBoth'>"
if((i % DNA_BLOCK_SIZE) == 0 && i < len)
temp_html += "<div class='dnaBlockNumber'>[(i / DNA_BLOCK_SIZE) + 1]</div>"
else
temp_html += "----"
temp_html += "</div></div></div>"
temp_html += "----------"
popup.set_content(temp_html.Join())
popup.open()
/obj/machinery/computer/scan_consolenew/proc/display_inactive_sequence(mutation)
var/temp_html = ""
var/class = "unselected"
var/mob/living/carbon/viable_occupant = get_viable_occupant()
if(!viable_occupant)
return
var/location = viable_occupant.dna.mutation_index.Find(mutation) //We do this because we dont want people using sysexp or similair tools to just read the mutations.
if(!location) //Do this only when needed, dont make a list with mutations for every iteration if you dont need to
var/list/mutations = get_mutation_list(TRUE)
if(mutation in mutations)
location = mutations.Find(mutation)
if(mutation == current_mutation)
class = "selected"
if(location > DNA_MUTATION_BLOCKS)
temp_html += "<a class='clean' href='?src=[REF(src)];task=inspect;num=[location];'><img class='[class]' src='dna_extra.gif' width = '65' alt='Extra Mutation'></a>"
else if(mutation in stored_research.discovered_mutations)
temp_html += "<a class='clean' href='?src=[REF(src)];task=inspect;num=[location];'><img class='[class]' src='dna_discovered.gif' width = '65' alt='Discovered Mutation'></a>"
else
temp_html += "<a class='clean' clean href='?src=[REF(src)];task=inspect;num=[location];'><img class='[class]' src='dna_undiscovered.gif' width = '65' alt=Undiscovered Mutation'></a>"
return temp_html
/obj/machinery/computer/scan_consolenew/proc/display_sequence(mutation, storage_slot) //Storage slot is for when viewing from the stored mutations
var/temp_html = ""
if(!mutation)
temp_html += "ERR-"
return
var/mut_name = "Unknown gene"
var/mut_desc = "No information available."
var/alias
var/discovered = FALSE
var/active = FALSE
var/scrambled = FALSE
var/instability
var/mob/living/carbon/viable_occupant = get_viable_occupant()
var/datum/mutation/human/HM = get_valid_mutation(mutation)
if(viable_occupant)
var/datum/mutation/human/M = viable_occupant.dna.get_mutation(mutation)
if(M)
scrambled = M.scrambled
active = TRUE
var/datum/mutation/human/A = GET_INITIALIZED_MUTATION(mutation)
alias = A.alias
if(active && !scrambled)
discover(mutation)
if(stored_research && (mutation in stored_research.discovered_mutations))
mut_name = A.name
mut_desc = A.desc
discovered = TRUE
instability = A.instability
var/extra
if(viable_occupant && !(storage_slot || viable_occupant.dna.mutation_in_sequence(mutation)))
extra = TRUE
if(discovered && !scrambled)
var/mutcolor
switch(A.quality)
if(POSITIVE)
mutcolor = "good"
if(MINOR_NEGATIVE)
mutcolor = "average"
if(NEGATIVE)
mutcolor = "bad"
if(HM)
instability *= GET_MUTATION_STABILIZER(HM)
temp_html += "<div class='statusDisplay'><div class='statusLine'><span class='[mutcolor]'><b>[mut_name]</b></span><small> ([alias])</small><br>"
temp_html += "<div class='statusLine'>Instability : [round(instability)]</span><br>"
else
temp_html += "<div class='statusDisplay'><div class='statusLine'><b>[alias]</b><br>"
temp_html += "<div class='statusLine'>[mut_desc]<br></div>"
if(active && !storage_slot)
if(HM?.can_chromosome && (HM in viable_occupant.dna.mutations))
var/i = viable_occupant.dna.mutations.Find(HM)
var/chromosome_name = "<a href='?src=[REF(src)];task=applychromosome;path=[mutation];num=[i];'>----</a>"
if(HM.chromosome_name)
chromosome_name = HM.chromosome_name
temp_html += "<div class='statusLine'>Chromosome status: [chromosome_name]<br></div>"
temp_html += "<div class='statusLine'>Sequence:<br><br></div>"
if(!scrambled)
for(var/block in 1 to A.blocks)
var/whole_sequence = get_valid_gene_string(mutation)
var/sequence = copytext(whole_sequence, 1+(block-1)*(DNA_SEQUENCE_LENGTH*2),(DNA_SEQUENCE_LENGTH*2*block+1))
temp_html += "<div class='statusLine'><table class='statusDisplay'><tr>"
for(var/i in 1 to DNA_SEQUENCE_LENGTH)
var/num = 1+(i-1)*2
var/genenum = num+(DNA_SEQUENCE_LENGTH*2*(block-1))
temp_html += "<td><div class='statusLine'><span class='dnaBlockNumber'><a href='?src=[REF(src)];task=pulsegene;num=[genenum];path=[mutation];'>[sequence[num]]</span></a></div></td>"
temp_html += "</tr><tr>"
for(var/i in 1 to DNA_SEQUENCE_LENGTH)
temp_html += "<td><div class='statusLine'>|</div></td>"
temp_html += "</tr><tr>"
for(var/i in 1 to DNA_SEQUENCE_LENGTH)
var/num = i*2
var/genenum = num+(DNA_SEQUENCE_LENGTH*2*(block-1))
temp_html += "<td><div class='statusLine'><span class='dnaBlockNumber'><a href='?src=[REF(src)];task=pulsegene;num=[genenum];path=[mutation];'>[sequence[num]]</span></a></div></td>"
temp_html += "</tr></table></div>"
temp_html += "<br><br><br><br><br>"
else
temp_html = "<div class='statusLine'>Sequence unreadable due to unpredictable mutation.</div>"
if((active || storage_slot) && (injectorready < world.time) && !scrambled)
temp_html += "<a href='?src=[REF(src)];task=activator;path=[mutation];slot=[storage_slot];'>Print Activator</a>"
temp_html += "<a href='?src=[REF(src)];task=mutator;path=[mutation];slot;=[storage_slot];'>Print Mutator</a>"
else
temp_html += "<span class='linkOff'>Print Activator</span>"
temp_html += "<span class='linkOff'>Print Mutator</span>"
temp_html += "<br><div class='statusLine'>"
if(storage_slot)
temp_html += "<a href='?src=[REF(src)];task=deletemut;num=[storage_slot];'>Delete</a>"
if((LAZYLEN(stored_mutations) < max_storage) && diskette && !diskette.read_only)
temp_html += "<a href='?src=[REF(src)];task=exportdiskmut;path=[mutation];'>Export</a>"
else
temp_html += "<span class='linkOff'>Export</span>"
temp_html += "<a href='?src=[REF(src)];task=screen;text=mutations;'>Back</a>"
else if(active && !scrambled)
temp_html += "<a href='?src=[REF(src)];task=savemut;path=[mutation];'>Store</a>"
temp_html += "<a href='?src=[REF(src)];task=expand_advinjector;path=[mutation];'>Adv. Injector</a>"
if(extra || scrambled)
temp_html += "<a href='?src=[REF(src)];task=nullify;'>Nullify</a>"
else
temp_html += "<span class='linkOff'>Nullify</span>"
temp_html += "</div></div>"
return temp_html
/obj/machinery/computer/scan_consolenew/Topic(href, href_list)
if(..())
return
if(!isturf(usr.loc))
return
if(!((isturf(loc) && in_range(src, usr)) || issilicon(usr)))
if(!((isturf(loc) && in_range(src, usr)) || hasSiliconAccessInArea(usr)))
return
if(current_screen == "working")
return
@@ -340,13 +594,15 @@
radstrength = WRAP(num, 1, RADIATION_STRENGTH_MAX+1)
if("screen")
current_screen = href_list["text"]
if("rejuv")
if(viable_occupant && viable_occupant.reagents)
var/potassiodide_amount = viable_occupant.reagents.get_reagent_amount(/datum/reagent/medicine/potass_iodide)
var/can_add = max(min(REJUVENATORS_MAX - potassiodide_amount, REJUVENATORS_INJECT), 0)
viable_occupant.reagents.add_reagent(/datum/reagent/medicine/potass_iodide, can_add)
if("scramble")
if(viable_occupant && (scrambleready < world.time))
viable_occupant.dna.remove_all_mutations(list(MUT_NORMAL, MUT_EXTRA))
viable_occupant.dna.generate_dna_blocks()
scrambleready = world.time + SCRAMBLE_TIMEOUT
to_chat(usr,"<span class'notice'>DNA scrambled.</span>")
viable_occupant.radiation += RADIATION_STRENGTH_MULTIPLIER*50/(connected.damage_coeff ** 2)
if("setbufferlabel")
var/text = sanitize(input(usr, "Input a new label:", "Input an Text", null) as text|null)
var/text = sanitize(input(usr, "Input a new label:", "Input a Text", null) as text|null)
if(num && text)
num = CLAMP(num, 1, NUMBER_OF_BUFFERS)
var/list/buffer_slot = buffer[num]
@@ -358,7 +614,6 @@
buffer[num] = list(
"label"="Buffer[num]:[viable_occupant.real_name]",
"UI"=viable_occupant.dna.uni_identity,
"SE"=viable_occupant.dna.struc_enzymes,
"UE"=viable_occupant.dna.unique_enzymes,
"name"=viable_occupant.real_name,
"blood_type"=viable_occupant.dna.blood_type
@@ -372,8 +627,6 @@
if("transferbuffer")
if(num && viable_occupant)
switch(href_list["text"]) //Numbers are this high because other way upgrading laser is just not worth the hassle, and i cant think of anything better to inmrove
if("se")
apply_buffer(SCANNER_ACTION_SE,num)
if("ui")
apply_buffer(SCANNER_ACTION_UI,num)
if("ue")
@@ -387,28 +640,6 @@
if(istype(buffer_slot))
var/obj/item/dnainjector/timed/I
switch(href_list["text"])
if("se")
if(buffer_slot["SE"])
I = new /obj/item/dnainjector/timed(loc)
var/powers = 0
for(var/datum/mutation/human/HM in GLOB.good_mutations + GLOB.bad_mutations + GLOB.not_good_mutations)
if(HM.check_block_string(buffer_slot["SE"]))
I.add_mutations.Add(HM)
if(HM in GLOB.good_mutations)
powers += 1
if(HM in GLOB.bad_mutations + GLOB.not_good_mutations)
powers -= 1 //To prevent just unlocking everything to get all powers to a syringe for max tech
else
I.remove_mutations.Add(HM)
var/time_coeff
for(var/datum/mutation/human/HM in I.add_mutations)
if(!time_coeff)
time_coeff = HM.time_coeff
continue
time_coeff = min(time_coeff,HM.time_coeff)
if(connected)
I.duration = I.duration * time_coeff * connected.damage_coeff
I.damage_coeff = connected.damage_coeff
if("ui")
if(buffer_slot["UI"])
I = new /obj/item/dnainjector/timed(loc)
@@ -447,7 +678,7 @@
if("setdelayed")
if(num)
delayed_action = list("action"=text2num(href_list["delayaction"]),"buffer"=num)
if("pulseui","pulsese")
if("pulseui")
if(num && viable_occupant && connected)
radduration = WRAP(radduration, 1, RADIATION_DURATION_MAX+1)
radstrength = WRAP(radstrength, 1, RADIATION_STRENGTH_MAX+1)
@@ -459,56 +690,250 @@
ui_interact(usr)
sleep(radduration*10)
current_screen = "mainmenu"
current_screen = "ui"
if(viable_occupant && connected && connected.occupant==viable_occupant)
viable_occupant.radiation += (RADIATION_IRRADIATION_MULTIPLIER*radduration*radstrength)/(connected.damage_coeff ** 2) //Read comment in "transferbuffer" section above for explanation
switch(href_list["task"]) //Same thing as there but values are even lower, on best part they are about 0.0*, effectively no damage
if("pulseui")
var/len = length(viable_occupant.dna.uni_identity)
var/len = length_char(viable_occupant.dna.uni_identity)
num = WRAP(num, 1, len+1)
num = randomize_radiation_accuracy(num, radduration + (connected.precision_coeff ** 2), len) //Each manipulator level above 1 makes randomization as accurate as selected time + manipulator lvl^2
//Value is this high for the same reason as with laser - not worth the hassle of upgrading if the bonus is low
//Value is this high for the same reason as with laser - not worth the hassle of upgrading if the bonus is low
var/block = round((num-1)/DNA_BLOCK_SIZE)+1
var/subblock = num - block*DNA_BLOCK_SIZE
last_change = "UI #[block]-[subblock]; "
var/hex = copytext(viable_occupant.dna.uni_identity, num, num+1)
var/hex = copytext_char(viable_occupant.dna.uni_identity, num, num+1)
last_change += "[hex]"
hex = scramble(hex, radstrength, radduration)
last_change += "->[hex]"
viable_occupant.dna.uni_identity = copytext(viable_occupant.dna.uni_identity, 1, num) + hex + copytext(viable_occupant.dna.uni_identity, num+1, 0)
viable_occupant.dna.uni_identity = copytext_char(viable_occupant.dna.uni_identity, 1, num) + hex + copytext_char(viable_occupant.dna.uni_identity, num + 1)
viable_occupant.updateappearance(mutations_overlay_update=1)
if("pulsese")
var/len = length(viable_occupant.dna.struc_enzymes)
num = WRAP(num, 1, len+1)
num = randomize_radiation_accuracy(num, radduration + (connected.precision_coeff ** 2), len)
var/block = round((num-1)/DNA_BLOCK_SIZE)+1
var/subblock = num - block*DNA_BLOCK_SIZE
last_change = "SE #[block]-[subblock]; "
var/hex = copytext(viable_occupant.dna.struc_enzymes, num, num+1)
last_change += "[hex]"
hex = scramble(hex, radstrength, radduration)
last_change += "->[hex]"
viable_occupant.dna.struc_enzymes = copytext(viable_occupant.dna.struc_enzymes, 1, num) + hex + copytext(viable_occupant.dna.struc_enzymes, num+1, 0)
viable_occupant.domutcheck()
else
current_screen = "mainmenu"
if(connected)
connected.locked = locked_state
if("inspect")
if(viable_occupant)
var/list/mutations = get_mutation_list(TRUE)
if(current_mutation == mutations[num])
current_mutation = null
else
current_mutation = mutations[num]
if("inspectstorage")
current_storage = num
current_screen = "info"
if("savemut")
if(viable_occupant)
var/succes
if(LAZYLEN(stored_mutations) < max_storage)
var/mutation = text2path(href_list["path"])
if(ispath(mutation, /datum/mutation/human)) //sanity checks
var/datum/mutation/human/HM = viable_occupant.dna.get_mutation(mutation)
if(HM)
var/datum/mutation/human/A = new HM.type()
A.copy_mutation(HM)
succes = TRUE
stored_mutations += A
to_chat(usr,"<span class='notice'>Mutation succesfully stored.</span>")
if(!succes) //we can exactly return here
to_chat(usr,"<span class='warning'>Mutation storage is full.</span>")
if("deletemut")
var/datum/mutation/human/HM = stored_mutations[num]
if(HM)
stored_mutations.Remove(HM)
qdel(HM)
current_screen = "mutations"
if("activator")
if(injectorready < world.time)
var/mutation = text2path(href_list["path"])
if(ispath(mutation, /datum/mutation/human))
var/datum/mutation/human/HM = get_valid_mutation(mutation)
if(HM)
var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc)
I.add_mutations += new HM.type (copymut = HM)
I.name = "[HM.name] activator"
I.research = TRUE
if(connected)
I.damage_coeff = connected.damage_coeff*4
injectorready = world.time + INJECTOR_TIMEOUT * (1 - 0.1 * connected.precision_coeff) //precision_coeff being the matter bin rating
else
injectorready = world.time + INJECTOR_TIMEOUT
if("mutator")
if(injectorready < world.time)
var/mutation = text2path(href_list["path"])
if(ispath(mutation, /datum/mutation/human))
var/datum/mutation/human/HM = get_valid_mutation(mutation)
if(HM)
var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc)
I.add_mutations += new HM.type (copymut = HM)
I.doitanyway = TRUE
I.name = "[HM.name] injector"
if(connected)
I.damage_coeff = connected.damage_coeff
injectorready = world.time + INJECTOR_TIMEOUT * 5 * (1 - 0.1 * connected.precision_coeff)
else
injectorready = world.time + INJECTOR_TIMEOUT * 5
if("advinjector")
var/selection = href_list["injector"]
if(injectorready < world.time)
if(injector_selection.Find(selection))
var/list/true_selection = injector_selection[selection]
if(LAZYLEN(injector_selection))
var/obj/item/dnainjector/activator/I = new /obj/item/dnainjector/activator(loc)
for(var/A in true_selection)
var/datum/mutation/human/HM = A
I.add_mutations += new HM.type (copymut = HM)
I.doitanyway = TRUE
I.name = "Advanced [selection] injector"
if(connected)
I.damage_coeff = connected.damage_coeff
injectorready = world.time + INJECTOR_TIMEOUT * 8 * (1 - 0.1 * connected.precision_coeff)
else
injectorready = world.time + INJECTOR_TIMEOUT * 8
if("nullify")
if(viable_occupant)
var/datum/mutation/human/A = viable_occupant.dna.get_mutation(current_mutation)
if(A && (!viable_occupant.dna.mutation_in_sequence(current_mutation) || A.scrambled))
viable_occupant.dna.remove_mutation(current_mutation)
current_screen = "mainmenu"
current_mutation = null
if("pulsegene")
if(current_screen != "info")
var/path = text2path(href_list["path"])
if(viable_occupant && num && (path in viable_occupant.dna.mutation_index))
var/list/genes = list("A","T","G","C","X")
if(jokerready < world.time)
genes += "JOKER"
var/sequence = GET_GENE_STRING(path, viable_occupant.dna)
var/original = sequence[num]
var/new_gene = input("From [original] to-", "New block", original) as null|anything in genes
if(!new_gene)
new_gene = original
if(viable_occupant == get_viable_occupant()) //No cheesing
if((new_gene == "JOKER") && (jokerready < world.time))
var/true_genes = GET_SEQUENCE(current_mutation)
new_gene = true_genes[num]
jokerready = world.time + JOKER_TIMEOUT - (JOKER_UPGRADE * (connected.precision_coeff-1))
sequence = copytext(sequence, 1, num) + new_gene + copytext(sequence, num+1, length(sequence)+1)
viable_occupant.dna.mutation_index[path] = sequence
viable_occupant.radiation += RADIATION_STRENGTH_MULTIPLIER/connected.damage_coeff
viable_occupant.domutcheck()
if("exportdiskmut")
if(diskette && !diskette.read_only)
var/path = text2path(href_list["path"])
if(ispath(path, /datum/mutation/human))
var/datum/mutation/human/A = get_valid_mutation(path)
if(A && diskette && (LAZYLEN(diskette.mutations) < diskette.max_mutations))
var/datum/mutation/human/HM = new A.type()
diskette.mutations += HM
HM.copy_mutation(A)
to_chat(usr, "<span class='notice'>Succesfully written [A.name] to [diskette.name].</span>")
if("deletediskmut")
if(diskette && !diskette.read_only)
if(num && (LAZYLEN(diskette.mutations) >= num))
var/datum/mutation/human/A = diskette.mutations[num]
diskette.mutations.Remove(A)
qdel(A)
if("importdiskmut")
if(diskette && (LAZYLEN(diskette.mutations) >= num))
if(LAZYLEN(stored_mutations) < max_storage)
var/datum/mutation/human/A = diskette.mutations[num]
var/datum/mutation/human/HM = new A.type()
HM.copy_mutation(A)
stored_mutations += HM
to_chat(usr,"<span class='notice'>Succesfully written [A.name] to storage.")
if("combine")
if(num && (LAZYLEN(stored_mutations) >= num))
if(LAZYLEN(stored_mutations) < max_storage)
var/datum/mutation/human/A = stored_mutations[num]
var/path = A.type
if(combine)
var/result_path = get_mixed_mutation(combine, path)
if(result_path)
stored_mutations += new result_path()
to_chat(usr, "<span class='boldnotice'>Succes! New mutation has been added to storage</span>")
discover(result_path)
combine = null
else
to_chat(usr, "<span class='warning'>Failed. No mutation could be created.</span>")
combine = null
else
combine = path
to_chat(usr,"<span class='notice'>Selected [A.name] for combining</span>")
else
to_chat(usr, "<span class='warning'>Not enough space to store potential mutation.</span>")
if("ejectchromosome")
if(LAZYLEN(stored_chromosomes) <= num)
var/obj/item/chromosome/CM = stored_chromosomes[num]
CM.forceMove(drop_location())
adjust_item_drop_location(CM)
stored_chromosomes -= CM
if("applychromosome")
if(viable_occupant && (LAZYLEN(viable_occupant.dna.mutations) <= num))
var/datum/mutation/human/HM = viable_occupant.dna.mutations[num]
var/list/chromosomes = list()
for(var/obj/item/chromosome/CM in stored_chromosomes)
if(CM.can_apply(HM))
chromosomes += CM
if(chromosomes.len)
var/obj/item/chromosome/CM = input("Select a chromosome to apply", "Apply Chromosome") as null|anything in sortNames(chromosomes)
if(CM)
to_chat(usr, "<span class='notice'>You apply [CM] to [HM.name].</span>")
stored_chromosomes -= CM
CM.apply(HM)
if("expand_advinjector")
var/mutation = text2path(href_list["path"])
var/datum/mutation/human/HM = get_valid_mutation(mutation)
if(HM && LAZYLEN(injector_selection))
var/which_injector = input(usr, "Select Adv. Injector", "Advanced Injectors") as null|anything in injector_selection
if(injector_selection.Find(which_injector))
var/list/true_selection = injector_selection[which_injector]
var/total_instability
for(var/B in true_selection)
var/datum/mutation/human/mootacion = B
total_instability += mootacion.instability
total_instability += HM.instability
if((total_instability > max_injector_instability) || (true_selection.len + 1) > max_injector_mutations)
to_chat(usr, "<span class='warning'>Adding more mutations would make the advanced injector too unstable!</span>")
else
true_selection += HM //reminder that this works. because I keep forgetting this works
if("remove_from_advinjector")
var/mutation = text2path(href_list["path"])
var/selection = href_list["injector"]
if(injector_selection.Find(selection))
var/list/true_selection = injector_selection[selection]
for(var/B in true_selection)
var/datum/mutation/human/HM = B
if(HM.type == mutation)
true_selection -= HM
break
if("remove_advinjector")
var/selection = href_list["injector"]
for(selection in injector_selection)
if(selection == selection)
injector_selection.Remove(selection)
if("add_advinjector")
if(LAZYLEN(injector_selection) < max_injector_selections)
var/new_selection = input(usr, "Enter Adv. Injector name", "Advanced Injectors") as text|null
if(new_selection && !(new_selection in injector_selection))
injector_selection[new_selection] = list()
ui_interact(usr,last_change)
/obj/machinery/computer/scan_consolenew/proc/scramble(input,rs,rd)
/obj/machinery/computer/scan_consolenew/proc/scramble(input,rs,rd) //hexadecimal genetics. dont confuse with scramble button
var/length = length(input)
var/ran = gaussian(0, rs*RADIATION_STRENGTH_MULTIPLIER)
if(ran == 0)
ran = pick(-1,1) //hacky, statistically should almost never happen. 0-change makes people mad though
ran = pick(-1,1) //hacky, statistically should almost never happen. 0-chance makes people mad though
else if(ran < 0)
ran = round(ran) //negative, so floor it
else
@@ -523,7 +948,7 @@
var/mob/living/carbon/viable_occupant = null
if(connected)
viable_occupant = connected.occupant
if(!istype(viable_occupant) || !viable_occupant.dna || HAS_TRAIT(viable_occupant, TRAIT_RADIMMUNE) || HAS_TRAIT(viable_occupant, TRAIT_NOCLONE))
if(!istype(viable_occupant) || !viable_occupant.dna || HAS_TRAIT_NOT_FROM(viable_occupant, TRAIT_RADIMMUNE,BLOODSUCKER_TRAIT) || HAS_TRAIT(viable_occupant, TRAIT_NOCLONE))
viable_occupant = null
return viable_occupant
@@ -537,10 +962,6 @@
//Each laser level reduces damage by lvl^2, so no effect on 1 lvl, 4 times less damage on 2 and 9 times less damage on 3
//Numbers are this high because other way upgrading laser is just not worth the hassle, and i cant think of anything better to inmrove
switch(action)
if(SCANNER_ACTION_SE)
if(buffer_slot["SE"])
viable_occupant.dna.struc_enzymes = buffer_slot["SE"]
viable_occupant.domutcheck()
if(SCANNER_ACTION_UI)
if(buffer_slot["UI"])
viable_occupant.dna.uni_identity = buffer_slot["UI"]
@@ -562,15 +983,54 @@
viable_occupant.dna.blood_type = buffer_slot["blood_type"]
/obj/machinery/computer/scan_consolenew/proc/on_scanner_close()
if(delayed_action && connected)
if(delayed_action && get_viable_occupant())
to_chat(connected.occupant, "<span class='notice'>[src] activates!</span>")
apply_buffer(delayed_action["action"],delayed_action["buffer"])
delayed_action = null //or make it stick + reset button ?
/obj/machinery/computer/scan_consolenew/proc/get_valid_mutation(mutation)
var/mob/living/carbon/C = get_viable_occupant()
if(C)
var/datum/mutation/human/HM = C.dna.get_mutation(mutation)
if(HM)
return HM
for(var/datum/mutation/human/A in stored_mutations)
if(A.type == mutation)
return A
/obj/machinery/computer/scan_consolenew/proc/get_mutation_list(include_storage) //Returns a list of the mutation index types and any extra mutations
var/mob/living/carbon/viable_occupant = get_viable_occupant()
var/list/paths = list()
if(viable_occupant)
for(var/A in viable_occupant.dna.mutation_index)
paths += A
for(var/datum/mutation/human/A in viable_occupant.dna.mutations)
if(A.class == MUT_EXTRA)
paths += A.type
if(include_storage)
for(var/datum/mutation/human/A in stored_mutations)
paths += A.type
return paths
/obj/machinery/computer/scan_consolenew/proc/get_valid_gene_string(mutation)
var/mob/living/carbon/C = get_viable_occupant()
if(C && (mutation in C.dna.mutation_index))
return GET_GENE_STRING(mutation, C.dna)
else if(C && (LAZYLEN(C.dna.mutations)))
for(var/datum/mutation/human/A in C.dna.mutations)
if(A.type == mutation)
return GET_SEQUENCE(mutation)
for(var/datum/mutation/human/A in stored_mutations)
if(A.type == mutation)
return GET_SEQUENCE(mutation)
/obj/machinery/computer/scan_consolenew/proc/discover(mutation)
if(stored_research && !(mutation in stored_research.discovered_mutations))
stored_research.discovered_mutations += mutation
return TRUE
/////////////////////////// DNA MACHINES
#undef INJECTOR_TIMEOUT
#undef REJUVENATORS_INJECT
#undef REJUVENATORS_MAX
#undef NUMBER_OF_BUFFERS
#undef RADIATION_STRENGTH_MAX
+85 -112
View File
@@ -1,11 +1,13 @@
/obj/machinery/computer/launchpad
name = "\improper launchpad control console"
name = "launchpad control console"
desc = "Used to teleport objects to and from a launchpad."
icon_screen = "teleport"
icon_keyboard = "teleport_key"
circuit = /obj/item/circuitboard/computer/launchpad_console
var/sending = TRUE
var/current_pad //current pad viewed on the screen
ui_x = 475
ui_y = 260
var/selected_id
var/list/obj/machinery/launchpad/launchpads
var/maximum_pads = 4
@@ -18,7 +20,9 @@
return
/obj/machinery/computer/launchpad/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/multitool))
if(W.tool_behaviour == TOOL_MULTITOOL)
if(!multitool_check_buffer(user, W))
return
var/obj/item/multitool/M = W
if(M.buffer && istype(M.buffer, /obj/machinery/launchpad))
if(LAZYLEN(launchpads) < maximum_pads)
@@ -36,55 +40,7 @@
return FALSE
return TRUE
/obj/machinery/computer/launchpad/proc/get_pad(number)
var/obj/machinery/launchpad/pad = launchpads[number]
return pad
/obj/machinery/computer/launchpad/ui_interact(mob/user)
. = ..()
var/list/t = list()
if(!LAZYLEN(launchpads))
obj_flags &= ~IN_USE //Yeah so if you deconstruct teleporter while its in the process of shooting it wont disable the console
t += "<div class='statusDisplay'>No launchpad located.</div><BR>"
else
for(var/i in 1 to LAZYLEN(launchpads))
if(pad_exists(i))
var/obj/machinery/launchpad/pad = get_pad(i)
if(pad.stat & NOPOWER)
t+= "<span class='linkOff'>[pad.display_name]</span>"
else
t+= "<A href='?src=[REF(src)];choose_pad=1;pad=[i]'>[pad.display_name]</A>"
else
launchpads -= get_pad(i)
t += "<BR>"
if(current_pad)
var/obj/machinery/launchpad/pad = get_pad(current_pad)
t += "<div class='statusDisplay'><b>[pad.display_name]</b></div>"
t += "<A href='?src=[REF(src)];change_name=1;pad=[current_pad]'>Rename</A>"
t += "<A href='?src=[REF(src)];remove=1;pad=[current_pad]'>Remove</A><BR><BR>"
t += "<A href='?src=[REF(src)];raisey=1;lowerx=1;pad=[current_pad]'>O</A>" //up-left
t += "<A href='?src=[REF(src)];raisey=1;pad=[current_pad]'>^</A>" //up
t += "<A href='?src=[REF(src)];raisey=1;raisex=1;pad=[current_pad]'>O</A><BR>" //up-right
t += "<A href='?src=[REF(src)];lowerx=1;pad=[current_pad]'><</A>"//left
t += "<A href='?src=[REF(src)];reset=1;pad=[current_pad]'>R</A>"//reset to 0
t += "<A href='?src=[REF(src)];raisex=1;pad=[current_pad]'>></A><BR>"//right
t += "<A href='?src=[REF(src)];lowery=1;lowerx=1;pad=[current_pad]'>O</A>"//down-left
t += "<A href='?src=[REF(src)];lowery=1;pad=[current_pad]'>v</A>"//down
t += "<A href='?src=[REF(src)];lowery=1;raisex=1;pad=[current_pad]'>O</A><BR>"//down-right
t += "<BR>"
t += "<div class='statusDisplay'>Current offset:</div><BR>"
t += "<div class='statusDisplay'>[abs(pad.y_offset)] [pad.y_offset > 0 ? "N":"S"] <a href='?src=[REF(src)];sety=1;pad=[current_pad]'>\[SET\]</a></div><BR>"
t += "<div class='statusDisplay'>[abs(pad.x_offset)] [pad.x_offset > 0 ? "E":"W"] <a href='?src=[REF(src)];setx=1;pad=[current_pad]'>\[SET\]</a></div><BR>"
t += "<BR><A href='?src=[REF(src)];launch=1;pad=[current_pad]'>Launch</A>"
t += " <A href='?src=[REF(src)];pull=1;pad=[current_pad]'>Pull</A>"
var/datum/browser/popup = new(user, "launchpad", name, 300, 500)
popup.set_content(t.Join())
popup.open()
/obj/machinery/computer/launchpad/proc/teleport(mob/user, obj/machinery/launchpad/pad)
/obj/machinery/computer/launchpad/proc/teleport(mob/user, obj/machinery/launchpad/pad, sending)
if(QDELETED(pad))
to_chat(user, "<span class='warning'>ERROR: Launchpad not responding. Check launchpad integrity.</span>")
return
@@ -93,66 +49,83 @@
return
pad.doteleport(user, sending)
/obj/machinery/computer/launchpad/Topic(href, href_list)
var/obj/machinery/launchpad/pad
if(href_list["pad"])
pad = get_pad(text2num(href_list["pad"]))
/obj/machinery/computer/launchpad/proc/get_pad(number)
var/obj/machinery/launchpad/pad = launchpads[number]
return pad
/obj/machinery/computer/launchpad/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "launchpad_console", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/launchpad/ui_data(mob/user)
var/list/data = list()
var/list/pad_list = list()
for(var/i in 1 to LAZYLEN(launchpads))
if(pad_exists(i))
var/obj/machinery/launchpad/pad = get_pad(i)
var/list/this_pad = list()
this_pad["name"] = pad.display_name
this_pad["id"] = i
if(pad.stat & NOPOWER)
this_pad["inactive"] = TRUE
pad_list += list(this_pad)
else
launchpads -= get_pad(i)
data["launchpads"] = pad_list
data["selected_id"] = selected_id
if(selected_id)
var/obj/machinery/launchpad/current_pad = launchpads[selected_id]
data["x"] = current_pad.x_offset
data["y"] = current_pad.y_offset
data["pad_name"] = current_pad.display_name
data["range"] = current_pad.range
data["selected_pad"] = current_pad
if(QDELETED(current_pad) || (current_pad.stat & NOPOWER))
data["pad_active"] = FALSE
return data
data["pad_active"] = TRUE
return data
/obj/machinery/computer/launchpad/ui_act(action, params)
if(..())
return
if(!LAZYLEN(launchpads))
updateDialog()
return
var/obj/machinery/launchpad/current_pad = launchpads[selected_id]
switch(action)
if("select_pad")
selected_id = text2num(params["id"])
. = TRUE
if("set_pos")
var/new_x = text2num(params["x"])
var/new_y = text2num(params["y"])
current_pad.set_offset(new_x, new_y)
. = TRUE
if("move_pos")
var/plus_x = text2num(params["x"])
var/plus_y = text2num(params["y"])
current_pad.set_offset(
x = current_pad.x_offset + plus_x,
y = current_pad.y_offset + plus_y
)
. = TRUE
if("rename")
. = TRUE
var/new_name = params["name"]
if(!new_name)
return
current_pad.display_name = new_name
if("remove")
if(usr && alert(usr, "Are you sure?", "Unlink Launchpad", "I'm Sure", "Abort") != "Abort")
launchpads -= current_pad
selected_id = null
. = TRUE
if("launch")
teleport(usr, current_pad, TRUE)
. = TRUE
if(href_list["choose_pad"])
current_pad = text2num(href_list["pad"])
if(href_list["raisex"])
if(pad.x_offset < pad.range)
pad.x_offset++
if(href_list["lowerx"])
if(pad.x_offset > (pad.range * -1))
pad.x_offset--
if(href_list["raisey"])
if(pad.y_offset < pad.range)
pad.y_offset++
if(href_list["lowery"])
if(pad.y_offset > (pad.range * -1))
pad.y_offset--
if(href_list["reset"])
pad.y_offset = 0
pad.x_offset = 0
if(href_list["change_name"])
var/new_name = stripped_input(usr, "What do you wish to name the launchpad?", "Launchpad", pad.display_name, 15)
if(!new_name)
return
pad.display_name = new_name
if(href_list["setx"])
var/newx = input(usr, "Input new x offset", pad.display_name, pad.x_offset) as null|num
if(!isnull(newx))
pad.x_offset = CLAMP(newx, -pad.range, pad.range)
if(href_list["sety"])
var/newy = input(usr, "Input new y offset", pad.display_name, pad.y_offset) as null|num
if(!isnull(newy))
pad.y_offset = CLAMP(newy, -pad.range, pad.range)
if(href_list["remove"])
if(usr && alert(usr, "Are you sure?", "Remove Launchpad", "I'm Sure", "Abort") != "Abort")
launchpads -= pad
if(href_list["launch"])
sending = TRUE
teleport(usr, pad)
if(href_list["pull"])
sending = FALSE
teleport(usr, pad)
updateDialog()
if("pull")
teleport(usr, current_pad, FALSE)
. = TRUE
. = TRUE
+6 -6
View File
@@ -145,7 +145,7 @@
if(5)
dat += "<CENTER><B>Virus Database</B></CENTER>"
for(var/Dt in typesof(/datum/disease/))
var/datum/disease/Dis = new Dt(0)
var/datum/disease/Dis = new Dt(FALSE)
if(istype(Dis, /datum/disease/advance))
continue // TODO (tm): Add advance diseases to the virus database which no one uses.
if(!Dis.desc)
@@ -190,7 +190,7 @@
if(!(active2 in GLOB.data_core.medical))
active2 = null
if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || issilicon(usr) || IsAdminGhost(usr))
if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || hasSiliconAccessInArea(usr) || IsAdminGhost(usr))
usr.set_machine(src)
if(href_list["temp"])
temp = null
@@ -216,7 +216,7 @@
else if(href_list["login"])
var/mob/M = usr
var/obj/item/card/id/I = M.get_idcard(TRUE)
if(issilicon(M))
if(hasSiliconAccessInArea(M))
active1 = null
active2 = null
authenticated = 1
@@ -248,7 +248,7 @@
else if(href_list["vir"])
var/type = href_list["vir"]
var/datum/disease/Dis = new type(0)
var/datum/disease/Dis = new type(FALSE)
var/AfS = ""
for(var/mob/M in Dis.viable_mobtypes)
AfS += " [initial(M.name)];"
@@ -478,7 +478,7 @@
var/counter = 1
while(active2.fields[text("com_[]", counter)])
counter++
active2.fields[text("com_[]", counter)] = text("Made by [] ([]) on [] [], []<BR>[]", authenticated, rank, STATION_TIME_TIMESTAMP("hh:mm:ss"), time2text(world.realtime, "MMM DD"), GLOB.year_integer, t1)
active2.fields[text("com_[]", counter)] = text("Made by [] ([]) on [] [], []<BR>[]", authenticated, rank, STATION_TIME_TIMESTAMP("hh:mm:ss", world.time), time2text(world.realtime, "MMM DD"), GLOB.year_integer, t1)
else if(href_list["del_c"])
if((istype(active2, /datum/data/record) && active2.fields[text("com_[]", href_list["del_c"])]))
@@ -569,7 +569,7 @@
if(user)
if(message)
if(authenticated)
if(user.canUseTopic(src, !issilicon(user)))
if(user.canUseTopic(src, !hasSiliconAccessInArea(user)))
if(!record1 || record1 == active1)
if(!record2 || record2 == active2)
return 1
+1 -1
View File
@@ -86,7 +86,7 @@
/obj/machinery/computer/pod/Topic(href, href_list)
if(..())
return
if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || issilicon(usr))
if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || hasSiliconAccessInArea(usr))
usr.set_machine(src)
if(href_list["power"])
var/t = text2num(href_list["power"])
@@ -6,6 +6,9 @@
icon_keyboard = "security_key"
req_access = list(ACCESS_ARMORY)
circuit = /obj/item/circuitboard/computer/gulag_teleporter_console
ui_x = 350
ui_y = 295
var/default_goal = 200
var/obj/machinery/gulag_teleporter/teleporter = null
var/obj/structure/gulag_beacon/beacon = null
@@ -22,7 +25,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "gulag_console", name, 455, 440, master_ui, state)
ui = new(user, src, ui_key, "gulag_console", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/prisoner/gulag_teleporter_computer/ui_data(mob/user)
@@ -50,13 +53,19 @@
data["teleporter_location"] = "([teleporter.x], [teleporter.y], [teleporter.z])"
data["teleporter_lock"] = teleporter.locked
data["teleporter_state_open"] = teleporter.state_open
else
data["teleporter"] = null
if(beacon)
data["beacon"] = beacon
data["beacon_location"] = "([beacon.x], [beacon.y], [beacon.z])"
else
data["beacon"] = null
if(contained_id)
data["id"] = contained_id
data["id_name"] = contained_id.registered_name
data["goal"] = contained_id.goal
else
data["id"] = null
data["can_teleport"] = can_teleport
return data
@@ -72,36 +81,41 @@
switch(action)
if("scan_teleporter")
teleporter = findteleporter()
return TRUE
if("scan_beacon")
beacon = findbeacon()
return TRUE
if("handle_id")
if(contained_id)
id_eject(usr)
else
id_insert(usr)
return TRUE
if("set_goal")
var/new_goal = input("Set the amount of points:", "Points", contained_id.goal) as num|null
var/new_goal = text2num(params["value"])
if(!isnum(new_goal))
return
if(!new_goal)
new_goal = default_goal
if (new_goal > 1000)
to_chat(usr, "The entered amount of points is too large. Points have instead been set to the maximum allowed amount.")
contained_id.goal = CLAMP(new_goal, 0, 1000) //maximum 1000 points
return TRUE
if("toggle_open")
if(teleporter.locked)
to_chat(usr, "The teleporter is locked")
to_chat(usr, "<span class='alert'>The teleporter must be unlocked first.</span>")
return
teleporter.toggle_open()
return TRUE
if("teleporter_lock")
if(teleporter.state_open)
to_chat(usr, "Close the teleporter before locking!")
to_chat(usr, "<span class='alert'>The teleporter must be closed first.</span>")
return
teleporter.locked = !teleporter.locked
return TRUE
if("teleport")
if(!teleporter || !beacon)
return
addtimer(CALLBACK(src, .proc/teleport, usr), 5)
return TRUE
/obj/machinery/computer/prisoner/gulag_teleporter_computer/proc/scan_machinery()
teleporter = findteleporter()
@@ -129,12 +143,12 @@
say("[contained_id]'s ID card goal defaulting to [contained_id.goal] points.")
log_game("[key_name(user)] teleported [key_name(prisoner)] to the Labor Camp [COORD(beacon)] for [id_goal_not_set ? "default goal of ":""][contained_id.goal] points.")
teleporter.handle_prisoner(contained_id, temporary_record)
playsound(src, 'sound/weapons/emitter.ogg', 50, 1)
playsound(src, 'sound/weapons/emitter.ogg', 50, TRUE)
prisoner.forceMove(get_turf(beacon))
prisoner.Stun(40) // small travel dizziness
to_chat(prisoner, "<span class='warning'>The teleportation makes you a little dizzy.</span>")
new /obj/effect/particle_effect/sparks(get_turf(prisoner))
playsound(src, "sparks", 50, 1)
playsound(src, "sparks", 50, TRUE)
if(teleporter.locked)
teleporter.locked = FALSE
teleporter.toggle_open()
@@ -87,7 +87,7 @@
/obj/machinery/computer/prisoner/management/Topic(href, href_list)
if(..())
return
if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || issilicon(usr))
if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || hasSiliconAccessInArea(usr))
usr.set_machine(src)
if(href_list["id"])
@@ -125,7 +125,7 @@
to_chat(usr, "<span class='danger'>Unauthorized access.</span>")
else if(href_list["warn"])
var/warning = copytext(sanitize(input(usr,"Message:","Enter your message here!","")),1,MAX_MESSAGE_LEN)
var/warning = stripped_input(usr, "Message:", "Enter your message here!", "", MAX_MESSAGE_LEN)
if(!warning)
return
var/obj/item/implant/I = locate(href_list["warn"]) in GLOB.tracked_implants
+17 -15
View File
@@ -11,16 +11,19 @@
/obj/machinery/computer/robotics/proc/can_control(mob/user, mob/living/silicon/robot/R)
if(!istype(R))
return 0
return FALSE
if(isAI(user))
if (R.connected_ai != user)
return 0
return FALSE
if(iscyborg(user))
if (R != user)
return 0
return FALSE
if(R.scrambledcodes)
return 0
return 1
return FALSE
if (hasSiliconAccessInArea(user) && !issilicon(user))
if (!Adjacent(user))
return FALSE
return TRUE
/obj/machinery/computer/robotics/ui_interact(mob/user)
. = ..()
@@ -37,12 +40,11 @@
dat += "[R.name] |"
if(R.stat)
dat += " Not Responding |"
else if (!R.canmove)
else if(R.locked_down)
dat += " Locked Down |"
else
dat += " Operating Normally |"
if (!R.canmove)
else if(R.cell)
if(R.cell)
dat += " Battery Installed ([R.cell.charge]/[R.cell.maxcharge]) |"
else
dat += " No Cell Installed |"
@@ -62,7 +64,7 @@
dat += "<A href='?src=[REF(src)];magbot=[REF(R)]'>(<font color=blue><i>Hack</i></font>)</A> "
else if(IsAdminGhost(user) && !R.emagged)
dat += "<A href='?src=[REF(src)];magbot=[REF(R)]'>(<font color=blue><i>Hack</i></font>)</A> "
dat += "<A href='?src=[REF(src)];stopbot=[REF(R)]'>(<font color=green><i>[R.canmove ? "Lockdown" : "Release"]</i></font>)</A> "
dat += "<A href='?src=[REF(src)];stopbot=[REF(R)]'>(<font color=green><i>[R.locked_down? "Lockdown" : "Release"]</i></font>)</A> "
dat += "<A href='?src=[REF(src)];killbot=[REF(R)]'>(<font color=red><i>Destroy</i></font>)</A>"
dat += "<BR>"
@@ -116,14 +118,14 @@
if(src.allowed(usr))
var/mob/living/silicon/robot/R = locate(href_list["stopbot"]) in GLOB.silicon_mobs
if(can_control(usr, R))
var/choice = input("Are you certain you wish to [R.canmove ? "lock down" : "release"] [R.name]?") in list("Confirm", "Abort")
var/choice = input("Are you certain you wish to [!R.locked_down ? "lock down" : "release"] [R.name]?") in list("Confirm", "Abort")
if(choice == "Confirm" && can_control(usr, R) && !..())
message_admins("<span class='notice'>[ADMIN_LOOKUPFLW(usr)] [R.canmove ? "locked down" : "released"] [key_name(R, R.client)][ADMIN_LOOKUPFLW(R)]!</span>")
log_game("[key_name(usr)] [R.canmove ? "locked down" : "released"] [key_name(R)]!")
R.SetLockdown(!R.lockcharge)
to_chat(R, "[!R.lockcharge ? "<span class='notice'>Your lockdown has been lifted!" : "<span class='alert'>You have been locked down!"]</span>")
message_admins("<span class='notice'>[ADMIN_LOOKUPFLW(usr)] [!R.locked_down ? "locked down" : "released"] [key_name(R, R.client)][ADMIN_LOOKUPFLW(R)]!</span>")
log_game("[key_name(usr)] [!R.locked_down ? "locked down" : "released"] [key_name(R)]!")
R.SetLockdown(!R.locked_down)
to_chat(R, "[!R.locked_down ? "<span class='notice'>Your lockdown has been lifted!" : "<span class='alert'>You have been locked down!"]</span>")
if(R.connected_ai)
to_chat(R.connected_ai, "[!R.lockcharge ? "<span class='notice'>NOTICE - Cyborg lockdown lifted" : "<span class='alert'>ALERT - Cyborg lockdown detected"]: <a href='?src=[REF(R.connected_ai)];track=[html_encode(R.name)]'>[R.name]</a></span><br>")
to_chat(R.connected_ai, "[!R.locked_down ? "<span class='notice'>NOTICE - Cyborg lockdown lifted" : "<span class='alert'>ALERT - Cyborg lockdown detected"]: <a href='?src=[REF(R.connected_ai)];track=[html_encode(R.name)]'>[R.name]</a></span><br>")
else
to_chat(usr, "<span class='danger'>Access Denied.</span>")
+7 -7
View File
@@ -265,7 +265,7 @@ What a mess.*/
active1 = null
if(!( GLOB.data_core.security.Find(active2) ))
active2 = null
if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || issilicon(usr) || IsAdminGhost(usr))
if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || hasSiliconAccessInArea(usr) || IsAdminGhost(usr))
usr.set_machine(src)
switch(href_list["choice"])
// SORTING!
@@ -299,7 +299,7 @@ What a mess.*/
if("Log In")
var/mob/M = usr
var/obj/item/card/id/I = M.get_idcard(TRUE)
if(issilicon(M))
if(hasSiliconAccessInArea(M))
var/mob/living/silicon/borg = M
active1 = null
active2 = null
@@ -456,7 +456,7 @@ What a mess.*/
var/counter = 1
while(active2.fields[text("com_[]", counter)])
counter++
active2.fields[text("com_[]", counter)] = text("Made by [] ([]) on [] [], []<BR>[]", src.authenticated, src.rank, STATION_TIME_TIMESTAMP("hh:mm:ss"), time2text(world.realtime, "MMM DD"), GLOB.year_integer, t1)
active2.fields[text("com_[]", counter)] = text("Made by [] ([]) on [] [], []<BR>[]", src.authenticated, src.rank, STATION_TIME_TIMESTAMP("hh:mm:ss", world.time), time2text(world.realtime, "MMM DD"), GLOB.year_integer, t1)
if("Delete Record (ALL)")
if(active1)
@@ -544,7 +544,7 @@ What a mess.*/
switch(href_list["field"])
if("name")
if(istype(active1, /datum/data/record) || istype(active2, /datum/data/record))
var/t1 = copytext(sanitize(input("Please input name:", "Secure. records", active1.fields["name"], null) as text),1,MAX_MESSAGE_LEN)
var/t1 = stripped_input(usr, "Please input name:", "Secure. records", active1.fields["name"], MAX_MESSAGE_LEN)
if(!canUseSecurityRecordsConsole(usr, t1, a1))
return
if(istype(active1, /datum/data/record))
@@ -636,7 +636,7 @@ What a mess.*/
var/t2 = stripped_input(usr, "Please input minor crime details:", "Secure. records", "", null)
if(!canUseSecurityRecordsConsole(usr, t1, null, a2))
return
var/crime = GLOB.data_core.createCrimeEntry(t1, t2, authenticated, STATION_TIME_TIMESTAMP("hh:mm:ss"))
var/crime = GLOB.data_core.createCrimeEntry(t1, t2, authenticated, STATION_TIME_TIMESTAMP("hh:mm:ss", world.time))
GLOB.data_core.addMinorCrime(active1.fields["id"], crime)
investigate_log("New Minor Crime: <strong>[t1]</strong>: [t2] | Added to [active1.fields["name"]] by [key_name(usr)]", INVESTIGATE_RECORDS)
if("mi_crim_delete")
@@ -651,7 +651,7 @@ What a mess.*/
var/t2 = stripped_input(usr, "Please input major crime details:", "Secure. records", "", null)
if(!canUseSecurityRecordsConsole(usr, t1, null, a2))
return
var/crime = GLOB.data_core.createCrimeEntry(t1, t2, authenticated, STATION_TIME_TIMESTAMP("hh:mm:ss"))
var/crime = GLOB.data_core.createCrimeEntry(t1, t2, authenticated, STATION_TIME_TIMESTAMP("hh:mm:ss", world.time))
GLOB.data_core.addMajorCrime(active1.fields["id"], crime)
investigate_log("New Major Crime: <strong>[t1]</strong>: [t2] | Added to [active1.fields["name"]] by [key_name(usr)]", INVESTIGATE_RECORDS)
if("ma_crim_delete")
@@ -802,7 +802,7 @@ What a mess.*/
/obj/machinery/computer/secure_data/proc/canUseSecurityRecordsConsole(mob/user, message1 = 0, record1, record2)
if(user)
if(authenticated)
if(user.canUseTopic(src, !issilicon(user)))
if(user.canUseTopic(src, !hasSiliconAccessInArea(user)))
if(!trim(message1))
return 0
if(!record1 || record1 == active1)
+14 -15
View File
@@ -20,17 +20,18 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "station_alert", name, 300, 500, master_ui, state)
ui = new(user, src, ui_key, "station_alert", name, 325, 500, master_ui, state)
ui.open()
/obj/machinery/computer/station_alert/ui_data(mob/user)
. = list()
var/list/data = list()
.["alarms"] = list()
data["alarms"] = list()
for(var/class in alarms)
.["alarms"][class] = list()
data["alarms"][class] = list()
for(var/area in alarms[class])
.["alarms"][class] += area
data["alarms"][class] += area
return data
/obj/machinery/computer/station_alert/proc/triggerAlarm(class, area/A, O, obj/source)
if(source.z != z)
@@ -74,25 +75,23 @@
L -= I
return !cleared
/obj/machinery/computer/station_alert/update_icon()
..()
cut_overlays()
/obj/machinery/computer/station_alert/update_overlays()
. = ..()
SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
var/overlay_state = icon_screen
if(stat & (NOPOWER|BROKEN))
add_overlay("[icon_keyboard]_off")
. |= "[icon_keyboard]_off"
return
add_overlay(icon_keyboard)
. |= icon_keyboard
var/active_alarms = FALSE
for(var/cat in alarms)
var/list/L = alarms[cat]
if(L.len)
if(length(alarms[cat]))
active_alarms = TRUE
break
if(active_alarms)
overlay_state = "alert:2"
add_overlay("alert:2")
else
overlay_state = "alert:0"
add_overlay("alert:0")
. |= overlay_state
SSvis_overlays.add_vis_overlay(src, icon, overlay_state, layer, plane, dir)
SSvis_overlays.add_vis_overlay(src, icon, overlay_state, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir, alpha=128)
SSvis_overlays.add_vis_overlay(src, icon, overlay_state, EMISSIVE_LAYER, EMISSIVE_PLANE, dir, alpha=128)
@@ -44,8 +44,8 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E
else
to_chat(user, "<span class='notice'>[I] doesn't appear to be an uplink...</span>")
/obj/machinery/computer/telecrystals/uplinker/update_icon()
..()
/obj/machinery/computer/telecrystals/uplinker/update_overlays()
. = ..()
if(uplinkholder)
add_overlay("[initial(icon_state)]-closed")
@@ -136,7 +136,7 @@ GLOBAL_LIST_INIT(possible_uplinker_IDs, list("Alfa","Bravo","Charlie","Delta","E
var/list/transferlog = list()
/obj/machinery/computer/telecrystals/boss/proc/logTransfer(logmessage)
transferlog += ("<b>[STATION_TIME_TIMESTAMP("hh:mm:ss")]</b> [logmessage]")
transferlog += ("<b>[STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)]</b> [logmessage]")
/obj/machinery/computer/telecrystals/boss/proc/scanUplinkers()
for(var/obj/machinery/computer/telecrystals/uplinker/A in urange(scanrange, src.loc))
+55 -56
View File
@@ -5,6 +5,8 @@
icon_keyboard = "teleport_key"
light_color = LIGHT_COLOR_BLUE
circuit = /obj/item/circuitboard/computer/teleporter
ui_x = 475
ui_y = 130
var/regime_set = "Teleporter"
var/id
var/obj/machinery/teleport/station/power_station
@@ -32,34 +34,30 @@
break
return power_station
/obj/machinery/computer/teleporter/ui_interact(mob/user)
. = ..()
var/data = "<h3>Teleporter Status</h3>"
if(!power_station)
data += "<div class='statusDisplay'>No power station linked.</div>"
else if(!power_station.teleporter_hub)
data += "<div class='statusDisplay'>No hub linked.</div>"
obj/machinery/computer/teleporter/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "teleporter", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/teleporter/ui_data(mob/user)
var/list/data = list()
data["power_station"] = power_station ? TRUE : FALSE
data["teleporter_hub"] = power_station?.teleporter_hub ? TRUE : FALSE
data["regime_set"] = regime_set
data["target"] = !target ? "None" : "[get_area(target)] [(regime_set != "Gate") ? "" : "Teleporter"]"
data["calibrating"] = calibrating
if(power_station?.teleporter_hub?.calibrated || power_station?.teleporter_hub?.accuracy >= 3)
data["calibrated"] = TRUE
else
data += "<div class='statusDisplay'>Current regime: [regime_set]<BR>"
data += "Current target: [(!target) ? "None" : "[get_area(target)] [(regime_set != "Gate") ? "" : "Teleporter"]"]<BR>"
if(calibrating)
data += "Calibration: <font color='yellow'>In Progress</font>"
else if(power_station.teleporter_hub.calibrated || power_station.teleporter_hub.accurate >= 3)
data += "Calibration: <font color='green'>Optimal</font>"
else
data += "Calibration: <font color='red'>Sub-Optimal</font>"
data += "</div><BR>"
data["calibrated"] = FALSE
data += "<A href='?src=[REF(src)];regimeset=1'>Change regime</A><BR>"
data += "<A href='?src=[REF(src)];settarget=1'>Set target</A><BR>"
return data
data += "<BR><A href='?src=[REF(src)];calibrate=1'>Calibrate Hub</A>"
var/datum/browser/popup = new(user, "teleporter", name, 400, 400)
popup.set_content(data)
popup.open()
/obj/machinery/computer/teleporter/Topic(href, href_list)
/obj/machinery/computer/teleporter/ui_act(action, params)
if(..())
return
@@ -70,38 +68,39 @@
say("Error: Calibration in progress. Stand by.")
return
if(href_list["regimeset"])
power_station.engaged = 0
power_station.teleporter_hub.update_icon()
power_station.teleporter_hub.calibrated = 0
reset_regime()
if(href_list["settarget"])
power_station.engaged = 0
power_station.teleporter_hub.update_icon()
power_station.teleporter_hub.calibrated = 0
set_target(usr)
if(href_list["calibrate"])
if(!target)
say("Error: No target set to calibrate to.")
return
if(power_station.teleporter_hub.calibrated || power_station.teleporter_hub.accurate >= 3)
say("Hub is already calibrated!")
return
say("Processing hub calibration to target...")
switch(action)
if("regimeset")
power_station.engaged = FALSE
power_station.teleporter_hub.update_icon()
power_station.teleporter_hub.calibrated = FALSE
reset_regime()
. = TRUE
if("settarget")
power_station.engaged = FALSE
power_station.teleporter_hub.update_icon()
power_station.teleporter_hub.calibrated = FALSE
set_target(usr)
. = TRUE
if("calibrate")
if(!target)
say("Error: No target set to calibrate to.")
return
if(power_station.teleporter_hub.calibrated || power_station.teleporter_hub.accuracy >= 3)
say("Hub is already calibrated!")
return
calibrating = 1
power_station.update_icon()
spawn(50 * (3 - power_station.teleporter_hub.accurate)) //Better parts mean faster calibration
calibrating = 0
if(check_hub_connection())
power_station.teleporter_hub.calibrated = 1
say("Calibration complete.")
else
say("Error: Unable to detect hub.")
say("Processing hub calibration to target...")
calibrating = TRUE
power_station.update_icon()
updateDialog()
updateDialog()
spawn(50 * (3 - power_station.teleporter_hub.accuracy)) //Better parts mean faster calibration
calibrating = FALSE
if(check_hub_connection())
power_station.teleporter_hub.calibrated = TRUE
say("Calibration complete.")
else
say("Error: Unable to detect hub.")
power_station.update_icon()
. = TRUE
/obj/machinery/computer/teleporter/proc/check_hub_connection()
if(!power_station)
@@ -141,7 +140,7 @@
L[avoid_assoc_duplicate_keys(M.real_name, areaindex)] = M
var/desc = input("Please select a location to lock in.", "Locking Computer") as null|anything in L
if(!user.canUseTopic(src, !issilicon(user), NO_DEXTERY)) //check if we are still around
if(!user.canUseTopic(src, !hasSiliconAccessInArea(user), NO_DEXTERY)) //check if we are still around
return
target = L[desc]
if(imp_t)
@@ -169,7 +168,7 @@
to_chat(user, "<span class='alert'>No active connected stations located.</span>")
return
var/desc = input("Please select a station to lock in.", "Locking Computer") as null|anything in L
if(!user.canUseTopic(src, !issilicon(user), NO_DEXTERY)) //again, check if we are still around
if(!user.canUseTopic(src, !hasSiliconAccessInArea(user), NO_DEXTERY)) //again, check if we are still around
return
var/obj/machinery/teleport/station/target_station = L[desc]
if(!target_station || !target_station.teleporter_hub)
+6 -3
View File
@@ -119,6 +119,9 @@
if(istype(P, /obj/item/circuitboard/machine))
var/obj/item/circuitboard/machine/B = P
if(!B.build_path)
to_chat(user, "<span class'warning'>This circuitboard seems to be broken.</span>")
return
if(!anchored && B.needs_anchored)
to_chat(user, "<span class='warning'>The frame needs to be secured first!</span>")
return
@@ -181,6 +184,9 @@
if(component_check)
P.play_tool_sound(src)
var/obj/machinery/new_machine = new circuit.build_path(loc)
if(new_machine.circuit)
QDEL_NULL(new_machine.circuit)
new_machine.circuit = circuit
new_machine.setAnchored(anchored)
new_machine.on_construction()
for(var/obj/O in new_machine.component_parts)
@@ -189,9 +195,6 @@
for(var/obj/O in src)
O.moveToNullspace()
new_machine.component_parts += O
if(new_machine.circuit)
QDEL_NULL(new_machine.circuit)
new_machine.circuit = circuit
circuit.moveToNullspace()
new_machine.RefreshParts()
qdel(src)
+59 -61
View File
@@ -6,7 +6,6 @@
* ~ Zuhayr
*/
//Main cryopod console.
/obj/machinery/computer/cryopod
@@ -18,7 +17,6 @@
density = FALSE
interaction_flags_machine = INTERACT_MACHINE_OFFLINE
req_one_access = list(ACCESS_HEADS, ACCESS_ARMORY) //Heads of staff or the warden can go here to claim recover items from their department that people went were cryodormed with.
var/mode = null
var/menu = 1 //Which menu screen to display
@@ -118,7 +116,7 @@
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
I.forceMove(drop_location())
if(user && Adjacent(user) && !issiliconoradminghost(user))
if(user && Adjacent(user) && user.can_hold_items())
user.put_in_hands(I)
frozen_items -= I
updateUsrDialog()
@@ -130,7 +128,8 @@
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
updateUsrDialog()
return
if(!allow_items) return
if(!allow_items)
return
if(frozen_items.len == 0)
to_chat(user, "<span class='notice'>There is nothing to recover from storage.</span>")
@@ -182,7 +181,7 @@
var/last_no_computer_message = 0
// These items are preserved when the process() despawn proc occurs.
var/list/preserve_items = list(
var/static/list/preserve_items = typecacheof(list(
/obj/item/hand_tele,
/obj/item/card/id/captains_spare,
/obj/item/aicard,
@@ -203,9 +202,9 @@
/obj/item/tank/jetpack,
/obj/item/documents,
/obj/item/nuke_core_container
)
))
// These items will NOT be preserved
var/list/do_not_preserve_items = list (
var/static/list/do_not_preserve_items = typecacheof(list(
/obj/item/mmi/posibrain,
/obj/item/gun/energy/laser/mounted,
/obj/item/gun/energy/e_gun/advtaser/mounted,
@@ -215,7 +214,7 @@
/obj/item/gun/energy/printer,
/obj/item/gun/energy/kinetic_accelerator/cyborg,
/obj/item/gun/energy/laser/cyborg
)
))
/obj/machinery/cryopod/Initialize(mapload)
. = ..()
@@ -287,14 +286,15 @@
#define CRYO_DESTROY 0
#define CRYO_PRESERVE 1
#define CRYO_OBJECTIVE 2
#define CRYO_IGNORE 3
#define CRYO_DESTROY_LATER 4
/obj/machinery/cryopod/proc/should_preserve_item(obj/item/I)
for(var/datum/objective_item/steal/T in control_computer.theft_cache)
if(istype(I, T.targetitem) && T.check_special_completion(I))
return CRYO_OBJECTIVE
for(var/T in preserve_items)
if(istype(I, T) && !(I.type in do_not_preserve_items))
return CRYO_PRESERVE
if(preserve_items[I] && !do_not_preserve_items[I])
return CRYO_PRESERVE
return CRYO_DESTROY
// This function can not be undone; do not call this unless you are sure
@@ -303,54 +303,51 @@
find_control_computer()
var/mob/living/mob_occupant = occupant
var/list/obj/item/cryo_items = list()
//Handle Borg stuff first
if(iscyborg(mob_occupant))
var/mob/living/silicon/robot/R = mob_occupant
R.contents -= R.mmi
qdel(R.mmi)
if(R.mmi?.brain)
cryo_items[R.mmi] = CRYO_DESTROY_LATER
cryo_items[R.mmi.brain] = CRYO_DESTROY_LATER
for(var/obj/item/I in R.module) // the tools the borg has; metal, glass, guns etc
for(var/obj/item/O in I) // the things inside the tools, if anything; mainly for janiborg trash bags
if(should_preserve_item(O) != CRYO_DESTROY) // Preserve important things inside the item
continue
cryo_items[O] = should_preserve_item(O)
O.forceMove(src)
R.module.remove_module(I, TRUE) //delete the module itself so it doesn't transfer over.
//Drop all items into the pod.
for(var/obj/item/I in mob_occupant)
mob_occupant.doUnEquip(I)
I.forceMove(src)
if(cryo_items[I] == CRYO_IGNORE || cryo_items[I] ==CRYO_DESTROY_LATER)
continue
cryo_items[I] = should_preserve_item(I)
mob_occupant.transferItemToLoc(I, src, TRUE)
if(I.contents.len) //Make sure we catch anything not handled by qdel() on the items.
if(should_preserve_item(I) != CRYO_DESTROY) // Don't remove the contents of things that need preservation
if(cryo_items[I] != CRYO_DESTROY) // Don't remove the contents of things that need preservation
continue
for(var/obj/item/O in I.contents)
if(istype(O, /obj/item/tank)) //Stop eating pockets, you fuck!
continue
cryo_items[O] = should_preserve_item(O)
O.forceMove(src)
//Delete all items not on the preservation list.
var/list/items = contents
items -= mob_occupant // Don't delete the occupant
for(var/obj/item/I in items)
if(istype(I, /obj/item/pda))
var/obj/item/pda/P = I
QDEL_NULL(P.id)
qdel(P)
for(var/A in cryo_items)
var/obj/item/I = A
if(QDELETED(I)) //edge cases and DROPDEL.
continue
var/preserve = should_preserve_item(I)
if(preserve == CRYO_DESTROY)
qdel(I)
else if(control_computer && control_computer.allow_items)
control_computer.frozen_items += I
if(preserve == CRYO_OBJECTIVE)
control_computer.objective_items += I
I.loc = null
else
I.forceMove(loc)
var/preserve = cryo_items[I]
if(preserve == CRYO_DESTROY_LATER)
continue
if(preserve != CRYO_IGNORE)
if(preserve == CRYO_DESTROY)
qdel(I)
else if(control_computer?.allow_items)
control_computer.frozen_items += I
if(preserve == CRYO_OBJECTIVE)
control_computer.objective_items += I
I.moveToNullspace()
else
I.forceMove(loc)
cryo_items -= I
//Update any existing objectives involving this mob.
for(var/datum/objective/O in GLOB.objectives)
@@ -411,12 +408,18 @@
mob_occupant.ghostize(FALSE, penalize = TRUE)
QDEL_NULL(occupant)
for(var/I in cryo_items) //only "CRYO_DESTROY_LATER" atoms are left)
var/atom/A = I
if(!QDELETED(A))
qdel(A)
open_machine()
name = initial(name)
#undef CRYO_DESTROY
#undef CRYO_PRESERVE
#undef CRYO_OBJECTIVE
#undef CRYO_IGNORE
#undef CRYO_DESTROY_LATER
/obj/machinery/cryopod/MouseDrop_T(mob/living/target, mob/user)
if(!istype(target) || user.incapacitated() || !target.Adjacent(user) || !Adjacent(user) || !ismob(target) || (!ishuman(user) && !iscyborg(user)) || !istype(user.loc, /turf) || target.buckled)
@@ -443,29 +446,24 @@
var/generic_plsnoleave_message = " Please adminhelp before leaving the round, even if there are no administrators online!"
if(target == user && world.time - target.client.cryo_warned > 5 MINUTES)//if we haven't warned them in the last 5 minutes
var/caught = FALSE
var/list/caught_string
var/addendum = ""
if(target.mind.assigned_role in GLOB.command_positions)
alert("<span class='userdanger'>You're a Head of Staff![generic_plsnoleave_message] Be sure to put your locker items back into your locker!</span>")
caught = TRUE
LAZYADD(caught_string, "Head of Staff")
addendum = " Be sure to put your locker items back into your locker!"
if(iscultist(target) || is_servant_of_ratvar(target))
to_chat(target, "<span class='userdanger'>You're a Cultist![generic_plsnoleave_message]</span>")
caught = TRUE
LAZYADD(caught_string, "Cultist")
if(is_devil(target))
alert("<span class='userdanger'>You're a Devil![generic_plsnoleave_message]</span>")
caught = TRUE
if(istype(SSticker.mode, /datum/antagonist/gang))
if(target.mind.has_antag_datum(/datum/antagonist/gang))
alert("<span class='userdanger'>You're a Gangster![generic_plsnoleave_message]</span>")
caught = TRUE
if(istype(SSticker.mode, /datum/antagonist/rev))
if(target.mind.has_antag_datum(/datum/antagonist/rev/head))
alert("<span class='userdanger'>You're a Head Revolutionary![generic_plsnoleave_message]</span>")
caught = TRUE
else if(target.mind.has_antag_datum(/datum/antagonist/rev))
alert("<span class='userdanger'>You're a Revolutionary![generic_plsnoleave_message]</span>")
caught = TRUE
LAZYADD(caught_string, "Devil")
if(target.mind.has_antag_datum(/datum/antagonist/gang))
LAZYADD(caught_string, "Gangster")
if(target.mind.has_antag_datum(/datum/antagonist/rev/head))
LAZYADD(caught_string, "Head Revolutionary")
if(target.mind.has_antag_datum(/datum/antagonist/rev))
LAZYADD(caught_string, "Revolutionary")
if(caught)
if(caught_string)
alert(target, "You're a [english_list(caught_string)]![generic_plsnoleave_message][addendum]")
target.client.cryo_warned = world.time
return
+10 -9
View File
@@ -45,7 +45,7 @@
return
return ..()
/obj/machinery/jukebox/update_icon()
/obj/machinery/jukebox/update_icon_state()
if(active)
icon_state = "[initial(icon_state)]-active"
else
@@ -53,7 +53,7 @@
/obj/machinery/jukebox/ui_interact(mob/user)
. = ..()
if(!user.canUseTopic(src, !issilicon(user)))
if(!user.canUseTopic(src, !hasSiliconAccessInArea(user)))
return
if (!anchored)
to_chat(user,"<span class='warning'>This device must be anchored by a wrench!</span>")
@@ -202,6 +202,8 @@
for(var/i in 1 to 10)
spawn_atom_to_turf(/obj/effect/temp_visual/hierophant/telegraph/edge, src, 1, FALSE)
sleep(5)
if(QDELETED(src))
return
#define DISCO_INFENO_RANGE (rand(85, 115)*0.01)
@@ -406,12 +408,11 @@
lying_prev = 0
/obj/machinery/jukebox/proc/dance_over()
SSjukeboxes.removejukebox(SSjukeboxes.findjukeboxindex(src))
var/position = SSjukeboxes.findjukeboxindex(src)
if(!position)
return
SSjukeboxes.removejukebox(position)
STOP_PROCESSING(SSobj, src)
for(var/mob/living/L in rangers)
if(!L || !L.client)
continue
L.stop_sound_channel(CHANNEL_JUKEBOX)
rangers = list()
/obj/machinery/jukebox/disco/dance_over()
@@ -431,6 +432,6 @@
/obj/machinery/jukebox/disco/process()
. = ..()
if(active)
for(var/mob/M in rangers)
if(prob(5+(allowed(M)*4)) && M.canmove)
for(var/mob/living/M in rangers)
if(prob(5+(allowed(M)*4)) && CHECK_MOBILITY(M, MOBILITY_MOVE))
dance(M)
+5 -4
View File
@@ -120,7 +120,7 @@
/obj/machinery/defibrillator_mount/AltClick(mob/living/carbon/user)
. = ..()
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE))
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
return
. = TRUE
if(!defib)
@@ -129,9 +129,10 @@
if(clamps_locked)
to_chat(user, "<span class='warning'>You try to tug out [defib], but the mount's clamps are locked tight!</span>")
return
if(!user.put_in_hands(defib))
if(!user.get_empty_held_indexes())
to_chat(user, "<span class='warning'>You need a free hand!</span>")
return
user.put_in_hands(defib)
user.visible_message("<span class='notice'>[user] unhooks [defib] from [src].</span>", \
"<span class='notice'>You slide out [defib] from [src] and unhook the charging cables.</span>")
playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE)
@@ -144,7 +145,7 @@
desc = "A frame for a defibrillator mount. It can't be removed once it's placed."
icon = 'icons/obj/machines/defib_mount.dmi'
icon_state = "defibrillator_mount"
materials = list(MAT_METAL = 300, MAT_GLASS = 100)
custom_materials = list(/datum/material/iron = 300, /datum/material/glass = 100)
w_class = WEIGHT_CLASS_BULKY
result_path = /obj/machinery/defibrillator_mount
pixel_shift = -28
pixel_shift = -28
+4 -4
View File
@@ -15,7 +15,7 @@
density = TRUE
max_integrity = 100
var/proj_pass_rate = 50 //How many projectiles will pass the cover. Lower means stronger cover
var/material = METAL
var/bar_material = METAL
/obj/structure/barricade/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
@@ -26,7 +26,7 @@
return
/obj/structure/barricade/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/weldingtool) && user.a_intent != INTENT_HARM && material == METAL)
if(istype(I, /obj/item/weldingtool) && user.a_intent != INTENT_HARM && bar_material == METAL)
if(obj_integrity < max_integrity)
if(!I.tool_start_check(user, amount=0))
return
@@ -61,7 +61,7 @@
desc = "This space is blocked off by a wooden barricade."
icon = 'icons/obj/structures.dmi'
icon_state = "woodenbarricade"
material = WOOD
bar_material = WOOD
var/drop_amount = 3
/obj/structure/barricade/wooden/attackby(obj/item/I, mob/user)
@@ -106,7 +106,7 @@
max_integrity = 280
proj_pass_rate = 20
pass_flags = LETPASSTHROW
material = SAND
bar_material = SAND
climbable = TRUE
smooth = SMOOTH_TRUE
canSmoothWith = list(/obj/structure/barricade/sandbags, /turf/closed/wall, /turf/closed/wall/r_wall, /obj/structure/falsewall, /obj/structure/falsewall/reinforced, /turf/closed/wall/rust, /turf/closed/wall/r_wall/rust, /obj/structure/barricade/security)
+1 -1
View File
@@ -98,7 +98,7 @@
/obj/machinery/dish_drive/AltClick(mob/living/user)
. = ..()
if(user.canUseTopic(src, !issilicon(user)))
if(user.canUseTopic(src, !hasSiliconAccessInArea(user)))
do_the_dishes(TRUE)
return TRUE
+10 -3
View File
@@ -22,12 +22,19 @@
precision_coeff = 0
for(var/obj/item/stock_parts/scanning_module/P in component_parts)
scan_level += P.rating
for(var/obj/item/stock_parts/manipulator/P in component_parts)
for(var/obj/item/stock_parts/matter_bin/P in component_parts)
precision_coeff = P.rating
for(var/obj/item/stock_parts/micro_laser/P in component_parts)
damage_coeff = P.rating
/obj/machinery/dna_scannernew/update_icon()
/obj/machinery/dna_scannernew/examine(mob/user)
. = ..()
if(in_range(user, src) || isobserver(user))
. += "<span class='notice'>The status display reads: Radiation pulse accuracy increased by factor <b>[precision_coeff**2]</b>.<br>Radiation pulse damage decreased by factor <b>[damage_coeff**2]</b>.</span>"
if(scan_level >= 3)
. += "<span class='notice'>Scanner has been upgraded to support autoprocessing.<span>"
/obj/machinery/dna_scannernew/update_icon_state()
//no power or maintenance
if(stat & (NOPOWER|BROKEN))
@@ -165,7 +172,7 @@
/obj/machinery/dna_scannernew/AltClick(mob/user)
. = ..()
if(!user.canUseTopic(src, !issilicon(user)))
if(!user.canUseTopic(src, !hasSiliconAccessInArea(user)))
return
interact(user)
return TRUE
+39 -117
View File
@@ -47,7 +47,7 @@
icon_state = "closed"
max_integrity = 300
var/normal_integrity = AIRLOCK_INTEGRITY_N
integrity_failure = 70
integrity_failure = 0.25
damage_deflection = AIRLOCK_DAMAGE_DEFLECTION_N
autoclose = TRUE
secondsElectrified = 0 //How many seconds remain until the door is no longer electrified. -1 if it is permanently electrified until someone fixes it.
@@ -469,7 +469,7 @@
panel_overlay = get_airlock_overlay("panel_closed", overlays_file)
if(welded)
weld_overlay = get_airlock_overlay("welded", overlays_file)
if(obj_integrity <integrity_failure)
if(obj_integrity < integrity_failure * max_integrity)
damag_overlay = get_airlock_overlay("sparks_broken", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
else if(obj_integrity < (0.75 * max_integrity))
damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
@@ -495,7 +495,7 @@
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)
if(obj_integrity < integrity_failure * max_integrity)
damag_overlay = get_airlock_overlay("sparks_broken", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
else if(obj_integrity < (0.75 * max_integrity))
damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
@@ -517,7 +517,7 @@
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)
if(obj_integrity < integrity_failure * max_integrity)
damag_overlay = get_airlock_overlay("sparks_broken", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
else if(obj_integrity < (0.75 * max_integrity))
damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
@@ -676,7 +676,7 @@
else
. += "It looks very robust."
if(issilicon(user) && (!stat & BROKEN))
if(hasSiliconAccessInArea(user) && (!stat & BROKEN))
. += "<span class='notice'>Shift-click [src] to [ density ? "open" : "close"] it.</span>"
. += "<span class='notice'>Ctrl-click [src] to [ locked ? "raise" : "drop"] its bolts.</span>"
. += "<span class='notice'>Alt-click [src] to [ secondsElectrified ? "un-electrify" : "permanently electrify"] it.</span>"
@@ -769,7 +769,7 @@
if(!istype(H.head, /obj/item/clothing/head/helmet))
H.visible_message("<span class='danger'>[user] headbutts the airlock.</span>", \
"<span class='userdanger'>You headbutt the airlock!</span>")
H.Knockdown(100)
H.DefaultCombatKnockdown(100)
H.apply_damage(10, BRUTE, BODY_ZONE_HEAD)
else
visible_message("<span class='danger'>[user] headbutts the airlock. Good thing [user.p_theyre()] wearing a helmet.</span>")
@@ -1033,7 +1033,7 @@
if(!I.use_tool(src, user, 150, volume=50))
to_chat(user, "<span class='warning'>You slip and [charge] detonates!</span>")
charge.ex_act(EXPLODE_DEVASTATE)
user.Knockdown(60)
user.DefaultCombatKnockdown(60)
return
user.visible_message("<span class='notice'>[user] removes [charge] from [src].</span>", \
"<span class='notice'>You gently pry out [charge] from [src] and unhook its wires.</span>")
@@ -1322,9 +1322,9 @@
if(density && !open(2)) //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/hostile_lockdown(mob/origin)
/obj/machinery/door/airlock/hostile_lockdown(mob/origin, aicontrolneeded = TRUE)
// Must be powered and have working AI wire.
if(canAIControl(src) && !stat)
if((aicontrolneeded && canAIControl(src) && !stat) || !aicontrolneeded)
locked = FALSE //For airlocks that were bolted open.
safe = FALSE //DOOR CRUSH
close()
@@ -1334,9 +1334,9 @@
LAZYADD(shockedby, "\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(origin)]")
/obj/machinery/door/airlock/disable_lockdown()
/obj/machinery/door/airlock/disable_lockdown(aicontrolneeded = TRUE)
// Must be powered and have working AI wire.
if(canAIControl(src) && !stat)
if((aicontrolneeded && canAIControl(src) && !stat) || !aicontrolneeded)
unbolt()
set_electrified(NOT_ELECTRIFIED)
open()
@@ -1434,7 +1434,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "ai_airlock", name, 550, 456, master_ui, state)
ui = new(user, src, ui_key, "ai_airlock", name, 500, 390, master_ui, state)
ui.open()
return TRUE
@@ -1503,83 +1503,24 @@
if("shock-perm")
shock_perm(usr)
. = TRUE
if("idscan-on")
if(wires.is_cut(WIRE_IDSCAN))
to_chat(usr, "You can't enable IdScan - The IdScan wire has been cut.")
else if(src.aiDisabledIdScanner)
aiDisabledIdScanner = FALSE
else
to_chat(usr, "The IdScan feature is not disabled.")
if("idscan-toggle")
aiDisabledIdScanner = !aiDisabledIdScanner
. = TRUE
if("idscan-off")
if(wires.is_cut(WIRE_IDSCAN))
to_chat(usr, "The IdScan wire has been cut - So, you can't disable it, but it is already disabled anyways.")
else if(aiDisabledIdScanner)
to_chat(usr, "You've already disabled the IdScan feature.")
else
aiDisabledIdScanner = TRUE
if("emergency-toggle")
toggle_emergency(usr)
. = TRUE
if("emergency-on")
emergency_on(usr)
if("bolt-toggle")
toggle_bolt(usr)
. = TRUE
if("emergency-off")
emergency_off(usr)
if("light-toggle")
lights = !lights
update_icon()
. = TRUE
if("bolt-raise")
bolt_raise(usr)
if("safe-toggle")
safe = !safe
. = TRUE
if("bolt-drop")
bolt_drop(usr)
. = TRUE
if("light-on")
if(wires.is_cut(WIRE_LIGHT))
to_chat(usr, "Control to door bolt lights has been severed.")
else if (!src.lights)
lights = TRUE
update_icon()
else
to_chat(usr, text("Door bolt lights are already enabled!"))
. = TRUE
if("light-off")
if(wires.is_cut(WIRE_LIGHT))
to_chat(usr, "Control to door bolt lights has been severed.")
else if (lights)
lights = FALSE
update_icon()
else
to_chat(usr, "Door bolt lights are already disabled!")
. = TRUE
if("safe-on")
if(wires.is_cut(WIRE_SAFETY))
to_chat(usr, "Control to door sensors is disabled.")
else if (!src.safe)
safe = TRUE
else
to_chat(usr, "Firmware reports safeties already in place.")
. = TRUE
if("safe-off")
if(wires.is_cut(WIRE_SAFETY))
to_chat(usr, "Control to door sensors is disabled.")
else if (safe)
safe = FALSE
else
to_chat(usr, "Firmware reports safeties already overridden.")
. = TRUE
if("speed-on")
if(wires.is_cut(WIRE_TIMING))
to_chat(usr, "Control to door timing circuitry has been severed.")
else if (!src.normalspeed)
normalspeed = 1
else
to_chat(usr,"Door timing circuitry currently operating normally.")
. = TRUE
if("speed-off")
if(wires.is_cut(WIRE_TIMING))
to_chat(usr, "Control to door timing circuitry has been severed.")
else if (normalspeed)
normalspeed = 0
else
to_chat(usr, "Door timing circuitry already accelerated.")
if("speed-toggle")
normalspeed = !normalspeed
. = TRUE
if("open-close")
@@ -1587,7 +1528,7 @@
. = TRUE
/obj/machinery/door/airlock/proc/user_allowed(mob/user)
return (issilicon(user) && canAIControl(user)) || IsAdminGhost(user)
return (hasSiliconAccessInArea(user) && canAIControl(user)) || IsAdminGhost(user)
/obj/machinery/door/airlock/proc/shock_restore(mob/user)
if(!user_allowed(user))
@@ -1617,45 +1558,26 @@
log_combat(user, src, "electrified")
set_electrified(ELECTRIFIED_PERMANENT)
/obj/machinery/door/airlock/proc/emergency_on(mob/user)
if(!user_allowed(user))
return
if (!emergency)
emergency = TRUE
update_icon()
else
to_chat(user, "Emergency access is already enabled!")
/obj/machinery/door/airlock/proc/emergency_off(mob/user)
if(!user_allowed(user))
return
if (emergency)
emergency = FALSE
update_icon()
else
to_chat(user, "Emergency access is already disabled!")
/obj/machinery/door/airlock/proc/bolt_raise(mob/user)
/obj/machinery/door/airlock/proc/toggle_bolt(mob/user)
if(!user_allowed(user))
return
if(wires.is_cut(WIRE_BOLTS))
to_chat(user, "The door bolt drop wire is cut - you can't raise the door bolts")
else if(!src.locked)
to_chat(user, "The door bolts are already up")
else
if(src.hasPower())
unbolt()
to_chat(user, "<span class='warning'>The door bolt drop wire is cut - you can't toggle the door bolts.</span>")
return
if(locked)
if(!hasPower())
to_chat(user, "<span class='warning'>The door has no power - you can't raise the door bolts.</span>")
else
to_chat(user, "Cannot raise door bolts due to power failure")
/obj/machinery/door/airlock/proc/bolt_drop(mob/user)
if(!user_allowed(user))
return
if(wires.is_cut(WIRE_BOLTS))
to_chat(user, "You can't drop the door bolts - The door bolt dropping wire has been cut.")
unbolt()
else
bolt()
/obj/machinery/door/airlock/proc/toggle_emergency(mob/user)
if(!user_allowed(user))
return
emergency = !emergency
update_icon()
/obj/machinery/door/airlock/proc/user_toggle_open(mob/user)
if(!user_allowed(user))
return
@@ -14,7 +14,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "airlock_electronics", name, 975, 420, master_ui, state)
ui = new(user, src, ui_key, "airlock_electronics", name, 420, 485, master_ui, state)
ui.open()
/obj/item/electronics/airlock/ui_data()
@@ -43,13 +43,16 @@
if(..())
return
switch(action)
if("clear")
if("clear_all")
accesses = list()
one_access = 0
. = TRUE
if("one_access")
one_access = !one_access
. = TRUE
if("grant_all")
accesses = get_all_accesses()
. = TRUE
if("set")
var/access = text2num(params["access"])
if (!(access in accesses))
+1 -1
View File
@@ -508,7 +508,7 @@
throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(L, src)))
SEND_SOUND(L, sound(pick('sound/hallucinations/turn_around1.ogg','sound/hallucinations/turn_around2.ogg'),0,1,50))
flash_color(L, flash_color="#960000", flash_time=20)
L.Knockdown(40)
L.DefaultCombatKnockdown(40)
L.throw_at(throwtarget, 5, 1)
return 0
+11 -17
View File
@@ -2,13 +2,11 @@
#define FONT_SIZE "5pt"
#define FONT_COLOR "#09f"
#define FONT_STYLE "Arial Black"
#define MAX_TIMER 9000
#define PRESET_SHORT 1200
#define PRESET_MEDIUM 1800
#define PRESET_LONG 3000
#define MAX_TIMER 15 MINUTES
#define PRESET_SHORT 2 MINUTES
#define PRESET_MEDIUM 3 MINUTES
#define PRESET_LONG 5 MINUTES
///////////////////////////////////////////////////////////////////////////////////////////////
// Brig Door control displays.
@@ -25,7 +23,7 @@
desc = "A remote control for a door."
req_access = list(ACCESS_SECURITY)
density = FALSE
var/id = null // id of linked machinery/lockers
var/id // id of linked machinery/lockers
var/activation_time = 0
var/timer_duration = 0
@@ -43,8 +41,6 @@
Radio = new/obj/item/radio(src)
Radio.listening = 0
/obj/machinery/door_timer/Initialize()
. = ..()
if(id != null)
for(var/obj/machinery/door/window/brigdoor/M in urange(20, src))
if (M.id == id)
@@ -71,7 +67,7 @@
return
if(timing)
if(world.realtime - activation_time >= timer_duration)
if(REALTIMEOFDAY - activation_time >= timer_duration)
timer_end() // open doors, reset timer, clear status screen
update_icon()
@@ -80,14 +76,13 @@
..()
update_icon()
// open/closedoor checks if door_timer has power, if so it checks if the
// linked door is open/closed (by density) then opens it/closes it.
/obj/machinery/door_timer/proc/timer_start()
if(stat & (NOPOWER|BROKEN))
return 0
activation_time = world.realtime
activation_time = REALTIMEOFDAY
timing = TRUE
for(var/obj/machinery/door/window/brigdoor/door in targets)
@@ -104,7 +99,6 @@
C.update_icon()
return 1
/obj/machinery/door_timer/proc/timer_end(forced = FALSE)
if(stat & (NOPOWER|BROKEN))
@@ -136,7 +130,7 @@
/obj/machinery/door_timer/proc/time_left(seconds = FALSE)
. = max(0,timer_duration - (activation_time ? world.realtime - activation_time : 0))
. = max(0,timer_duration - (activation_time ? REALTIMEOFDAY - activation_time : 0))
if(seconds)
. /= 10
@@ -149,7 +143,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "brig_timer", name, 300, 200, master_ui, state)
ui = new(user, src, ui_key, "brig_timer", name, 300, 138, master_ui, state)
ui.open()
//icon update function
@@ -168,7 +162,7 @@
if(timing)
var/disp1 = id
var/time_left = time_left(seconds = TRUE)
var/disp2 = "[add_zero(num2text((time_left / 60) % 60),2)]~[add_zero(num2text(time_left % 60), 2)]"
var/disp2 = "[add_leading(num2text((time_left / 60) % 60), 2, "0")]:[add_leading(num2text(time_left % 60), 2, "0")]"
if(length(disp2) > CHARS_PER_LINE)
disp2 = "Error"
update_display(disp1, disp2)
@@ -240,7 +234,7 @@
preset_time = PRESET_LONG
. = set_timer(preset_time)
if(timing)
activation_time = world.realtime
activation_time = REALTIMEOFDAY
else
. = FALSE
+6 -6
View File
@@ -5,6 +5,7 @@
icon_state = "door1"
opacity = 1
density = TRUE
move_resist = MOVE_FORCE_VERY_STRONG
layer = OPEN_DOOR_LAYER
power_channel = ENVIRON
max_integrity = 350
@@ -229,7 +230,7 @@
/obj/machinery/door/proc/unelectrify()
secondsElectrified = 0
/obj/machinery/door/update_icon()
/obj/machinery/door/update_icon_state()
if(density)
icon_state = "door1"
else
@@ -287,9 +288,10 @@
return
operating = TRUE
do_animate("closing")
layer = closingLayer
if(!safe)
crush()
sleep(5)
density = TRUE
sleep(5)
@@ -301,8 +303,6 @@
update_freelook_sight()
if(safe)
CheckForMobs()
else
crush()
return 1
/obj/machinery/door/proc/CheckForMobs()
@@ -319,10 +319,10 @@
else if(ishuman(L)) //For humans
L.adjustBruteLoss(DOOR_CRUSH_DAMAGE)
L.emote("scream")
L.Knockdown(100)
L.DefaultCombatKnockdown(100)
else if(ismonkey(L)) //For monkeys
L.adjustBruteLoss(DOOR_CRUSH_DAMAGE)
L.Knockdown(100)
L.DefaultCombatKnockdown(100)
else //for simple_animals & borgs
L.adjustBruteLoss(DOOR_CRUSH_DAMAGE)
var/turf/location = get_turf(src)
+13 -10
View File
@@ -32,7 +32,7 @@
CalculateAffectingAreas()
/obj/machinery/door/firedoor/examine(mob/user)
..()
. = ..()
if(!density)
. += "<span class='notice'>It is open, but could be <b>pried</b> closed.</span>"
else if(!welded)
@@ -44,7 +44,7 @@
/obj/machinery/door/firedoor/proc/CalculateAffectingAreas()
remove_from_areas()
affecting_areas = get_adjacent_open_areas(src) | get_area(src)
affecting_areas = get_adjacent_open_areas(src) | get_base_area(src)
for(var/I in affecting_areas)
var/area/A = I
LAZYADD(A.firedoors, src)
@@ -171,16 +171,20 @@
if("closing")
flick("door_closing", src)
/obj/machinery/door/firedoor/update_icon()
cut_overlays()
/obj/machinery/door/firedoor/update_icon_state()
if(density)
icon_state = "door_closed"
if(welded)
add_overlay("welded")
else
icon_state = "door_open"
if(welded)
add_overlay("welded_open")
/obj/machinery/door/firedoor/update_overlays()
. = ..()
if(!welded)
return
if(density)
. += "welded"
else
. += "welded_open"
/obj/machinery/door/firedoor/open()
. = ..()
@@ -283,8 +287,7 @@
if(CONSTRUCTION_NOCIRCUIT)
. += "<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()
..()
/obj/structure/firelock_frame/update_icon_state()
icon_state = "frame[constructionStep]"
/obj/structure/firelock_frame/attackby(obj/item/C, mob/user)
+1 -1
View File
@@ -42,7 +42,7 @@
else
do_animate("deny")
/obj/machinery/door/password/update_icon()
/obj/machinery/door/password/update_icon_state()
if(density)
icon_state = "closed"
else
+1 -1
View File
@@ -77,7 +77,7 @@
flick("closing", src)
playsound(src, 'sound/machines/blastdoor.ogg', 30, 1)
/obj/machinery/door/poddoor/update_icon()
/obj/machinery/door/poddoor/update_icon_state()
if(density)
icon_state = "closed"
else
+56 -1
View File
@@ -1,13 +1,68 @@
/obj/machinery/door/poddoor/shutters
gender = PLURAL
name = "shutters"
desc = "Heavy duty metal shutters that open mechanically."
desc = "Mechanical metal shutters operated by a button with a magnetic seal, keeping them airtight."
icon = 'icons/obj/doors/shutters.dmi'
layer = SHUTTER_LAYER
closingLayer = SHUTTER_LAYER
armor = list("melee" = 20, "bullet" = 20, "laser" = 20, "energy" = 75, "bomb" = 25, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70)
damage_deflection = 20
max_integrity = 100
/obj/machinery/door/poddoor/shutters/preopen
icon_state = "open"
density = FALSE
opacity = 0
// The below shutters are the original ones from the TG codebase. They are remaining as "secure" shutters. If anyone wants to improve their animation, feel free.
// The original shutters are now shutters_old.dmi; copy the naming format of the files into new a new .dmi to add new shutters that work with the poddoor code for animating the doors.
// Originally, the shutters were reskins of blast doors. Eighty hits with the Cap's sabre to destroy one shutter is far too powerful considering shutters cannot be deconstructed (yet).
// If you're a mapper and want super strong shutter, use the 'old' ones.
/obj/machinery/door/poddoor/shutters/old
name = "strong shutters"
desc = "These shutters have an armoured frame; it looks like plasteel. These shutters look robust enough to survive explosions."
icon = 'icons/obj/doors/shutters_old.dmi'
icon_state = "closed"
armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 75, "bomb" = 30, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70)
max_integrity = 300
/obj/machinery/door/poddoor/shutters/old/preopen
icon_state = "open"
density = FALSE
opacity = 0
// End of old shutter stuff. Credit for the old shutter sprites to TG.
/obj/machinery/door/poddoor/shutters/radiation
name = "radiation shutters"
desc = "Lead-lined shutters painted yellow with a radioactive hazard symbol on it. Blocks out most radiation"
icon = 'icons/obj/doors/shutters_radiation.dmi'
icon_state = "closed"
rad_insulation = 0.2
/obj/machinery/door/poddoor/shutters/radiation/preopen
icon_state = "open"
density = FALSE
opacity = 0
rad_insulation = 1
/obj/machinery/door/poddoor/shutters/radiation/do_animate(animation)
..()
switch(animation)
if("opening")
rad_insulation = 1
if("closing")
rad_insulation = 0.2
/obj/machinery/door/poddoor/shutters/window
name = "windowed shutters"
desc = "Mechanical shutters that have some form of plastic window in them, allowing you to see through the shutters at all times."
icon = 'icons/obj/doors/shutters_window.dmi'
icon_state = "closed"
opacity = 0
glass = 1
/obj/machinery/door/poddoor/shutters/window/preopen
icon_state = "open"
density = FALSE
+1 -1
View File
@@ -48,7 +48,7 @@
electronics = null
return ..()
/obj/machinery/door/window/update_icon()
/obj/machinery/door/window/update_icon_state()
if(density)
icon_state = base_state
else
+6 -6
View File
@@ -11,7 +11,7 @@
density = TRUE
max_integrity = 250
integrity_failure = 80
integrity_failure = 0.33
// These allow for different icons when creating custom dispensers
var/icon_off = "off"
@@ -50,10 +50,10 @@
/obj/machinery/droneDispenser/Initialize()
. = ..()
var/datum/component/material_container/materials = AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS), MINERAL_MATERIAL_AMOUNT * MAX_STACK_SIZE * 2, TRUE, /obj/item/stack)
materials.insert_amount(starting_amount)
var/datum/component/material_container/materials = AddComponent(/datum/component/material_container, list(/datum/material/iron, /datum/material/glass), MINERAL_MATERIAL_AMOUNT * MAX_STACK_SIZE * 2, TRUE, /obj/item/stack)
materials.insert_amount_mat(starting_amount)
materials.precise_insertion = TRUE
using_materials = list(MAT_METAL=metal_cost, MAT_GLASS=glass_cost)
using_materials = list(/datum/material/iron = metal_cost, /datum/material/glass = glass_cost)
/obj/machinery/droneDispenser/preloaded
starting_amount = 5000
@@ -168,7 +168,7 @@
update_icon()
if(DRONE_PRODUCTION)
materials.use_amount(using_materials)
materials.use_materials(using_materials)
if(power_used)
use_power(power_used)
@@ -199,7 +199,7 @@
if(istype(a, dispense_type))
.++
/obj/machinery/droneDispenser/update_icon()
/obj/machinery/droneDispenser/update_icon_state()
if(stat & (BROKEN|NOPOWER))
icon_state = icon_off
else if(mode == DRONE_RECHARGING)
@@ -84,7 +84,7 @@
busy = FALSE
update_icon()
/obj/machinery/doorButtons/access_button/update_icon()
/obj/machinery/doorButtons/access_button/update_icon_state()
if(stat & NOPOWER)
icon_state = "access_button_off"
else
@@ -249,7 +249,7 @@
else if(A.id_tag == idExterior)
exteriorAirlock = A
/obj/machinery/doorButtons/airlock_controller/update_icon()
/obj/machinery/doorButtons/airlock_controller/update_icon_state()
if(stat & NOPOWER)
icon_state = "access_control_off"
return
@@ -255,7 +255,7 @@
new_prog.master = src
program = new_prog
/obj/machinery/embedded_controller/radio/airlock_controller/update_icon()
/obj/machinery/embedded_controller/radio/airlock_controller/update_icon_state()
if(on && program)
if(program.memory["processing"])
icon_state = "airlock_control_process"
@@ -33,8 +33,6 @@
popup.set_content(return_text())
popup.open()
/obj/machinery/embedded_controller/update_icon()
/obj/machinery/embedded_controller/proc/return_text()
/obj/machinery/embedded_controller/proc/post_signal(datum/signal/signal, comm_line)
@@ -54,7 +54,7 @@
new_prog.master = src
program = new_prog
/obj/machinery/embedded_controller/radio/simple_vent_controller/update_icon()
/obj/machinery/embedded_controller/radio/simple_vent_controller/update_icon_state()
if(on && program)
icon_state = "airlock_control_standby"
else
+14 -14
View File
@@ -9,7 +9,7 @@
internal_radio = FALSE
//Start growing a human clone in the pod!
/obj/machinery/clonepod/experimental/growclone(ckey, clonename, ui, se, datum/species/mrace, list/features, factions)
/obj/machinery/clonepod/experimental/growclone(clonename, ui, mutation_index, mindref, last_death, blood_type, datum/species/mrace, list/features, factions, list/quirks)
if(panel_open)
return FALSE
if(mess || attempting)
@@ -20,15 +20,15 @@
var/mob/living/carbon/human/H = new /mob/living/carbon/human(src)
H.hardset_dna(ui, se, H.real_name, null, mrace, features)
H.hardset_dna(ui, mutation_index, H.real_name, blood_type, mrace, features)
if(efficiency > 2)
var/list/unclean_mutations = (GLOB.not_good_mutations|GLOB.bad_mutations)
H.dna.remove_mutation_group(unclean_mutations)
if(efficiency > 5 && prob(20))
H.randmutvg()
H.easy_randmut(POSITIVE)
if(efficiency < 3 && prob(50))
var/mob/M = H.randmutb()
var/mob/M = H.easy_randmut(NEGATIVE+MINOR_NEGATIVE)
if(ismob(M))
H = M
@@ -42,18 +42,17 @@
icon_state = "pod_1"
//Get the clone body ready
maim_clone(H)
ADD_TRAIT(H, TRAIT_STABLEHEART, CLONING_POD_TRAIT)
ADD_TRAIT(H, TRAIT_STABLELIVER, CLONING_POD_TRAIT)
ADD_TRAIT(H, TRAIT_EMOTEMUTE, CLONING_POD_TRAIT)
ADD_TRAIT(H, TRAIT_MUTE, CLONING_POD_TRAIT)
ADD_TRAIT(H, TRAIT_NOBREATH, CLONING_POD_TRAIT)
ADD_TRAIT(H, TRAIT_NOCRITDAMAGE, CLONING_POD_TRAIT)
ADD_TRAIT(H, TRAIT_STABLEHEART, "cloning")
ADD_TRAIT(H, TRAIT_EMOTEMUTE, "cloning")
ADD_TRAIT(H, TRAIT_MUTE, "cloning")
ADD_TRAIT(H, TRAIT_NOBREATH, "cloning")
ADD_TRAIT(H, TRAIT_NOCRITDAMAGE, "cloning")
H.Unconscious(80)
var/list/candidates = pollCandidatesForMob("Do you want and agree to play as a [clonename]'s defective clone, respect their character and not engage in ERP without permission from the original?", null, null, null, 100, H, POLL_IGNORE_CLONE)
var/list/candidates = pollCandidatesForMob("Do you want to play as [clonename]'s defective clone?", null, null, null, 100, H)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
C.transfer_ckey(H)
var/mob/C = pick(candidates)
H.key = C.key
if(grab_ghost_when == CLONER_FRESH_CLONE)
H.grab_ghost()
@@ -293,6 +292,7 @@
temp = "<font class='bad'>Cloning cycle already in progress.</font>"
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else
pod.growclone(null, mob_occupant.real_name, dna.uni_identity, dna.struc_enzymes, clone_species, dna.features, mob_occupant.faction)
pod.growclone(mob_occupant.real_name, dna.uni_identity, dna.mutation_index, null, null, dna.blood_type, clone_species, dna.features, mob_occupant.faction)
temp = "[mob_occupant.real_name] => <font class='good'>Cloning data sent to pod.</font>"
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
+24 -23
View File
@@ -17,7 +17,7 @@
icon = 'icons/obj/monitors.dmi'
icon_state = "fire0"
max_integrity = 250
integrity_failure = 100
integrity_failure = 0.4
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 30)
use_power = IDLE_POWER_USE
idle_power_usage = 2
@@ -44,7 +44,7 @@
pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24)
pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0
update_icon()
myarea = get_area(src)
myarea = get_base_area(src)
LAZYADD(myarea.firealarms, src)
/obj/machinery/firealarm/Destroy()
@@ -55,10 +55,7 @@
..()
update_icon()
/obj/machinery/firealarm/update_icon()
cut_overlays()
SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
/obj/machinery/firealarm/update_icon_state()
if(panel_open)
icon_state = "fire_b[buildstage]"
return
@@ -72,27 +69,31 @@
if(stat & NOPOWER)
return
add_overlay("fire_overlay")
/obj/machinery/firealarm/update_overlays()
. = ..()
SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
. += "fire_overlay"
if(is_station_level(z))
add_overlay("fire_[GLOB.security_level]")
SSvis_overlays.add_vis_overlay(src, icon, "fire_[GLOB.security_level]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
. += "fire_[GLOB.security_level]"
SSvis_overlays.add_vis_overlay(src, icon, "fire_[GLOB.security_level]", EMISSIVE_LAYER, EMISSIVE_PLANE, dir)
else
add_overlay("fire_[SEC_LEVEL_GREEN]")
SSvis_overlays.add_vis_overlay(src, icon, "fire_[SEC_LEVEL_GREEN]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
. += "fire_[SEC_LEVEL_GREEN]"
SSvis_overlays.add_vis_overlay(src, icon, "fire_[SEC_LEVEL_GREEN]", EMISSIVE_LAYER, EMISSIVE_PLANE, dir)
var/area/A = src.loc
A = A.loc
if(!detecting || !A.fire)
add_overlay("fire_off")
SSvis_overlays.add_vis_overlay(src, icon, "fire_off", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
. += "fire_off"
SSvis_overlays.add_vis_overlay(src, icon, "fire_off", EMISSIVE_LAYER, EMISSIVE_PLANE, dir)
else if(obj_flags & EMAGGED)
add_overlay("fire_emagged")
SSvis_overlays.add_vis_overlay(src, icon, "fire_emagged", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
. += "fire_emagged"
SSvis_overlays.add_vis_overlay(src, icon, "fire_emagged", EMISSIVE_LAYER, EMISSIVE_PLANE, dir)
else
add_overlay("fire_on")
SSvis_overlays.add_vis_overlay(src, icon, "fire_on", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
. += "fire_on"
SSvis_overlays.add_vis_overlay(src, icon, "fire_on", EMISSIVE_LAYER, EMISSIVE_PLANE, dir)
/obj/machinery/firealarm/emp_act(severity)
. = ..()
@@ -124,7 +125,7 @@
if(!is_operational() || (last_alarm+FIREALARM_COOLDOWN > world.time))
return
last_alarm = world.time
var/area/A = get_area(src)
var/area/A = get_base_area(src)
A.firealert(src)
playsound(loc, 'goon/sound/machinery/FireAlarm.ogg', 75)
if(user)
@@ -133,7 +134,7 @@
/obj/machinery/firealarm/proc/reset(mob/user)
if(!is_operational())
return
var/area/A = get_area(src)
var/area/A = get_base_area(src)
A.firereset(src)
if(user)
log_game("[user] reset a fire alarm at [COORD(src)]")
@@ -142,7 +143,7 @@
if(buildstage != 2)
return ..()
add_fingerprint(user)
var/area/A = get_area(src)
var/area/A = get_base_area(src)
if(A.fire)
reset(user)
else
@@ -198,7 +199,7 @@
return
else if(W.force) //hit and turn it on
..()
var/area/A = get_area(src)
var/area/A = get_base_area(src)
if(!A.fire)
alarm()
return
@@ -322,7 +323,7 @@
/obj/machinery/firealarm/partyalarm/reset()
if (stat & (NOPOWER|BROKEN))
return
var/area/A = get_area(src)
var/area/A = get_base_area(src)
if (!A || !A.party)
return
A.party = FALSE
@@ -331,7 +332,7 @@
/obj/machinery/firealarm/partyalarm/alarm()
if (stat & (NOPOWER|BROKEN))
return
var/area/A = get_area(src)
var/area/A = get_base_area(src)
if (!A || A.party || A.name == "Space")
return
A.party = TRUE
+2 -2
View File
@@ -6,7 +6,7 @@
icon = 'icons/obj/stationobjs.dmi'
icon_state = "mflash1"
max_integrity = 250
integrity_failure = 100
integrity_failure = 0.4
light_color = LIGHT_COLOR_WHITE
light_power = FLASH_LIGHT_POWER
var/obj/item/assembly/flash/handheld/bulb
@@ -115,7 +115,7 @@
continue
if(L.flash_act(affect_silicon = 1))
L.Knockdown(strength)
L.DefaultCombatKnockdown(strength)
flashed = TRUE
if(flashed)
+1 -1
View File
@@ -31,7 +31,7 @@
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "gulag_item_reclaimer", name, 455, 440, master_ui, state)
ui = new(user, src, ui_key, "gulag_item_reclaimer", name, 300, 400, master_ui, state)
ui.open()
/obj/machinery/gulag_item_reclaimer/ui_data(mob/user)

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