Merge branch 'master' of https://github.com/Citadel-Station-13/Citadel-Station-13 into station_traits
This commit is contained in:
@@ -1,182 +0,0 @@
|
||||
GLOBAL_LIST_EMPTY(active_alternate_appearances)
|
||||
|
||||
|
||||
/atom
|
||||
var/list/alternate_appearances
|
||||
|
||||
/atom/proc/remove_alt_appearance(key)
|
||||
if(alternate_appearances)
|
||||
for(var/K in alternate_appearances)
|
||||
var/datum/atom_hud/alternate_appearance/AA = alternate_appearances[K]
|
||||
if(AA.appearance_key == key)
|
||||
AA.remove_from_hud(src)
|
||||
break
|
||||
|
||||
/atom/proc/add_alt_appearance(type, key, ...)
|
||||
if(!type || !key)
|
||||
return
|
||||
if(alternate_appearances && alternate_appearances[key])
|
||||
return
|
||||
var/list/arguments = args.Copy(2)
|
||||
new type(arglist(arguments))
|
||||
|
||||
/datum/atom_hud/alternate_appearance
|
||||
var/appearance_key
|
||||
|
||||
/datum/atom_hud/alternate_appearance/New(key)
|
||||
..()
|
||||
GLOB.active_alternate_appearances += src
|
||||
appearance_key = key
|
||||
|
||||
/datum/atom_hud/alternate_appearance/Destroy()
|
||||
GLOB.active_alternate_appearances -= src
|
||||
return ..()
|
||||
|
||||
/datum/atom_hud/alternate_appearance/proc/onNewMob(mob/M)
|
||||
if(mobShouldSee(M))
|
||||
add_hud_to(M)
|
||||
|
||||
/datum/atom_hud/alternate_appearance/proc/mobShouldSee(mob/M)
|
||||
return FALSE
|
||||
|
||||
/datum/atom_hud/alternate_appearance/add_to_hud(atom/A, image/I)
|
||||
. = ..()
|
||||
if(.)
|
||||
LAZYINITLIST(A.alternate_appearances)
|
||||
A.alternate_appearances[appearance_key] = src
|
||||
|
||||
/datum/atom_hud/alternate_appearance/remove_from_hud(atom/A)
|
||||
. = ..()
|
||||
if(.)
|
||||
LAZYREMOVE(A.alternate_appearances, appearance_key)
|
||||
|
||||
|
||||
//an alternate appearance that attaches a single image to a single atom
|
||||
/datum/atom_hud/alternate_appearance/basic
|
||||
var/atom/target
|
||||
var/image/theImage
|
||||
var/add_ghost_version = FALSE
|
||||
var/ghost_appearance
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/New(key, image/I, target_sees_appearance = TRUE)
|
||||
..()
|
||||
theImage = I
|
||||
target = I.loc
|
||||
hud_icons = list(appearance_key)
|
||||
add_to_hud(target, I)
|
||||
if(target_sees_appearance && ismob(target))
|
||||
add_hud_to(target)
|
||||
if(add_ghost_version)
|
||||
var/image/ghost_image = image(icon = I.icon , icon_state = I.icon_state, loc = I.loc)
|
||||
ghost_image.override = FALSE
|
||||
ghost_image.alpha = 128
|
||||
ghost_appearance = new /datum/atom_hud/alternate_appearance/basic/observers(key + "_observer", ghost_image, FALSE)
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/Destroy()
|
||||
. = ..()
|
||||
if(ghost_appearance)
|
||||
QDEL_NULL(ghost_appearance)
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/add_to_hud(atom/A)
|
||||
LAZYINITLIST(A.hud_list)
|
||||
A.hud_list[appearance_key] = theImage
|
||||
. = ..()
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/remove_from_hud(atom/A)
|
||||
. = ..()
|
||||
A.hud_list -= appearance_key
|
||||
if(. && !QDELETED(src))
|
||||
qdel(src)
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/everyone
|
||||
add_ghost_version = TRUE
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/everyone/New()
|
||||
..()
|
||||
for(var/mob in GLOB.mob_list)
|
||||
if(mobShouldSee(mob))
|
||||
add_hud_to(mob)
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/everyone/mobShouldSee(mob/M)
|
||||
return !isobserver(M)
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/silicons
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/silicons/New()
|
||||
..()
|
||||
for(var/mob in GLOB.silicon_mobs)
|
||||
if(mobShouldSee(mob))
|
||||
add_hud_to(mob)
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/silicons/mobShouldSee(mob/M)
|
||||
if(issilicon(M))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/observers
|
||||
add_ghost_version = FALSE //just in case, to prevent infinite loops
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/observers/New()
|
||||
..()
|
||||
for(var/mob in GLOB.dead_mob_list)
|
||||
if(mobShouldSee(mob))
|
||||
add_hud_to(mob)
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/observers/mobShouldSee(mob/M)
|
||||
return isobserver(M)
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/noncult
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/noncult/New()
|
||||
..()
|
||||
for(var/mob in GLOB.player_list)
|
||||
if(mobShouldSee(mob))
|
||||
add_hud_to(mob)
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/noncult/mobShouldSee(mob/M)
|
||||
if(!iscultist(M))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/cult
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/cult/New()
|
||||
..()
|
||||
for(var/mob in GLOB.player_list)
|
||||
if(mobShouldSee(mob))
|
||||
add_hud_to(mob)
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/cult/mobShouldSee(mob/M)
|
||||
if(iscultist(M))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/blessedAware
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/blessedAware/New()
|
||||
..()
|
||||
for(var/mob in GLOB.mob_list)
|
||||
if(mobShouldSee(mob))
|
||||
add_hud_to(mob)
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/blessedAware/mobShouldSee(mob/M)
|
||||
if(M.mind && (M.mind.assigned_role == "Chaplain"))
|
||||
return TRUE
|
||||
if (istype(M, /mob/living/simple_animal/hostile/construct/wraith))
|
||||
return TRUE
|
||||
if(isrevenant(M) || iseminence(M) || iswizard(M))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/onePerson
|
||||
var/mob/seer
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/onePerson/mobShouldSee(mob/M)
|
||||
if(M == seer)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/atom_hud/alternate_appearance/basic/onePerson/New(key, image/I, mob/living/M)
|
||||
..(key, I, FALSE)
|
||||
seer = M
|
||||
add_hud_to(seer)
|
||||
@@ -50,7 +50,12 @@
|
||||
|
||||
var/has_gravity = FALSE
|
||||
|
||||
var/parallax_movedir = 0
|
||||
/// Parallax moving?
|
||||
var/parallax_moving = FALSE
|
||||
/// Parallax move speed - 0 to disable
|
||||
var/parallax_move_speed = 0
|
||||
/// Parallax move dir - degrees clockwise from north
|
||||
var/parallax_move_angle = 0
|
||||
|
||||
var/list/ambientsounds = GENERIC
|
||||
flags_1 = CAN_BE_DIRTY_1
|
||||
|
||||
+15
-4
@@ -1215,7 +1215,7 @@
|
||||
|
||||
if(user != target)
|
||||
var/reverse_message = "has been [what_done] by [ssource][postfix]"
|
||||
target.log_message(reverse_message, LOG_ATTACK, color="orange", log_globally=FALSE)
|
||||
target.log_message(reverse_message, LOG_VICTIM, color="orange", log_globally=FALSE)
|
||||
|
||||
/**
|
||||
* log_wound() is for when someone is *attacked* and suffers a wound. Note that this only captures wounds from damage, so smites/forced wounds aren't logged, as well as demotions like cuts scabbing over
|
||||
@@ -1291,9 +1291,7 @@
|
||||
|
||||
/obj/item/update_filters()
|
||||
. = ..()
|
||||
for(var/X in actions)
|
||||
var/datum/action/A = X
|
||||
A.UpdateButtonIcon()
|
||||
update_action_buttons()
|
||||
|
||||
/atom/proc/get_filter(name)
|
||||
if(filter_data && filter_data[name])
|
||||
@@ -1404,3 +1402,16 @@
|
||||
*/
|
||||
/atom/proc/setClosed()
|
||||
return
|
||||
|
||||
//Update the screentip to reflect what we're hoverin over
|
||||
/atom/MouseEntered(location, control, params)
|
||||
. = ..()
|
||||
// Screentips
|
||||
var/client/client = usr?.client
|
||||
var/datum/hud/active_hud = client?.mob?.hud_used
|
||||
if(active_hud)
|
||||
if(!client.prefs.screentip_pref || (flags_1 & NO_SCREENTIPS_1))
|
||||
active_hud.screentip_text.maptext = ""
|
||||
else
|
||||
//We inline a MAPTEXT() here, because there's no good way to statically add to a string like this
|
||||
active_hud.screentip_text.maptext = MAPTEXT("<span style='text-align: center; font-size: 32px; color: [client?.prefs?.screentip_color]'>[name]</span>")
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
var/pass_flags = 0
|
||||
var/moving_diagonally = 0 //0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move
|
||||
var/atom/movable/moving_from_pull //attempt to resume grab after moving instead of before.
|
||||
var/list/client_mobs_in_contents // This contains all the client mobs within this container
|
||||
var/list/acted_explosions //for explosion dodging
|
||||
var/datum/forced_movement/force_moving = null //handled soley by forced_movement.dm
|
||||
|
||||
@@ -108,8 +107,6 @@
|
||||
for(var/movable_content in contents)
|
||||
qdel(movable_content)
|
||||
|
||||
LAZYCLEARLIST(client_mobs_in_contents)
|
||||
|
||||
moveToNullspace()
|
||||
|
||||
/atom/movable/proc/update_emissive_block()
|
||||
|
||||
@@ -48,8 +48,17 @@
|
||||
continue
|
||||
var/atom/movable/thing = i
|
||||
thing.Crossed(src)
|
||||
//
|
||||
////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* meant for movement with zero side effects. only use for objects that are supposed to move "invisibly" (like camera mobs or ghosts)
|
||||
* if you want something to move onto a tile with a beartrap or recycler or tripmine or mouse without that object knowing about it at all, use this
|
||||
* most of the time you want forceMove()
|
||||
*/
|
||||
/atom/movable/proc/abstract_move(atom/new_loc)
|
||||
var/atom/old_loc = loc
|
||||
// move_stacks++
|
||||
loc = new_loc
|
||||
Moved(old_loc)
|
||||
|
||||
/atom/movable/Move(atom/newloc, direct, glide_size_override = 0)
|
||||
var/atom/movable/pullee = pulling
|
||||
@@ -167,12 +176,8 @@
|
||||
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)
|
||||
|
||||
@@ -193,7 +193,9 @@ GLOBAL_LIST_INIT(reverseradiochannels, list(
|
||||
var/frequency = 0
|
||||
var/transmission_method
|
||||
var/list/data
|
||||
var/logging_data
|
||||
|
||||
/datum/signal/New(data, transmission_method = TRANSMISSION_RADIO)
|
||||
/datum/signal/New(data, transmission_method = TRANSMISSION_RADIO, logging_data = null)
|
||||
src.data = data || list()
|
||||
src.transmission_method = transmission_method
|
||||
src.logging_data = logging_data
|
||||
|
||||
@@ -1,523 +0,0 @@
|
||||
/*
|
||||
* Data HUDs have been rewritten in a more generic way.
|
||||
* In short, they now use an observer-listener pattern.
|
||||
* See code/datum/hud.dm for the generic hud datum.
|
||||
* Update the HUD icons when needed with the appropriate hook. (see below)
|
||||
*/
|
||||
|
||||
/* DATA HUD DATUMS */
|
||||
|
||||
/atom/proc/add_to_all_human_data_huds()
|
||||
for(var/datum/atom_hud/data/human/hud in GLOB.huds)
|
||||
hud.add_to_hud(src)
|
||||
|
||||
/atom/proc/remove_from_all_data_huds()
|
||||
for(var/datum/atom_hud/data/hud in GLOB.huds)
|
||||
hud.remove_from_hud(src)
|
||||
|
||||
/datum/atom_hud/data
|
||||
|
||||
/datum/atom_hud/data/human/medical
|
||||
hud_icons = list(STATUS_HUD, HEALTH_HUD, NANITE_HUD, RAD_HUD)
|
||||
|
||||
/datum/atom_hud/data/human/medical/basic
|
||||
|
||||
/datum/atom_hud/data/human/medical/basic/proc/check_sensors(mob/living/carbon/human/H)
|
||||
if(!istype(H))
|
||||
return 0
|
||||
var/obj/item/clothing/under/U = H.w_uniform
|
||||
if(!istype(U))
|
||||
return 0
|
||||
if(U.sensor_mode <= SENSOR_VITALS)
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/datum/atom_hud/data/human/medical/basic/add_to_single_hud(mob/M, mob/living/carbon/H)
|
||||
if(check_sensors(H))
|
||||
..()
|
||||
|
||||
/datum/atom_hud/data/human/medical/basic/proc/update_suit_sensors(mob/living/carbon/H)
|
||||
check_sensors(H) ? add_to_hud(H) : remove_from_hud(H)
|
||||
|
||||
/datum/atom_hud/data/human/medical/advanced
|
||||
|
||||
/datum/atom_hud/data/human/security
|
||||
|
||||
/datum/atom_hud/data/human/security/basic
|
||||
hud_icons = list(ID_HUD)
|
||||
|
||||
/datum/atom_hud/data/human/security/advanced
|
||||
hud_icons = list(ID_HUD, IMPTRACK_HUD, IMPLOYAL_HUD, IMPCHEM_HUD, WANTED_HUD, NANITE_HUD)
|
||||
|
||||
/datum/atom_hud/data/diagnostic
|
||||
|
||||
/datum/atom_hud/data/diagnostic/basic
|
||||
hud_icons = list(DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD, DIAG_CIRCUIT_HUD, DIAG_TRACK_HUD, DIAG_AIRLOCK_HUD, DIAG_NANITE_FULL_HUD)
|
||||
|
||||
/datum/atom_hud/data/diagnostic/advanced
|
||||
hud_icons = list(DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD, DIAG_CIRCUIT_HUD, DIAG_TRACK_HUD, DIAG_AIRLOCK_HUD, DIAG_NANITE_FULL_HUD, DIAG_PATH_HUD)
|
||||
|
||||
/datum/atom_hud/data/bot_path
|
||||
hud_icons = list(DIAG_PATH_HUD)
|
||||
|
||||
/datum/atom_hud/abductor
|
||||
hud_icons = list(GLAND_HUD)
|
||||
|
||||
/datum/atom_hud/sentient_disease
|
||||
hud_icons = list(SENTIENT_DISEASE_HUD)
|
||||
|
||||
/datum/atom_hud/ai_detector
|
||||
hud_icons = list(AI_DETECT_HUD)
|
||||
|
||||
/datum/atom_hud/ai_detector/add_hud_to(mob/M)
|
||||
..()
|
||||
if(M && (hudusers.len == 1))
|
||||
for(var/V in GLOB.aiEyes)
|
||||
var/mob/camera/aiEye/E = V
|
||||
E.update_ai_detect_hud()
|
||||
|
||||
/* MED/SEC/DIAG HUD HOOKS */
|
||||
|
||||
/*
|
||||
* THESE HOOKS SHOULD BE CALLED BY THE MOB SHOWING THE HUD
|
||||
*/
|
||||
|
||||
/***********************************************
|
||||
Medical HUD! Basic mode needs suit sensors on.
|
||||
************************************************/
|
||||
|
||||
//HELPERS
|
||||
|
||||
//called when a carbon changes virus
|
||||
/mob/living/carbon/proc/check_virus()
|
||||
var/threat
|
||||
for(var/thing in diseases)
|
||||
var/datum/disease/D = thing
|
||||
if(!(D.visibility_flags & HIDDEN_SCANNER))
|
||||
if(!threat || D.severity > threat) //a buffing virus gets an icon
|
||||
threat = D.severity
|
||||
return threat
|
||||
|
||||
//helper for getting the appropriate health status
|
||||
/proc/RoundHealth(mob/living/M)
|
||||
if(M.stat == DEAD || (HAS_TRAIT(M, TRAIT_FAKEDEATH)))
|
||||
return "health-100" //what's our health? it doesn't matter, we're dead, or faking
|
||||
var/maxi_health = M.maxHealth
|
||||
if(iscarbon(M) && M.health < 0)
|
||||
maxi_health = 100 //so crit shows up right for aliens and other high-health carbon mobs; noncarbons don't have crit.
|
||||
var/resulthealth = (M.health / maxi_health) * 100
|
||||
switch(resulthealth)
|
||||
if(100 to INFINITY)
|
||||
return "health100"
|
||||
if(90.625 to 100)
|
||||
return "health93.75"
|
||||
if(84.375 to 90.625)
|
||||
return "health87.5"
|
||||
if(78.125 to 84.375)
|
||||
return "health81.25"
|
||||
if(71.875 to 78.125)
|
||||
return "health75"
|
||||
if(65.625 to 71.875)
|
||||
return "health68.75"
|
||||
if(59.375 to 65.625)
|
||||
return "health62.5"
|
||||
if(53.125 to 59.375)
|
||||
return "health56.25"
|
||||
if(46.875 to 53.125)
|
||||
return "health50"
|
||||
if(40.625 to 46.875)
|
||||
return "health43.75"
|
||||
if(34.375 to 40.625)
|
||||
return "health37.5"
|
||||
if(28.125 to 34.375)
|
||||
return "health31.25"
|
||||
if(21.875 to 28.125)
|
||||
return "health25"
|
||||
if(15.625 to 21.875)
|
||||
return "health18.75"
|
||||
if(9.375 to 15.625)
|
||||
return "health12.5"
|
||||
if(1 to 9.375)
|
||||
return "health6.25"
|
||||
if(-50 to 1)
|
||||
return "health0"
|
||||
if(-85 to -50)
|
||||
return "health-50"
|
||||
if(-99 to -85)
|
||||
return "health-85"
|
||||
else
|
||||
return "health-100"
|
||||
|
||||
//HOOKS
|
||||
|
||||
//called when a human changes suit sensors
|
||||
/mob/living/carbon/proc/update_suit_sensors()
|
||||
var/datum/atom_hud/data/human/medical/basic/B = GLOB.huds[DATA_HUD_MEDICAL_BASIC]
|
||||
B.update_suit_sensors(src)
|
||||
|
||||
//called when a living mob changes health
|
||||
/mob/living/proc/med_hud_set_health()
|
||||
var/image/holder = hud_list[HEALTH_HUD]
|
||||
holder.icon_state = "hud[RoundHealth(src)]"
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
med_hud_set_radstatus()
|
||||
|
||||
//for carbon suit sensors
|
||||
/mob/living/carbon/med_hud_set_health()
|
||||
..()
|
||||
|
||||
//called when a carbon changes stat, virus or XENO_HOST
|
||||
/mob/living/proc/med_hud_set_status()
|
||||
var/image/holder = hud_list[STATUS_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
if(stat == DEAD || (HAS_TRAIT(src, TRAIT_FAKEDEATH)))
|
||||
holder.icon_state = "huddead"
|
||||
else
|
||||
holder.icon_state = "hudhealthy"
|
||||
|
||||
/mob/living/carbon/med_hud_set_status()
|
||||
var/image/holder = hud_list[STATUS_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
var/virus_threat = check_virus()
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
if(HAS_TRAIT(src, TRAIT_XENO_HOST))
|
||||
holder.icon_state = "hudxeno"
|
||||
else if(stat == DEAD || (HAS_TRAIT(src, TRAIT_FAKEDEATH)))
|
||||
if(tod)
|
||||
var/tdelta = round(world.time - timeofdeath)
|
||||
if(tdelta < (DEFIB_TIME_LIMIT * 10))
|
||||
var/obj/item/organ/heart/He = getorgan(/obj/item/organ/heart)
|
||||
if(He)
|
||||
holder.icon_state = "huddefib"
|
||||
if(He.organ_flags & ORGAN_FAILING)
|
||||
holder.icon_state = "huddefibheart"
|
||||
else
|
||||
holder.icon_state = "huddefibheart"
|
||||
return
|
||||
holder.icon_state = "huddead"
|
||||
else
|
||||
switch(virus_threat)
|
||||
if(DISEASE_SEVERITY_BIOHAZARD)
|
||||
holder.icon_state = "hudill5"
|
||||
if(DISEASE_SEVERITY_DANGEROUS)
|
||||
holder.icon_state = "hudill4"
|
||||
if(DISEASE_SEVERITY_HARMFUL)
|
||||
holder.icon_state = "hudill3"
|
||||
if(DISEASE_SEVERITY_MEDIUM)
|
||||
holder.icon_state = "hudill2"
|
||||
if(DISEASE_SEVERITY_MINOR)
|
||||
holder.icon_state = "hudill1"
|
||||
if(DISEASE_SEVERITY_NONTHREAT)
|
||||
holder.icon_state = "hudill0"
|
||||
if(DISEASE_SEVERITY_POSITIVE)
|
||||
holder.icon_state = "hudbuff"
|
||||
if(null)
|
||||
holder.icon_state = "hudhealthy"
|
||||
|
||||
|
||||
/mob/living/proc/med_hud_set_radstatus()
|
||||
var/image/radholder = hud_list[RAD_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
radholder.pixel_y = I.Height() - world.icon_size
|
||||
var/mob/living/M = src
|
||||
var/rads = M.radiation
|
||||
switch(rads)
|
||||
if(-INFINITY to RAD_MOB_SAFE)
|
||||
radholder.icon_state = "hudradsafe"
|
||||
if((RAD_MOB_SAFE+1) to RAD_MOB_MUTATE)
|
||||
radholder.icon_state = "hudraddanger"
|
||||
if((RAD_MOB_MUTATE+1) to RAD_MOB_VOMIT)
|
||||
radholder.icon_state = "hudradlethal"
|
||||
if((RAD_MOB_VOMIT+1) to INFINITY)
|
||||
radholder.icon_state = "hudradnuke"
|
||||
|
||||
/***********************************************
|
||||
Security HUDs! Basic mode shows only the job.
|
||||
************************************************/
|
||||
|
||||
//HOOKS
|
||||
|
||||
/mob/living/carbon/human/proc/sec_hud_set_ID()
|
||||
var/image/holder = hud_list[ID_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
holder.icon_state = "hudno_id"
|
||||
if(wear_id?.GetID())
|
||||
holder.icon_state = "hud[ckey(wear_id.GetJobName())]"
|
||||
sec_hud_set_security_status()
|
||||
|
||||
/mob/living/proc/sec_hud_set_implants()
|
||||
var/image/holder
|
||||
for(var/i in list(IMPTRACK_HUD, IMPLOYAL_HUD, IMPCHEM_HUD))
|
||||
holder = hud_list[i]
|
||||
holder.icon_state = null
|
||||
for(var/obj/item/implant/I in implants)
|
||||
if(istype(I, /obj/item/implant/tracking))
|
||||
holder = hud_list[IMPTRACK_HUD]
|
||||
var/icon/IC = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = IC.Height() - world.icon_size
|
||||
holder.icon_state = "hud_imp_tracking"
|
||||
else if(istype(I, /obj/item/implant/chem))
|
||||
holder = hud_list[IMPCHEM_HUD]
|
||||
var/icon/IC = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = IC.Height() - world.icon_size
|
||||
holder.icon_state = "hud_imp_chem"
|
||||
if(HAS_TRAIT(src, TRAIT_MINDSHIELD))
|
||||
holder = hud_list[IMPLOYAL_HUD]
|
||||
var/icon/IC = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = IC.Height() - world.icon_size
|
||||
holder.icon_state = "hud_imp_loyal"
|
||||
|
||||
/mob/living/carbon/human/proc/sec_hud_set_security_status()
|
||||
var/image/holder = hud_list[WANTED_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
var/perpname = get_face_name(get_id_name(""))
|
||||
if(perpname && GLOB.data_core)
|
||||
var/datum/data/record/R = find_record("name", perpname, GLOB.data_core.security)
|
||||
if(R)
|
||||
switch(R.fields["criminal"])
|
||||
if("*Arrest*")
|
||||
holder.icon_state = "hudwanted"
|
||||
return
|
||||
if("Incarcerated")
|
||||
holder.icon_state = "hudincarcerated"
|
||||
return
|
||||
if("Paroled")
|
||||
holder.icon_state = "hudparolled"
|
||||
return
|
||||
if("Discharged")
|
||||
holder.icon_state = "huddischarged"
|
||||
return
|
||||
holder.icon_state = null
|
||||
|
||||
/***********************************************
|
||||
Diagnostic HUDs!
|
||||
************************************************/
|
||||
|
||||
/mob/living/proc/hud_set_nanite_indicator()
|
||||
var/image/holder = hud_list[NANITE_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
holder.icon_state = null
|
||||
if(src in SSnanites.nanite_monitored_mobs)
|
||||
holder.icon_state = "nanite_ping"
|
||||
|
||||
//For Diag health and cell bars!
|
||||
/proc/RoundDiagBar(value)
|
||||
switch(value * 100)
|
||||
if(95 to INFINITY)
|
||||
return "max"
|
||||
if(80 to 100)
|
||||
return "good"
|
||||
if(60 to 80)
|
||||
return "high"
|
||||
if(40 to 60)
|
||||
return "med"
|
||||
if(20 to 40)
|
||||
return "low"
|
||||
if(1 to 20)
|
||||
return "crit"
|
||||
else
|
||||
return "dead"
|
||||
|
||||
//Sillycone hooks
|
||||
/mob/living/silicon/proc/diag_hud_set_health()
|
||||
var/image/holder = hud_list[DIAG_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
if(stat == DEAD)
|
||||
holder.icon_state = "huddiagdead"
|
||||
else
|
||||
holder.icon_state = "huddiag[RoundDiagBar(health/maxHealth)]"
|
||||
|
||||
/mob/living/silicon/proc/diag_hud_set_status()
|
||||
var/image/holder = hud_list[DIAG_STAT_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
switch(stat)
|
||||
if(CONSCIOUS)
|
||||
holder.icon_state = "hudstat"
|
||||
if(UNCONSCIOUS)
|
||||
holder.icon_state = "hudoffline"
|
||||
else
|
||||
holder.icon_state = "huddead2"
|
||||
|
||||
//Borgie battery tracking!
|
||||
/mob/living/silicon/robot/proc/diag_hud_set_borgcell()
|
||||
var/image/holder = hud_list[DIAG_BATT_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
if(cell)
|
||||
var/chargelvl = (cell.charge/cell.maxcharge)
|
||||
holder.icon_state = "hudbatt[RoundDiagBar(chargelvl)]"
|
||||
else
|
||||
holder.icon_state = "hudnobatt"
|
||||
|
||||
//borg-AI shell tracking
|
||||
/mob/living/silicon/robot/proc/diag_hud_set_aishell() //Shows tracking beacons on the mech
|
||||
var/image/holder = hud_list[DIAG_TRACK_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
if(!shell) //Not an AI shell
|
||||
holder.icon_state = null
|
||||
else if(deployed) //AI shell in use by an AI
|
||||
holder.icon_state = "hudtrackingai"
|
||||
else //Empty AI shell
|
||||
holder.icon_state = "hudtracking"
|
||||
|
||||
//AI side tracking of AI shell control
|
||||
/mob/living/silicon/ai/proc/diag_hud_set_deployed() //Shows tracking beacons on the mech
|
||||
var/image/holder = hud_list[DIAG_TRACK_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
if(!deployed_shell)
|
||||
holder.icon_state = null
|
||||
else //AI is currently controlling a shell
|
||||
holder.icon_state = "hudtrackingai"
|
||||
|
||||
/*~~~~~~~~~~~~~~~~~~~~
|
||||
BIG STOMPY MECHS
|
||||
~~~~~~~~~~~~~~~~~~~~~*/
|
||||
/obj/mecha/proc/diag_hud_set_mechhealth()
|
||||
var/image/holder = hud_list[DIAG_MECH_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
holder.icon_state = "huddiag[RoundDiagBar(obj_integrity/max_integrity)]"
|
||||
|
||||
|
||||
/obj/mecha/proc/diag_hud_set_mechcell()
|
||||
var/image/holder = hud_list[DIAG_BATT_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
if(cell)
|
||||
var/chargelvl = cell.charge/cell.maxcharge
|
||||
holder.icon_state = "hudbatt[RoundDiagBar(chargelvl)]"
|
||||
else
|
||||
holder.icon_state = "hudnobatt"
|
||||
|
||||
|
||||
/obj/mecha/proc/diag_hud_set_mechstat()
|
||||
var/image/holder = hud_list[DIAG_STAT_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
holder.icon_state = null
|
||||
if(internal_damage)
|
||||
holder.icon_state = "hudwarn"
|
||||
|
||||
/obj/mecha/proc/diag_hud_set_mechtracking() //Shows tracking beacons on the mech
|
||||
var/image/holder = hud_list[DIAG_TRACK_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
var/new_icon_state //This var exists so that the holder's icon state is set only once in the event of multiple mech beacons.
|
||||
for(var/obj/item/mecha_parts/mecha_tracking/T in trackers)
|
||||
if(T.ai_beacon) //Beacon with AI uplink
|
||||
new_icon_state = "hudtrackingai"
|
||||
break //Immediately terminate upon finding an AI beacon to ensure it is always shown over the normal one, as mechs can have several trackers.
|
||||
else
|
||||
new_icon_state = "hudtracking"
|
||||
holder.icon_state = new_icon_state
|
||||
|
||||
/*~~~~~~~~~
|
||||
Bots!
|
||||
~~~~~~~~~~*/
|
||||
/mob/living/simple_animal/bot/proc/diag_hud_set_bothealth()
|
||||
var/image/holder = hud_list[DIAG_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
holder.icon_state = "huddiag[RoundDiagBar(health/maxHealth)]"
|
||||
|
||||
/mob/living/simple_animal/bot/proc/diag_hud_set_botstat() //On (With wireless on or off), Off, EMP'ed
|
||||
var/image/holder = hud_list[DIAG_STAT_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
if(on)
|
||||
holder.icon_state = "hudstat"
|
||||
else if(stat) //Generally EMP causes this
|
||||
holder.icon_state = "hudoffline"
|
||||
else //Bot is off
|
||||
holder.icon_state = "huddead2"
|
||||
|
||||
/mob/living/simple_animal/bot/proc/diag_hud_set_botmode() //Shows a bot's current operation
|
||||
var/image/holder = hud_list[DIAG_BOT_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
if(client) //If the bot is player controlled, it will not be following mode logic!
|
||||
holder.icon_state = "hudsentient"
|
||||
return
|
||||
|
||||
switch(mode)
|
||||
if(BOT_SUMMON, BOT_RESPONDING) //Responding to PDA or AI summons
|
||||
holder.icon_state = "hudcalled"
|
||||
if(BOT_CLEANING, BOT_REPAIRING, BOT_HEALING) //Cleanbot cleaning, Floorbot fixing, or Medibot Healing
|
||||
holder.icon_state = "hudworking"
|
||||
if(BOT_PATROL, BOT_START_PATROL) //Patrol mode
|
||||
holder.icon_state = "hudpatrol"
|
||||
if(BOT_PREP_ARREST, BOT_ARREST, BOT_HUNT) //STOP RIGHT THERE, CRIMINAL SCUM!
|
||||
holder.icon_state = "hudalert"
|
||||
if(BOT_MOVING, BOT_DELIVER, BOT_GO_HOME, BOT_NAV) //Moving to target for normal bots, moving to deliver or go home for MULES.
|
||||
holder.icon_state = "hudmove"
|
||||
else
|
||||
holder.icon_state = ""
|
||||
|
||||
/*~~~~~~~~~~~~
|
||||
Circutry!
|
||||
~~~~~~~~~~~~~*/
|
||||
/obj/item/electronic_assembly/proc/diag_hud_set_circuithealth(hide = FALSE)
|
||||
var/image/holder = hud_list[DIAG_CIRCUIT_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
if((!isturf(loc))||hide) //if not on the ground dont show overlay
|
||||
holder.icon_state = null
|
||||
else
|
||||
holder.icon_state = "huddiag[RoundDiagBar(obj_integrity/max_integrity)]"
|
||||
|
||||
/obj/item/electronic_assembly/proc/diag_hud_set_circuitcell(hide = FALSE)
|
||||
var/image/holder = hud_list[DIAG_BATT_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
if((!isturf(loc))||hide) //if not on the ground dont show overlay
|
||||
holder.icon_state = null
|
||||
else if(battery)
|
||||
var/chargelvl = battery.charge/battery.maxcharge
|
||||
holder.icon_state = "hudbatt[RoundDiagBar(chargelvl)]"
|
||||
else
|
||||
holder.icon_state = "hudnobatt"
|
||||
|
||||
/obj/item/electronic_assembly/proc/diag_hud_set_circuitstat(hide = FALSE) //On, On and dangerous, or Off
|
||||
var/image/holder = hud_list[DIAG_STAT_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
if((!isturf(loc))||hide) //if not on the ground don't show overlay
|
||||
holder.icon_state = null
|
||||
else if(!battery)
|
||||
holder.icon_state = "hudoffline"
|
||||
else if(battery.charge == 0)
|
||||
holder.icon_state = "hudoffline"
|
||||
else if(combat_circuits) //has a circuit that can harm people
|
||||
holder.icon_state = prefered_hud_icon + "-red"
|
||||
else //Bot is on and not dangerous
|
||||
holder.icon_state = prefered_hud_icon
|
||||
|
||||
/obj/item/electronic_assembly/proc/diag_hud_set_circuittracking(hide = FALSE)
|
||||
var/image/holder = hud_list[DIAG_TRACK_HUD]
|
||||
var/icon/I = icon(icon, icon_state, dir)
|
||||
holder.pixel_y = I.Height() - world.icon_size
|
||||
if((!isturf(loc))||hide) //if not on the ground dont show overlay
|
||||
holder.icon_state = null
|
||||
else if(long_range_circuits)
|
||||
holder.icon_state = "hudtracking"
|
||||
else
|
||||
holder.icon_state = null
|
||||
|
||||
/*~~~~~~~~~~~~
|
||||
Airlocks!
|
||||
~~~~~~~~~~~~~*/
|
||||
/obj/machinery/door/airlock/proc/diag_hud_set_electrified()
|
||||
var/image/holder = hud_list[DIAG_AIRLOCK_HUD]
|
||||
if(secondsElectrified != 0)
|
||||
holder.icon_state = "electrified"
|
||||
else
|
||||
holder.icon_state = ""
|
||||
@@ -1,6 +1,6 @@
|
||||
GLOBAL_LIST_INIT(possible_changeling_IDs, list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega"))
|
||||
GLOBAL_LIST_INIT(slots, list("head", "wear_mask", "back", "wear_suit", "w_uniform", "shoes", "belt", "gloves", "glasses", "ears", "wear_id", "s_store"))
|
||||
GLOBAL_LIST_INIT(slot2slot, list("head" = SLOT_HEAD, "wear_mask" = SLOT_WEAR_MASK, "neck" = SLOT_NECK, "back" = SLOT_BACK, "wear_suit" = SLOT_WEAR_SUIT, "w_uniform" = SLOT_W_UNIFORM, "shoes" = SLOT_SHOES, "belt" = SLOT_BELT, "gloves" = SLOT_GLOVES, "glasses" = SLOT_GLASSES, "ears" = SLOT_EARS, "wear_id" = SLOT_WEAR_ID, "s_store" = SLOT_S_STORE))
|
||||
GLOBAL_LIST_INIT(slot2slot, list("head" = ITEM_SLOT_HEAD, "wear_mask" = ITEM_SLOT_MASK, "neck" = ITEM_SLOT_NECK, "back" = ITEM_SLOT_BACK, "wear_suit" = ITEM_SLOT_OCLOTHING, "w_uniform" = ITEM_SLOT_ICLOTHING, "shoes" = ITEM_SLOT_FEET, "belt" = ITEM_SLOT_BELT, "gloves" = ITEM_SLOT_GLOVES, "glasses" = ITEM_SLOT_EYES, "ears" = ITEM_SLOT_EARS, "wear_id" = ITEM_SLOT_ID, "s_store" = ITEM_SLOT_SUITSTORE))
|
||||
GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "wear_mask" = /obj/item/clothing/mask/changeling, "back" = /obj/item/changeling, "wear_suit" = /obj/item/clothing/suit/changeling, "w_uniform" = /obj/item/clothing/under/changeling, "shoes" = /obj/item/clothing/shoes/changeling, "belt" = /obj/item/changeling, "gloves" = /obj/item/clothing/gloves/changeling, "glasses" = /obj/item/clothing/glasses/changeling, "ears" = /obj/item/changeling, "wear_id" = /obj/item/changeling, "s_store" = /obj/item/changeling))
|
||||
GLOBAL_VAR(changeling_team_objective_type) //If this is not null, we hand our this objective to all lings
|
||||
|
||||
|
||||
@@ -205,11 +205,11 @@ Credit where due:
|
||||
var/mob/living/carbon/human/L = M
|
||||
var/obj/item/clockwork/slab/S = new
|
||||
var/slot = "At your feet"
|
||||
var/list/slots = list("In your left pocket" = SLOT_L_STORE, "In your right pocket" = SLOT_R_STORE, "In your backpack" = SLOT_IN_BACKPACK)
|
||||
var/list/slots = list("In your left pocket" = ITEM_SLOT_LPOCKET, "In your right pocket" = ITEM_SLOT_RPOCKET, "In your backpack" = ITEM_SLOT_BACKPACK)
|
||||
if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
var/obj/item/clockwork/replica_fabricator/F = new
|
||||
if(H.equip_to_slot_or_del(F, SLOT_IN_BACKPACK))
|
||||
if(H.equip_to_slot_or_del(F, ITEM_SLOT_BACKPACK))
|
||||
to_chat(H, "<span class='brass'>You have been equipped with a replica fabricator, an advanced tool that can convert objects like doors, tables or even coats into clockwork equivalents.</span>")
|
||||
slot = H.equip_in_one_of_slots(S, slots)
|
||||
if(slot == "In your backpack")
|
||||
|
||||
@@ -37,17 +37,17 @@
|
||||
if(!H.w_uniform || H.dropItemToGround(H.w_uniform))
|
||||
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)
|
||||
H.equip_to_slot_or_del(C, ITEM_SLOT_ICLOTHING)
|
||||
|
||||
if(!H.shoes || H.dropItemToGround(H.shoes))
|
||||
C = new /obj/item/clothing/shoes/clown_shoes(H)
|
||||
ADD_TRAIT(C, TRAIT_NODROP, CLOWN_NUKE_TRAIT)
|
||||
H.equip_to_slot_or_del(C, SLOT_SHOES)
|
||||
H.equip_to_slot_or_del(C, ITEM_SLOT_FEET)
|
||||
|
||||
if(!H.wear_mask || H.dropItemToGround(H.wear_mask))
|
||||
C = new /obj/item/clothing/mask/gas/clown_hat(H)
|
||||
ADD_TRAIT(C, TRAIT_NODROP, CLOWN_NUKE_TRAIT)
|
||||
H.equip_to_slot_or_del(C, SLOT_WEAR_MASK)
|
||||
H.equip_to_slot_or_del(C, ITEM_SLOT_MASK)
|
||||
|
||||
H.dna.add_mutation(CLOWNMUT)
|
||||
H.dna.add_mutation(SMILE)
|
||||
|
||||
@@ -214,7 +214,7 @@
|
||||
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()
|
||||
C.equip_to_slot_or_del(the_stash, SLOT_WEAR_MASK, TRUE, TRUE, TRUE, TRUE)
|
||||
C.equip_to_slot_or_del(the_stash, ITEM_SLOT_MASK, TRUE, TRUE, TRUE, TRUE)
|
||||
|
||||
/obj/item/clothing/mask/fakemoustache/sticky
|
||||
var/unstick_time = 2 MINUTES
|
||||
@@ -226,7 +226,7 @@
|
||||
|
||||
/obj/item/clothing/mask/fakemoustache/sticky/equipped(mob/user, slot)
|
||||
. = ..()
|
||||
if(slot == SLOT_WEAR_MASK)
|
||||
if(slot == ITEM_SLOT_MASK)
|
||||
ADD_TRAIT(user, TRAIT_NO_INTERNALS, STICKY_MOUSTACHE_TRAIT)
|
||||
|
||||
/obj/item/clothing/mask/fakemoustache/sticky/dropped(mob/user)
|
||||
|
||||
@@ -242,6 +242,69 @@
|
||||
log_game("DYNAMIC: [key_name(M)] was selected by the [name] ruleset and has been made into a midround traitor.")
|
||||
return TRUE
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
// FAMILIES //
|
||||
// //
|
||||
//////////////////////////////////////////////
|
||||
|
||||
/datum/dynamic_ruleset/midround/families
|
||||
name = "Family Head Aspirants"
|
||||
persistent = TRUE
|
||||
antag_datum = /datum/antagonist/gang
|
||||
antag_flag = ROLE_FAMILY_HEAD_ASPIRANT
|
||||
antag_flag_override = ROLE_FAMILIES
|
||||
protected_roles = list("Prisoner", "Head of Personnel")
|
||||
restricted_roles = list("AI", "Cyborg", "Prisoner", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Chaplain", "Head of Personnel", "Quartermaster", "Chief Engineer", "Chief Medical Officer", "Research Director")
|
||||
required_candidates = 9
|
||||
weight = 3
|
||||
cost = 15
|
||||
requirements = list(101,101,101,50,30,20,10,10,10,10)
|
||||
flags = HIGH_IMPACT_RULESET
|
||||
blocking_rules = list(/datum/dynamic_ruleset/roundstart/families)
|
||||
/// A reference to the handler that is used to run pre_execute(), execute(), etc..
|
||||
var/datum/gang_handler/handler
|
||||
|
||||
/datum/dynamic_ruleset/midround/families/trim_candidates()
|
||||
..()
|
||||
candidates = living_players
|
||||
for(var/mob/living/player in candidates)
|
||||
if(issilicon(player))
|
||||
candidates -= player
|
||||
else if(is_centcom_level(player.z))
|
||||
candidates -= player
|
||||
else if(player.mind && (player.mind.special_role || player.mind.antag_datums?.len > 0))
|
||||
candidates -= player
|
||||
else if(HAS_TRAIT(player, TRAIT_MINDSHIELD))
|
||||
candidates -= player
|
||||
|
||||
|
||||
/datum/dynamic_ruleset/midround/families/ready(forced = FALSE)
|
||||
if (required_candidates > living_players.len)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/dynamic_ruleset/midround/families/pre_execute()
|
||||
..()
|
||||
handler = new /datum/gang_handler(candidates,restricted_roles)
|
||||
handler.gang_balance_cap = clamp((indice_pop - 3), 2, 5) // gang_balance_cap by indice_pop: (2,2,2,2,2,3,4,5,5,5)
|
||||
handler.midround_ruleset = TRUE
|
||||
handler.use_dynamic_timing = TRUE
|
||||
return handler.pre_setup_analogue()
|
||||
|
||||
/datum/dynamic_ruleset/midround/families/execute()
|
||||
return handler.post_setup_analogue(TRUE)
|
||||
|
||||
/datum/dynamic_ruleset/midround/families/clean_up()
|
||||
QDEL_NULL(handler)
|
||||
..()
|
||||
|
||||
/datum/dynamic_ruleset/midround/families/rule_process()
|
||||
return handler.process_analogue()
|
||||
|
||||
/datum/dynamic_ruleset/midround/families/round_result()
|
||||
return handler.set_round_result_analogue()
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
// Malfunctioning AI //
|
||||
|
||||
@@ -485,6 +485,47 @@
|
||||
SSticker.mode_result = "loss - servants failed their objective (summon ratvar)"
|
||||
SSticker.news_report = CULT_FAILURE
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
// FAMILIES //
|
||||
// //
|
||||
//////////////////////////////////////////////
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/families
|
||||
name = "Families"
|
||||
persistent = TRUE
|
||||
antag_datum = /datum/antagonist/gang
|
||||
antag_flag = ROLE_FAMILIES
|
||||
protected_roles = list("Prisoner", "Head of Personnel")
|
||||
restricted_roles = list("AI", "Cyborg", "Prisoner", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Chaplain", "Head of Personnel", "Quartermaster", "Chief Engineer", "Chief Medical Officer", "Research Director")
|
||||
required_candidates = 9
|
||||
weight = 3
|
||||
cost = 15
|
||||
requirements = list(101,101,101,50,30,20,10,10,10,10)
|
||||
flags = HIGH_IMPACT_RULESET
|
||||
/// A reference to the handler that is used to run pre_execute(), execute(), etc..
|
||||
var/datum/gang_handler/handler
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/families/pre_execute()
|
||||
..()
|
||||
handler = new /datum/gang_handler(candidates,restricted_roles)
|
||||
handler.gangs_to_generate = (antag_cap[indice_pop] / 2)
|
||||
handler.gang_balance_cap = clamp((indice_pop - 3), 2, 5) // gang_balance_cap by indice_pop: (2,2,2,2,2,3,4,5,5,5)
|
||||
return handler.pre_setup_analogue()
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/families/execute()
|
||||
return handler.post_setup_analogue(TRUE)
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/families/clean_up()
|
||||
QDEL_NULL(handler)
|
||||
..()
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/families/rule_process()
|
||||
return handler.process_analogue()
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/families/round_result()
|
||||
return handler.set_round_result_analogue()
|
||||
|
||||
// Admin only rulesets. The threat requirement is 101 so it is not possible to roll them.
|
||||
|
||||
//////////////////////////////////////////////
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/datum/game_mode/gang
|
||||
name = "Families"
|
||||
config_tag = "families"
|
||||
antag_flag = ROLE_TRAITOR
|
||||
false_report_weight = 5
|
||||
required_players = 0
|
||||
required_enemies = 1
|
||||
recommended_enemies = 4
|
||||
announce_span = "danger"
|
||||
announce_text = "Grove For Lyfe!"
|
||||
reroll_friendly = FALSE
|
||||
restricted_jobs = list("Cyborg", "AI", "Prisoner","Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel")//N O
|
||||
protected_jobs = list()
|
||||
|
||||
/// A reference to the handler that is used to run pre_setup(), post_setup(), etc..
|
||||
var/datum/gang_handler/handler
|
||||
|
||||
/datum/game_mode/gang/warriors
|
||||
name = "Warriors"
|
||||
config_tag = "warriors"
|
||||
announce_text = "Can you survive this onslaught?"
|
||||
|
||||
/datum/game_mode/gang/warriors/pre_setup()
|
||||
handler = new /datum/gang_handler(antag_candidates,restricted_jobs)
|
||||
var/list/datum/antagonist/gang/gangs_to_generate = subtypesof(/datum/antagonist/gang)
|
||||
handler.gangs_to_generate = gangs_to_generate.len
|
||||
handler.gang_balance_cap = 3
|
||||
return handler.pre_setup_analogue()
|
||||
|
||||
/datum/game_mode/gang/pre_setup()
|
||||
handler = new /datum/gang_handler(antag_candidates,restricted_jobs)
|
||||
return handler.pre_setup_analogue()
|
||||
|
||||
/datum/game_mode/gang/Destroy()
|
||||
QDEL_NULL(handler)
|
||||
return ..()
|
||||
|
||||
/datum/game_mode/gang/post_setup()
|
||||
handler.post_setup_analogue(FALSE)
|
||||
gamemode_ready = TRUE
|
||||
return ..()
|
||||
|
||||
/datum/game_mode/gang/process()
|
||||
handler.process_analogue()
|
||||
|
||||
/datum/game_mode/gang/set_round_result()
|
||||
return handler.set_round_result_analogue()
|
||||
|
||||
/datum/game_mode/gang/generate_report()
|
||||
return "Something something grove street home at least until I fucked everything up idk nobody reads these reports."
|
||||
@@ -1,247 +0,0 @@
|
||||
#define DOM_BLOCKED_SPAM_CAP 6
|
||||
//32 instead of 40 for safety reasons. How many turfs aren't walls around dominator for it to work
|
||||
//Update ppl somehow fuckup at 32, now we are down to 25. I hope to god they don't try harder to wall it.
|
||||
#define DOM_REQUIRED_TURFS 25
|
||||
#define DOM_HULK_HITS_REQUIRED 10
|
||||
|
||||
/obj/machinery/dominator
|
||||
name = "dominator"
|
||||
desc = "A visibly sinister device. Looks like you can break it if you hit it enough."
|
||||
icon = 'icons/obj/machines/dominator.dmi'
|
||||
icon_state = "dominator"
|
||||
density = TRUE
|
||||
anchored = TRUE
|
||||
layer = HIGH_OBJ_LAYER
|
||||
max_integrity = 300
|
||||
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
|
||||
var/warned = FALSE //if this device has set off the warning at <3 minutes yet
|
||||
var/spam_prevention = DOM_BLOCKED_SPAM_CAP //first message is immediate
|
||||
var/datum/effect_system/spark_spread/spark_system
|
||||
var/obj/effect/countdown/dominator/countdown
|
||||
|
||||
/obj/machinery/dominator/Initialize()
|
||||
. = ..()
|
||||
set_light(l_range = 2, l_power = 0.75)
|
||||
GLOB.poi_list |= src
|
||||
spark_system = new
|
||||
spark_system.set_up(5, TRUE, src)
|
||||
countdown = new(src)
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/dominator/Destroy()
|
||||
if(!(stat & BROKEN))
|
||||
set_broken()
|
||||
GLOB.poi_list.Remove(src)
|
||||
gang = null
|
||||
QDEL_NULL(spark_system)
|
||||
QDEL_NULL(countdown)
|
||||
STOP_PROCESSING(SSmachines, src)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/dominator/emp_act(severity)
|
||||
take_damage(75+severity/4, BURN, "energy", 0)
|
||||
..()
|
||||
|
||||
/obj/machinery/dominator/hulk_damage()
|
||||
return (max_integrity - integrity_failure) / DOM_HULK_HITS_REQUIRED
|
||||
|
||||
/obj/machinery/dominator/update_icon()
|
||||
cut_overlays()
|
||||
if(stat & BROKEN)
|
||||
icon_state = "dominator-broken"
|
||||
return
|
||||
icon_state = "dominator"
|
||||
if(operating)
|
||||
var/mutable_appearance/dominator_overlay = mutable_appearance('icons/obj/machines/dominator.dmi', "dominator-overlay")
|
||||
if(gang)
|
||||
dominator_overlay.color = gang.color
|
||||
add_overlay(dominator_overlay)
|
||||
if(obj_integrity/max_integrity < 0.66)
|
||||
add_overlay("damage")
|
||||
|
||||
/obj/machinery/dominator/examine(mob/user)
|
||||
. = ..()
|
||||
if(stat & BROKEN)
|
||||
return
|
||||
|
||||
if(gang && gang.domination_time != NOT_DOMINATING)
|
||||
if(gang.domination_time > world.time)
|
||||
. += "<span class='notice'>Hostile Takeover in progress. Estimated [gang.domination_time_remaining()] seconds remain.</span>"
|
||||
else
|
||||
. += "<span class='notice'>Hostile Takeover of [station_name()] successful. Have a great day.</span>"
|
||||
else
|
||||
. += "<span class='notice'>System on standby.</span>"
|
||||
. += "<span class='danger'>System Integrity: [round((obj_integrity/max_integrity)*100,1)]%</span>"
|
||||
|
||||
/obj/machinery/dominator/process()
|
||||
..()
|
||||
if(gang && gang.domination_time != NOT_DOMINATING)
|
||||
var/time_remaining = gang.domination_time_remaining()
|
||||
if(time_remaining > 0)
|
||||
if(!is_station_level(z))
|
||||
explosion(src, 5, 10, 20, 30) //you now get a nice explosion if this moves off station.
|
||||
qdel(src) //to make sure it doesn't continue to exist.
|
||||
if(excessive_walls_check())
|
||||
gang.domination_time += 20
|
||||
if(spam_prevention < DOM_BLOCKED_SPAM_CAP)
|
||||
spam_prevention++
|
||||
else
|
||||
playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0) // Play sound buzz-two.ogg, not before cause its annoying.
|
||||
gang.message_gangtools("Warning: There are too many walls around your gang's dominator, its signal is being blocked!")
|
||||
say("Error: Takeover signal is currently blocked! There are too many walls within 3 standard units of this device.")
|
||||
spam_prevention = 0
|
||||
return
|
||||
. = TRUE
|
||||
playsound(loc, 'sound/items/timer.ogg', 10, 0)
|
||||
if(!warned && (time_remaining < 180))
|
||||
warned = TRUE
|
||||
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
|
||||
if(tempgang != gang)
|
||||
tempgang.message_gangtools("WARNING: [gang.name] Gang takeover imminent. Their dominator at [domloc.map_name] must be destroyed!",1,1)
|
||||
else
|
||||
endgame()
|
||||
|
||||
if(!.)
|
||||
STOP_PROCESSING(SSmachines, src)
|
||||
|
||||
/obj/machinery/dominator/proc/endgame()
|
||||
set waitfor = FALSE
|
||||
Cinematic(CINEMATIC_MALF,world) //Here is the gang victory trigger on the dominator ending.
|
||||
gang.winner = TRUE
|
||||
SSticker.news_report = GANG_VICTORY
|
||||
SSticker.force_ending = TRUE
|
||||
|
||||
/obj/machinery/dominator/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
|
||||
switch(damage_type)
|
||||
if(BRUTE)
|
||||
if(damage_amount)
|
||||
playsound(src, 'sound/effects/bang.ogg', 50, 1)
|
||||
else
|
||||
playsound(loc, 'sound/weapons/tap.ogg', 50, 1)
|
||||
if(BURN)
|
||||
playsound(src.loc, 'sound/items/welder.ogg', 100, 1)
|
||||
|
||||
/obj/machinery/dominator/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1)
|
||||
. = ..()
|
||||
if(.)
|
||||
if(obj_integrity/max_integrity > 0.66)
|
||||
if(prob(damage_amount*2))
|
||||
spark_system.start()
|
||||
else if(!(stat & BROKEN))
|
||||
spark_system.start()
|
||||
update_icon()
|
||||
|
||||
|
||||
/obj/machinery/dominator/obj_break(damage_flag)
|
||||
if(!(stat & BROKEN) && !(flags_1 & NODECONSTRUCT_1))
|
||||
set_broken()
|
||||
|
||||
/obj/machinery/dominator/deconstruct(disassembled = TRUE)
|
||||
if(!(flags_1 & NODECONSTRUCT_1))
|
||||
if(!(stat & BROKEN))
|
||||
set_broken()
|
||||
new /obj/item/stack/sheet/plasteel(src.loc)
|
||||
qdel(src)
|
||||
|
||||
/obj/machinery/dominator/attacked_by(obj/item/I, mob/living/user, attackchain_flags = NONE, damage_multiplier = 1)
|
||||
add_fingerprint(user)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/dominator/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
|
||||
if(operating || (stat & BROKEN))
|
||||
examine(user)
|
||||
return
|
||||
|
||||
var/datum/team/gang/tempgang
|
||||
|
||||
var/datum/antagonist/gang/GA = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(GA)
|
||||
tempgang = GA.gang
|
||||
if(!tempgang)
|
||||
examine(user)
|
||||
return
|
||||
|
||||
if(tempgang.domination_time != NOT_DOMINATING)
|
||||
to_chat(user, "<span class='warning'>Error: Hostile Takeover is already in progress.</span>")
|
||||
return
|
||||
|
||||
if(!tempgang.dom_attempts)
|
||||
to_chat(user, "<span class='warning'>Error: Unable to breach station network. Firewall has logged our signature and is blocking all further attempts.</span>")
|
||||
return
|
||||
|
||||
var/time = round(tempgang.determine_domination_time()/60,0.1)
|
||||
if(alert(user,"A takeover will require [time] minutes.\nYour gang will be unable to gain influence while it is active.\nThe entire station will likely be alerted to it once it starts.\nYou have [tempgang.dom_attempts] attempt(s) remaining. Are you ready?","Confirm","Ready","Later") == "Ready")
|
||||
if((tempgang.domination_time != NOT_DOMINATING) || !tempgang.dom_attempts || !in_range(src, user) || !isturf(loc))
|
||||
return 0
|
||||
|
||||
var/area/A = get_base_area(loc)
|
||||
var/locname = A.map_name
|
||||
|
||||
gang = tempgang
|
||||
gang.dom_attempts --
|
||||
priority_announce("Network breach detected in [locname]. The [gang.name] Gang is attempting to seize control of the station!","Network Alert")
|
||||
gang.domination()
|
||||
SSshuttle.registerHostileEnvironment(src)
|
||||
name = "[gang.name] Gang [name]"
|
||||
operating = TRUE
|
||||
update_icon()
|
||||
|
||||
countdown.start()
|
||||
countdown.color = gang.color
|
||||
|
||||
set_light(l_range = 3, l_power = 0.9)
|
||||
light_color = gang.color
|
||||
START_PROCESSING(SSmachines, src)
|
||||
|
||||
gang.message_gangtools("Hostile takeover in progress: Estimated [time] minutes until victory.[gang.dom_attempts ? "" : " This is your final attempt."]")
|
||||
for(var/G in GLOB.gangs)
|
||||
var/datum/team/gang/vagos = G
|
||||
if(vagos != gang)
|
||||
vagos.message_gangtools("Enemy takeover attempt detected in [locname]: Estimated [time] minutes until our defeat.",1,1)
|
||||
|
||||
/obj/machinery/dominator/proc/excessive_walls_check() // why the fuck was this even a global proc...
|
||||
var/open = 0
|
||||
for(var/turf/T in view(3, src))
|
||||
if(!iswallturf(T)) //Check for /closed/wall, isclosedturf() moves it back to just checking for /closed/ which makes it very finicky.
|
||||
open++
|
||||
//to_chat(world, "THE DOMINATOR SEES [open] OPEN TURFS") uncomment to see what this shitty fucking wallcheck sees
|
||||
if(open < DOM_REQUIRED_TURFS)
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
/obj/machinery/dominator/proc/set_broken()
|
||||
if(gang)
|
||||
gang.domination_time = NOT_DOMINATING
|
||||
|
||||
var/takeover_in_progress = FALSE
|
||||
for(var/G in GLOB.gangs)
|
||||
var/datum/team/gang/ballas = G
|
||||
if(ballas.domination_time != NOT_DOMINATING)
|
||||
takeover_in_progress = TRUE
|
||||
break
|
||||
if(!takeover_in_progress)
|
||||
var/was_stranded = SSshuttle.emergency.mode == SHUTTLE_STRANDED
|
||||
if(!was_stranded)
|
||||
priority_announce("All hostile activity within station systems has ceased.","Network Alert")
|
||||
|
||||
if(NUM2SECLEVEL(GLOB.security_level) == "delta")
|
||||
set_security_level("red")
|
||||
|
||||
SSshuttle.clearHostileEnvironment(src)
|
||||
gang.message_gangtools("Hostile takeover cancelled: Dominator is no longer operational.[gang.dom_attempts ? " You have [gang.dom_attempts] attempt remaining." : " The station network will have likely blocked any more attempts by us."]",1,1)
|
||||
|
||||
set_light(0)
|
||||
operating = FALSE
|
||||
stat |= BROKEN
|
||||
update_icon()
|
||||
STOP_PROCESSING(SSmachines, src)
|
||||
|
||||
#undef DOM_BLOCKED_SPAM_CAP
|
||||
#undef DOM_REQUIRED_TURFS
|
||||
#undef DOM_HULK_HITS_REQUIRED
|
||||
@@ -1,13 +0,0 @@
|
||||
/obj/effect/countdown/dominator
|
||||
name = "dominator countdown"
|
||||
text_size = 1
|
||||
color = "#e5e5e5" // Overwritten when the dominator starts
|
||||
|
||||
/obj/effect/countdown/dominator/get_value()
|
||||
var/obj/machinery/dominator/D = attached_to
|
||||
if(!istype(D))
|
||||
return
|
||||
else if(D.gang && D.gang.domination_time != NOT_DOMINATING)
|
||||
return D.gang.domination_time_remaining()
|
||||
else
|
||||
return "OFFLINE"
|
||||
@@ -1,479 +0,0 @@
|
||||
/datum/antagonist/gang
|
||||
name = "Gangster"
|
||||
roundend_category = "gangsters"
|
||||
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
|
||||
|
||||
/datum/antagonist/gang/can_be_owned(datum/mind/new_owner)
|
||||
. = ..()
|
||||
if(.)
|
||||
if(new_owner.unconvertable)
|
||||
return FALSE
|
||||
|
||||
/datum/antagonist/gang/apply_innate_effects(mob/living/mob_override)
|
||||
var/mob/living/M = mob_override || owner.current
|
||||
update_gang_icons_added(M)
|
||||
|
||||
/datum/antagonist/gang/remove_innate_effects(mob/living/mob_override)
|
||||
var/mob/living/M = mob_override || owner.current
|
||||
update_gang_icons_removed(M)
|
||||
|
||||
/datum/antagonist/gang/get_team()
|
||||
return gang
|
||||
|
||||
/datum/antagonist/gang/greet()
|
||||
gang.greet_gangster(owner)
|
||||
|
||||
/datum/antagonist/gang/farewell()
|
||||
if(ishuman(owner.current))
|
||||
owner.current.visible_message("<span class='deconversion_message'>[owner.current] looks like [owner.current.p_theyve()] just remembered [owner.current.p_their()] real allegiance!</span>", null, null, null, owner.current)
|
||||
to_chat(owner, "<span class='userdanger'>You are no longer a gangster! Your memories from the time you were in a gang are hazy... You don't seem to be able to recall the names of your previous allies, not even your bosses...</span>")
|
||||
|
||||
/datum/antagonist/gang/on_gain()
|
||||
if(!gang)
|
||||
create_team()
|
||||
..()
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
if(istype(H))
|
||||
if(owner.assigned_role == "Clown")
|
||||
to_chat(owner, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
|
||||
H.dna.remove_mutation(CLOWNMUT)
|
||||
add_to_gang()
|
||||
|
||||
/datum/antagonist/gang/on_removal()
|
||||
remove_from_gang()
|
||||
..()
|
||||
|
||||
/datum/antagonist/gang/create_team(team)
|
||||
if(!gang) // add_antag_datum calls create_team, so we need to avoid generating two gangs in that case
|
||||
if(team)
|
||||
gang = team
|
||||
return
|
||||
var/datum/team/gang/gangteam = pick_n_take(GLOB.possible_gangs)
|
||||
if(gangteam)
|
||||
gang = new gangteam
|
||||
|
||||
/datum/antagonist/gang/proc/equip_gang() // Bosses get equipped with their tools
|
||||
return
|
||||
|
||||
/datum/antagonist/gang/proc/update_gang_icons_added(mob/living/M)
|
||||
var/datum/atom_hud/antag/gang/ganghud = GLOB.huds[gang.hud_entry_num]
|
||||
if(!ganghud)
|
||||
ganghud = new/datum/atom_hud/antag/gang()
|
||||
gang.hud_entry_num = GLOB.huds.len+1 // this is the index the gang hud will be added at
|
||||
GLOB.huds += ganghud
|
||||
ganghud.color = gang.color
|
||||
ganghud.join_hud(M)
|
||||
set_antag_hud(M,hud_type)
|
||||
|
||||
/datum/antagonist/gang/proc/update_gang_icons_removed(mob/living/M)
|
||||
var/datum/atom_hud/antag/gang/ganghud = GLOB.huds[gang.hud_entry_num]
|
||||
if(ganghud)
|
||||
ganghud.leave_hud(M)
|
||||
set_antag_hud(M, null)
|
||||
|
||||
/datum/antagonist/gang/proc/can_be_converted(mob/living/candidate)
|
||||
if(!candidate.mind)
|
||||
return FALSE
|
||||
if(!can_be_owned(candidate.mind))
|
||||
return FALSE
|
||||
var/mob/living/carbon/human/H = candidate
|
||||
if(!istype(H)) //Can't nonhumans
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/antagonist/gang/proc/promote() // Bump up to boss
|
||||
var/datum/team/gang/old_gang = gang
|
||||
var/datum/mind/old_owner = owner
|
||||
owner.remove_antag_datum(/datum/antagonist/gang)
|
||||
var/datum/antagonist/gang/boss/lieutenant/new_boss = new
|
||||
new_boss.silent = TRUE
|
||||
old_owner.add_antag_datum(new_boss,old_gang)
|
||||
new_boss.silent = FALSE
|
||||
log_game("[key_name(old_owner)] has been promoted to Lieutenant in the [old_gang.name] Gang")
|
||||
to_chat(old_owner, "<FONT size=3 color=red><B>You have been promoted to Lieutenant!</B></FONT>")
|
||||
|
||||
|
||||
// Admin commands
|
||||
/datum/antagonist/gang/get_admin_commands()
|
||||
. = ..()
|
||||
.["Promote"] = CALLBACK(src,.proc/admin_promote)
|
||||
.["Set Influence"] = CALLBACK(src, .proc/admin_adjust_influence)
|
||||
if(gang.domination_time != NOT_DOMINATING)
|
||||
.["Set domination time left"] = CALLBACK(src, .proc/set_dom_time_left)
|
||||
|
||||
/datum/antagonist/gang/admin_add(datum/mind/new_owner,mob/admin)
|
||||
var/new_or_existing = input(admin, "Which gang do you want to be assigned to the user?", "Gangs") as null|anything in list("New","Existing")
|
||||
if(isnull(new_or_existing))
|
||||
return
|
||||
else if(new_or_existing == "New")
|
||||
var/newgang = input(admin, "Select a gang, or select random to pick a random one.", "New gang") as null|anything in GLOB.possible_gangs + "Random"
|
||||
if(isnull(newgang))
|
||||
return
|
||||
else if(newgang == "Random")
|
||||
var/datum/team/gang/G = pick_n_take(GLOB.possible_gangs)
|
||||
gang = new G
|
||||
else
|
||||
GLOB.possible_gangs -= newgang
|
||||
gang = new newgang
|
||||
else
|
||||
if(!GLOB.gangs.len) // no gangs exist
|
||||
to_chat(admin, "<span class='danger'>No gangs exist, please create a new one instead.</span>")
|
||||
return
|
||||
var/existinggang = input(admin, "Select a gang, or select random to pick a random one.", "Existing gang") as null|anything in GLOB.gangs + "Random"
|
||||
if(isnull(existinggang))
|
||||
return
|
||||
else if(existinggang == "Random")
|
||||
gang = pick(GLOB.gangs)
|
||||
else
|
||||
gang = existinggang
|
||||
..()
|
||||
return TRUE
|
||||
|
||||
/datum/antagonist/gang/proc/admin_promote(mob/admin)
|
||||
message_admins("[key_name_admin(admin)] has promoted [owner] to gang boss.")
|
||||
log_admin("[key_name(admin)] has promoted [owner] to boss.")
|
||||
promote()
|
||||
|
||||
/datum/antagonist/gang/proc/admin_adjust_influence()
|
||||
var/inf = input("Influence for [gang.name]","Gang influence", gang.influence) as null | num
|
||||
if(!isnull(inf))
|
||||
gang.influence = inf
|
||||
message_admins("[key_name_admin(usr)] changed [gang.name]'s influence to [inf].")
|
||||
log_admin("[key_name(usr)] changed [gang.name]'s influence to [inf].")
|
||||
|
||||
/datum/antagonist/gang/proc/add_to_gang()
|
||||
gang.add_member(owner)
|
||||
owner.current.log_message("<font color='red'>Has been converted to the [gang.name] gang!</font>", INDIVIDUAL_ATTACK_LOG)
|
||||
|
||||
/datum/antagonist/gang/proc/remove_from_gang()
|
||||
gang.remove_member(owner)
|
||||
owner.current.log_message("<font color='red'>Has been deconverted from the [gang.name] gang!</font>", INDIVIDUAL_ATTACK_LOG)
|
||||
|
||||
/datum/antagonist/gang/proc/set_dom_time_left(mob/admin)
|
||||
if(gang.domination_time == NOT_DOMINATING)
|
||||
return // an admin shouldn't need this
|
||||
var/seconds = input(admin, "Set the time left for the gang to win, in seconds", "Domination time left") as null|num
|
||||
if(seconds && seconds > 0)
|
||||
gang.domination_time = world.time + seconds*10
|
||||
gang.message_gangtools("Takeover shortened to [gang.domination_time_remaining()] seconds by your Syndicate benefactors.")
|
||||
|
||||
// Boss type. Those can use gang tools to buy items for their gang, in particular the Dominator, used to win the gamemode, along with more gang tools to promote fellow gangsters to boss status.
|
||||
/datum/antagonist/gang/boss
|
||||
name = "Gang boss"
|
||||
hud_type = "gang_boss"
|
||||
message_name = "Leader"
|
||||
threat = 10
|
||||
|
||||
/datum/antagonist/gang/boss/on_gain()
|
||||
..()
|
||||
if(gang)
|
||||
gang.leaders += owner
|
||||
|
||||
/datum/antagonist/gang/boss/on_removal()
|
||||
if(gang)
|
||||
gang.leaders -= owner
|
||||
..()
|
||||
|
||||
/datum/antagonist/gang/boss/antag_listing_name()
|
||||
return ..() + "(Boss)"
|
||||
|
||||
/datum/antagonist/gang/boss/equip_gang(gangtool = TRUE, pen = TRUE, spraycan = TRUE, hud = TRUE) // usually has to be called separately
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
if(!istype(H))
|
||||
return
|
||||
|
||||
var/list/slots = list (
|
||||
"backpack" = SLOT_IN_BACKPACK,
|
||||
"left pocket" = SLOT_L_STORE,
|
||||
"right pocket" = SLOT_R_STORE,
|
||||
"hands" = SLOT_HANDS
|
||||
)
|
||||
|
||||
if(gangtool)//Here is where all of the text occurs when a gang boss first spawns in.
|
||||
var/obj/item/device/gangtool/G = new()
|
||||
var/where = H.equip_in_one_of_slots(G, slots, critical = TRUE)
|
||||
if (!where)
|
||||
to_chat(H, "Your Syndicate benefactors were unfortunately unable to get you a Gangtool.")
|
||||
else
|
||||
G.register_device(H)
|
||||
to_chat(H, "The <b>Gangtool</b> in your [where] will allow you to purchase weapons and equipment, send messages to your gang, and recall the emergency shuttle from anywhere on the station.")
|
||||
to_chat(H, "As the gang boss, you can also promote your gang members to <b>lieutenant</b>. Unlike regular gangsters, Lieutenants cannot be deconverted and are able to use gangtools too.")
|
||||
|
||||
if(pen)
|
||||
var/obj/item/pen/gang/T = new()
|
||||
var/where2 = H.equip_in_one_of_slots(T, slots, critical = TRUE)
|
||||
if (!where2)
|
||||
to_chat(H, "Your Syndicate benefactors were unfortunately unable to get you a recruitment pen to start.")
|
||||
else
|
||||
to_chat(H, "The <b>recruitment pen</b> in your [where2] will help you get your gang started. Stab unsuspecting crew members with it to recruit them. All gangsters can use these, distribute them to see your gang grow.")
|
||||
|
||||
if(spraycan)
|
||||
var/obj/item/toy/crayon/spraycan/gang/SC = new(null,gang)
|
||||
var/where3 = H.equip_in_one_of_slots(SC, slots, critical = TRUE)
|
||||
if (!where3)
|
||||
to_chat(H, "Your Syndicate benefactors were unfortunately unable to get you a territory spraycan to start.")
|
||||
else
|
||||
to_chat(H, "The <b>territory spraycan</b> in your [where3] can be used to claim areas of the station for your gang. The more territory your gang controls, the more influence you get. All gangsters can use these, so distribute them to grow your influence faster.")
|
||||
|
||||
if(hud)
|
||||
var/obj/item/clothing/glasses/hud/security/chameleon/C = new(null,gang)
|
||||
var/where4 = H.equip_in_one_of_slots(C, slots, critical = TRUE)
|
||||
if (!where4)
|
||||
to_chat(H, "Your Syndicate benefactors were unfortunately unable to get you a chameleon security HUD.")
|
||||
else
|
||||
to_chat(H, "The <b>chameleon security HUD</b> in your [where4] will help you keep track of who is mindshield-implanted, and unable to be recruited.")
|
||||
|
||||
// Admin commands for bosses
|
||||
/datum/antagonist/gang/boss/admin_add(datum/mind/new_owner,mob/admin)
|
||||
if(!new_owner.has_antag_datum(parent_type))
|
||||
..()
|
||||
to_chat(new_owner.current, "<span class='userdanger'>You are a member of the [gang.name] Gang leadership now!</span>")
|
||||
return
|
||||
promote()
|
||||
message_admins("[key_name_admin(admin)] has made [new_owner.current] a boss of the [gang.name] gang.")
|
||||
log_admin("[key_name(admin)] has made [new_owner.current] a boss of the [gang.name] gang.")
|
||||
to_chat(new_owner.current, "<span class='userdanger'>You are a member of the [gang.name] Gang leadership now!</span>")
|
||||
|
||||
/datum/antagonist/gang/boss/get_admin_commands()
|
||||
. = ..()
|
||||
. -= "Promote"
|
||||
.["Take gangtool"] = CALLBACK(src,.proc/admin_take_gangtool)
|
||||
.["Give gangtool"] = CALLBACK(src,.proc/admin_give_gangtool)
|
||||
.["Demote"] = CALLBACK(src,.proc/admin_demote)
|
||||
|
||||
/datum/antagonist/gang/boss/proc/demote()
|
||||
var/old_gang = gang
|
||||
var/datum/mind/old_owner = owner
|
||||
silent = TRUE
|
||||
owner.remove_antag_datum(/datum/antagonist/gang/boss)
|
||||
var/datum/antagonist/gang/new_gangster = new /datum/antagonist/gang()
|
||||
new_gangster.silent = TRUE
|
||||
old_owner.add_antag_datum(new_gangster,old_gang)
|
||||
new_gangster.silent = FALSE
|
||||
log_game("[key_name(old_owner)] has been demoted to Gangster in the [gang.name] Gang")
|
||||
to_chat(old_owner, "<span class='userdanger'>The gang has been disappointed of your leader traits! You are a regular gangster now!</span>")
|
||||
|
||||
/datum/antagonist/gang/boss/proc/admin_take_gangtool(mob/admin)
|
||||
var/list/L = owner.current.get_contents()
|
||||
var/obj/item/device/gangtool/gangtool = locate() in L
|
||||
if (!gangtool)
|
||||
to_chat(admin, "<span class='danger'>Deleting gangtool failed!</span>")
|
||||
return
|
||||
qdel(gangtool)
|
||||
|
||||
/datum/antagonist/gang/boss/proc/admin_give_gangtool(mob/admin)
|
||||
equip_gang(TRUE, FALSE, FALSE, FALSE)
|
||||
|
||||
/datum/antagonist/gang/boss/proc/admin_demote(datum/mind/target,mob/user)
|
||||
message_admins("[key_name_admin(user)] has demoted [owner.current] from gang boss.")
|
||||
log_admin("[key_name(user)] has demoted [owner.current] from gang boss.")
|
||||
admin_take_gangtool(user)
|
||||
demote()
|
||||
|
||||
/datum/antagonist/gang/boss/lieutenant
|
||||
name = "Gang Lieutenant"
|
||||
message_name = "Lieutenant"
|
||||
hud_type = "gang_lt"
|
||||
|
||||
#define MAXIMUM_RECALLS 3
|
||||
#define INFLUENCE_INTERVAL 1200 //This handles the interval between each count of influence.
|
||||
// Gang team datum. This handles the gang itself.
|
||||
/datum/team/gang
|
||||
name = "Gang"
|
||||
member_name = "gangster"
|
||||
var/hud_entry_num // because if you put something other than a number in GLOB.huds, god have mercy on your fucking soul friend
|
||||
var/list/leaders = list() // bosses
|
||||
var/max_leaders = MAX_LEADERS_GANG
|
||||
var/list/territories = list() // territories owned by the gang.
|
||||
var/list/lost_territories = list() // territories lost by the gang.
|
||||
var/list/new_territories = list() // territories captured by the gang.
|
||||
var/list/gangtools = list()
|
||||
var/domination_time = NOT_DOMINATING
|
||||
var/dom_attempts = INITIAL_DOM_ATTEMPTS
|
||||
var/color
|
||||
var/influence = 0 // influence of the gang, based on how many territories they own. Can be used to buy weapons and tools from a gang uplink.
|
||||
var/winner // Once the gang wins with a dominator, this becomes true. For roundend credits purposes.
|
||||
var/list/inner_outfits = list()
|
||||
var/list/outer_outfits = list()
|
||||
var/next_point_time
|
||||
var/recalls = MAXIMUM_RECALLS // Once this reaches 0, this gang cannot force recall the shuttle with their gangtool anymore
|
||||
|
||||
/datum/team/gang/New(starting_members)
|
||||
. = ..()
|
||||
GLOB.gangs += src
|
||||
if(starting_members)
|
||||
if(islist(starting_members))
|
||||
for(var/datum/mind/groveboss in starting_members)
|
||||
leaders += groveboss
|
||||
var/datum/antagonist/gang/boss/gb = new
|
||||
groveboss.add_antag_datum(gb, src)
|
||||
gb.equip_gang()
|
||||
|
||||
else
|
||||
var/datum/mind/CJ = starting_members
|
||||
if(istype(CJ))
|
||||
leaders += CJ
|
||||
var/datum/antagonist/gang/boss/bossdatum = new
|
||||
CJ.add_antag_datum(bossdatum, src)
|
||||
bossdatum.equip_gang()
|
||||
next_point_time = world.time + INFLUENCE_INTERVAL
|
||||
addtimer(CALLBACK(src, .proc/handle_territories), INFLUENCE_INTERVAL)
|
||||
|
||||
/datum/team/gang/Destroy()
|
||||
GLOB.gangs -= src
|
||||
..()
|
||||
|
||||
/datum/team/gang/roundend_report() //roundend report.
|
||||
var/list/report = list()
|
||||
report += "<span class='header'>[name]:</span>"
|
||||
if(winner)
|
||||
report += "<span class='greentext'>The [name] gang successfully activated the mind dominator!</span>"
|
||||
else
|
||||
report += "<span class='redtext'>The [name] gang has failed!</span>"
|
||||
|
||||
report += "The [name] gang bosses were:"
|
||||
report += printplayerlist(leaders)
|
||||
report += "The [name] [member_name]s were:"
|
||||
report += printplayerlist(members-leaders)
|
||||
|
||||
return "<div class='panel redborder'>[report.Join("<br>")]</div>"
|
||||
|
||||
/datum/team/gang/proc/greet_gangster(datum/mind/gangster) //The text a person receives when recruited.
|
||||
var/message = "<FONT size=3 color=red><B>You are now a member of the <font color='[color]'>[name]</font> Gang!</B></FONT>"
|
||||
message += "<font color='red'>Help your bosses take over the station by claiming territory with <b>spraycans</b>. Simply spray on any unclaimed area of the station.</font>"
|
||||
message += "<font color='red'>You can also use recruitment pens to recruit more to your cause, If your boss provides you one.</font>"
|
||||
message += "<font color='red'>Their ultimate objective is to take over the station with a Dominator machine.</font>"
|
||||
message += "<font color='red'>You can identify your mates by their <b>large, <font color='[color]'> \[G\]</font> icon</b>.</font>"
|
||||
to_chat(gangster, message)
|
||||
gangster.store_memory("You are a member of the [name] Gang!")
|
||||
|
||||
/datum/team/gang/proc/handle_territories()
|
||||
next_point_time = world.time + INFLUENCE_INTERVAL
|
||||
if(!leaders.len)
|
||||
return
|
||||
var/added_names = ""
|
||||
var/lost_names = ""
|
||||
|
||||
//Re-add territories that were reclaimed, so if they got tagged over, they can still earn income if they tag it back before the next status report
|
||||
var/list/reclaimed_territories = new_territories & lost_territories
|
||||
territories |= reclaimed_territories
|
||||
new_territories -= reclaimed_territories
|
||||
lost_territories -= reclaimed_territories
|
||||
|
||||
//Process lost territories
|
||||
for(var/area in lost_territories)
|
||||
if(lost_names != "")
|
||||
lost_names += ", "
|
||||
lost_names += "[lost_territories[area]]"
|
||||
territories -= area
|
||||
|
||||
//Calculate and report influence growth
|
||||
|
||||
//Process new territories
|
||||
for(var/area in new_territories)
|
||||
if(added_names != "")
|
||||
added_names += ", "
|
||||
added_names += "[new_territories[area]]"
|
||||
territories += area
|
||||
|
||||
//Report territory changes
|
||||
var/message = "<b>[src] Gang Status Report:</b>.<BR>*---------*<BR>"
|
||||
message += "<b>[new_territories.len] new territories:</b><br><i>[added_names]</i><br>"
|
||||
message += "<b>[lost_territories.len] territories lost:</b><br><i>[lost_names]</i><br>"
|
||||
//Clear the lists
|
||||
new_territories = list()
|
||||
lost_territories = list()
|
||||
var/total_territories = total_claimable_territories()
|
||||
var/control = round((territories.len/total_territories)*100, 1)
|
||||
var/uniformed = check_clothing()
|
||||
message += "Your gang now has <b>[control]% control</b> of the station.<BR>*---------*<BR>"
|
||||
if(domination_time != NOT_DOMINATING)
|
||||
var/new_time = max(world.time, domination_time - (uniformed * 4) - (territories.len * 2))
|
||||
if(new_time < domination_time)
|
||||
message += "Takeover shortened by [(domination_time - new_time)*0.1] seconds for defending [territories.len] territories.<BR>"
|
||||
domination_time = new_time
|
||||
message += "<b>[domination_time_remaining()] seconds remain</b> in hostile takeover.<BR>"
|
||||
else
|
||||
var/new_influence = check_territory_income()
|
||||
if(new_influence != influence)
|
||||
message += "Gang influence has increased by [new_influence - influence] for defending [territories.len] territories and [uniformed] uniformed gangsters.<BR>"
|
||||
influence = new_influence
|
||||
message += "Your gang now has <b>[influence] influence</b>.<BR>"
|
||||
message_gangtools(message)
|
||||
addtimer(CALLBACK(src, .proc/handle_territories), INFLUENCE_INTERVAL)
|
||||
|
||||
/datum/team/gang/proc/total_claimable_territories()
|
||||
var/list/valid_territories = list()
|
||||
for(var/z in SSmapping.levels_by_trait(ZTRAIT_STATION)) //First, collect all area types on the station zlevel
|
||||
for(var/ar in SSmapping.areas_in_z["[z]"])
|
||||
var/area/A = ar
|
||||
if(!(A.type in valid_territories) && (A.area_flags & VALID_TERRITORY))
|
||||
valid_territories |= A.type
|
||||
return valid_territories.len
|
||||
|
||||
/datum/team/gang/proc/check_territory_income()
|
||||
var/new_influence = min(999,influence + 15 + (check_clothing() * 2) + territories.len)
|
||||
return new_influence
|
||||
|
||||
/datum/team/gang/proc/check_clothing()
|
||||
//Count uniformed gangsters
|
||||
var/uniformed = 0
|
||||
for(var/datum/mind/gangmind in members)
|
||||
if(ishuman(gangmind.current))
|
||||
var/mob/living/carbon/human/gangster = gangmind.current
|
||||
//Gangster must be alive and should return 0 not continue if conditions are met.
|
||||
if(!istype(gangster) || gangster.stat == DEAD)
|
||||
return 0
|
||||
|
||||
var/obj/item/clothing/outfit
|
||||
var/obj/item/clothing/gang_outfit
|
||||
if(gangster.w_uniform)
|
||||
outfit = gangster.w_uniform
|
||||
if(outfit.type in inner_outfits)
|
||||
gang_outfit = outfit
|
||||
if(gangster.wear_suit)
|
||||
outfit = gangster.wear_suit
|
||||
if(outfit.type in outer_outfits)
|
||||
gang_outfit = outfit
|
||||
|
||||
if(gang_outfit)
|
||||
uniformed++
|
||||
return uniformed
|
||||
|
||||
/datum/team/gang/proc/adjust_influence(value)
|
||||
influence = max(0, influence + value)
|
||||
|
||||
/datum/team/gang/proc/message_gangtools(message)
|
||||
if(!gangtools.len || !message)
|
||||
return
|
||||
for(var/i in gangtools)
|
||||
var/obj/item/device/gangtool/tool = i
|
||||
var/mob/living/mob = get(tool.loc, /mob/living)
|
||||
if(mob && mob.mind && mob.stat == CONSCIOUS)
|
||||
var/datum/antagonist/gang/gangster = mob.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(gangster.gang == src)
|
||||
to_chat(mob, "<span class='warning'>[icon2html(tool, mob)] [message]</span>")
|
||||
playsound(mob.loc, 'sound/machines/twobeep.ogg', 50, 1)
|
||||
return
|
||||
|
||||
/datum/team/gang/proc/domination()
|
||||
domination_time = world.time + determine_domination_time()*10
|
||||
set_security_level("delta")
|
||||
|
||||
/datum/team/gang/proc/determine_domination_time() // calculates the value in seconds (this is the initial domination time!)
|
||||
var/total_territories = total_claimable_territories()
|
||||
return max(180,480 - (round((territories.len/total_territories)*100, 1) * 9))
|
||||
|
||||
/datum/team/gang/proc/domination_time_remaining() // retrieves the value from world.time based deciseconds to seconds
|
||||
var/diff = domination_time - world.time
|
||||
return round(diff * 0.1)
|
||||
|
||||
|
||||
#undef MAXIMUM_RECALLS
|
||||
#undef INFLUENCE_INTERVAL
|
||||
@@ -1,139 +0,0 @@
|
||||
// Gang datums go here. If you want to create a new gang, you must be sure to edit:
|
||||
// name
|
||||
// color (must be a hex, "blue" isn't acceptable due to how spraycans are handled)
|
||||
// inner_outfits (must be a list() with typepaths of the clothes in it. One is fine, but there is support for multiple: one will be picked at random when bought)
|
||||
// outer_outfits (same as above)
|
||||
// You also need to make a gang graffiti, that will go in crayondecal.dmi inside our icons, with the same name of the gang it's assigned to. Nothing else,just the icon.
|
||||
// Those are all required. If one is missed, stuff could break.
|
||||
|
||||
/datum/team/gang/clandestine
|
||||
name = "Clandestine"
|
||||
color = "#FF0000"
|
||||
inner_outfits = list(/obj/item/clothing/under/syndicate/combat)
|
||||
outer_outfits = list(/obj/item/clothing/suit/jacket)
|
||||
|
||||
/datum/team/gang/prima
|
||||
name = "Prima"
|
||||
color = "#FFFF00"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/yellow)
|
||||
outer_outfits = list(/obj/item/clothing/suit/hastur)
|
||||
|
||||
/datum/team/gang/zerog
|
||||
name = "Zero-G"
|
||||
color = "#C0C0C0"
|
||||
inner_outfits = list(/obj/item/clothing/under/suit/white)
|
||||
outer_outfits = list(/obj/item/clothing/suit/hooded/wintercoat)
|
||||
|
||||
/datum/team/gang/max
|
||||
name = "Max"
|
||||
color = "#800000"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/maroon)
|
||||
outer_outfits = list(/obj/item/clothing/suit/poncho/red)
|
||||
|
||||
/datum/team/gang/blasto
|
||||
name = "Blasto"
|
||||
color = "#000080"
|
||||
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/green)
|
||||
outer_outfits = list(/obj/item/clothing/suit/poncho)
|
||||
|
||||
/datum/team/gang/north
|
||||
name = "North"
|
||||
color = "#00FF00"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/green)
|
||||
outer_outfits = list(/obj/item/clothing/suit/poncho/green)
|
||||
|
||||
/datum/team/gang/omni
|
||||
name = "Omni"
|
||||
color = "#008080"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/teal)
|
||||
outer_outfits = list(/obj/item/clothing/suit/chaplain/studentuni)
|
||||
|
||||
/datum/team/gang/newton
|
||||
name = "Newton"
|
||||
color = "#A52A2A"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/brown)
|
||||
outer_outfits = list(/obj/item/clothing/suit/toggle/owlwings)
|
||||
|
||||
/datum/team/gang/cyber
|
||||
name = "Cyber"
|
||||
color = "#00f904" //Cyber and waffle shared colors, I made these guys green and made weed darker green.
|
||||
inner_outfits = list(/obj/item/clothing/under/color/lightbrown)
|
||||
outer_outfits = list(/obj/item/clothing/suit/chaplain/pharaoh)
|
||||
|
||||
/datum/team/gang/donk
|
||||
name = "Donk"
|
||||
color = "#0000FF"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/darkblue)
|
||||
outer_outfits = list(/obj/item/clothing/suit/apron/overalls)
|
||||
|
||||
/datum/team/gang/gene
|
||||
name = "Gene"
|
||||
color = "#00FFFF"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/blue)
|
||||
outer_outfits = list(/obj/item/clothing/suit/apron)
|
||||
|
||||
/datum/team/gang/gib
|
||||
name = "Gib"
|
||||
color = "#636060" //Applying black to grayscale... Zero-G is already grey too. oh well.
|
||||
inner_outfits = list(/obj/item/clothing/under/color/black)
|
||||
outer_outfits = list(/obj/item/clothing/suit/jacket/leather/overcoat)
|
||||
|
||||
/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/costume/villain)
|
||||
outer_outfits = list(/obj/item/clothing/suit/jacket/leather)
|
||||
|
||||
/datum/team/gang/diablo
|
||||
name = "Diablo"
|
||||
color = "#FF0000" //literal early 90s skinhead regalia.
|
||||
inner_outfits = list(/obj/item/clothing/under/pants/classicjeans)
|
||||
outer_outfits = list(/obj/item/clothing/suit/suspenders)
|
||||
|
||||
/datum/team/gang/psyke
|
||||
name = "Psyke"
|
||||
color = "#808080"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/grey)
|
||||
outer_outfits = list(/obj/item/clothing/suit/toggle/owlwings/griffinwings)
|
||||
|
||||
/datum/team/gang/osiron
|
||||
name = "Osiron"
|
||||
color = "#FFFFFF"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/white)
|
||||
outer_outfits = list(/obj/item/clothing/suit/toggle/labcoat)
|
||||
|
||||
/datum/team/gang/sirius
|
||||
name = "Sirius"
|
||||
color = "#FFC0CB"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/pink)
|
||||
outer_outfits = list(/obj/item/clothing/suit/jacket/puffer/vest)
|
||||
|
||||
/datum/team/gang/sleepingcarp
|
||||
name = "Sleeping Carp"
|
||||
color = "#800080"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/lightpurple)
|
||||
outer_outfits = list(/obj/item/clothing/suit/hooded/carp_costume)
|
||||
|
||||
/datum/team/gang/h
|
||||
name = "H"
|
||||
color = "#993333"
|
||||
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/civilian/chef)
|
||||
outer_outfits = list(/obj/item/clothing/suit/apron/chef)
|
||||
|
||||
/datum/team/gang/weed
|
||||
name = "Weed"
|
||||
color = "#6cd648"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/darkgreen)
|
||||
outer_outfits = list(/obj/item/clothing/suit/vapeshirt)
|
||||
@@ -1,38 +0,0 @@
|
||||
/obj/effect/decal/cleanable/crayon/Initialize(mapload, main, type, e_name, graf_rot, alt_icon = null)
|
||||
. = ..()
|
||||
if(type == "poseur tag")
|
||||
var/datum/team/gang/gang = pick(subtypesof(/datum/team/gang))
|
||||
var/gangname = initial(gang.name)
|
||||
icon = 'icons/effects/crayondecal.dmi'
|
||||
icon_state = "[gangname]"
|
||||
type = null
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/gang
|
||||
icon = 'icons/effects/crayondecal.dmi'
|
||||
layer = ABOVE_NORMAL_TURF_LAYER //Harder to hide
|
||||
plane = ABOVE_WALL_PLANE
|
||||
do_icon_rotate = FALSE //These are designed to always face south, so no rotation please.
|
||||
var/datum/team/gang/gang
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/gang/Initialize(mapload, datum/team/gang/G, e_name = "gang tag", rotation = 0, mob/user)
|
||||
if(!G)
|
||||
return INITIALIZE_HINT_QDEL
|
||||
gang = G
|
||||
var/newcolor = G.color
|
||||
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.
|
||||
.=..(mapload, newcolor, icon_state, e_name, rotation)
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/gang/Destroy()
|
||||
if(gang)
|
||||
var/area/territory = get_base_area(src)
|
||||
gang.territories -= territory.type
|
||||
gang.new_territories -= territory.type
|
||||
gang.lost_territories |= list(territory.type = territory.name)
|
||||
gang = null
|
||||
return ..()
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/NeverShouldHaveComeHere(turf/T)
|
||||
return isspaceturf(T) || islava(T) || istype(T, /turf/open/water) || ischasm(T)
|
||||
@@ -1,34 +0,0 @@
|
||||
/datum/atom_hud/antag/gang
|
||||
var/color = null
|
||||
|
||||
/datum/atom_hud/antag/gang/add_to_hud(atom/A)
|
||||
if(!A)
|
||||
return
|
||||
var/image/holder = A.hud_list[ANTAG_HUD]
|
||||
if(holder)
|
||||
holder.color = color
|
||||
..()
|
||||
|
||||
/datum/atom_hud/antag/gang/remove_from_hud(atom/A)
|
||||
if(!A)
|
||||
return
|
||||
var/image/holder = A.hud_list[ANTAG_HUD]
|
||||
if(holder)
|
||||
holder.color = null
|
||||
..()
|
||||
|
||||
/datum/atom_hud/antag/gang/join_hud(mob/M)
|
||||
if(!istype(M))
|
||||
CRASH("join_hud(): [M] ([M.type]) is not a mob!")
|
||||
var/image/holder = M.hud_list[ANTAG_HUD]
|
||||
if(holder)
|
||||
holder.color = color
|
||||
..()
|
||||
|
||||
/datum/atom_hud/antag/gang/leave_hud(mob/M)
|
||||
if(!istype(M))
|
||||
CRASH("leave_hud(): [M] ([M.type]) is not a mob!")
|
||||
var/image/holder = M.hud_list[ANTAG_HUD]
|
||||
if(holder)
|
||||
holder.color = null
|
||||
..()
|
||||
@@ -1,411 +0,0 @@
|
||||
/datum/gang_item
|
||||
var/name
|
||||
var/item_path
|
||||
var/cost
|
||||
var/spawn_msg
|
||||
var/category
|
||||
var/list/gang_whitelist = list()
|
||||
var/list/gang_blacklist = list()
|
||||
var/id
|
||||
|
||||
/datum/gang_item/proc/purchase(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool, check_canbuy = TRUE)
|
||||
if(check_canbuy && !can_buy(user, gang, gangtool))
|
||||
return FALSE
|
||||
var/real_cost = get_cost(user, gang, gangtool)
|
||||
if(!spawn_item(user, gang, gangtool))
|
||||
gang.adjust_influence(-real_cost)
|
||||
to_chat(user, "<span class='notice'>You bought \the [name].</span>")
|
||||
return TRUE
|
||||
|
||||
/datum/gang_item/proc/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) // If this returns anything other than null, something fucked up and influence won't lower.
|
||||
if(item_path)
|
||||
var/obj/item/O = new item_path(user.loc)
|
||||
user.put_in_hands(O)
|
||||
else
|
||||
return TRUE
|
||||
if(spawn_msg)
|
||||
to_chat(user, "[spawn_msg]")
|
||||
|
||||
/datum/gang_item/proc/can_buy(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
return gang && (gang.influence >= get_cost(user, gang, gangtool)) && can_see(user, gang, gangtool)
|
||||
|
||||
/datum/gang_item/proc/can_see(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
return TRUE
|
||||
|
||||
/datum/gang_item/proc/get_cost(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
return cost
|
||||
|
||||
/datum/gang_item/proc/get_cost_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
return "([get_cost(user, gang, gangtool)] Influence)"
|
||||
|
||||
/datum/gang_item/proc/get_name_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
return name
|
||||
|
||||
/datum/gang_item/proc/get_extra_info(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
return
|
||||
|
||||
///////////////////
|
||||
//CLOTHING
|
||||
///////////////////
|
||||
|
||||
/datum/gang_item/clothing
|
||||
category = "Purchase Gang Clothes (Only the jumpsuit and suit give you added influence):"
|
||||
|
||||
/datum/gang_item/clothing/under
|
||||
name = "Gang Uniform"
|
||||
id = "under"
|
||||
cost = 1
|
||||
|
||||
/datum/gang_item/clothing/under/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(gang.inner_outfits.len)
|
||||
var/outfit = pick(gang.inner_outfits)
|
||||
if(outfit)
|
||||
var/obj/item/O = new outfit(user.loc)
|
||||
user.put_in_hands(O)
|
||||
to_chat(user, "<span class='notice'> This is your gang's official uniform, wearing it will increase your influence")
|
||||
return
|
||||
return TRUE
|
||||
|
||||
/datum/gang_item/clothing/suit
|
||||
name = "Gang Armored Outerwear"
|
||||
id = "suit"
|
||||
cost = 1
|
||||
|
||||
/datum/gang_item/clothing/suit/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(gang.outer_outfits.len)
|
||||
var/outfit = pick(gang.outer_outfits)
|
||||
if(outfit)
|
||||
var/obj/item/O = new outfit(user.loc)
|
||||
O.armor = O.armor.setRating(melee = 25, bullet = 35, laser = 15, energy = 10, bomb = 30, bio = 0, rad = 0, fire = 30, acid = 30)
|
||||
O.desc += " Tailored for the [gang.name] Gang to offer the wearer moderate protection against ballistics and physical trauma."
|
||||
user.put_in_hands(O)
|
||||
to_chat(user, "<span class='notice'> This is your gang's official outerwear, wearing it will increase your influence")
|
||||
return
|
||||
return TRUE
|
||||
|
||||
/datum/gang_item/clothing/hat
|
||||
name = "Pimp Hat"
|
||||
id = "hat"
|
||||
cost = 16
|
||||
item_path = /obj/item/clothing/head/collectable/petehat/gang
|
||||
|
||||
/obj/item/clothing/head/collectable/petehat/gang
|
||||
name = "pimpin' hat"
|
||||
desc = "The undisputed king of style."
|
||||
|
||||
/datum/gang_item/clothing/mask
|
||||
name = "Golden Death Mask"
|
||||
id = "mask"
|
||||
cost = 18
|
||||
item_path = /obj/item/clothing/mask/gskull
|
||||
|
||||
/obj/item/clothing/mask/gskull
|
||||
name = "golden death mask"
|
||||
icon_state = "gskull"
|
||||
desc = "Strike terror, and envy, into the hearts of your enemies."
|
||||
|
||||
/datum/gang_item/clothing/shoes
|
||||
name = "Bling Boots"
|
||||
id = "boots"
|
||||
cost = 20
|
||||
item_path = /obj/item/clothing/shoes/gang
|
||||
|
||||
/obj/item/clothing/shoes/gang
|
||||
name = "blinged-out boots"
|
||||
desc = "Stand aside peasants."
|
||||
icon_state = "bling"
|
||||
|
||||
/datum/gang_item/clothing/neck
|
||||
name = "Gold Necklace"
|
||||
id = "necklace"
|
||||
cost = 9
|
||||
item_path = /obj/item/clothing/neck/necklace/dope
|
||||
|
||||
/datum/gang_item/clothing/hands
|
||||
name = "Decorative Brass Knuckles"
|
||||
id = "hand"
|
||||
cost = 11
|
||||
item_path = /obj/item/clothing/gloves/gang
|
||||
|
||||
/obj/item/clothing/gloves/gang
|
||||
name = "braggadocio's brass knuckles"
|
||||
desc = "Purely decorative, don't find out the hard way."
|
||||
icon_state = "knuckles"
|
||||
w_class = 3
|
||||
|
||||
/datum/gang_item/clothing/shades //Addition: Why not have cool shades on a gang member anyways?
|
||||
name = "Cool Sunglasses"
|
||||
id = "glasses"
|
||||
cost = 5
|
||||
item_path = /obj/item/clothing/glasses/sunglasses
|
||||
|
||||
/datum/gang_item/clothing/belt
|
||||
name = "Badass Belt"
|
||||
id = "belt"
|
||||
cost = 13
|
||||
item_path = /obj/item/storage/belt/military/gang
|
||||
|
||||
/obj/item/storage/belt/military/gang
|
||||
name = "badass belt"
|
||||
icon_state = "gangbelt"
|
||||
item_state = "gang"
|
||||
desc = "The belt buckle simply reads 'BAMF'."
|
||||
|
||||
///////////////////
|
||||
//WEAPONS
|
||||
///////////////////
|
||||
|
||||
/datum/gang_item/weapon
|
||||
category = "Purchase Weapons:"
|
||||
|
||||
/datum/gang_item/weapon/ammo
|
||||
|
||||
/datum/gang_item/weapon/shuriken
|
||||
name = "Shuriken"
|
||||
id = "shuriken"
|
||||
cost = 2
|
||||
item_path = /obj/item/throwing_star
|
||||
|
||||
/datum/gang_item/weapon/switchblade
|
||||
name = "Switchblade"
|
||||
id = "switchblade"
|
||||
cost = 5
|
||||
item_path = /obj/item/switchblade
|
||||
|
||||
/datum/gang_item/weapon/surplus //For when a gang boss is extra broke or cheap.
|
||||
name = "Surplus Rifle"
|
||||
id = "surplus"
|
||||
cost = 6
|
||||
item_path = /obj/item/gun/ballistic/automatic/surplus
|
||||
|
||||
/datum/gang_item/weapon/ammo/surplus_ammo
|
||||
name = "Surplus Rifle Ammo"
|
||||
id = "surplus_ammo"
|
||||
cost = 3
|
||||
item_path = /obj/item/ammo_box/magazine/m10mm/rifle
|
||||
|
||||
/datum/gang_item/weapon/improvised
|
||||
name = "Sawn-Off Improvised Shotgun"
|
||||
id = "sawn"
|
||||
cost = 5
|
||||
item_path = /obj/item/gun/ballistic/revolver/doublebarrel/improvised/sawn
|
||||
|
||||
/datum/gang_item/weapon/ammo/improvised_ammo
|
||||
name = "Box of Buckshot"
|
||||
id = "buckshot"
|
||||
cost = 5
|
||||
item_path = /obj/item/storage/box/lethalshot
|
||||
|
||||
/datum/gang_item/weapon/pistol
|
||||
name = "10mm Pistol"
|
||||
id = "pistol"
|
||||
cost = 25
|
||||
item_path = /obj/item/gun/ballistic/automatic/pistol
|
||||
|
||||
/datum/gang_item/weapon/ammo/pistol_ammo
|
||||
name = "10mm Ammo"
|
||||
id = "pistol_ammo"
|
||||
cost = 10
|
||||
item_path = /obj/item/ammo_box/magazine/m10mm
|
||||
|
||||
/datum/gang_item/weapon/sniper
|
||||
name = "Black Market .50cal Sniper Rifle"
|
||||
id = "sniper"
|
||||
cost = 35
|
||||
item_path = /obj/item/gun/ballistic/automatic/sniper_rifle
|
||||
|
||||
/datum/gang_item/weapon/ammo/sniper_ammo
|
||||
name = "Smuggled .50cal Sniper Rounds"
|
||||
id = "sniper_ammo"
|
||||
cost = 15
|
||||
item_path = /obj/item/ammo_box/magazine/sniper_rounds
|
||||
|
||||
/*/datum/gang_item/weapon/ammo/sleeper_ammo //no. absolutely no.
|
||||
name = "Illicit Soporific Cartridges"
|
||||
id = "sniper_ammo"
|
||||
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"
|
||||
id = "MG"
|
||||
cost = 45
|
||||
item_path = /obj/machinery/manned_turret
|
||||
spawn_msg = "<span class='notice'>The mounted machine gun features enhanced responsiveness. Hold down on the trigger while firing to control where you're shooting.</span>"
|
||||
|
||||
/datum/gang_item/weapon/machinegun/spawn_item(mob/living/carbon/user, obj/item/device/gangtool/gangtool)
|
||||
new item_path(user.loc)
|
||||
to_chat(user, spawn_msg)
|
||||
|
||||
/datum/gang_item/weapon/uzi
|
||||
name = "Uzi SMG"
|
||||
id = "uzi"
|
||||
cost = 50
|
||||
item_path = /obj/item/gun/ballistic/automatic/mini_uzi
|
||||
|
||||
/datum/gang_item/weapon/ammo/uzi_ammo
|
||||
name = "Uzi Ammo"
|
||||
id = "uzi_ammo"
|
||||
cost = 20
|
||||
item_path = /obj/item/ammo_box/magazine/uzim9mm
|
||||
|
||||
///////////////////
|
||||
//EQUIPMENT
|
||||
///////////////////
|
||||
|
||||
/datum/gang_item/equipment
|
||||
category = "Purchase Equipment:"
|
||||
|
||||
/datum/gang_item/equipment/spraycan
|
||||
name = "Territory Spraycan"
|
||||
id = "spraycan"
|
||||
cost = 1
|
||||
item_path = /obj/item/toy/crayon/spraycan/gang
|
||||
|
||||
/datum/gang_item/equipment/spraycan/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
var/obj/item/O = new item_path(user.loc, gang)
|
||||
user.put_in_hands(O)
|
||||
|
||||
/datum/gang_item/equipment/sharpener
|
||||
name = "Sharpener"
|
||||
id = "whetstone"
|
||||
cost = 3
|
||||
item_path = /obj/item/sharpener
|
||||
|
||||
/datum/gang_item/equipment/emp
|
||||
name = "EMP Grenade"
|
||||
id = "EMP"
|
||||
cost = 7
|
||||
item_path = /obj/item/grenade/empgrenade
|
||||
|
||||
/datum/gang_item/equipment/c4
|
||||
name = "C4 Explosive"
|
||||
id = "c4"
|
||||
cost = 7
|
||||
item_path = /obj/item/grenade/plastic/c4
|
||||
|
||||
/datum/gang_item/equipment/frag
|
||||
name = "Fragmentation Grenade"
|
||||
id = "frag nade"
|
||||
cost = 5
|
||||
item_path = /obj/item/grenade/frag
|
||||
|
||||
/datum/gang_item/equipment/implant_breaker
|
||||
name = "Implant Breaker"
|
||||
id = "implant_breaker"
|
||||
cost = 10
|
||||
item_path = /obj/item/implanter/gang
|
||||
|
||||
/datum/gang_item/equipment/implant_breaker/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
var/obj/item/O = new item_path(user.loc, gang)
|
||||
user.put_in_hands(O)
|
||||
to_chat(user, "<span class='notice'>The <b>implant breaker</b> is a single-use device that destroys all implants within the target before trying to recruit them to your gang. Also works on enemy gangsters.</span>")
|
||||
|
||||
/datum/gang_item/equipment/wetwork_boots
|
||||
name = "Wetwork boots"
|
||||
id = "wetwork"
|
||||
cost = 8
|
||||
item_path = /obj/item/clothing/shoes/combat/gang
|
||||
|
||||
/obj/item/clothing/shoes/combat/gang
|
||||
name = "Wetwork boots"
|
||||
desc = "A gang's best hitmen are prepared for anything."
|
||||
permeability_coefficient = 0.01
|
||||
clothing_flags = NOSLIP
|
||||
|
||||
/datum/gang_item/equipment/shield
|
||||
name = "Riot Shield"
|
||||
id = "riot_shield"
|
||||
cost = 25
|
||||
item_path = /obj/item/shield/riot
|
||||
|
||||
/datum/gang_item/equipment/gangsheild
|
||||
name = "Tower Shield"
|
||||
id = "metal"
|
||||
cost = 45 //High block of melee and even higher for bullets
|
||||
item_path = /obj/item/shield/riot/tower
|
||||
|
||||
/datum/gang_item/equipment/pen
|
||||
name = "Recruitment Pen"
|
||||
id = "pen"
|
||||
cost = 20
|
||||
item_path = /obj/item/pen/gang
|
||||
spawn_msg = "<span class='notice'>More <b>recruitment pens</b> will allow you to recruit gangsters faster. Only gang leaders can recruit with pens.</span>"
|
||||
|
||||
/datum/gang_item/equipment/pen/purchase(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(..())
|
||||
gangtool.free_pen = FALSE
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/gang_item/equipment/pen/get_cost(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(gangtool && gangtool.free_pen)
|
||||
return 0
|
||||
return ..()
|
||||
|
||||
/datum/gang_item/equipment/pen/get_cost_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(gangtool && gangtool.free_pen)
|
||||
return "(GET ONE FREE)"
|
||||
return ..()
|
||||
|
||||
/datum/gang_item/equipment/gangtool
|
||||
id = "gangtool"
|
||||
cost = 5
|
||||
|
||||
/datum/gang_item/equipment/gangtool/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
var/item_type
|
||||
if(gang)
|
||||
item_type = /obj/item/device/gangtool/spare/lt
|
||||
if(gang.leaders.len < MAX_LEADERS_GANG)
|
||||
to_chat(user, "<span class='notice'><b>Gangtools</b> allow you to promote a gangster to be your Lieutenant, enabling them to recruit and purchase items like you. Simply have them register the gangtool. You may promote up to [MAX_LEADERS_GANG-gang.leaders.len] more Lieutenants</span>")
|
||||
else
|
||||
item_type = /obj/item/device/gangtool/spare
|
||||
var/obj/item/device/gangtool/spare/tool = new item_type(user.loc)
|
||||
user.put_in_hands(tool)
|
||||
|
||||
/datum/gang_item/equipment/gangtool/get_name_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(gang && (gang.leaders.len < gang.max_leaders))
|
||||
return "Promote a Gangster"
|
||||
return "Spare Gangtool"
|
||||
|
||||
/datum/gang_item/equipment/dominator
|
||||
name = "Station Dominator"
|
||||
id = "dominator"
|
||||
cost = 30
|
||||
item_path = /obj/machinery/dominator
|
||||
spawn_msg = "<span class='notice'>The <b>dominator</b> will secure your gang's dominance over the station. Turn it on when you are ready to defend it.</span>"
|
||||
|
||||
/datum/gang_item/equipment/dominator/can_buy(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(!gang || !gang.dom_attempts)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/gang_item/equipment/dominator/get_name_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(!gang || !gang.dom_attempts)
|
||||
return ..()
|
||||
return "<b>[..()]</b>"
|
||||
|
||||
/datum/gang_item/equipment/dominator/get_cost_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(!gang || !gang.dom_attempts)
|
||||
return "(Out of stock)"
|
||||
return ..()
|
||||
|
||||
/datum/gang_item/equipment/dominator/get_extra_info(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(gang)
|
||||
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_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
|
||||
for(var/obj/obj in get_turf(user))
|
||||
if(obj.density)
|
||||
to_chat(user, "<span class='warning'>There's not enough room here!</span>")
|
||||
return FALSE
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/gang_item/equipment/dominator/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
new item_path(user.loc)
|
||||
to_chat(user, spawn_msg)
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Gang Boss Pens
|
||||
*/
|
||||
/obj/item/pen/gang
|
||||
var/cooldown
|
||||
var/last_used
|
||||
|
||||
/obj/item/pen/gang/Initialize()
|
||||
. = ..()
|
||||
last_used = world.time
|
||||
|
||||
/obj/item/pen/gang/attack(mob/living/M, mob/user, stealth = TRUE) //ha
|
||||
if(!istype(M))
|
||||
return
|
||||
if(!ishuman(M) || !ishuman(user) || M.stat == DEAD)
|
||||
return ..()
|
||||
//var/datum/antagonist/gang/boss/L = user.mind.has_antag_datum(/datum/antagonist/gang/boss) //Pen works with bosses only.
|
||||
var/datum/antagonist/gang/L = user.mind.has_antag_datum(/datum/antagonist/gang) //Pen works with anyone in gang.
|
||||
if(!L)
|
||||
return ..()
|
||||
if(!..())
|
||||
return
|
||||
if(cooldown)
|
||||
to_chat(user, "<span class='warning'>[src] needs more time to recharge before it can be used.</span>")
|
||||
return
|
||||
if(!M.client || !M.mind)
|
||||
to_chat(user, "<span class='warning'>A braindead gangster is an useless gangster!</span>")
|
||||
return
|
||||
var/datum/team/gang/gang = L.gang
|
||||
if(!add_gangster(user, gang, M.mind))
|
||||
return
|
||||
cooldown = TRUE
|
||||
icon_state = "pen_blink"
|
||||
var/cooldown_time = 600/gang.leaders.len
|
||||
addtimer(CALLBACK(src, .proc/cooldown), cooldown_time)
|
||||
|
||||
/obj/item/pen/gang/proc/cooldown()
|
||||
cooldown = FALSE
|
||||
icon_state = "pen"
|
||||
var/mob/M = loc
|
||||
if(istype(M))
|
||||
to_chat(M, "<span class='notice'>[icon2html(src, M)] [src][(loc == M)?(""):(" in your [loc]")] vibrates softly. It is ready to be used again.</span>")
|
||||
|
||||
/obj/item/pen/gang/proc/add_gangster(mob/user, datum/team/gang/gang, datum/mind/gangster_mind, check = TRUE) // Basically a wrapper to add_antag_datum.
|
||||
var/datum/antagonist/dudegang = gangster_mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(dudegang)
|
||||
if(dudegang == gang)
|
||||
to_chat(user, "<span class='danger'>This mind is already controlled by your gang!</span>")
|
||||
return
|
||||
to_chat(user, "<span class='danger'>This mind is already controlled by someone else!</span>")
|
||||
return
|
||||
if(check && HAS_TRAIT(gangster_mind.current, TRAIT_MINDSHIELD)) //Check to see if the potential gangster is implanted
|
||||
to_chat(user, "<span class='danger'>This mind is too strong to control!</span>")
|
||||
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.DefaultCombatKnockdown(100)
|
||||
gangster_mind.add_antag_datum(/datum/antagonist/gang, gang)
|
||||
return TRUE
|
||||
@@ -1,67 +0,0 @@
|
||||
//gang.dm
|
||||
//Gang War Game Mode
|
||||
GLOBAL_LIST_INIT(possible_gangs, subtypesof(/datum/team/gang))
|
||||
GLOBAL_LIST_EMPTY(gangs)
|
||||
/datum/game_mode/gang
|
||||
name = "gang war"
|
||||
config_tag = "gang"
|
||||
antag_flag = ROLE_GANG
|
||||
chaos = 9
|
||||
restricted_jobs = list("Prisoner", "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
|
||||
enemy_minimum_age = 14
|
||||
|
||||
announce_span = "danger"
|
||||
announce_text = "A violent turf war has erupted on the station!\n\
|
||||
<span class='danger'>Gangsters</span>: Take over the station with a dominator.\n\
|
||||
<span class='notice'>Crew</span>: Prevent the gangs from expanding and initiating takeover."
|
||||
|
||||
var/list/datum/mind/gangboss_candidates = list()
|
||||
|
||||
/datum/game_mode/gang/generate_report()
|
||||
return "Cybersun Industries representatives claimed that they, in joint research with the Tiger Cooperative, have made a major breakthrough in brainwashing technology, and have \
|
||||
made the nanobots that apply the \"conversion\" very small and capable of fitting into usually innocent objects - namely, pens. While they refused to outsource this technology for \
|
||||
months to come due to its flaws, they reported some as missing but passed it off to carelessness. At Central Command, we don't like mysteries, and we have reason to believe that this \
|
||||
technology was stolen for anti-Nanotrasen use. Be on the lookout for territory claims and unusually violent crew behavior, applying mindshield implants as necessary."
|
||||
|
||||
/datum/game_mode/gang/pre_setup()
|
||||
if(CONFIG_GET(flag/protect_roles_from_antagonist))
|
||||
restricted_jobs += protected_jobs
|
||||
|
||||
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
|
||||
restricted_jobs += "Assistant"
|
||||
|
||||
//Spawn more bosses depending on server population
|
||||
var/gangs_to_create = 2
|
||||
if(prob(num_players()) && num_players() > 1.5*required_players)
|
||||
gangs_to_create++
|
||||
if(prob(num_players()) && num_players() > 2*required_players)
|
||||
gangs_to_create++
|
||||
gangs_to_create = min(gangs_to_create, GLOB.possible_gangs.len)
|
||||
|
||||
for(var/i in 1 to gangs_to_create)
|
||||
if(!antag_candidates.len)
|
||||
break
|
||||
|
||||
//Now assign a boss for the gang
|
||||
var/datum/mind/boss = pick_n_take(antag_candidates)
|
||||
antag_candidates -= boss
|
||||
gangboss_candidates += boss
|
||||
boss.restricted_roles = restricted_jobs
|
||||
|
||||
if(gangboss_candidates.len < 1) //Need at least one gangs
|
||||
return
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/game_mode/gang/post_setup()
|
||||
set waitfor = FALSE
|
||||
..()
|
||||
for(var/i in gangboss_candidates)
|
||||
var/datum/mind/M = i
|
||||
var/datum/antagonist/gang/boss/B = new()
|
||||
M.add_antag_datum(B)
|
||||
B.equip_gang()
|
||||
@@ -1,259 +0,0 @@
|
||||
//gangtool device
|
||||
/obj/item/device/gangtool
|
||||
name = "suspicious device"
|
||||
desc = "A strange device of sorts. Hard to really make out what it actually does if you don't know how to operate it."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "gangtool"
|
||||
item_state = "radio"
|
||||
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
|
||||
throwforce = 0
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
throw_speed = 3
|
||||
throw_range = 7
|
||||
flags_1 = CONDUCT_1
|
||||
var/datum/team/gang/gang //Which gang uses this?
|
||||
var/recalling = 0
|
||||
var/outfits = 2
|
||||
var/free_pen = 0
|
||||
var/promotable = FALSE
|
||||
var/static/list/buyable_items = list()
|
||||
var/list/tags = list()
|
||||
|
||||
/obj/item/device/gangtool/Initialize()
|
||||
. = ..()
|
||||
update_icon()
|
||||
for(var/i in subtypesof(/datum/gang_item))
|
||||
var/datum/gang_item/G = i
|
||||
var/id = initial(G.id)
|
||||
var/cat = initial(G.category)
|
||||
if(id)
|
||||
if(!islist(buyable_items[cat]))
|
||||
buyable_items[cat] = list()
|
||||
buyable_items[cat][id] = new G
|
||||
/obj/item/device/gangtool/Destroy()
|
||||
if(gang)
|
||||
gang.gangtools -= src
|
||||
return ..()
|
||||
|
||||
/obj/item/device/gangtool/attack_self(mob/user)
|
||||
..()
|
||||
if (!can_use(user))
|
||||
return
|
||||
var/datum/antagonist/gang/boss/L = user.mind.has_antag_datum(/datum/antagonist/gang/boss)
|
||||
var/dat
|
||||
if(!gang)
|
||||
dat += "This device is not registered.<br><br>"
|
||||
if(L)
|
||||
if(promotable && L.gang.leaders.len < L.gang.max_leaders)
|
||||
dat += "Give this device to another member of your organization to use to promote them to Lieutenant.<br><br>"
|
||||
dat += "If this is meant as a spare device for yourself:<br>"
|
||||
dat += "<a href='?src=[REF(src)];register=1'>Register Device as Spare</a><br>"
|
||||
else if(promotable)
|
||||
var/datum/antagonist/gang/sweet = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(sweet.gang.leaders.len < sweet.gang.max_leaders)
|
||||
dat += "You have been selected for a promotion!<br>"
|
||||
dat += "<a href='?src=[REF(src)];register=1'>Accept Promotion</a><br>"
|
||||
else
|
||||
dat += "No promotions available: All positions filled.<br>"
|
||||
else
|
||||
dat += "This device is not authorized to promote.<br>"
|
||||
else
|
||||
if(gang.domination_time != NOT_DOMINATING)
|
||||
dat += "<center><font color='red'>Takeover In Progress:<br><B>[DisplayTimeText(gang.domination_time_remaining() * 10)] remain</B></font></center>"
|
||||
|
||||
dat += "Registration: <B>[gang.name] Gang Boss</B><br>"
|
||||
dat += "Organization Size: <B>[gang.members.len]</B> | Station Control: <B>[gang.territories.len] territories under control.</B> | Influence: <B>[gang.influence]</B><br>"
|
||||
dat += "Time until Influence grows: <B>[time2text(gang.next_point_time - world.time, "mm:ss")]</B><br>"
|
||||
dat += "<a href='?src=[REF(src)];commute=1'>Send message to Gang</a><br>"
|
||||
dat += "<a href='?src=[REF(src)];recall=1'>Recall shuttle</a><br>"
|
||||
dat += "<hr>"
|
||||
for(var/cat in buyable_items)
|
||||
dat += "<b>[cat]</b><br>"
|
||||
for(var/id in buyable_items[cat])
|
||||
var/datum/gang_item/G = buyable_items[cat][id]
|
||||
if(!G.can_see(user, gang, src))
|
||||
continue
|
||||
|
||||
var/cost = G.get_cost_display(user, gang, src)
|
||||
if(cost)
|
||||
dat += cost + " "
|
||||
|
||||
var/toAdd = G.get_name_display(user, gang, src)
|
||||
if(G.can_buy(user, gang, src))
|
||||
toAdd = "<a href='?src=[REF(src)];purchase=1;id=[id];cat=[cat]'>[toAdd]</a>"
|
||||
dat += toAdd
|
||||
var/extra = G.get_extra_info(user, gang, src)
|
||||
if(extra)
|
||||
dat += "<br><i>[extra]</i>"
|
||||
dat += "<br>"
|
||||
dat += "<br>"
|
||||
|
||||
dat += "<a href='?src=[REF(src)];choice=refresh'>Refresh</a><br>"
|
||||
|
||||
var/datum/browser/popup = new(user, "gangtool", "Welcome to GangTool v4.0", 340, 625)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
|
||||
/obj/item/device/gangtool/Topic(href, href_list)
|
||||
if(!can_use(usr))
|
||||
return
|
||||
|
||||
add_fingerprint(usr)
|
||||
|
||||
if(href_list["register"])
|
||||
register_device(usr)
|
||||
|
||||
else if(!gang) //Gangtool must be registered before you can use the functions below
|
||||
return
|
||||
|
||||
if(href_list["purchase"])
|
||||
if(islist(buyable_items[href_list["cat"]]))
|
||||
var/list/L = buyable_items[href_list["cat"]]
|
||||
var/datum/gang_item/G = L[href_list["id"]]
|
||||
if(G && G.can_buy(usr, gang, src))
|
||||
G.purchase(usr, gang, src, FALSE)
|
||||
|
||||
if(href_list["commute"])
|
||||
ping_gang(usr)
|
||||
if(href_list["recall"])
|
||||
recall(usr)
|
||||
attack_self(usr)
|
||||
|
||||
/obj/item/device/gangtool/update_icon()
|
||||
overlays.Cut()
|
||||
var/image/I = new(icon, "[icon_state]-overlay")
|
||||
if(gang)
|
||||
I.color = gang.color
|
||||
overlays.Add(I)
|
||||
|
||||
/obj/item/device/gangtool/proc/ping_gang(mob/user)
|
||||
if(!can_use(user))
|
||||
return
|
||||
var/message = stripped_input(user,"Discreetly send a gang-wide message.","Send Message")
|
||||
if(!message || !can_use(user))
|
||||
return
|
||||
if(!is_station_level(user.z))
|
||||
to_chat(user, "<span class='info'>[icon2html(src, user)]Error: Station out of range.</span>")
|
||||
return
|
||||
if(gang.members.len)
|
||||
var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(!G)
|
||||
return
|
||||
var/ping = "<span class='danger'><B><i>[gang.name] [G.message_name] [user.real_name]</i>: [message]</B></span>"
|
||||
for(var/datum/mind/ganger in gang.members)
|
||||
if(ganger.current && is_station_level(ganger.current.z) && (ganger.current.stat == CONSCIOUS))
|
||||
to_chat(ganger.current, ping)
|
||||
for(var/mob/M in GLOB.dead_mob_list)
|
||||
var/link = FOLLOW_LINK(M, user)
|
||||
to_chat(M, "[link] [ping]")
|
||||
user.log_talk(message,LOG_SAY, tag="[gang.name] gangster")
|
||||
|
||||
/obj/item/device/gangtool/proc/register_device(mob/user)
|
||||
if(gang) //It's already been registered!
|
||||
return
|
||||
var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(G)
|
||||
gang = G.gang
|
||||
gang.gangtools += src
|
||||
update_icon()
|
||||
if(!(user.mind in gang.leaders) && promotable)
|
||||
G.promote()
|
||||
free_pen = TRUE
|
||||
gang.message_gangtools("[user] has been promoted to Lieutenant.")
|
||||
to_chat(user, "The <b>Gangtool</b> you registered will allow you to purchase weapons and equipment, and send messages to your gang.")
|
||||
to_chat(user, "Unlike regular gangsters, you may use <b>recruitment pens</b> to add recruits to your gang. Use them on unsuspecting crew members to recruit them. Don't forget to get your one free pen from the gangtool.")
|
||||
else
|
||||
to_chat(user, "<span class='warning'>ACCESS DENIED: Unauthorized user.</span>")
|
||||
|
||||
/obj/item/device/gangtool/proc/recall(mob/user)
|
||||
if(!recallchecks(user))
|
||||
return
|
||||
if(recalling)
|
||||
to_chat(user, "<span class='warning'>Error: Recall already in progress.</span>")
|
||||
return
|
||||
gang.message_gangtools("[user] is attempting to recall the emergency shuttle.")
|
||||
recalling = TRUE
|
||||
to_chat(user, "<span class='info'>[icon2html(src, loc)]Generating shuttle recall order with codes retrieved from last call signal...</span>")
|
||||
addtimer(CALLBACK(src, .proc/recall2, user), rand(100,300))
|
||||
|
||||
/obj/item/device/gangtool/proc/recall2(mob/user)
|
||||
if(!recallchecks(user))
|
||||
return
|
||||
to_chat(user, "<span class='info'>[icon2html(src, loc)]Shuttle recall order generated. Accessing station long-range communication arrays...</span>")
|
||||
addtimer(CALLBACK(src, .proc/recall3, user), rand(100,300))
|
||||
|
||||
/obj/item/device/gangtool/proc/recall3(mob/user)
|
||||
if(!recallchecks(user))
|
||||
return
|
||||
var/list/living_crew = list()//shamelessly copied from mulligan code, there should be a helper for this
|
||||
for(var/mob/Player in GLOB.mob_list)
|
||||
if(Player.mind && Player.stat != DEAD && !isnewplayer(Player) && !isbrain(Player) && Player.client)
|
||||
living_crew += Player
|
||||
var/malc = CONFIG_GET(number/midround_antag_life_check)
|
||||
if(living_crew.len / GLOB.joined_player_list.len <= malc) //Shuttle cannot be recalled if too many people died
|
||||
to_chat(user, "<span class='warning'>[icon2html(src, user)]Error: Station communication systems compromised. Unable to establish connection.</span>")
|
||||
recalling = FALSE
|
||||
return
|
||||
to_chat(user, "<span class='info'>[icon2html(src, loc)]Comm arrays accessed. Broadcasting recall signal...</span>")
|
||||
addtimer(CALLBACK(src, .proc/recallfinal, user), rand(100,300))
|
||||
|
||||
/obj/item/device/gangtool/proc/recallfinal(mob/user)
|
||||
if(!recallchecks(user))
|
||||
return
|
||||
recalling = FALSE
|
||||
log_game("[key_name(user)] has tried to recall the shuttle with a gangtool.")
|
||||
message_admins("[key_name_admin(user)] has tried to recall the shuttle with a gangtool.", 1)
|
||||
if(SSshuttle.cancelEvac(user))
|
||||
gang.recalls--
|
||||
return TRUE
|
||||
|
||||
to_chat(user, "<span class='info'>[icon2html(src, loc)]No response recieved. Emergency shuttle cannot be recalled at this time.</span>")
|
||||
return
|
||||
|
||||
/obj/item/device/gangtool/proc/recallchecks(mob/user)
|
||||
if(!can_use(user))
|
||||
return
|
||||
if(SSshuttle.emergencyNoRecall)
|
||||
return
|
||||
if(!gang.recalls)
|
||||
to_chat(user, "<span class='warning'>Error: Unable to access communication arrays. Firewall has logged our signature and is blocking all further attempts.</span>")
|
||||
return
|
||||
if(SSshuttle.emergency.mode != SHUTTLE_CALL) //Shuttle can only be recalled when it's moving to the station
|
||||
to_chat(user, "<span class='warning'>[icon2html(src, user)]Emergency shuttle cannot be recalled at this time.</span>")
|
||||
recalling = FALSE
|
||||
return
|
||||
if(!gang.dom_attempts)
|
||||
to_chat(user, "<span class='warning'>[icon2html(src, user)]Error: Unable to access communication arrays. Firewall has logged our signature and is blocking all further attempts.</span>")
|
||||
recalling = FALSE
|
||||
return
|
||||
if(!is_station_level(user.z)) //Shuttle can only be recalled while on station
|
||||
to_chat(user, "<span class='warning'>[icon2html(src, user)]Error: Device out of range of station communication arrays.</span>")
|
||||
recalling = FALSE
|
||||
return
|
||||
return TRUE
|
||||
|
||||
/obj/item/device/gangtool/proc/can_use(mob/living/carbon/human/user)
|
||||
if(!istype(user))
|
||||
return
|
||||
if(user.incapacitated())
|
||||
return
|
||||
if(!(src in user.contents))
|
||||
return
|
||||
if(!user.mind)
|
||||
return
|
||||
var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(!G)
|
||||
to_chat(user, "<span class='notice'>Huh, what's this?</span>")
|
||||
return
|
||||
if(!isnull(gang) && G.gang != gang)
|
||||
to_chat(user, "<span class='danger'>You cannot use gang tools owned by enemy gangs!</span>")
|
||||
return
|
||||
return TRUE
|
||||
|
||||
|
||||
/obj/item/device/gangtool/spare
|
||||
outfits = TRUE
|
||||
|
||||
/obj/item/device/gangtool/spare/lt
|
||||
promotable = TRUE
|
||||
@@ -1,61 +0,0 @@
|
||||
/obj/item/implant/gang
|
||||
name = "gang implant"
|
||||
desc = "Makes you a gangster or such."
|
||||
activated = 0
|
||||
var/datum/team/gang/gang
|
||||
|
||||
/obj/item/implant/gang/Initialize(loc, setgang)
|
||||
.=..()
|
||||
gang = setgang
|
||||
|
||||
/obj/item/implant/gang/Destroy()
|
||||
gang = null
|
||||
return ..()
|
||||
|
||||
/obj/item/implant/gang/get_data()
|
||||
var/dat = {"<b>Implant Specifications:</b><BR>
|
||||
<b>Name:</b> Criminal brainwash implant<BR>
|
||||
<b>Life:</b> A few seconds after injection.<BR>
|
||||
<b>Important Notes:</b> Illegal<BR>
|
||||
<HR>
|
||||
<b>Implant Details:</b><BR>
|
||||
<b>Function:</b> Contains a small pod of nanobots that change the host's brain to be loyal to a certain organization.<BR>
|
||||
<b>Special Features:</b> This device will also emit a small EMP pulse, destroying any other implants within the host's brain.<BR>
|
||||
<b>Integrity:</b> Implant's EMP function will destroy itself in the process."}
|
||||
return dat
|
||||
|
||||
/obj/item/implant/gang/implant(mob/living/target, mob/user, silent = 0)
|
||||
if(!target || !target.mind || target.stat == DEAD)
|
||||
return 0
|
||||
var/datum/antagonist/gang/G = target.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(G && G.gang == G)
|
||||
return 0 // it's pointless
|
||||
if(..())
|
||||
for(var/obj/item/implant/I in target.implants)
|
||||
if(I != src)
|
||||
qdel(I)
|
||||
|
||||
if(ishuman(target))
|
||||
var/success
|
||||
if(G)
|
||||
if(!istype(G, /datum/antagonist/gang/boss))
|
||||
success = TRUE //Was not a gang boss, convert as usual
|
||||
target.mind.remove_antag_datum(/datum/antagonist/gang)
|
||||
else
|
||||
success = TRUE
|
||||
if(!success)
|
||||
target.visible_message("<span class='warning'>[target] seems to resist the implant!</span>", "<span class='warning'>You feel the influence of your enemies try to invade your mind!</span>")
|
||||
return FALSE
|
||||
target.mind.add_antag_datum(/datum/antagonist/gang, gang)
|
||||
qdel(src)
|
||||
return TRUE
|
||||
|
||||
/obj/item/implanter/gang
|
||||
name = "implanter (gang)"
|
||||
|
||||
/obj/item/implanter/gang/Initialize(loc, gang)
|
||||
if(!gang)
|
||||
qdel(src)
|
||||
return
|
||||
imp = new /obj/item/implant/gang(src,gang)
|
||||
.=..()
|
||||
@@ -154,7 +154,7 @@
|
||||
|
||||
if(tc)
|
||||
var/obj/item/U = new uplink_type(H, H.key, tc)
|
||||
H.equip_to_slot_or_del(U, SLOT_IN_BACKPACK)
|
||||
H.equip_to_slot_or_del(U, ITEM_SLOT_BACKPACK)
|
||||
|
||||
var/obj/item/implant/weapons_auth/W = new
|
||||
W.implant(H)
|
||||
@@ -170,7 +170,7 @@
|
||||
mask = /obj/item/clothing/mask/gas/syndicate
|
||||
suit = /obj/item/clothing/suit/space/hardsuit/syndi
|
||||
r_pocket = /obj/item/tank/internals/emergency_oxygen/engi
|
||||
internals_slot = SLOT_R_STORE
|
||||
internals_slot = ITEM_SLOT_RPOCKET
|
||||
belt = /obj/item/storage/belt/military
|
||||
r_hand = /obj/item/gun/ballistic/automatic/shotgun/bulldog
|
||||
backpack_contents = list(/obj/item/storage/box/survival/syndie=1,\
|
||||
@@ -186,7 +186,7 @@
|
||||
suit = /obj/item/clothing/suit/space/syndicate/black/red
|
||||
head = /obj/item/clothing/head/helmet/space/syndicate/black/red
|
||||
r_pocket = /obj/item/tank/internals/emergency_oxygen/engi
|
||||
internals_slot = SLOT_R_STORE
|
||||
internals_slot = ITEM_SLOT_RPOCKET
|
||||
belt = /obj/item/storage/belt/military
|
||||
backpack_contents = list(/obj/item/storage/box/survival/syndie=1,\
|
||||
/obj/item/tank/jetpack/oxygen/harness=1,\
|
||||
|
||||
@@ -13,6 +13,7 @@ GLOBAL_LIST_EMPTY(objectives)
|
||||
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.
|
||||
var/objective_name = "Objective" //name used in printing this objective (Objective #1)
|
||||
|
||||
/datum/objective/New(var/text)
|
||||
GLOB.objectives += src // CITADEL EDIT FOR CRYOPODS
|
||||
@@ -164,7 +165,7 @@ If not set, defaults to check_completion instead. Set it. It's used by cryo.
|
||||
if(receiver && receiver.current)
|
||||
if(ishuman(receiver.current))
|
||||
var/mob/living/carbon/human/H = receiver.current
|
||||
var/list/slots = list("backpack" = SLOT_IN_BACKPACK)
|
||||
var/list/slots = list("backpack" = ITEM_SLOT_BACKPACK)
|
||||
for(var/eq_path in special_equipment)
|
||||
var/obj/O = new eq_path
|
||||
. += O
|
||||
@@ -778,6 +779,24 @@ GLOBAL_LIST_EMPTY(possible_items_special)
|
||||
target_amount = count
|
||||
update_explanation_text()
|
||||
*/
|
||||
/datum/objective/protect_object
|
||||
name = "protect object"
|
||||
var/obj/protect_target
|
||||
|
||||
/datum/objective/protect_object/proc/set_target(obj/O)
|
||||
protect_target = O
|
||||
update_explanation_text()
|
||||
|
||||
/datum/objective/protect_object/update_explanation_text()
|
||||
. = ..()
|
||||
if(protect_target)
|
||||
explanation_text = "Protect \the [protect_target] at all costs."
|
||||
else
|
||||
explanation_text = "Free objective."
|
||||
|
||||
/datum/objective/protect_object/check_completion()
|
||||
return !QDELETED(protect_target)
|
||||
|
||||
//Changeling Objectives
|
||||
|
||||
/datum/objective/absorb
|
||||
@@ -1178,7 +1197,7 @@ GLOBAL_LIST_EMPTY(cult_contraband)
|
||||
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), critical = TRUE)
|
||||
H.equip_in_one_of_slots(I, list("backpack" = ITEM_SLOT_BACKPACK), critical = TRUE)
|
||||
hoarded_item = I
|
||||
|
||||
|
||||
|
||||
@@ -247,61 +247,94 @@ Class Procs:
|
||||
|
||||
/obj/machinery/proc/auto_use_power()
|
||||
if(!powered(power_channel))
|
||||
return 0
|
||||
return FALSE
|
||||
if(use_power == 1)
|
||||
use_power(idle_power_usage,power_channel)
|
||||
else if(use_power >= 2)
|
||||
use_power(active_power_usage,power_channel)
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/proc/is_operational()
|
||||
return !(stat & (NOPOWER|BROKEN|MAINT))
|
||||
|
||||
/obj/machinery/can_interact(mob/user)
|
||||
var/silicon = hasSiliconAccessInArea(user) || IsAdminGhost(user)
|
||||
if((stat & (NOPOWER|BROKEN)) && !(interaction_flags_machine & INTERACT_MACHINE_OFFLINE))
|
||||
if((stat & (NOPOWER|BROKEN)) && !(interaction_flags_machine & INTERACT_MACHINE_OFFLINE)) // Check if the machine is broken, and if we can still interact with it if so
|
||||
return FALSE
|
||||
if(panel_open && !(interaction_flags_machine & INTERACT_MACHINE_OPEN))
|
||||
if(!silicon || !(interaction_flags_machine & INTERACT_MACHINE_OPEN_SILICON))
|
||||
return FALSE
|
||||
|
||||
if(silicon)
|
||||
if(IsAdminGhost(user))
|
||||
return TRUE //if you're an admin, you probably know what you're doing (or at least have permission to do what you're doing)
|
||||
|
||||
if(!isliving(user))
|
||||
return FALSE //no ghosts in the machine allowed, sorry
|
||||
|
||||
// if(SEND_SIGNAL(user, COMSIG_TRY_USE_MACHINE, src) & COMPONENT_CANT_USE_MACHINE_INTERACT)
|
||||
// return FALSE
|
||||
|
||||
var/mob/living/living_user = user
|
||||
|
||||
var/is_dextrous = FALSE
|
||||
if(isanimal(user))
|
||||
var/mob/living/simple_animal/user_as_animal = user
|
||||
if (user_as_animal.dextrous)
|
||||
is_dextrous = TRUE
|
||||
|
||||
if(!issilicon(user) && !is_dextrous && !user.can_hold_items())
|
||||
return FALSE //spiders gtfo
|
||||
|
||||
if(issilicon(user)) // If we are a silicon, make sure the machine allows silicons to interact with it
|
||||
if(!(interaction_flags_machine & INTERACT_MACHINE_ALLOW_SILICON))
|
||||
return FALSE
|
||||
else
|
||||
if(interaction_flags_machine & INTERACT_MACHINE_REQUIRES_SILICON)
|
||||
|
||||
if(panel_open && !(interaction_flags_machine & INTERACT_MACHINE_OPEN) && !(interaction_flags_machine & INTERACT_MACHINE_OPEN_SILICON))
|
||||
return FALSE
|
||||
if(!Adjacent(user))
|
||||
var/mob/living/carbon/H = user
|
||||
if(!(istype(H) && H.has_dna() && H.dna.check_mutation(TK)))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
return TRUE //silicons don't care about petty mortal concerns like needing to be next to a machine to use it
|
||||
|
||||
if(living_user.incapacitated()) //idk why silicons aren't supposed to care about incapacitation when interacting with machines, but it was apparently like this before
|
||||
return FALSE
|
||||
|
||||
// TODO: nerf blind people
|
||||
// if((interaction_flags_machine & INTERACT_MACHINE_REQUIRES_SIGHT) && user.is_blind())
|
||||
// to_chat(user, span_warning("This machine requires sight to use."))
|
||||
// return FALSE
|
||||
|
||||
if(panel_open && !(interaction_flags_machine & INTERACT_MACHINE_OPEN))
|
||||
return FALSE
|
||||
|
||||
if(interaction_flags_machine & INTERACT_MACHINE_REQUIRES_SILICON) //if the user was a silicon, we'd have returned out earlier, so the user must not be a silicon
|
||||
return FALSE
|
||||
|
||||
if(!Adjacent(user)) // Next make sure we are next to the machine unless we have telekinesis
|
||||
var/mob/living/carbon/carbon_user = living_user
|
||||
if(!istype(carbon_user) || !carbon_user.has_dna() || !carbon_user.dna.check_mutation(TK))
|
||||
return FALSE
|
||||
|
||||
return TRUE // If we passed all of those checks, woohoo! We can interact with this machine.
|
||||
|
||||
/obj/machinery/proc/check_nap_violations()
|
||||
if(!SSeconomy.full_ancap)
|
||||
return TRUE
|
||||
if(occupant && !state_open)
|
||||
if(ishuman(occupant))
|
||||
var/mob/living/carbon/human/H = occupant
|
||||
var/obj/item/card/id/I = H.get_idcard()
|
||||
if(I)
|
||||
var/datum/bank_account/insurance = I.registered_account
|
||||
if(!insurance)
|
||||
say("[market_verb] NAP Violation: No bank account found.")
|
||||
nap_violation()
|
||||
return FALSE
|
||||
else
|
||||
if(!insurance.adjust_money(-fair_market_price))
|
||||
say("[market_verb] NAP Violation: Unable to pay.")
|
||||
nap_violation()
|
||||
return FALSE
|
||||
var/datum/bank_account/D = SSeconomy.get_dep_account(payment_department)
|
||||
if(D)
|
||||
D.adjust_money(fair_market_price)
|
||||
else
|
||||
say("[market_verb] NAP Violation: No ID card found.")
|
||||
nap_violation()
|
||||
var/mob/living/L = occupant
|
||||
var/obj/item/card/id/I = L.get_idcard(TRUE)
|
||||
if(I)
|
||||
var/datum/bank_account/insurance = I.registered_account
|
||||
if(!insurance)
|
||||
say("[market_verb] NAP Violation: No bank account found.")
|
||||
nap_violation(L)
|
||||
return FALSE
|
||||
else
|
||||
if(!insurance.adjust_money(-fair_market_price))
|
||||
say("[market_verb] NAP Violation: Unable to pay.")
|
||||
nap_violation(L)
|
||||
return FALSE
|
||||
var/datum/bank_account/D = SSeconomy.get_dep_account(payment_department)
|
||||
if(D)
|
||||
D.adjust_money(fair_market_price)
|
||||
else
|
||||
say("[market_verb] NAP Violation: No ID card found.")
|
||||
nap_violation(L)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/proc/nap_violation(mob/violator)
|
||||
@@ -322,11 +355,11 @@ Class Procs:
|
||||
/obj/machinery/Topic(href, href_list)
|
||||
..()
|
||||
if(!can_interact(usr))
|
||||
return 1
|
||||
return TRUE
|
||||
if(!usr.canUseTopic(src))
|
||||
return 1
|
||||
return TRUE
|
||||
add_fingerprint(usr)
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
+208
-217
@@ -1,7 +1,3 @@
|
||||
#define AUTOLATHE_MAIN_MENU 1
|
||||
#define AUTOLATHE_CATEGORY_MENU 2
|
||||
#define AUTOLATHE_SEARCH_MENU 3
|
||||
|
||||
/obj/machinery/autolathe
|
||||
name = "autolathe"
|
||||
desc = "It produces items using metal and glass."
|
||||
@@ -24,13 +20,14 @@
|
||||
var/shock_wire
|
||||
|
||||
var/busy = FALSE
|
||||
var/prod_coeff = 1
|
||||
|
||||
///the multiplier for how much materials the created object takes from this machines stored materials
|
||||
var/creation_efficiency = 1.6
|
||||
|
||||
var/datum/design/being_built
|
||||
var/datum/techweb/stored_research
|
||||
var/list/datum/design/matching_designs
|
||||
var/selected_category
|
||||
var/screen = 1
|
||||
var/selected_category = "None"
|
||||
var/base_price = 25
|
||||
var/hacked_price = 50
|
||||
|
||||
@@ -60,7 +57,7 @@
|
||||
QDEL_NULL(wires)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/autolathe/ui_interact(mob/user)
|
||||
/obj/machinery/autolathe/ui_interact(mob/user, datum/tgui/ui)
|
||||
. = ..()
|
||||
if(!is_operational())
|
||||
return
|
||||
@@ -68,101 +65,129 @@
|
||||
if(shocked && !(stat & NOPOWER))
|
||||
shock(user,50)
|
||||
|
||||
var/dat
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "Autolathe", capitalize(src.name))
|
||||
ui.open()
|
||||
|
||||
switch(screen)
|
||||
if(AUTOLATHE_MAIN_MENU)
|
||||
dat = main_win(user)
|
||||
if(AUTOLATHE_CATEGORY_MENU)
|
||||
dat = category_win(user,selected_category)
|
||||
if(AUTOLATHE_SEARCH_MENU)
|
||||
dat = search_win(user)
|
||||
|
||||
var/datum/browser/popup = new(user, "autolathe", name, 400, 500)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/autolathe/on_deconstruction()
|
||||
/obj/machinery/autolathe/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["materials"] = list()
|
||||
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
|
||||
materials.retrieve_all()
|
||||
data["materialtotal"] = materials.total_amount
|
||||
data["materialsmax"] = materials.max_amount
|
||||
data["categories"] = categories
|
||||
data["designs"] = list()
|
||||
data["active"] = busy
|
||||
|
||||
/obj/machinery/autolathe/attackby(obj/item/O, mob/user, params)
|
||||
if (busy)
|
||||
to_chat(user, "<span class=\"alert\">The autolathe is busy. Please wait for completion of previous operation.</span>")
|
||||
return TRUE
|
||||
|
||||
if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", O))
|
||||
updateUsrDialog()
|
||||
return TRUE
|
||||
|
||||
if(default_deconstruction_crowbar(O))
|
||||
return TRUE
|
||||
|
||||
if(panel_open && is_wire_tool(O))
|
||||
wires.interact(user)
|
||||
return TRUE
|
||||
|
||||
if(user.a_intent == INTENT_HARM) //so we can hit the machine
|
||||
return ..()
|
||||
|
||||
if(stat)
|
||||
return TRUE
|
||||
|
||||
if(istype(O, /obj/item/disk/design_disk))
|
||||
user.visible_message("<span class='notice'>[user] begins to load \the [O] in \the [src]...</span>",
|
||||
"<span class='notice'>You begin to load a design from \the [O]...</span>",
|
||||
"<span class='hear'>You hear the chatter of a floppy drive.</span>")
|
||||
busy = TRUE
|
||||
var/obj/item/disk/design_disk/D = O
|
||||
if(do_after(user, 14.4, target = src))
|
||||
for(var/B in D.blueprints)
|
||||
if(B)
|
||||
stored_research.add_design(B)
|
||||
busy = FALSE
|
||||
return TRUE
|
||||
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/machinery/autolathe/proc/AfterMaterialInsert(item_inserted, id_inserted, amount_inserted)
|
||||
if(istype(item_inserted, /obj/item/stack/ore/bluespace_crystal))
|
||||
use_power(MINERAL_MATERIAL_AMOUNT / 10)
|
||||
else if(custom_materials && custom_materials.len && custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)])
|
||||
flick("autolathe_r",src)//plays glass insertion animation by default otherwise
|
||||
for(var/mat_id in materials.materials)
|
||||
var/datum/material/M = mat_id
|
||||
var/mineral_count = materials.materials[mat_id]
|
||||
var/list/material_data = list(
|
||||
name = M.name,
|
||||
mineral_amount = mineral_count,
|
||||
matcolour = M.color,
|
||||
)
|
||||
data["materials"] += list(material_data)
|
||||
if(selected_category != "None" && !length(matching_designs))
|
||||
data["designs"] = handle_designs(stored_research.researched_designs, TRUE)
|
||||
else
|
||||
flick("autolathe_o",src)//plays metal insertion animation
|
||||
data["designs"] = handle_designs(matching_designs, FALSE)
|
||||
return data
|
||||
|
||||
/obj/machinery/autolathe/proc/handle_designs(list/designs, categorycheck)
|
||||
var/list/output = list()
|
||||
for(var/v in designs)
|
||||
var/datum/design/D = categorycheck ? SSresearch.techweb_design_by_id(v) : v
|
||||
if(categorycheck)
|
||||
if(!(selected_category in D.category))
|
||||
continue
|
||||
var/unbuildable = FALSE // we can't build the design currently
|
||||
var/m10 = FALSE // 10x mult
|
||||
var/m25 = FALSE // 25x mult
|
||||
var/m50 = FALSE // 50x mult
|
||||
var/m5 = FALSE // 5x mult
|
||||
var/sheets = FALSE // sheets or no?
|
||||
if(disabled || !can_build(D))
|
||||
unbuildable = TRUE
|
||||
var/max_multiplier = unbuildable ? 0 : 1
|
||||
if(ispath(D.build_path, /obj/item/stack))
|
||||
sheets = TRUE
|
||||
if(!unbuildable)
|
||||
var/datum/component/material_container/mats = GetComponent(/datum/component/material_container)
|
||||
for(var/datum/material/mat in D.materials)
|
||||
max_multiplier = min(D.maxstack, round(mats.get_material_amount(mat)/D.materials[mat]))
|
||||
if (max_multiplier>10 && !disabled)
|
||||
m10 = TRUE
|
||||
if (max_multiplier>25 && !disabled)
|
||||
m25 = TRUE
|
||||
else
|
||||
if(!unbuildable)
|
||||
if(!disabled && can_build(D, 5))
|
||||
m5 = TRUE
|
||||
if(!disabled && can_build(D, 10))
|
||||
m10 = TRUE
|
||||
var/datum/component/material_container/mats = GetComponent(/datum/component/material_container)
|
||||
for(var/datum/material/mat in D.materials)
|
||||
max_multiplier = min(50, round(mats.get_material_amount(mat)/(D.materials[mat] * creation_efficiency)))
|
||||
|
||||
use_power(min(1000, amount_inserted / 100))
|
||||
updateUsrDialog()
|
||||
var/list/design = list(
|
||||
name = D.name,
|
||||
id = D.id,
|
||||
ref = REF(src),
|
||||
cost = get_design_cost(D),
|
||||
buildable = unbuildable,
|
||||
mult5 = m5,
|
||||
mult10 = m10,
|
||||
mult25 = m25,
|
||||
mult50 = m50,
|
||||
sheet = sheets,
|
||||
maxmult = max_multiplier,
|
||||
)
|
||||
output += list(design)
|
||||
return output
|
||||
|
||||
/obj/machinery/autolathe/Topic(href, href_list)
|
||||
if(..())
|
||||
/obj/machinery/autolathe/ui_act(action, params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if (!busy)
|
||||
if(href_list["menu"])
|
||||
screen = text2num(href_list["menu"])
|
||||
updateUsrDialog()
|
||||
if(action == "menu")
|
||||
selected_category = null
|
||||
matching_designs.Cut()
|
||||
. = TRUE
|
||||
|
||||
if(href_list["category"])
|
||||
selected_category = href_list["category"]
|
||||
updateUsrDialog()
|
||||
if(action == "category")
|
||||
selected_category = params["selectedCategory"]
|
||||
matching_designs.Cut()
|
||||
. = TRUE
|
||||
|
||||
if(href_list["make"])
|
||||
if(action == "search")
|
||||
matching_designs.Cut()
|
||||
|
||||
for(var/v in stored_research.researched_designs)
|
||||
var/datum/design/D = SSresearch.techweb_design_by_id(v)
|
||||
if(findtext(D.name,params["to_search"]))
|
||||
matching_designs.Add(D)
|
||||
. = TRUE
|
||||
|
||||
if(action == "make")
|
||||
if (!busy)
|
||||
/////////////////
|
||||
//href protection
|
||||
being_built = stored_research.isDesignResearchedID(href_list["make"])
|
||||
being_built = stored_research.isDesignResearchedID(params["id"])
|
||||
if(!being_built)
|
||||
return
|
||||
|
||||
var/multiplier = text2num(href_list["multiplier"])
|
||||
var/multiplier = text2num(params["multiplier"])
|
||||
if(!multiplier)
|
||||
to_chat(usr, span_alert("[src] only accepts a numerical multiplier!"))
|
||||
return
|
||||
var/is_stack = ispath(being_built.build_path, /obj/item/stack)
|
||||
multiplier = clamp(multiplier,1,50)
|
||||
multiplier = clamp(round(multiplier),1,50)
|
||||
|
||||
/////////////////
|
||||
|
||||
var/coeff = (is_stack ? 1 : prod_coeff) //stacks are unaffected by production coefficient
|
||||
var/coeff = (is_stack ? 1 : creation_efficiency) //stacks are unaffected by production coefficient
|
||||
var/total_amount = 0
|
||||
|
||||
for(var/MAT in being_built.materials)
|
||||
@@ -184,8 +209,8 @@
|
||||
if(materials.materials[i] > 0)
|
||||
list_to_show += i
|
||||
|
||||
used_material = input("Choose [used_material]", "Custom Material") as null|anything in sortList(list_to_show, /proc/cmp_typepaths_asc)
|
||||
if(!used_material)
|
||||
used_material = tgui_input_list(usr, "Choose [used_material]", "Custom Material", sortList(list_to_show, /proc/cmp_typepaths_asc))
|
||||
if(isnull(used_material))
|
||||
return //Didn't pick any material, so you can't build shit either.
|
||||
custom_materials[used_material] += amount_needed
|
||||
|
||||
@@ -193,27 +218,95 @@
|
||||
|
||||
if(materials.has_materials(materials_used))
|
||||
busy = TRUE
|
||||
to_chat(usr, span_notice("You print [multiplier] item(s) from the [src]"))
|
||||
use_power(power)
|
||||
icon_state = "autolathe_n"
|
||||
var/time = is_stack ? 32 : (32 * coeff * multiplier) ** 0.8
|
||||
addtimer(CALLBACK(src, .proc/make_item, power, materials_used, custom_materials, multiplier, coeff, is_stack, usr), time)
|
||||
. = TRUE
|
||||
else
|
||||
to_chat(usr, "<span class=\"alert\">Not enough materials for this operation.</span>")
|
||||
to_chat(usr, span_alert("Not enough materials for this operation."))
|
||||
else
|
||||
to_chat(usr, span_alert("The autolathe is busy. Please wait for completion of previous operation."))
|
||||
|
||||
if(href_list["search"])
|
||||
matching_designs.Cut()
|
||||
/obj/machinery/autolathe/on_deconstruction()
|
||||
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
|
||||
materials.retrieve_all()
|
||||
|
||||
for(var/v in stored_research.researched_designs)
|
||||
var/datum/design/D = SSresearch.techweb_design_by_id(v)
|
||||
if(findtext(D.name,href_list["to_search"]))
|
||||
matching_designs.Add(D)
|
||||
updateUsrDialog()
|
||||
/obj/machinery/autolathe/attackby(obj/item/O, mob/living/user, params)
|
||||
if(busy)
|
||||
balloon_alert(user, "it's busy!")
|
||||
return TRUE
|
||||
|
||||
if(user.a_intent == INTENT_HARM) //so we can hit the machine
|
||||
return ..()
|
||||
|
||||
if(stat)
|
||||
return TRUE
|
||||
|
||||
if(istype(O, /obj/item/disk/design_disk))
|
||||
user.visible_message(span_notice("[user] begins to load \the [O] in \the [src]..."),
|
||||
balloon_alert(user, "uploading design..."),
|
||||
span_hear("You hear the chatter of a floppy drive."))
|
||||
busy = TRUE
|
||||
if(do_after(user, 14.4, target = src))
|
||||
var/obj/item/disk/design_disk/disky = O
|
||||
var/list/not_imported
|
||||
for(var/datum/design/blueprint as anything in disky.blueprints)
|
||||
if(!blueprint)
|
||||
continue
|
||||
if(blueprint.build_type & AUTOLATHE)
|
||||
stored_research.add_design(blueprint)
|
||||
else
|
||||
LAZYADD(not_imported, blueprint.name)
|
||||
if(not_imported)
|
||||
to_chat(user, span_warning("The following design[length(not_imported) > 1 ? "s" : ""] couldn't be imported: [english_list(not_imported)]"))
|
||||
busy = FALSE
|
||||
return TRUE
|
||||
|
||||
if(panel_open)
|
||||
balloon_alert(user, "close the panel first!")
|
||||
return FALSE
|
||||
|
||||
return ..()
|
||||
|
||||
/obj/machinery/autolathe/screwdriver_act(mob/living/user, obj/item/I)
|
||||
. = ..()
|
||||
if(busy)
|
||||
balloon_alert(user, "it's busy!")
|
||||
return STOP_ATTACK_PROC_CHAIN
|
||||
|
||||
if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", I))
|
||||
return STOP_ATTACK_PROC_CHAIN
|
||||
|
||||
/obj/machinery/autolathe/crowbar_act(mob/living/user, obj/item/I)
|
||||
. = ..()
|
||||
if(busy)
|
||||
balloon_alert(user, "it's busy!")
|
||||
return STOP_ATTACK_PROC_CHAIN
|
||||
|
||||
if(default_deconstruction_crowbar(I))
|
||||
return STOP_ATTACK_PROC_CHAIN
|
||||
|
||||
/obj/machinery/autolathe/multitool_act(mob/living/user, obj/item/I)
|
||||
. = ..()
|
||||
if(busy)
|
||||
balloon_alert(user, "it's busy!")
|
||||
return STOP_ATTACK_PROC_CHAIN
|
||||
|
||||
if(panel_open)
|
||||
wires.interact(user)
|
||||
return STOP_ATTACK_PROC_CHAIN
|
||||
|
||||
/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(custom_materials && custom_materials.len && custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)])
|
||||
flick("autolathe_r", src)//plays glass insertion animation by default otherwise
|
||||
else
|
||||
to_chat(usr, "<span class=\"alert\">The autolathe is busy. Please wait for completion of previous operation.</span>")
|
||||
flick("autolathe_o", src)//plays metal insertion animation
|
||||
|
||||
updateUsrDialog()
|
||||
|
||||
return
|
||||
use_power(min(1000, amount_inserted / 100))
|
||||
|
||||
/obj/machinery/autolathe/proc/make_item(power, list/materials_used, list/picked_materials, multiplier, coeff, is_stack, mob/user)
|
||||
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
|
||||
@@ -223,11 +316,11 @@
|
||||
materials.use_materials(materials_used)
|
||||
|
||||
if(is_stack)
|
||||
var/obj/item/stack/N = new being_built.build_path(A, multiplier)
|
||||
N.update_icon()
|
||||
var/obj/item/stack/N = new being_built.build_path(A, multiplier, FALSE)
|
||||
N.update_appearance()
|
||||
N.autolathe_crafted(src)
|
||||
else
|
||||
for(var/i=1, i<=multiplier, i++)
|
||||
for(var/i in 1 to multiplier)
|
||||
var/obj/item/new_item = new being_built.build_path(A)
|
||||
new_item.autolathe_crafted(src)
|
||||
|
||||
@@ -241,132 +334,30 @@
|
||||
|
||||
icon_state = "autolathe"
|
||||
busy = FALSE
|
||||
updateDialog()
|
||||
|
||||
/obj/machinery/autolathe/RefreshParts()
|
||||
var/T = 0
|
||||
for(var/obj/item/stock_parts/matter_bin/MB in component_parts)
|
||||
T += MB.rating*75000
|
||||
var/mat_capacity = 0
|
||||
for(var/obj/item/stock_parts/matter_bin/new_matter_bin in component_parts)
|
||||
mat_capacity += new_matter_bin.rating*75000
|
||||
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
|
||||
materials.max_amount = T
|
||||
T=1.2
|
||||
for(var/obj/item/stock_parts/manipulator/M in component_parts)
|
||||
T -= M.rating*0.2
|
||||
prod_coeff = min(1,max(0,T)) // Coeff going 1 -> 0,8 -> 0,6 -> 0,4
|
||||
materials.max_amount = mat_capacity
|
||||
|
||||
var/efficiency=1.2
|
||||
for(var/obj/item/stock_parts/manipulator/new_manipulator in component_parts)
|
||||
efficiency -= new_manipulator.rating*0.2
|
||||
creation_efficiency = max(1,efficiency) // creation_efficiency goes 1 -> 0,8 -> 0,6 -> 0,4 per level of manipulator efficiency
|
||||
|
||||
/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()
|
||||
|
||||
dat += "<form name='search' action='?src=[REF(src)]'>\
|
||||
<input type='hidden' name='src' value='[REF(src)]'>\
|
||||
<input type='hidden' name='search' value='to_search'>\
|
||||
<input type='hidden' name='menu' value='[AUTOLATHE_SEARCH_MENU]'>\
|
||||
<input type='text' name='to_search'>\
|
||||
<input type='submit' value='Search'>\
|
||||
</form><hr>"
|
||||
|
||||
var/line_length = 1
|
||||
dat += "<table style='width:100%' align='center'><tr>"
|
||||
|
||||
for(var/C in categories)
|
||||
if(line_length > 2)
|
||||
dat += "</tr><tr>"
|
||||
line_length = 1
|
||||
|
||||
dat += "<td><A href='?src=[REF(src)];category=[C];menu=[AUTOLATHE_CATEGORY_MENU]'>[C]</A></td>"
|
||||
line_length++
|
||||
|
||||
dat += "</tr></table></div>"
|
||||
return dat
|
||||
|
||||
/obj/machinery/autolathe/proc/category_win(mob/user,selected_category)
|
||||
var/dat = "<A href='?src=[REF(src)];menu=[AUTOLATHE_MAIN_MENU]'>Return to main menu</A>"
|
||||
dat += "<div class='statusDisplay'><h3>Browsing [selected_category]:</h3><br>"
|
||||
dat += materials_printout()
|
||||
|
||||
for(var/v in stored_research.researched_designs)
|
||||
var/datum/design/D = SSresearch.techweb_design_by_id(v)
|
||||
if(!(selected_category in D.category))
|
||||
continue
|
||||
|
||||
if(disabled || !can_build(D))
|
||||
dat += "<span class='linkOff'>[D.name]</span>"
|
||||
else
|
||||
dat += "<a href='?src=[REF(src)];make=[D.id];multiplier=1'>[D.name]</a>"
|
||||
|
||||
if(ispath(D.build_path, /obj/item/stack))
|
||||
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
|
||||
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)
|
||||
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=25'>x25</a>"
|
||||
if(max_multiplier > 0 && !disabled)
|
||||
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=[max_multiplier]'>x[max_multiplier]</a>"
|
||||
else
|
||||
if(!disabled && can_build(D, 5))
|
||||
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=5'>x5</a>"
|
||||
if(!disabled && can_build(D, 10))
|
||||
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=10'>x10</a>"
|
||||
|
||||
dat += "[get_design_cost(D)]<br>"
|
||||
|
||||
dat += "</div>"
|
||||
return dat
|
||||
|
||||
/obj/machinery/autolathe/proc/search_win(mob/user)
|
||||
var/dat = "<A href='?src=[REF(src)];menu=[AUTOLATHE_MAIN_MENU]'>Return to main menu</A>"
|
||||
dat += "<div class='statusDisplay'><h3>Search results:</h3><br>"
|
||||
dat += materials_printout()
|
||||
|
||||
for(var/v in matching_designs)
|
||||
var/datum/design/D = v
|
||||
if(disabled || !can_build(D))
|
||||
dat += "<span class='linkOff'>[D.name]</span>"
|
||||
else
|
||||
dat += "<a href='?src=[REF(src)];make=[D.id];multiplier=1'>[D.name]</a>"
|
||||
|
||||
if(ispath(D.build_path, /obj/item/stack))
|
||||
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
|
||||
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)
|
||||
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=25'>x25</a>"
|
||||
if(max_multiplier > 0 && !disabled)
|
||||
dat += " <a href='?src=[REF(src)];make=[D.id];multiplier=[max_multiplier]'>x[max_multiplier]</a>"
|
||||
|
||||
dat += "[get_design_cost(D)]<br>"
|
||||
|
||||
dat += "</div>"
|
||||
return dat
|
||||
|
||||
/obj/machinery/autolathe/proc/materials_printout()
|
||||
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 = 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
|
||||
. += span_notice("The status display reads: Storing up to <b>[materials.max_amount]</b> material units.<br>Material consumption at <b>[creation_efficiency*100]%</b>.")
|
||||
|
||||
/obj/machinery/autolathe/proc/can_build(datum/design/D, amount = 1)
|
||||
if(D.make_reagents.len)
|
||||
if(length(D.make_reagents))
|
||||
return FALSE
|
||||
|
||||
var/coeff = (ispath(D.build_path, /obj/item/stack) ? 1 : prod_coeff)
|
||||
var/coeff = (ispath(D.build_path, /obj/item/stack) ? 1 : creation_efficiency)
|
||||
|
||||
var/list/required_materials = list()
|
||||
|
||||
@@ -379,7 +370,7 @@
|
||||
|
||||
|
||||
/obj/machinery/autolathe/proc/get_design_cost(datum/design/D)
|
||||
var/coeff = (ispath(D.build_path, /obj/item/stack) ? 1 : prod_coeff)
|
||||
var/coeff = (ispath(D.build_path, /obj/item/stack) ? 1 : creation_efficiency)
|
||||
var/dat
|
||||
for(var/i in D.materials)
|
||||
if(istext(i)) //Category handling
|
||||
@@ -402,7 +393,7 @@
|
||||
disabled = FALSE
|
||||
|
||||
/obj/machinery/autolathe/proc/shock(mob/user, prb)
|
||||
if(stat & (BROKEN|NOPOWER)) // unpowered, no shock
|
||||
if(stat & (BROKEN|NOPOWER)) // unpowered, no shock
|
||||
return FALSE
|
||||
if(!prob(prb))
|
||||
return FALSE
|
||||
@@ -424,7 +415,7 @@
|
||||
else
|
||||
stored_research.remove_design(D)
|
||||
|
||||
/obj/machinery/autolathe/hacked/Initialize()
|
||||
/obj/machinery/autolathe/hacked/Initialize(mapload)
|
||||
. = ..()
|
||||
adjust_hacked(TRUE)
|
||||
|
||||
|
||||
@@ -407,7 +407,7 @@
|
||||
|
||||
/obj/machinery/camera/get_remote_view_fullscreens(mob/user)
|
||||
if(view_range == short_range) //unfocused
|
||||
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 2)
|
||||
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/scaled/impaired, 2)
|
||||
|
||||
/obj/machinery/camera/update_remote_sight(mob/living/user)
|
||||
user.see_invisible = SEE_INVISIBLE_LIVING //can't see ghosts through cameras
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
desc = "A machine to give your apparel a fresh new color! Recommended to use with white items for best results."
|
||||
icon = 'icons/obj/vending.dmi'
|
||||
icon_state = "colormate"
|
||||
// light_mask = "colormate-light-mask"
|
||||
density = TRUE
|
||||
anchored = TRUE
|
||||
circuit = /obj/item/circuitboard/machine/colormate
|
||||
|
||||
@@ -275,7 +275,7 @@
|
||||
if(final)
|
||||
playsound(origin, 'sound/machines/terminal_prompt_confirm.ogg', 25, 0)
|
||||
remote_eye.setLoc(get_turf(final))
|
||||
C.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash/static)
|
||||
C.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/tiled/flash/static)
|
||||
C.clear_fullscreen("flash", 3) //Shorter flash than normal since it's an ~~advanced~~ console!
|
||||
else
|
||||
playsound(origin, 'sound/machines/terminal_prompt_deny.ogg', 25, 0)
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
/obj/machinery/computer/communications/Initialize()
|
||||
. = ..()
|
||||
GLOB.shuttle_caller_list += src
|
||||
AddComponent(/datum/component/gps, "Secured Communications Signal")
|
||||
|
||||
/// Are we NOT a silicon, AND we're logged in as the captain?
|
||||
/obj/machinery/computer/communications/proc/authenticated_as_non_silicon_captain(mob/user)
|
||||
@@ -79,13 +80,11 @@
|
||||
if (obj_flags & EMAGGED)
|
||||
return
|
||||
obj_flags |= EMAGGED
|
||||
SSshuttle.shuttle_purchase_requirements_met |= "emagged"
|
||||
if (authenticated)
|
||||
authorize_access = get_all_accesses()
|
||||
to_chat(user, "<span class='danger'>You scramble the communication routing circuits!</span>")
|
||||
to_chat(user, span_danger("You scramble the communication routing circuits!"))
|
||||
playsound(src, 'sound/machines/terminal_alert.ogg', 50, FALSE)
|
||||
SSshuttle.shuttle_purchase_requirements_met["emagged"] = TRUE
|
||||
return
|
||||
|
||||
/obj/machinery/computer/communications/ui_act(action, list/params)
|
||||
var/static/list/approved_states = list(STATE_BUYING_SHUTTLE, STATE_CHANGING_STATUS, STATE_MAIN, STATE_MESSAGES)
|
||||
@@ -104,6 +103,7 @@
|
||||
if ("answerMessage")
|
||||
if (!authenticated(usr))
|
||||
return
|
||||
|
||||
var/answer_index = params["answer"]
|
||||
var/message_index = params["message"]
|
||||
|
||||
@@ -136,11 +136,11 @@
|
||||
var/obj/item/held_item = usr.get_active_held_item()
|
||||
var/obj/item/card/id/id_card = held_item?.GetID()
|
||||
if (!istype(id_card))
|
||||
to_chat(usr, "<span class='warning'>You need to swipe your ID!</span>")
|
||||
to_chat(usr, span_warning("You need to swipe your ID!"))
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
|
||||
return
|
||||
if (!(ACCESS_CAPTAIN in id_card.access))
|
||||
to_chat(usr, "<span class='warning'>You are not authorized to do this!</span>")
|
||||
to_chat(usr, span_warning("You are not authorized to do this!"))
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
|
||||
return
|
||||
|
||||
@@ -152,26 +152,28 @@
|
||||
|
||||
set_security_level(new_sec_level)
|
||||
|
||||
to_chat(usr, "<span class='notice'>Authorization confirmed. Modifying security level.</span>")
|
||||
to_chat(usr, span_notice("Authorization confirmed. Modifying security level."))
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
|
||||
|
||||
// Only notify people if an actual change happened
|
||||
log_game("[key_name(usr)] has changed the security level to [params["newSecurityLevel"]] with [src] at [AREACOORD(usr)].")
|
||||
message_admins("[ADMIN_LOOKUPFLW(usr)] has changed the security level to [params["newSecurityLevel"]] with [src] at [AREACOORD(usr)].")
|
||||
deadchat_broadcast(" has changed the security level to [params["newSecurityLevel"]] with [src] at <span class='name'>[get_area_name(usr, TRUE)]</span>.", "<span class='name'>[usr.real_name]</span>", usr)
|
||||
deadchat_broadcast(" has changed the security level to [params["newSecurityLevel"]] with [src] at [span_name("[get_area_name(usr, TRUE)]")].", usr, src.loc, message_type=DEADCHAT_ANNOUNCEMENT)
|
||||
|
||||
alert_level_tick += 1
|
||||
if ("deleteMessage")
|
||||
if (!authenticated(usr))
|
||||
return
|
||||
var/message_index = params["message"]
|
||||
|
||||
if(!isnum(message_index))
|
||||
message_admins("[ADMIN_LOOKUPFLW(usr)] provided an invalid index type when deleting a message on [src] [ADMIN_JMP(src)]. This should not happen. Please check with a maintainer and/or consult tgui logs.")
|
||||
CRASH("Non-numeric index provided when deleting comms console message.")
|
||||
var/message_index = text2num(params["message"])
|
||||
if (!message_index)
|
||||
return
|
||||
LAZYREMOVE(messages, LAZYACCESS(messages, message_index))
|
||||
if ("emergency_meeting")
|
||||
if(!(SSevents.holidays && SSevents.holidays[APRIL_FOOLS]))
|
||||
return
|
||||
if (!authenticated_as_silicon_or_captain(usr))
|
||||
return
|
||||
emergency_meeting(usr)
|
||||
if ("makePriorityAnnouncement")
|
||||
if (!authenticated_as_silicon_or_captain(usr))
|
||||
return
|
||||
@@ -188,32 +190,36 @@
|
||||
var/emagged = obj_flags & EMAGGED
|
||||
if (emagged)
|
||||
message_syndicate(message, usr)
|
||||
to_chat(usr, "<span class='danger'>SYSERR @l(19833)of(transmit.dm): !@$ MESSAGE TRANSMITTED TO SYNDICATE COMMAND.</span>")
|
||||
to_chat(usr, span_danger("SYSERR @l(19833)of(transmit.dm): !@$ MESSAGE TRANSMITTED TO SYNDICATE COMMAND."))
|
||||
else
|
||||
message_centcom(message, usr)
|
||||
to_chat(usr, "<span class='notice'>Message transmitted to Central Command.</span>")
|
||||
to_chat(usr, span_notice("Message transmitted to Central Command."))
|
||||
|
||||
var/associates = emagged ? "the Syndicate": "CentCom"
|
||||
usr.log_talk(message, LOG_SAY, tag = "message to [associates]")
|
||||
deadchat_broadcast(" has messaged [associates], \"[message]\" at <span class='name'>[get_area_name(usr, TRUE)]</span>.", "<span class='name'>[usr.real_name]</span>", usr)
|
||||
deadchat_broadcast(" has messaged [associates], \"[message]\" at [span_name("[get_area_name(usr, TRUE)]")].", usr, src.loc, message_type = DEADCHAT_ANNOUNCEMENT)
|
||||
COOLDOWN_START(src, important_action_cooldown, IMPORTANT_ACTION_COOLDOWN)
|
||||
if ("purchaseShuttle")
|
||||
var/can_buy_shuttles_or_fail_reason = can_buy_shuttles(usr)
|
||||
if (can_buy_shuttles_or_fail_reason != TRUE)
|
||||
if (can_buy_shuttles_or_fail_reason != FALSE)
|
||||
to_chat(usr, "<span class='alert'>[can_buy_shuttles_or_fail_reason]</span>")
|
||||
to_chat(usr, span_alert("[can_buy_shuttles_or_fail_reason]"))
|
||||
return
|
||||
var/list/shuttles = flatten_list(SSmapping.shuttle_templates)
|
||||
var/datum/map_template/shuttle/shuttle = locate(params["shuttle"]) in shuttles
|
||||
if (!istype(shuttle))
|
||||
return
|
||||
// if (!can_purchase_this_shuttle(shuttle))
|
||||
// return
|
||||
if (!shuttle.prerequisites_met())
|
||||
to_chat(usr, "<span class='alert'>You have not met the requirements for purchasing this shuttle.</span>")
|
||||
to_chat(usr, span_alert("You have not met the requirements for purchasing this shuttle."))
|
||||
return
|
||||
var/datum/bank_account/bank_account = SSeconomy.get_dep_account(ACCOUNT_CAR)
|
||||
if (bank_account.account_balance < shuttle.credit_cost)
|
||||
return
|
||||
SSshuttle.shuttle_purchased = SHUTTLEPURCHASE_PURCHASED
|
||||
// for(var/datum/round_event_control/shuttle_insurance/insurance_event in SSevents.control)
|
||||
// insurance_event.weight *= 20
|
||||
SSshuttle.unload_preview()
|
||||
SSshuttle.existing_shuttle = SSshuttle.emergency
|
||||
SSshuttle.action_load(shuttle, replace = TRUE)
|
||||
@@ -235,7 +241,7 @@
|
||||
return
|
||||
var/reason = trim(html_encode(params["reason"]), MAX_MESSAGE_LEN)
|
||||
nuke_request(reason, usr)
|
||||
to_chat(usr, "<span class='notice'>Request sent.</span>")
|
||||
to_chat(usr, span_notice("Request sent."))
|
||||
usr.log_message("has requested the nuclear codes from CentCom with reason \"[reason]\"", LOG_SAY)
|
||||
priority_announce("The codes for the on-station nuclear self-destruct have been requested by [usr]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self-Destruct Codes Requested", "commandreport")
|
||||
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
|
||||
@@ -245,7 +251,7 @@
|
||||
return
|
||||
if (!(obj_flags & EMAGGED))
|
||||
return
|
||||
to_chat(usr, "<span class='notice'>Backup routing data restored.</span>")
|
||||
to_chat(usr, span_notice("Backup routing data restored."))
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
|
||||
obj_flags &= ~EMAGGED
|
||||
if ("sendToOtherSector")
|
||||
@@ -256,7 +262,7 @@
|
||||
if (!COOLDOWN_FINISHED(src, important_action_cooldown))
|
||||
return
|
||||
|
||||
var/message = trim(html_encode(params["message"]), MAX_MESSAGE_LEN)
|
||||
var/message = trim(params["message"], MAX_MESSAGE_LEN)
|
||||
if (!message)
|
||||
return
|
||||
|
||||
@@ -268,12 +274,13 @@
|
||||
var/network_name = CONFIG_GET(string/cross_comms_network)
|
||||
if (network_name)
|
||||
payload["network"] = network_name
|
||||
payload["sender_ckey"] = usr.ckey
|
||||
|
||||
send2otherserver(station_name(), message, "Comms_Console", destination == "all" ? null : list(destination), additional_data = payload)
|
||||
send2otherserver(html_decode(station_name()), message, "Comms_Console", destination == "all" ? null : list(destination), additional_data = payload)
|
||||
minor_announce(message, title = "Outgoing message to allied station")
|
||||
usr.log_talk(message, LOG_SAY, tag = "message to the other server")
|
||||
message_admins("[ADMIN_LOOKUPFLW(usr)] has sent a message to the other server\[s].")
|
||||
deadchat_broadcast(" has sent an outgoing message to the other station(s).</span>", "<span class='bold'>[usr.real_name]", usr)
|
||||
deadchat_broadcast(" has sent an outgoing message to the other station(s).</span>", usr, src.loc, message_type = DEADCHAT_ANNOUNCEMENT)
|
||||
|
||||
COOLDOWN_START(src, important_action_cooldown, IMPORTANT_ACTION_COOLDOWN)
|
||||
if ("setState")
|
||||
@@ -315,7 +322,7 @@
|
||||
authenticated = TRUE
|
||||
authorize_access = get_all_accesses()
|
||||
authorize_name = "Unknown"
|
||||
to_chat(usr, "<span class='warning'>[src] lets out a quiet alarm as its login is overridden.</span>")
|
||||
to_chat(usr, span_warning("[src] lets out a quiet alarm as its login is overridden."))
|
||||
playsound(src, 'sound/machines/terminal_alert.ogg', 25, FALSE)
|
||||
else if(isliving(usr))
|
||||
var/mob/living/L = usr
|
||||
@@ -334,22 +341,36 @@
|
||||
revoke_maint_all_access()
|
||||
log_game("[key_name(usr)] disabled emergency maintenance access.")
|
||||
message_admins("[ADMIN_LOOKUPFLW(usr)] disabled emergency maintenance access.")
|
||||
deadchat_broadcast(" disabled emergency maintenance access at <span class='name'>[get_area_name(usr, TRUE)]</span>.", "<span class='name'>[usr.real_name]</span>", usr)
|
||||
deadchat_broadcast(" disabled emergency maintenance access at [span_name("[get_area_name(usr, TRUE)]")].", usr, src.loc, message_type = DEADCHAT_ANNOUNCEMENT)
|
||||
else
|
||||
make_maint_all_access()
|
||||
log_game("[key_name(usr)] enabled emergency maintenance access.")
|
||||
message_admins("[ADMIN_LOOKUPFLW(usr)] enabled emergency maintenance access.")
|
||||
deadchat_broadcast(" enabled emergency maintenance access at <span class='name'>[get_area_name(usr, TRUE)]</span>.", "<span class='name'>[usr.real_name]</span>", usr)
|
||||
deadchat_broadcast(" enabled emergency maintenance access at [span_name("[get_area_name(usr, TRUE)]")].", usr, src.loc, message_type = DEADCHAT_ANNOUNCEMENT)
|
||||
|
||||
/obj/machinery/computer/communications/ui_data(mob/user)
|
||||
var/list/data = list(
|
||||
"authenticated" = FALSE,
|
||||
"emagged" = FALSE,
|
||||
"hasConnection" = has_communication(),
|
||||
)
|
||||
|
||||
var/ui_state = issilicon(user) ? cyborg_state : state
|
||||
|
||||
var/has_connection = has_communication()
|
||||
data["hasConnection"] = has_connection
|
||||
|
||||
// if(!SSjob.assigned_captain && !SSjob.safe_code_requested && SSid_access.spare_id_safe_code && has_connection)
|
||||
// data["canRequestSafeCode"] = TRUE
|
||||
// data["safeCodeDeliveryWait"] = 0
|
||||
// else
|
||||
// data["canRequestSafeCode"] = FALSE
|
||||
// if(SSjob.safe_code_timer_id && has_connection)
|
||||
// data["safeCodeDeliveryWait"] = timeleft(SSjob.safe_code_timer_id)
|
||||
// data["safeCodeDeliveryArea"] = get_area(SSjob.safe_code_request_loc)
|
||||
// else
|
||||
// data["safeCodeDeliveryWait"] = 0
|
||||
// data["safeCodeDeliveryArea"] = null
|
||||
|
||||
if (authenticated || issilicon(user))
|
||||
data["authenticated"] = TRUE
|
||||
data["canLogOut"] = !issilicon(user)
|
||||
@@ -371,16 +392,17 @@
|
||||
data["importantActionReady"] = COOLDOWN_FINISHED(src, important_action_cooldown)
|
||||
data["shuttleCalled"] = FALSE
|
||||
data["shuttleLastCalled"] = FALSE
|
||||
|
||||
data["aprilFools"] = SSevents.holidays && SSevents.holidays[APRIL_FOOLS]
|
||||
data["alertLevel"] = NUM2SECLEVEL(GLOB.security_level)
|
||||
data["authorizeName"] = authorize_name
|
||||
data["canLogOut"] = !issilicon(user)
|
||||
data["shuttleCanEvacOrFailReason"] = SSshuttle.canEvac(user, TRUE)
|
||||
data["shuttleCanEvacOrFailReason"] = SSshuttle.canEvac(user)
|
||||
|
||||
if (authenticated_as_non_silicon_captain(user))
|
||||
data["canRequestNuke"] = TRUE
|
||||
if (authenticated_as_non_silicon_command(user))
|
||||
data["canMessageAssociates"] = TRUE
|
||||
|
||||
if (can_send_messages_to_other_sectors(user))
|
||||
data["canSendToSectors"] = TRUE
|
||||
|
||||
@@ -428,8 +450,13 @@
|
||||
|
||||
for (var/shuttle_id in SSmapping.shuttle_templates)
|
||||
var/datum/map_template/shuttle/shuttle_template = SSmapping.shuttle_templates[shuttle_id]
|
||||
if (!shuttle_template.can_be_bought || shuttle_template.credit_cost == INFINITY)
|
||||
|
||||
if (shuttle_template.credit_cost == INFINITY)
|
||||
continue
|
||||
|
||||
if (!shuttle_template.can_be_bought)
|
||||
continue
|
||||
|
||||
shuttles += list(list(
|
||||
"name" = shuttle_template.name,
|
||||
"description" = shuttle_template.description,
|
||||
@@ -478,6 +505,7 @@
|
||||
return FALSE
|
||||
if (!authenticated_as_non_silicon_captain(user))
|
||||
return FALSE
|
||||
|
||||
if (SSshuttle.emergency.mode != SHUTTLE_RECALL && SSshuttle.emergency.mode != SHUTTLE_IDLE)
|
||||
return "The shuttle is already in transit."
|
||||
if (SSshuttle.shuttle_purchased == SHUTTLEPURCHASE_PURCHASED)
|
||||
@@ -492,21 +520,38 @@
|
||||
|
||||
return length(CONFIG_GET(keyed_list/cross_server)) > 0
|
||||
|
||||
/**
|
||||
* Call an emergency meeting
|
||||
*
|
||||
* Comm Console wrapper for the Communications subsystem wrapper for the call_emergency_meeting world proc.
|
||||
* Checks to make sure the proc can be called, and handles relevant feedback, logging and timing.
|
||||
* See the SScommunications proc definition for more detail, in short, teleports the entire crew to
|
||||
* the bridge for a meetup. Should only really happen during april fools.
|
||||
* Arguments:
|
||||
* * user - Mob who called the meeting
|
||||
*/
|
||||
/obj/machinery/computer/communications/proc/emergency_meeting(mob/living/user)
|
||||
if(!SScommunications.can_make_emergency_meeting(user))
|
||||
to_chat(user, span_alert("The emergency meeting button doesn't seem to work right now. Please stand by."))
|
||||
return
|
||||
SScommunications.emergency_meeting(user)
|
||||
deadchat_broadcast(" called an emergency meeting from [span_name("[get_area_name(usr, TRUE)]")].", span_name("[user.real_name]"), user, message_type=DEADCHAT_ANNOUNCEMENT)
|
||||
|
||||
/obj/machinery/computer/communications/proc/make_announcement(mob/living/user)
|
||||
var/is_ai = issilicon(user)
|
||||
if(!SScommunications.can_announce(user, is_ai))
|
||||
to_chat(user, "<span class='alert'>Intercomms recharging. Please stand by.</span>")
|
||||
to_chat(user, span_alert("Intercomms recharging. Please stand by."))
|
||||
return
|
||||
var/input = stripped_input(user, "Please choose a message to announce to the station crew.", "What?")
|
||||
if(!input || !user.canUseTopic(src, !issilicon(usr)))
|
||||
return
|
||||
if(!(user.can_speak())) //No more cheating, mime/random mute guy!
|
||||
input = "..."
|
||||
to_chat(user, "<span class='warning'>You find yourself unable to speak.</span>")
|
||||
to_chat(user, span_warning("You find yourself unable to speak."))
|
||||
else
|
||||
input = user.treat_message(input) //Adds slurs and so on. Someone should make this use languages too.
|
||||
SScommunications.make_announcement(user, is_ai, input)
|
||||
deadchat_broadcast(" made a priority announcement from <span class='name'>[get_area_name(usr, TRUE)]</span>.", "<span class='name'>[user.real_name]</span>", user)
|
||||
deadchat_broadcast(" made a priority announcement from [span_name("[get_area_name(usr, TRUE)]")].", user, src.loc, message_type=DEADCHAT_ANNOUNCEMENT)
|
||||
|
||||
/obj/machinery/computer/communications/proc/post_status(command, data1, data2)
|
||||
|
||||
|
||||
+271
-259
@@ -1,3 +1,5 @@
|
||||
#define AHELP_FIRST_MESSAGE "Please adminhelp before leaving the round, even if there are no administrators online!"
|
||||
|
||||
/*
|
||||
* Cryogenic refrigeration unit. Basically a despawner.
|
||||
* Stealing a lot of concepts/code from sleepers due to massive laziness.
|
||||
@@ -5,6 +7,7 @@
|
||||
* since time_entered, which is world.time when the occupant moves in.
|
||||
* ~ Zuhayr
|
||||
*/
|
||||
GLOBAL_LIST_EMPTY(cryopod_computers)
|
||||
|
||||
//Main cryopod console.
|
||||
|
||||
@@ -13,159 +16,113 @@
|
||||
desc = "An interface between crew and the cryogenic storage oversight systems."
|
||||
icon = 'icons/obj/Cryogenic2.dmi'
|
||||
icon_state = "cellconsole_1"
|
||||
icon_keyboard = null
|
||||
circuit = /obj/item/circuitboard/cryopodcontrol
|
||||
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.
|
||||
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
|
||||
|
||||
//Used for logging people entering cryosleep and important items they are carrying.
|
||||
// Used for logging people entering cryosleep and important items they are carrying.
|
||||
var/list/frozen_crew = list()
|
||||
var/list/obj/stored_packages = list()
|
||||
|
||||
var/list/stored_packages = list()
|
||||
/// Does this console store items? if NOT, will dump all items when the user cryo's instead
|
||||
var/allow_items = TRUE
|
||||
|
||||
var/storage_type = "crewmembers"
|
||||
var/storage_name = "Cryogenic Oversight Control"
|
||||
|
||||
/obj/machinery/computer/cryopod/deconstruct()
|
||||
. = ..()
|
||||
for(var/i in stored_packages)
|
||||
var/obj/O = i
|
||||
O.forceMove(drop_location())
|
||||
|
||||
/obj/machinery/computer/cryopod/attack_ai()
|
||||
attack_hand()
|
||||
/obj/machinery/computer/cryopod/Initialize()
|
||||
. = ..()
|
||||
GLOB.cryopod_computers += src
|
||||
|
||||
/obj/machinery/computer/cryopod/ui_interact(mob/user = usr)
|
||||
if(!is_operational())
|
||||
/obj/machinery/computer/cryopod/Destroy()
|
||||
GLOB.cryopod_computers -= src
|
||||
return ..()
|
||||
|
||||
/obj/machinery/computer/cryopod/update_icon_state()
|
||||
if(stat & (NOPOWER|BROKEN))
|
||||
icon_state = "cellconsole"
|
||||
return ..()
|
||||
icon_state = "cellconsole_1"
|
||||
return ..()
|
||||
|
||||
/obj/machinery/computer/cryopod/ui_interact(mob/user, datum/tgui/ui)
|
||||
if(stat & (NOPOWER|BROKEN))
|
||||
return
|
||||
|
||||
user.set_machine(src)
|
||||
add_fingerprint(user)
|
||||
|
||||
var/dat = ""
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "CryopodConsole", name)
|
||||
ui.open()
|
||||
|
||||
dat += "<h2>Welcome, [user.real_name].</h2><hr/>"
|
||||
dat += "<br><br>"
|
||||
/obj/machinery/computer/cryopod/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["frozen_crew"] = frozen_crew
|
||||
var/list/item_meta = list()
|
||||
|
||||
switch(src.menu)
|
||||
if(1)
|
||||
dat += "<a href='byond://?src=[REF(src)];menu=2'>View crew storage log</a><br><br>"
|
||||
if(allow_items)
|
||||
dat += "<a href='byond://?src=[REF(src)];menu=3'>View objects storage log</a><br><br>"
|
||||
dat += "<a href='byond://?src=[REF(src)];item=1'>Recover object</a><br><br>"
|
||||
dat += "<a href='byond://?src=[REF(src)];allitems=1'>Recover all objects</a><br>"
|
||||
if(2)
|
||||
dat += "<a href='byond://?src=[REF(src)];menu=1'><< Back</a><br><br>"
|
||||
dat += "<h3>Recently stored Crew</h3><br/><hr/><br/>"
|
||||
if(!frozen_crew.len)
|
||||
dat += "There has been no storage usage at this terminal.<br/>"
|
||||
else
|
||||
for(var/person in frozen_crew)
|
||||
dat += "[person]<br/>"
|
||||
dat += "<hr/>"
|
||||
if(3)
|
||||
dat += "<a href='byond://?src=[REF(src)];menu=1'><< Back</a><br><br>"
|
||||
dat += "<h3>Recently stored objects</h3><br/><hr/><br/>"
|
||||
if(!stored_packages.len)
|
||||
dat += "There has been no storage usage at this terminal.<br/>"
|
||||
else
|
||||
for(var/obj/O in stored_packages)
|
||||
dat += "[O.name]<br/>"
|
||||
dat += "<hr/>"
|
||||
for(var/obj/item/storage/box/blue/cryostorage_items/O as anything in stored_packages)
|
||||
item_meta += list(list("name" = O.real_name, "ref" = REF(O))) // i am truely livid about byond lists
|
||||
data["item_meta"] = item_meta
|
||||
|
||||
var/datum/browser/popup = new(user, "cryopod_console", "Cryogenic System Control")
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
var/obj/item/card/id/id_card
|
||||
var/datum/bank_account/current_user
|
||||
if(isliving(user))
|
||||
var/mob/living/person = user
|
||||
id_card = person.get_idcard()
|
||||
if(id_card?.registered_account)
|
||||
current_user = id_card.registered_account
|
||||
if(current_user)
|
||||
data["account_name"] = current_user.account_holder // i do not know why but this uses budget?
|
||||
|
||||
/obj/machinery/computer/cryopod/Topic(href, href_list)
|
||||
return data
|
||||
|
||||
/obj/machinery/computer/cryopod/ui_act(action, params)
|
||||
if(..())
|
||||
return TRUE
|
||||
return
|
||||
|
||||
var/mob/user = usr
|
||||
|
||||
add_fingerprint(user)
|
||||
|
||||
if(href_list["item"])
|
||||
if(!allowed(user) && !(obj_flags & EMAGGED))
|
||||
to_chat(user, "<span class='warning'>Access Denied.</span>")
|
||||
if(action == "item")
|
||||
if(!allowed(usr) && !(obj_flags & EMAGGED))
|
||||
to_chat(usr, "<span class='warning'>Access Denied.</span>")
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
updateUsrDialog()
|
||||
return
|
||||
|
||||
if(!allow_items)
|
||||
return
|
||||
|
||||
if(stored_packages.len == 0)
|
||||
to_chat(user, "<span class='notice'>There is nothing to recover from storage.</span>")
|
||||
to_chat(usr, "<span class='notice'>There is nothing to recover from storage.</span>")
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
updateUsrDialog()
|
||||
return
|
||||
|
||||
var/obj/I = input(user, "Please choose which object to retrieve.","Object recovery",null) as null|anything in stored_packages
|
||||
var/obj/I = locate(params["item"])
|
||||
playsound(src, "terminal_type", 25, 0)
|
||||
|
||||
if(!I)
|
||||
return
|
||||
|
||||
if(!(I in stored_packages))
|
||||
to_chat(user, "<span class='notice'>\The [I] is no longer in storage.</span>")
|
||||
to_chat(usr, "<span class='notice'>\The [I] is no longer in storage.</span>")
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
updateUsrDialog()
|
||||
return
|
||||
|
||||
visible_message("<span class='notice'>The console beeps happily as it disgorges \the [I].</span>")
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
|
||||
|
||||
I.forceMove(drop_location())
|
||||
if(user && Adjacent(user) && user.can_hold_items())
|
||||
user.put_in_hands(I)
|
||||
if(usr && Adjacent(usr) && usr.can_hold_items())
|
||||
usr.put_in_hands(I)
|
||||
stored_packages -= I
|
||||
updateUsrDialog()
|
||||
|
||||
else if(href_list["allitems"])
|
||||
playsound(src, "terminal_type", 25, 0)
|
||||
if(!allowed(user) && !(obj_flags & EMAGGED))
|
||||
to_chat(user, "<span class='warning'>Access Denied.</span>")
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
updateUsrDialog()
|
||||
return
|
||||
|
||||
if(!allow_items)
|
||||
return
|
||||
|
||||
if(stored_packages.len == 0)
|
||||
to_chat(user, "<span class='notice'>There is nothing to recover from storage.</span>")
|
||||
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
|
||||
return
|
||||
|
||||
visible_message("<span class='notice'>The console beeps happily as it disgorges the desired objects.</span>")
|
||||
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
|
||||
|
||||
for(var/obj/O in stored_packages)
|
||||
O.forceMove(get_turf(src))
|
||||
stored_packages.Cut()
|
||||
updateUsrDialog()
|
||||
|
||||
else if (href_list["menu"])
|
||||
src.menu = text2num(href_list["menu"])
|
||||
playsound(src, "terminal_type", 25, 0)
|
||||
updateUsrDialog()
|
||||
|
||||
ui_interact(usr)
|
||||
updateUsrDialog()
|
||||
return
|
||||
|
||||
/obj/item/circuitboard/cryopodcontrol
|
||||
name = "Circuit board (Cryogenic Oversight Console)"
|
||||
build_path = "/obj/machinery/computer/cryopod"
|
||||
|
||||
/obj/machinery/computer/cryopod/contents_explosion(severity, target, origin)
|
||||
return
|
||||
|
||||
/// The box
|
||||
/obj/item/storage/box/blue/cryostorage_items
|
||||
w_class = WEIGHT_CLASS_HUGE
|
||||
|
||||
//Cryopods themselves.
|
||||
// Cryopods themselves.
|
||||
/obj/machinery/cryopod
|
||||
name = "cryogenic freezer"
|
||||
desc = "Suited for Cyborgs and Humanoids, the pod is a safe place for personnel affected by the Space Sleep Disorder to get some rest."
|
||||
@@ -178,47 +135,55 @@
|
||||
var/on_store_message = "has entered long-term storage."
|
||||
var/on_store_name = "Cryogenic Oversight"
|
||||
|
||||
// 15 minutes-ish safe period before being despawned.
|
||||
var/time_till_despawn = 15 * 600 // This is reduced by 90% if a player manually enters cryo
|
||||
var/despawn_world_time = null // Used to keep track of the safe period.
|
||||
/// Time until despawn when a mob enters a cryopod. You can cryo other people in pods.
|
||||
var/time_till_despawn = 30 SECONDS
|
||||
/// Cooldown for when it's now safe to try an despawn the player.
|
||||
COOLDOWN_DECLARE(despawn_world_time)
|
||||
|
||||
var/obj/machinery/computer/cryopod/control_computer
|
||||
var/item_storage_type = /obj/item/storage/box/blue/cryostorage_items //with how storage components work this can be anything the player can open or anything with a storage component.
|
||||
var/last_no_computer_message = 0
|
||||
///Weakref to our controller
|
||||
var/datum/weakref/control_computer_weakref
|
||||
COOLDOWN_DECLARE(last_no_computer_message)
|
||||
|
||||
/obj/machinery/cryopod/Initialize(mapload)
|
||||
. = ..()
|
||||
/obj/machinery/cryopod/Initialize()
|
||||
..()
|
||||
return INITIALIZE_HINT_LATELOAD //Gotta populate the cryopod computer GLOB first
|
||||
|
||||
/obj/machinery/cryopod/LateInitialize()
|
||||
update_icon()
|
||||
find_control_computer(mapload)
|
||||
find_control_computer()
|
||||
|
||||
// This is not a good situation
|
||||
/obj/machinery/cryopod/Destroy()
|
||||
control_computer_weakref = null
|
||||
return ..()
|
||||
|
||||
/obj/machinery/cryopod/proc/find_control_computer(urgent = FALSE)
|
||||
for(var/obj/machinery/computer/cryopod/C in get_area(src))
|
||||
control_computer = C
|
||||
if(C)
|
||||
return C
|
||||
break
|
||||
for(var/cryo_console as anything in GLOB.cryopod_computers)
|
||||
var/obj/machinery/computer/cryopod/console = cryo_console
|
||||
if(get_area(console) == get_area(src))
|
||||
control_computer_weakref = WEAKREF(console)
|
||||
break
|
||||
|
||||
// Don't send messages unless we *need* the computer, and less than five minutes have passed since last time we messaged
|
||||
if(!control_computer && urgent && last_no_computer_message + 5*60*10 < world.time)
|
||||
if(!control_computer_weakref && urgent && COOLDOWN_FINISHED(src, last_no_computer_message))
|
||||
COOLDOWN_START(src, last_no_computer_message, 5 MINUTES)
|
||||
log_admin("Cryopod in [get_area(src)] could not find control computer!")
|
||||
message_admins("Cryopod in [get_area(src)] could not find control computer!")
|
||||
last_no_computer_message = world.time
|
||||
|
||||
return control_computer != null
|
||||
return control_computer_weakref != null
|
||||
|
||||
/obj/machinery/cryopod/close_machine(mob/user)
|
||||
if(!control_computer)
|
||||
/obj/machinery/cryopod/close_machine(atom/movable/target)
|
||||
if(!control_computer_weakref)
|
||||
find_control_computer(TRUE)
|
||||
if((isnull(user) || istype(user)) && state_open && !panel_open)
|
||||
..(user)
|
||||
if((isnull(target) || isliving(target)) && state_open && !panel_open)
|
||||
..(target)
|
||||
var/mob/living/mob_occupant = occupant
|
||||
investigate_log("Cryogenics machine closed with occupant [key_name(occupant)] by user [key_name(user)].", INVESTIGATE_CRYOGENICS)
|
||||
investigate_log("Cryogenics machine closed with occupant [key_name(occupant)] by user [key_name(target)].", INVESTIGATE_CRYOGENICS)
|
||||
if(mob_occupant && mob_occupant.stat != DEAD)
|
||||
to_chat(occupant, "<span class='boldnotice'>You feel cool air surround you. You go numb as your senses turn inward.</span>")
|
||||
if(mob_occupant.client)//if they're logged in
|
||||
despawn_world_time = world.time + (time_till_despawn * 0.1)
|
||||
else
|
||||
despawn_world_time = world.time + time_till_despawn
|
||||
to_chat(occupant, span_notice("<b>You feel cool air surround you. You go numb as your senses turn inward.</b>"))
|
||||
|
||||
COOLDOWN_START(src, despawn_world_time, time_till_despawn)
|
||||
icon_state = "cryopod"
|
||||
|
||||
/obj/machinery/cryopod/open_machine()
|
||||
@@ -231,8 +196,8 @@
|
||||
|
||||
/obj/machinery/cryopod/container_resist(mob/living/user)
|
||||
investigate_log("Cryogenics machine container resisted by [key_name(user)] with occupant [key_name(occupant)].", INVESTIGATE_CRYOGENICS)
|
||||
visible_message("<span class='notice'>[occupant] emerges from [src]!</span>",
|
||||
"<span class='notice'>You climb out of [src]!</span>")
|
||||
visible_message(span_notice("[occupant] emerges from [src]!"),
|
||||
span_notice("You climb out of [src]!"))
|
||||
open_machine()
|
||||
|
||||
/obj/machinery/cryopod/relaymove(mob/user)
|
||||
@@ -243,36 +208,122 @@
|
||||
return
|
||||
|
||||
var/mob/living/mob_occupant = occupant
|
||||
if(mob_occupant)
|
||||
// Eject dead people
|
||||
if(mob_occupant.stat == DEAD)
|
||||
open_machine()
|
||||
if(mob_occupant.stat == DEAD)
|
||||
open_machine()
|
||||
|
||||
if(!(world.time > despawn_world_time + 100))//+ 10 seconds
|
||||
return
|
||||
if(!mob_occupant.client && COOLDOWN_FINISHED(src, despawn_world_time))
|
||||
if(!control_computer_weakref)
|
||||
find_control_computer(urgent = TRUE)
|
||||
|
||||
if(!mob_occupant.client && mob_occupant.stat < 2) //Occupant is living and has no client.
|
||||
if(!control_computer)
|
||||
find_control_computer(urgent = TRUE)//better hope you found it this time
|
||||
despawn_occupant()
|
||||
|
||||
despawn_occupant()
|
||||
/obj/machinery/cryopod/proc/handle_objectives()
|
||||
var/mob/living/mob_occupant = occupant
|
||||
// Update any existing objectives involving this mob.
|
||||
for(var/datum/objective/objective in GLOB.objectives)
|
||||
// We don't want revs to get objectives that aren't for heads of staff. Letting
|
||||
// them win or lose based on cryo is silly so we remove the objective.
|
||||
if(istype(objective,/datum/objective/mutiny) && objective.target == mob_occupant.mind)
|
||||
objective.team.objectives -= objective
|
||||
qdel(objective)
|
||||
for(var/datum/mind/mind in objective.team.members)
|
||||
to_chat(mind.current, "<BR>[span_userdanger("Your target is no longer within reach. Objective removed!")]")
|
||||
mind.announce_objectives()
|
||||
else if(istype(objective.target) && objective.target == mob_occupant.mind)
|
||||
if(istype(objective, /datum/objective/contract))
|
||||
var/datum/antagonist/traitor/affected_traitor = objective.owner.has_antag_datum(/datum/antagonist/traitor)
|
||||
var/datum/contractor_hub/affected_contractor_hub = affected_traitor.contractor_hub
|
||||
for(var/datum/syndicate_contract/affected_contract as anything in affected_contractor_hub.assigned_contracts)
|
||||
if(affected_contract.contract == objective)
|
||||
affected_contract.generate(affected_contractor_hub.assigned_targets)
|
||||
affected_contractor_hub.assigned_targets.Add(affected_contract.contract.target)
|
||||
to_chat(objective.owner.current, "<BR>[span_userdanger("Contract target out of reach. Contract rerolled.")]")
|
||||
break
|
||||
else
|
||||
var/old_target = objective.target
|
||||
objective.target = null
|
||||
if(!objective)
|
||||
return
|
||||
objective.find_target()
|
||||
if(!objective.target && objective.owner)
|
||||
to_chat(objective.owner.current, "<BR>[span_userdanger("Your target is no longer within reach. Objective removed!")]")
|
||||
for(var/datum/antagonist/antag in objective.owner.antag_datums)
|
||||
antag.objectives -= objective
|
||||
if (!objective.team)
|
||||
objective.update_explanation_text()
|
||||
objective.owner.announce_objectives()
|
||||
to_chat(objective.owner.current, "<BR>[span_userdanger("You get the feeling your target is no longer within reach. Time for Plan [pick("A","B","C","D","X","Y","Z")]. Objectives updated!")]")
|
||||
else
|
||||
var/list/objectivestoupdate
|
||||
for(var/datum/mind/objective_owner in objective.get_owners())
|
||||
to_chat(objective_owner.current, "<BR>[span_userdanger("You get the feeling your target is no longer within reach. Time for Plan [pick("A","B","C","D","X","Y","Z")]. Objectives updated!")]")
|
||||
for(var/datum/objective/update_target_objective in objective_owner.get_all_objectives())
|
||||
LAZYADD(objectivestoupdate, update_target_objective)
|
||||
objectivestoupdate += objective.team.objectives
|
||||
for(var/datum/objective/update_objective in objectivestoupdate)
|
||||
if(update_objective.target != old_target || !istype(update_objective,objective.type))
|
||||
continue
|
||||
update_objective.target = objective.target
|
||||
update_objective.update_explanation_text()
|
||||
to_chat(objective.owner.current, "<BR>[span_userdanger("You get the feeling your target is no longer within reach. Time for Plan [pick("A","B","C","D","X","Y","Z")]. Objectives updated!")]")
|
||||
update_objective.owner.announce_objectives()
|
||||
qdel(objective)
|
||||
|
||||
/obj/machinery/cryopod/proc/should_preserve_item(obj/item/item)
|
||||
for(var/datum/objective_item/steal/possible_item in GLOB.possible_items)
|
||||
if(istype(item, possible_item.targetitem))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
// This function can not be undone; do not call this unless you are sure
|
||||
/obj/machinery/cryopod/proc/despawn_occupant()
|
||||
if(!control_computer)
|
||||
find_control_computer()
|
||||
|
||||
var/mob/living/mob_occupant = occupant
|
||||
var/list/crew_member = list()
|
||||
|
||||
crew_member["name"] = mob_occupant.real_name
|
||||
|
||||
if(mob_occupant.mind)
|
||||
// Handle job slot/tater cleanup.
|
||||
var/job = mob_occupant.mind.assigned_role
|
||||
crew_member["job"] = job
|
||||
SSjob.FreeRole(job)
|
||||
// if(LAZYLEN(mob_occupant.mind.objectives))
|
||||
// mob_occupant.mind.objectives.Cut()
|
||||
mob_occupant.mind.special_role = null
|
||||
else
|
||||
crew_member["job"] = "N/A"
|
||||
|
||||
// Delete them from datacore.
|
||||
var/announce_rank = null
|
||||
for(var/datum/data/record/medical_record as anything in GLOB.data_core.medical)
|
||||
if(medical_record.fields["name"] == mob_occupant.real_name)
|
||||
qdel(medical_record)
|
||||
for(var/datum/data/record/security_record as anything in GLOB.data_core.security)
|
||||
if(security_record.fields["name"] == mob_occupant.real_name)
|
||||
qdel(security_record)
|
||||
for(var/datum/data/record/general_record as anything in GLOB.data_core.general)
|
||||
if(general_record.fields["name"] == mob_occupant.real_name)
|
||||
announce_rank = general_record.fields["rank"]
|
||||
qdel(general_record)
|
||||
|
||||
var/obj/machinery/computer/cryopod/control_computer = control_computer_weakref?.resolve()
|
||||
if(!control_computer)
|
||||
control_computer_weakref = null
|
||||
else
|
||||
control_computer.frozen_crew += list(crew_member)
|
||||
|
||||
// Make an announcement and log the person entering storage.
|
||||
if(GLOB.announcement_systems.len)
|
||||
var/obj/machinery/announcement_system/announcer = pick(GLOB.announcement_systems)
|
||||
announcer.announce("CRYOSTORAGE", mob_occupant.real_name, announce_rank, list())
|
||||
|
||||
visible_message(span_notice("[src] hums and hisses as it moves [mob_occupant.real_name] into storage."))
|
||||
|
||||
/* ============================= */
|
||||
var/list/obj/item/storing = list()
|
||||
var/list/obj/item/destroying = list()
|
||||
var/list/obj/item/destroy_later = list()
|
||||
|
||||
investigate_log("Despawning [key_name(mob_occupant)].", INVESTIGATE_CRYOGENICS)
|
||||
|
||||
var/atom/target_store = (control_computer?.allow_items && control_computer) || src //the double control computer check makes it return the control computer.
|
||||
var/drop_to_ground = !istype(target_store, /obj/machinery/computer/cryopod)
|
||||
|
||||
var/drop_to_ground = !istype(control_computer, /obj/machinery/computer/cryopod) || !control_computer.allow_items
|
||||
var/mind_identity = mob_occupant.mind?.name
|
||||
var/occupant_identity = mob_occupant.real_name
|
||||
|
||||
@@ -293,7 +344,6 @@
|
||||
AM.forceMove(src)
|
||||
R.module.remove_module(I, TRUE)
|
||||
else
|
||||
|
||||
if(ishuman(mob_occupant))
|
||||
var/mob/living/carbon/human/H = mob_occupant
|
||||
if(H.mind && H.client && H.client.prefs && H == H.mind.original_character)
|
||||
@@ -303,145 +353,89 @@
|
||||
if(iscarbon(mob_occupant)) // sorry simp-le-mobs deserve no mercy
|
||||
var/mob/living/carbon/C = mob_occupant
|
||||
gear = C.get_all_gear()
|
||||
for(var/i in gear)
|
||||
var/obj/item/I = i
|
||||
I.forceMove(src)
|
||||
if(!istype(I))
|
||||
destroying += I
|
||||
|
||||
for(var/obj/item/item_content as anything in gear)
|
||||
if(!istype(item_content) || HAS_TRAIT(item_content, TRAIT_NODROP))
|
||||
destroying += item_content
|
||||
continue
|
||||
if(I.item_flags & (DROPDEL | ABSTRACT))
|
||||
destroying += I
|
||||
continue
|
||||
if(HAS_TRAIT(I, TRAIT_NODROP))
|
||||
destroying += I
|
||||
if(item_content.item_flags & (DROPDEL | ABSTRACT))
|
||||
destroying += item_content
|
||||
continue
|
||||
|
||||
// destroying stays in mob for a bit
|
||||
item_content.forceMove(src)
|
||||
|
||||
// WEE WOO SNOWFLAKE TIME
|
||||
if(istype(I, /obj/item/pda))
|
||||
var/obj/item/pda/P = I
|
||||
if(istype(item_content, /obj/item/pda))
|
||||
var/obj/item/pda/P = item_content
|
||||
if((P.owner == mind_identity) || (P.owner == occupant_identity))
|
||||
destroying += P
|
||||
else
|
||||
storing += P
|
||||
else if(istype(I, /obj/item/card/id))
|
||||
var/obj/item/card/id/idcard = I
|
||||
else if(istype(item_content, /obj/item/card/id))
|
||||
var/obj/item/card/id/idcard = item_content
|
||||
if((idcard.registered_name == mind_identity) || (idcard.registered_name == occupant_identity))
|
||||
destroying += idcard
|
||||
else
|
||||
storing += idcard
|
||||
else
|
||||
storing += I
|
||||
storing += item_content
|
||||
|
||||
// get rid of mobs
|
||||
for(var/mob/living/L in mob_occupant.GetAllContents() - mob_occupant)
|
||||
L.forceMove(drop_location())
|
||||
|
||||
if(storing.len)
|
||||
var/obj/O = new item_storage_type
|
||||
var/obj/item/storage/box/blue/cryostorage_items/O = new /obj/item/storage/box/blue/cryostorage_items
|
||||
O.name = "cryogenic retrieval package: [mob_occupant.real_name]"
|
||||
O.real_name = mob_occupant.real_name
|
||||
for(var/i in storing)
|
||||
var/obj/item/I = i
|
||||
I.forceMove(O)
|
||||
O.forceMove(drop_to_ground? target_store.drop_location() : target_store)
|
||||
if((target_store == control_computer) && !drop_to_ground)
|
||||
O.forceMove(drop_to_ground ? control_computer.drop_location() : control_computer)
|
||||
if((control_computer == control_computer) && !drop_to_ground)
|
||||
control_computer.stored_packages += O
|
||||
|
||||
QDEL_LIST(destroying)
|
||||
|
||||
//Update any existing objectives involving this mob.
|
||||
for(var/i in GLOB.objectives)
|
||||
var/datum/objective/O = i
|
||||
// We don't want revs to get objectives that aren't for heads of staff. Letting
|
||||
// them win or lose based on cryo is silly so we remove the objective.
|
||||
if(istype(O,/datum/objective/mutiny) && O.target == mob_occupant.mind)
|
||||
qdel(O)
|
||||
else if(O.target && istype(O.target, /datum/mind))
|
||||
if(O.target != mob_occupant.mind)
|
||||
continue
|
||||
if(O.check_midround_completion())
|
||||
continue
|
||||
if(O.owner && O.owner.current)
|
||||
to_chat(O.owner.current, "<BR><span class='userdanger'>You get the feeling your target is no longer within reach. Time for Plan [pick("A","B","C","D","X","Y","Z")]. Objectives updated!</span>")
|
||||
O.target = null
|
||||
spawn(10) //This should ideally fire after the occupant is deleted.
|
||||
if(!O)
|
||||
return
|
||||
O.find_target()
|
||||
O.update_explanation_text()
|
||||
if(!(O.target))
|
||||
qdel(O)
|
||||
|
||||
if(mob_occupant.mind)
|
||||
//Handle job slot/tater cleanup.
|
||||
if(mob_occupant.mind.assigned_role)
|
||||
var/job = mob_occupant.mind.assigned_role
|
||||
SSjob.FreeRole(job)
|
||||
mob_occupant.mind.special_role = null
|
||||
|
||||
// Delete them from datacore.
|
||||
|
||||
var/announce_rank = null
|
||||
for(var/datum/data/record/R in GLOB.data_core.medical)
|
||||
if((R.fields["name"] == mob_occupant.real_name))
|
||||
qdel(R)
|
||||
for(var/datum/data/record/T in GLOB.data_core.security)
|
||||
if((T.fields["name"] == mob_occupant.real_name))
|
||||
qdel(T)
|
||||
for(var/datum/data/record/G in GLOB.data_core.general)
|
||||
if((G.fields["name"] == mob_occupant.real_name))
|
||||
announce_rank = G.fields["rank"]
|
||||
qdel(G)
|
||||
|
||||
for(var/obj/machinery/computer/cloning/cloner in world)
|
||||
for(var/datum/data/record/R in cloner.records)
|
||||
if(R.fields["name"] == mob_occupant.real_name)
|
||||
cloner.records.Remove(R)
|
||||
|
||||
//Make an announcement and log the person entering storage.
|
||||
if(control_computer)
|
||||
control_computer.frozen_crew += "[mob_occupant.real_name]"
|
||||
|
||||
if(GLOB.announcement_systems.len)
|
||||
var/obj/machinery/announcement_system/announcer = pick(GLOB.announcement_systems)
|
||||
announcer.announce("CRYOSTORAGE", mob_occupant.real_name, announce_rank, list())
|
||||
visible_message("<span class='notice'>\The [src] hums and hisses as it moves [mob_occupant.real_name] into storage.</span>")
|
||||
/* ============================= */
|
||||
|
||||
// Ghost and delete the mob.
|
||||
// they already did ghost verb
|
||||
var/mob/dead/observer/G = mob_occupant.get_ghost(TRUE)
|
||||
if(G)
|
||||
G.voluntary_ghosted = TRUE
|
||||
// they did not ghost verb
|
||||
else
|
||||
mob_occupant.ghostize(FALSE, penalize = TRUE, voluntary = TRUE, cryo = TRUE)
|
||||
|
||||
QDEL_LIST(destroying)
|
||||
handle_objectives()
|
||||
QDEL_NULL(occupant)
|
||||
QDEL_LIST(destroy_later)
|
||||
open_machine()
|
||||
name = initial(name)
|
||||
|
||||
/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)
|
||||
if(!istype(target) || !can_interact(user) || !target.Adjacent(user) || !ismob(target) || isanimal(target) || !istype(user.loc, /turf) || target.buckled)
|
||||
return
|
||||
|
||||
if(occupant)
|
||||
to_chat(user, "<span class='boldnotice'>The cryo pod is already occupied!</span>")
|
||||
to_chat(user, span_notice("[src] is already occupied!"))
|
||||
return
|
||||
|
||||
if(target.stat == DEAD)
|
||||
to_chat(user, "<span class='notice'>Dead people can not be put into cryo.</span>")
|
||||
to_chat(user, span_notice("Dead people can not be put into cryo."))
|
||||
return
|
||||
|
||||
if(target.client && user != target)
|
||||
if(user != target && target.client)
|
||||
if(iscyborg(target))
|
||||
to_chat(user, "<span class='danger'>You can't put [target] into [src]. They're online.</span>")
|
||||
to_chat(user, span_danger("You can't put [target] into [src]. [target.p_theyre(capitalized = TRUE)] online."))
|
||||
else
|
||||
to_chat(user, "<span class='danger'>You can't put [target] into [src]. They're conscious.</span>")
|
||||
to_chat(user, span_danger("You can't put [target] into [src]. [target.p_theyre(capitalized = TRUE)] conscious."))
|
||||
return
|
||||
else if(target.client)
|
||||
if(alert(target,"Would you like to enter cryosleep?",,"Yes","No") == "No")
|
||||
else if(target.client) // mob has client
|
||||
if(tgalert(target, "Would you like to enter cryosleep?", "Enter Cryopod?", "Yes", "No") != "Yes")
|
||||
return
|
||||
|
||||
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
|
||||
if(target == user && world.time - target.client.cryo_warned > 5 MINUTES)
|
||||
var/list/caught_string
|
||||
var/addendum = ""
|
||||
if(target.mind.assigned_role in GLOB.command_positions)
|
||||
@@ -459,30 +453,48 @@
|
||||
LAZYADD(caught_string, "Revolutionary")
|
||||
|
||||
if(caught_string)
|
||||
alert(target, "You're a [english_list(caught_string)]![generic_plsnoleave_message][addendum]")
|
||||
tgui_alert(target, "You're a [english_list(caught_string)]! [AHELP_FIRST_MESSAGE][addendum]")
|
||||
target.client.cryo_warned = world.time
|
||||
return
|
||||
|
||||
if(!target || user.incapacitated() || !target.Adjacent(user) || !Adjacent(user) || (!ishuman(user) && !iscyborg(user)) || !istype(user.loc, /turf) || target.buckled)
|
||||
if(!istype(target) || !can_interact(user) || !target.Adjacent(user) || !ismob(target) || isanimal(target) || !istype(user.loc, /turf) || target.buckled)
|
||||
return
|
||||
//rerun the checks in case of shenanigans
|
||||
|
||||
if(target == user)
|
||||
visible_message("[user] starts climbing into the cryo pod.")
|
||||
else
|
||||
visible_message("[user] starts putting [target] into the cryo pod.")
|
||||
// rerun the checks in case of shenanigans
|
||||
|
||||
if(occupant)
|
||||
to_chat(user, "<span class='boldnotice'>\The [src] is in use.</span>")
|
||||
to_chat(user, span_notice("[src] is already occupied!"))
|
||||
return
|
||||
close_machine(target)
|
||||
|
||||
to_chat(target, "<span class='boldnotice'>If you ghost, log out or close your client now, your character will shortly be permanently removed from the round.</span>")
|
||||
name = "[name] ([occupant.name])"
|
||||
log_admin("<span class='notice'>[key_name(target)] entered a stasis pod.</span>")
|
||||
message_admins("[key_name_admin(target)] entered a stasis pod. (<A HREF='?_src_=holder;[HrefToken()];adminplayerobservecoodjump=1;X=[x];Y=[y];Z=[z]'>JMP</a>)")
|
||||
if(target == user)
|
||||
visible_message("<span class='infoplain'>[user] starts climbing into the cryo pod.</span>")
|
||||
else
|
||||
visible_message("<span class='infoplain'>[user] starts putting [target] into the cryo pod.</span>")
|
||||
|
||||
to_chat(target, span_warning("<b>If you ghost, log out or close your client now, your character will shortly be permanently removed from the round.</b>"))
|
||||
|
||||
log_admin("[key_name(target)] entered a stasis pod.")
|
||||
message_admins("[key_name_admin(target)] entered a stasis pod. [ADMIN_JMP(src)]")
|
||||
add_fingerprint(target)
|
||||
|
||||
//Attacks/effects.
|
||||
close_machine(target)
|
||||
name = "[name] ([target.name])"
|
||||
|
||||
// Attacks/effects.
|
||||
/obj/machinery/cryopod/blob_act()
|
||||
return //Sorta gamey, but we don't really want these to be destroyed.
|
||||
return // Sorta gamey, but we don't really want these to be destroyed.
|
||||
|
||||
#undef AHELP_FIRST_MESSAGE
|
||||
|
||||
/obj/item/circuitboard/cryopodcontrol
|
||||
name = "Circuit board (Cryogenic Oversight Console)"
|
||||
build_path = /obj/machinery/computer/cryopod
|
||||
|
||||
/obj/machinery/computer/cryopod/contents_explosion()
|
||||
return
|
||||
|
||||
/obj/machinery/computer/cryopod/contents_explosion()
|
||||
return //don't blow everyone's shit up.
|
||||
|
||||
/// The box
|
||||
/obj/item/storage/box/blue/cryostorage_items
|
||||
w_class = WEIGHT_CLASS_HUGE
|
||||
var/real_name = "fire coderbus"
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
rad_insulation = RAD_MEDIUM_INSULATION
|
||||
|
||||
var/static/list/airlock_overlays = list()
|
||||
|
||||
|
||||
/// sigh
|
||||
var/unelectrify_timerid
|
||||
|
||||
@@ -1278,8 +1278,8 @@
|
||||
assemblytype = /obj/structure/door_assembly/door_assembly_extmai
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/door/airlock/CanAStarPass(obj/item/card/id/ID)
|
||||
//Airlock is passable if it is open (!density), bot has access, and is not bolted shut or powered off)
|
||||
/obj/machinery/door/airlock/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller)
|
||||
//Airlock is passable if it is open (!density), bot has access, and is not bolted shut or powered off)
|
||||
return !density || (check_access(ID) && !locked && hasPower())
|
||||
|
||||
/obj/machinery/door/airlock/emag_act(mob/user)
|
||||
|
||||
@@ -194,22 +194,19 @@ GLOBAL_LIST_EMPTY(doppler_arrays)
|
||||
|
||||
/*****The Point Calculator*****/
|
||||
|
||||
if(orig_light < 10)
|
||||
if(orig_light < 5)
|
||||
say("Explosion not large enough for research calculations.")
|
||||
return
|
||||
else
|
||||
point_gain = (100000 * orig_light) / (orig_light + 5000)
|
||||
else if(orig_light < BOMB_TARGET_SIZE) // we want to give fewer points if below the target; this curve does that
|
||||
point_gain = (BOMB_TARGET_POINTS * orig_light ** BOMB_SUB_TARGET_EXPONENT) / (BOMB_TARGET_SIZE**BOMB_SUB_TARGET_EXPONENT)
|
||||
else // once we're at the target, switch to a hyperbolic function so we can't go too far above it, but bigger bombs always get more points
|
||||
point_gain = (BOMB_TARGET_POINTS * 2 * orig_light) / (orig_light + BOMB_TARGET_SIZE)
|
||||
|
||||
/*****The Point Capper*****/
|
||||
if(point_gain > linked_techweb.largest_bomb_value)
|
||||
if(point_gain <= TECHWEB_BOMB_POINTCAP || linked_techweb.largest_bomb_value < TECHWEB_BOMB_POINTCAP)
|
||||
var/old_tech_largest_bomb_value = linked_techweb.largest_bomb_value //held so we can pull old before we do math
|
||||
linked_techweb.largest_bomb_value = point_gain
|
||||
point_gain -= old_tech_largest_bomb_value
|
||||
point_gain = min(point_gain,TECHWEB_BOMB_POINTCAP)
|
||||
else
|
||||
linked_techweb.largest_bomb_value = TECHWEB_BOMB_POINTCAP
|
||||
point_gain = 1000
|
||||
var/old_tech_largest_bomb_value = linked_techweb.largest_bomb_value //held so we can pull old before we do math
|
||||
linked_techweb.largest_bomb_value = point_gain
|
||||
point_gain -= old_tech_largest_bomb_value
|
||||
var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_SCI)
|
||||
if(D)
|
||||
D.adjust_money(point_gain)
|
||||
|
||||
+101
-51
@@ -1,8 +1,8 @@
|
||||
/* Holograms!
|
||||
* Contains:
|
||||
* Holopad
|
||||
* Hologram
|
||||
* Other stuff
|
||||
* Holopad
|
||||
* Hologram
|
||||
* Other stuff
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -24,7 +24,6 @@ Possible to do for anyone motivated enough:
|
||||
* Holopad
|
||||
*/
|
||||
|
||||
GLOBAL_LIST_EMPTY(network_holopads)
|
||||
#define HOLOPAD_PASSIVE_POWER_USAGE 1
|
||||
#define HOLOGRAM_POWER_USAGE 2
|
||||
|
||||
@@ -32,6 +31,7 @@ GLOBAL_LIST_EMPTY(network_holopads)
|
||||
name = "holopad"
|
||||
desc = "It's a floor-mounted device for projecting holographic images."
|
||||
icon_state = "holopad0"
|
||||
base_icon_state = "holopad"
|
||||
layer = LOW_OBJ_LAYER
|
||||
plane = FLOOR_PLANE
|
||||
flags_1 = HEAR_1
|
||||
@@ -70,7 +70,7 @@ GLOBAL_LIST_EMPTY(network_holopads)
|
||||
var/obj/effect/overlay/holo_pad_hologram/replay_holo
|
||||
/// Calls will be automatically answered after a couple rings, here for debugging
|
||||
var/static/force_answer_call = FALSE
|
||||
// var/static/list/holopads = list()
|
||||
var/static/list/holopads = list()
|
||||
var/obj/effect/overlay/holoray/ray
|
||||
var/ringing = FALSE
|
||||
var/offset = FALSE
|
||||
@@ -107,26 +107,47 @@ GLOBAL_LIST_EMPTY(network_holopads)
|
||||
new_disk.forceMove(src)
|
||||
disk = new_disk
|
||||
|
||||
/obj/machinery/holopad/tutorial/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
|
||||
/obj/machinery/holopad/Moved(atom/OldLoc, Dir)
|
||||
. = ..()
|
||||
if(!loc)
|
||||
return
|
||||
// move any relevant holograms, basically non-AI, and rays with the pad
|
||||
if(replay_holo)
|
||||
replay_holo.abstract_move(loc)
|
||||
for(var/i in holorays)
|
||||
var/obj/effect/overlay/holoray/ray = holorays[i]
|
||||
ray.abstract_move(loc)
|
||||
var/list/non_call_masters = masters?.Copy()
|
||||
for(var/datum/holocall/holocall as anything in holo_calls)
|
||||
if(!holocall.user || !LAZYACCESS(masters, holocall.user))
|
||||
continue
|
||||
non_call_masters -= holocall.user
|
||||
// moving the eye moves the holo which updates the ray too
|
||||
holocall.eye.setLoc(locate(clamp(x + (holocall.hologram.x - OldLoc.x), 1, world.maxx), clamp(y + (holocall.hologram.y - OldLoc.y), 1, world.maxy), z))
|
||||
for(var/mob/living/holo_master as anything in non_call_masters)
|
||||
var/obj/effect/holo = masters[holo_master]
|
||||
update_holoray(holo_master, holo.loc)
|
||||
|
||||
/obj/machinery/holopad/tutorial/attack_hand(mob/user, list/modifiers)
|
||||
if(!istype(user))
|
||||
return
|
||||
if(user.incapacitated() || !is_operational())
|
||||
return
|
||||
if(replay_mode)
|
||||
replay_stop()
|
||||
else if(disk && disk.record)
|
||||
else if(disk?.record)
|
||||
replay_start()
|
||||
|
||||
/obj/machinery/holopad/tutorial/HasProximity(atom/movable/AM)
|
||||
if (!isliving(AM))
|
||||
return
|
||||
if(!replay_mode && (disk && disk.record))
|
||||
if(!replay_mode && (disk?.record))
|
||||
replay_start()
|
||||
|
||||
/obj/machinery/holopad/Initialize()
|
||||
. = ..()
|
||||
if(on_network)
|
||||
GLOB.network_holopads += src
|
||||
holopads += src
|
||||
|
||||
/obj/machinery/holopad/Destroy()
|
||||
if(outgoing_call)
|
||||
@@ -146,7 +167,7 @@ GLOBAL_LIST_EMPTY(network_holopads)
|
||||
|
||||
QDEL_NULL(disk)
|
||||
|
||||
GLOB.network_holopads -= src
|
||||
holopads -= src
|
||||
return ..()
|
||||
|
||||
/obj/machinery/holopad/power_change()
|
||||
@@ -172,8 +193,10 @@ GLOBAL_LIST_EMPTY(network_holopads)
|
||||
|
||||
/obj/machinery/holopad/examine(mob/user)
|
||||
. = ..()
|
||||
if(in_range(user, src) || isobserver(user))
|
||||
. += "<span class='notice'>The status display reads: Current projection range: <b>[holo_range]</b> units.</span>"
|
||||
if(isAI(user))
|
||||
. += span_notice("The status display reads: Current projection range: <b>[holo_range]</b> units. Use :h to speak through the projection. Right-click to project or cancel a projection. Alt-click to hangup all active and incomming calls. Ctrl-click to end projection without jumping to your last location.")
|
||||
else if(in_range(user, src) || isobserver(user))
|
||||
. += span_notice("The status display reads: Current projection range: <b>[holo_range]</b> units.")
|
||||
|
||||
/obj/machinery/holopad/attackby(obj/item/P, mob/user, params)
|
||||
if(default_deconstruction_screwdriver(user, "holopad_open", "holopad0", P))
|
||||
@@ -190,11 +213,11 @@ GLOBAL_LIST_EMPTY(network_holopads)
|
||||
|
||||
if(istype(P,/obj/item/disk/holodisk))
|
||||
if(disk)
|
||||
to_chat(user,"<span class='warning'>There's already a disk inside [src]!</span>")
|
||||
to_chat(user,span_warning("There's already a disk inside [src]!"))
|
||||
return
|
||||
if (!user.transferItemToLoc(P,src))
|
||||
return
|
||||
to_chat(user,"<span class='notice'>You insert [P] into [src].</span>")
|
||||
to_chat(user,span_notice("You insert [P] into [src]."))
|
||||
disk = P
|
||||
return
|
||||
|
||||
@@ -242,24 +265,29 @@ GLOBAL_LIST_EMPTY(network_holopads)
|
||||
|
||||
switch(action)
|
||||
if("AIrequest")
|
||||
if(isAI(usr))
|
||||
var/mob/living/silicon/ai/ai_user = usr
|
||||
ai_user.eyeobj.setLoc(get_turf(src))
|
||||
to_chat(usr, span_info("AIs can not request AI presence. Jumping instead."))
|
||||
return
|
||||
if(last_request + 200 < world.time)
|
||||
last_request = world.time
|
||||
to_chat(usr, "<span class='info'>You requested an AI's presence.</span>")
|
||||
to_chat(usr, span_info("You requested an AI's presence."))
|
||||
var/area/area = get_area(src)
|
||||
for(var/mob/living/silicon/ai/AI in GLOB.silicon_mobs)
|
||||
if(!AI.client)
|
||||
continue
|
||||
to_chat(AI, "<span class='info'>Your presence is requested at <a href='?src=[REF(AI)];jumptoholopad=[REF(src)]'>\the [area]</a>.</span>")
|
||||
to_chat(AI, span_info("Your presence is requested at <a href='?src=[REF(AI)];jumptoholopad=[REF(src)]'>\the [area]</a>.</span>")) // <a href='?src=[REF(AI)];project_to_holopad=[REF(src)]'>Project Hologram?</a>"))
|
||||
return TRUE
|
||||
else
|
||||
to_chat(usr, "<span class='info'>A request for AI presence was already sent recently.</span>")
|
||||
to_chat(usr, span_info("A request for AI presence was already sent recently."))
|
||||
return
|
||||
if("holocall")
|
||||
if(outgoing_call)
|
||||
return
|
||||
if(usr.loc == loc)
|
||||
var/list/callnames = list()
|
||||
for(var/I in GLOB.network_holopads)
|
||||
for(var/I in holopads)
|
||||
var/area/A = get_area(I)
|
||||
if(A)
|
||||
LAZYADD(callnames[A], I)
|
||||
@@ -274,7 +302,7 @@ GLOBAL_LIST_EMPTY(network_holopads)
|
||||
calling = TRUE
|
||||
return TRUE
|
||||
else
|
||||
to_chat(usr, "<span class='warning'>You must stand on the holopad to make a call!</span>")
|
||||
to_chat(usr, span_warning("You must stand on the holopad to make a call!"))
|
||||
if("connectcall")
|
||||
var/datum/holocall/call_to_connect = locate(params["holopad"]) in holo_calls
|
||||
if(!QDELETED(call_to_connect))
|
||||
@@ -285,6 +313,12 @@ GLOBAL_LIST_EMPTY(network_holopads)
|
||||
if(!QDELETED(call_to_disconnect))
|
||||
call_to_disconnect.Disconnect(src)
|
||||
return TRUE
|
||||
if("rejectall")
|
||||
for(var/datum/holocall/call_to_reject as anything in holo_calls)
|
||||
if(call_to_reject.connected_holopad == src) // do not kill the current connection
|
||||
continue
|
||||
call_to_reject.Disconnect(src)
|
||||
return TRUE
|
||||
if("disk_eject")
|
||||
if(disk && !replay_mode)
|
||||
disk.forceMove(drop_location())
|
||||
@@ -327,14 +361,13 @@ GLOBAL_LIST_EMPTY(network_holopads)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* hangup_all_calls: Disconnects all current holocalls from the holopad
|
||||
*/
|
||||
* hangup_all_calls: Disconnects all current holocalls from the holopad
|
||||
*/
|
||||
/obj/machinery/holopad/proc/hangup_all_calls()
|
||||
for(var/I in holo_calls)
|
||||
var/datum/holocall/HC = I
|
||||
HC.Disconnect(src)
|
||||
|
||||
//do not allow AIs to answer calls or people will use it to meta the AI sattelite
|
||||
/obj/machinery/holopad/attack_ai(mob/living/silicon/ai/user)
|
||||
if (!istype(user))
|
||||
return
|
||||
@@ -343,12 +376,25 @@ GLOBAL_LIST_EMPTY(network_holopads)
|
||||
/*There are pretty much only three ways to interact here.
|
||||
I don't need to check for client since they're clicking on an object.
|
||||
This may change in the future but for now will suffice.*/
|
||||
if(user.eyeobj.loc != src.loc)//Set client eye on the object if it's not already.
|
||||
user.eyeobj.setLoc(get_turf(src))
|
||||
else if(!LAZYLEN(masters) || !masters[user])//If there is no hologram, possibly make one.
|
||||
if(!LAZYLEN(masters) || !masters[user])//If there is no hologram, possibly make one.
|
||||
activate_holo(user)
|
||||
else//If there is a hologram, remove it.
|
||||
else//If there is a hologram, remove it, and jump to your last location.
|
||||
clear_holo(user)
|
||||
// if(user.lastloc)//only jump to your last location if your lastloc is set, which only sets if you projected from a request message.
|
||||
// user.eyeobj.setLoc(user.lastloc)
|
||||
// user.lastloc = null
|
||||
|
||||
/obj/machinery/holopad/AICtrlClick(mob/living/silicon/ai/user)
|
||||
if (!istype(user))
|
||||
return
|
||||
if (!on_network)
|
||||
return
|
||||
if(!LAZYLEN(masters) || !masters[user])//If there is no hologram, then this button does nothing.
|
||||
return
|
||||
else//If there is a hologram, remove it, but dont jump to your last location.
|
||||
// user.lastloc = null
|
||||
clear_holo(user)
|
||||
return
|
||||
|
||||
/obj/machinery/holopad/process()
|
||||
if(LAZYLEN(masters))
|
||||
@@ -378,25 +424,26 @@ GLOBAL_LIST_EMPTY(network_holopads)
|
||||
if(outgoing_call)
|
||||
HC.Disconnect(src)//can't answer calls while calling
|
||||
else
|
||||
playsound(src, 'sound/machines/twobeep.ogg', 100) //bring, bring!
|
||||
playsound(src, 'sound/machines/twobeep.ogg', 100) //bring, bring!
|
||||
ringing = TRUE
|
||||
|
||||
update_icon()
|
||||
update_appearance()
|
||||
|
||||
/obj/machinery/holopad/proc/activate_holo(mob/living/user)
|
||||
var/mob/living/silicon/ai/AI = user
|
||||
if(!istype(AI))
|
||||
AI = null
|
||||
|
||||
if(is_operational() && (!AI || AI.eyeobj.loc == loc))//If the projector has power and client eye is on it
|
||||
if (AI && istype(AI.current, /obj/machinery/holopad))
|
||||
to_chat(user, "<span class='danger'>ERROR:</span> \black Image feed in progress.")
|
||||
if(is_operational())//If the projector has power
|
||||
if(AI && istype(AI.current, /obj/machinery/holopad))
|
||||
to_chat(user, "[span_danger("ERROR:")] \black Image feed in progress.")
|
||||
return
|
||||
|
||||
var/obj/effect/overlay/holo_pad_hologram/Hologram = new(loc)//Spawn a blank effect at the location.
|
||||
if(AI)
|
||||
Hologram.icon = AI.holo_icon
|
||||
else //make it like real life
|
||||
AI.eyeobj.setLoc(get_turf(src)) //ensure the AI camera moves to the holopad
|
||||
else //make it like real life
|
||||
Hologram.icon = user.icon
|
||||
Hologram.icon_state = user.icon_state
|
||||
Hologram.copy_overlays(user, TRUE)
|
||||
@@ -407,17 +454,17 @@ GLOBAL_LIST_EMPTY(network_holopads)
|
||||
|
||||
Hologram.mouse_opacity = MOUSE_OPACITY_TRANSPARENT//So you can't click on it.
|
||||
Hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them.
|
||||
Hologram.setAnchored(TRUE)//So space wind cannot drag it.
|
||||
Hologram.set_anchored(TRUE)//So space wind cannot drag it.
|
||||
Hologram.name = "[user.name] (Hologram)"//If someone decides to right click.
|
||||
Hologram.set_light(2) //hologram lighting
|
||||
Hologram.set_light(2) //hologram lighting
|
||||
move_hologram()
|
||||
|
||||
set_holo(user, Hologram)
|
||||
visible_message("<span class='notice'>A holographic image of [user] flickers to life before your eyes!</span>")
|
||||
visible_message(span_notice("A holographic image of [user] flickers to life before your eyes!"))
|
||||
|
||||
return Hologram
|
||||
else
|
||||
to_chat(user, "<span class='danger'>ERROR:</span> Unable to project hologram.")
|
||||
to_chat(user, "[span_danger("ERROR:")] Unable to project hologram.")
|
||||
|
||||
/*This is the proc for special two-way communication between AI and holopad/people talking near holopad.
|
||||
For the other part of the code, check silicon say.dm. Particularly robot talk.*/
|
||||
@@ -430,10 +477,13 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
|
||||
|
||||
for(var/I in holo_calls)
|
||||
var/datum/holocall/HC = I
|
||||
if(HC.connected_holopad == src && speaker != HC.hologram)
|
||||
HC.user.Hear(message, speaker, message_language, raw_message, radio_freq, spans, message_mods)
|
||||
if(HC.connected_holopad == src)
|
||||
if(speaker == HC.hologram && HC.user.client?.prefs.chat_on_map)
|
||||
HC.user.create_chat_message(speaker, message_language, raw_message, spans)
|
||||
else
|
||||
HC.user.Hear(message, speaker, message_language, raw_message, radio_freq, spans, message_mods)
|
||||
|
||||
if(outgoing_call && speaker == outgoing_call.user)
|
||||
if(outgoing_call?.hologram && speaker == outgoing_call.user)
|
||||
outgoing_call.hologram.say(raw_message)
|
||||
|
||||
if(record_mode && speaker == record_user)
|
||||
@@ -447,16 +497,15 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
|
||||
set_light(2)
|
||||
else
|
||||
set_light(0)
|
||||
update_icon()
|
||||
update_appearance()
|
||||
|
||||
/obj/machinery/holopad/update_icon_state()
|
||||
var/total_users = LAZYLEN(masters) + LAZYLEN(holo_calls)
|
||||
if(ringing)
|
||||
icon_state = "holopad_ringing"
|
||||
else if(total_users || replay_mode)
|
||||
icon_state = "holopad1"
|
||||
else
|
||||
icon_state = "holopad0"
|
||||
icon_state = "[base_icon_state]_ringing"
|
||||
return ..()
|
||||
icon_state = "[base_icon_state][(total_users || replay_mode) ? 1 : 0]"
|
||||
return ..()
|
||||
|
||||
/obj/machinery/holopad/proc/set_holo(mob/living/user, obj/effect/overlay/holo_pad_hologram/h)
|
||||
LAZYSET(masters, user, h)
|
||||
@@ -488,7 +537,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
|
||||
var/obj/effect/overlay/holo_pad_hologram/h = masters[holo_owner]
|
||||
if(!h || h.HC) //Holocalls can't change source.
|
||||
return FALSE
|
||||
for(var/pad in GLOB.network_holopads)
|
||||
for(var/pad in holopads)
|
||||
var/obj/machinery/holopad/another = pad
|
||||
if(another == src)
|
||||
continue
|
||||
@@ -524,7 +573,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
|
||||
else
|
||||
transfered = TRUE
|
||||
//All is good.
|
||||
holo.forceMove(new_turf)
|
||||
holo.abstract_move(new_turf)
|
||||
if(!transfered)
|
||||
update_holoray(user,new_turf)
|
||||
return TRUE
|
||||
@@ -565,10 +614,10 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
|
||||
holder.selected_language = record.language
|
||||
Hologram.mouse_opacity = MOUSE_OPACITY_TRANSPARENT//So you can't click on it.
|
||||
Hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them.
|
||||
Hologram.setAnchored(TRUE)//So space wind cannot drag it.
|
||||
Hologram.set_anchored(TRUE)//So space wind cannot drag it.
|
||||
Hologram.name = "[record.caller_name] (Hologram)"//If someone decides to right click.
|
||||
Hologram.set_light(2) //hologram lighting
|
||||
visible_message("<span class='notice'>A holographic image of [record.caller_name] flickers to life before your eyes!</span>")
|
||||
Hologram.set_light(2) //hologram lighting
|
||||
visible_message(span_notice("A holographic image of [record.caller_name] flickers to life before your eyes!"))
|
||||
return Hologram
|
||||
|
||||
/obj/machinery/holopad/proc/replay_start()
|
||||
@@ -661,7 +710,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
|
||||
record_user = null
|
||||
|
||||
/obj/machinery/holopad/proc/record_clear()
|
||||
if(disk && disk.record)
|
||||
if(disk?.record)
|
||||
QDEL_NULL(disk.record)
|
||||
|
||||
/obj/effect/overlay/holo_pad_hologram
|
||||
@@ -673,6 +722,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
|
||||
Impersonation = null
|
||||
if(!QDELETED(HC))
|
||||
HC.Disconnect(HC.calling_holopad)
|
||||
HC = null
|
||||
return ..()
|
||||
|
||||
/obj/effect/overlay/holo_pad_hologram/Process_Spacemove(movement_dir = 0)
|
||||
|
||||
@@ -52,23 +52,30 @@
|
||||
|
||||
/obj/machinery/limbgrower/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
for(var/datum/reagent/reagent_id in reagents.reagent_list)
|
||||
var/list/reagent_data = list(
|
||||
reagent_name = reagent_id.name,
|
||||
reagent_amount = reagent_id.volume,
|
||||
reagent_type = reagent_id.type
|
||||
)
|
||||
data["reagents"] += list(reagent_data)
|
||||
|
||||
if(reagents.reagent_list.len)
|
||||
for(var/datum/reagent/reagent_id in reagents.reagent_list)
|
||||
var/list/reagent_data = list(
|
||||
reagent_name = reagent_id.name,
|
||||
reagent_amount = reagent_id.volume,
|
||||
reagent_type = reagent_id.type
|
||||
)
|
||||
data["reagents"] += list(reagent_data)
|
||||
else
|
||||
data["reagents"] = list()
|
||||
data["total_reagents"] = reagents.total_volume
|
||||
data["max_reagents"] = reagents.maximum_volume
|
||||
data["busy"] = busy
|
||||
var/list/disk_data = list()
|
||||
disk_data["disk"] = dna_disk //Do i, the machine, have a disk?
|
||||
disk_data["name"] = dna_disk?.fields["name"] //Name for the human saved if there is one
|
||||
if(dna_disk)
|
||||
if(dna_disk.fields["name"])
|
||||
disk_data["name"] = dna_disk.fields["name"]
|
||||
else if(dna_disk.genetic_makeup_buffer["name"])
|
||||
disk_data["name"] = dna_disk.genetic_makeup_buffer["name"]
|
||||
else
|
||||
disk_data["name"] = "No name"
|
||||
//Name for the human saved if there is one
|
||||
data["disk"] = disk_data
|
||||
|
||||
return data
|
||||
|
||||
/obj/machinery/limbgrower/ui_static_data(mob/user)
|
||||
@@ -119,6 +126,18 @@
|
||||
if (busy)
|
||||
to_chat(user, "<span class=\"alert\">\The [src] is busy. Please wait for completion of previous operation.</span>")
|
||||
return
|
||||
if(ispath(user_item.type, /obj/item/reagent_containers/blood) && user.a_intent != INTENT_HARM)
|
||||
var/obj/item/reagent_containers/blood/B = user_item
|
||||
if(!B.reagents.get_reagents())
|
||||
to_chat(user, "<span class=\"alert\">You can't fill [src] with an empty [B.name]!</span>")
|
||||
return
|
||||
if(reagents.total_volume == reagents.maximum_volume)
|
||||
to_chat(user, "<span class=\"alert\">\The [src] can't hold more blood!</span>")
|
||||
return
|
||||
user.visible_message("<span class='notice'>[user] drains the [user_item] into [src] using the blood bag port on [src].</span>",
|
||||
"You drain the [user_item] into [src] using the blood bag port.")
|
||||
B.reagents.trans_to(src, B.amount_per_transfer_from_this)
|
||||
return
|
||||
|
||||
if(default_deconstruction_screwdriver(user, "limbgrower_panelopen", "limbgrower_idleoff", user_item))
|
||||
ui_close(user)
|
||||
@@ -180,7 +199,7 @@
|
||||
for(var/reagent_id in consumed_reagents_list)
|
||||
consumed_reagents_list[reagent_id] *= production_coefficient
|
||||
if(!reagents.has_reagent(reagent_id, consumed_reagents_list[reagent_id]))
|
||||
audible_message("<span class='notice'>\The [src] buzzes.</span>")
|
||||
audible_message("<span class='warning'>\The [src] buzzes and states \"INSUFFICENT REAGENTS\"</span>")
|
||||
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
|
||||
return
|
||||
|
||||
@@ -206,17 +225,13 @@
|
||||
* modified_consumed_reagents_list - the list of reagents we will consume on build, modified by the production coefficient.
|
||||
*/
|
||||
/obj/machinery/limbgrower/proc/build_item(list/modified_consumed_reagents_list)
|
||||
for(var/reagent_id in modified_consumed_reagents_list)
|
||||
if(!reagents.has_reagent(reagent_id, modified_consumed_reagents_list[reagent_id]))
|
||||
audible_message("<span class='notice'>\The [src] buzzes.</span>")
|
||||
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
|
||||
break
|
||||
|
||||
reagents.remove_reagent(reagent_id, modified_consumed_reagents_list[reagent_id])
|
||||
|
||||
reagent_usage(modified_consumed_reagents_list)
|
||||
var/built_typepath = being_built.build_path
|
||||
// If we have a bodypart, we need to initialize the limb on its own. Otherwise we can build it here.
|
||||
if(ispath(built_typepath, /obj/item/bodypart))
|
||||
if(ispath(built_typepath, /mob/living/carbon/human/chestonly))
|
||||
if(!build_mob_chest(built_typepath, FALSE))
|
||||
reagent_usage(modified_consumed_reagents_list, FALSE)
|
||||
else if(ispath(built_typepath, /obj/item/bodypart))
|
||||
build_limb(built_typepath)
|
||||
else if(ispath(built_typepath, /obj/item/organ/genital)) //genitals are uhh... customizable
|
||||
build_genital(built_typepath)
|
||||
@@ -227,6 +242,25 @@
|
||||
flick("limbgrower_unfill", src)
|
||||
icon_state = "limbgrower_idleoff"
|
||||
|
||||
/obj/machinery/limbgrower/proc/reagent_usage(modified_consumed_reagents_list, remove = TRUE)
|
||||
// Apparently, having a boolean in a loop is worse than doing it twice
|
||||
if(remove)
|
||||
for(var/reagent_id in modified_consumed_reagents_list)
|
||||
if(reagent_sanity_check(reagent_id, modified_consumed_reagents_list[reagent_id]))
|
||||
reagents.remove_reagent(reagent_id, modified_consumed_reagents_list[reagent_id])
|
||||
else
|
||||
for(var/reagent_id in modified_consumed_reagents_list)
|
||||
if(reagent_sanity_check(reagent_id, modified_consumed_reagents_list[reagent_id]))
|
||||
reagents.add_reagent(reagent_id, modified_consumed_reagents_list[reagent_id])
|
||||
|
||||
|
||||
/obj/machinery/limbgrower/proc/reagent_sanity_check(reagent_id, amount)
|
||||
if(reagents.has_reagent(reagent_id, amount))
|
||||
return TRUE
|
||||
audible_message("<span class='warning'>\The [src] buzzes, with a screen showing: INSUFFICENT REAGENTS</span>")
|
||||
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
|
||||
return FALSE
|
||||
|
||||
/*
|
||||
* The process of putting together a limb.
|
||||
* This is called from after we remove the reagents, so this proc is just initializing the limb type.
|
||||
@@ -306,6 +340,53 @@
|
||||
else
|
||||
new built_typepath(loc)
|
||||
|
||||
/obj/machinery/limbgrower/proc/build_mob_chest(built_typepath)
|
||||
// Create a mob with a chest, but nothing else
|
||||
if(!ispath(built_typepath, /mob/living/carbon/human/chestonly))
|
||||
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
|
||||
visible_message(src, "Buzzes, an error screen appearing on its display.")
|
||||
return FALSE
|
||||
// Fields is from cloning, a much fuller scan, genetic_makeup_buffer is less so
|
||||
var/dna_genetics = dna_disk?.genetic_makeup_buffer
|
||||
var/dna_cloning = dna_disk?.fields
|
||||
var/datum/species/selected = GLOB.species_datums[selected_category]
|
||||
var/mob/living/carbon/human/chestonly/C = new(loc)
|
||||
C.real_name = length(dna_genetics) ? dna_genetics["name"] : "Synthetic Humanoid #[rand(10000, 99999)]"
|
||||
if(length(dna_cloning))
|
||||
C.hardset_dna(dna_cloning["UI"], dna_cloning["SE"], dna_cloning["name"], dna_cloning["blood_type"], dna_cloning["mrace"], dna_cloning["features"])
|
||||
else if(length(dna_genetics))
|
||||
C.hardset_dna(dna_genetics["UI"], null, dna_genetics["name"], dna_genetics["blood_type"])
|
||||
else
|
||||
C.real_name = "Synthetic Humanoid #[rand(10000, 99999)]"
|
||||
C.hair_style = "bald"
|
||||
C.skin_tone = "albino"
|
||||
C.set_species(selected)
|
||||
C.set_resting(TRUE, TRUE)
|
||||
// Don't want to cause it to deathgasp..
|
||||
C.stat = DEAD
|
||||
C.adjustOxyLoss(200)
|
||||
// Limb replacement causes toxloss, which can cause too much suffering for the doctor that I don't want
|
||||
C.adjustCloneLoss(45)
|
||||
C.med_hud_set_status()
|
||||
C.med_hud_set_health()
|
||||
C.underwear = "Nude"
|
||||
C.undershirt = "Nude"
|
||||
C.saved_underwear = ""
|
||||
C.saved_undershirt = ""
|
||||
|
||||
// Just enough to start reviving them, I hope
|
||||
C.blood_volume = BLOOD_VOLUME_SURVIVE
|
||||
// At some point, make a way to deal with species regenerate_organs
|
||||
// Remove all the organs
|
||||
for(var/organ in C.internal_organs)
|
||||
var/obj/item/organ/O = organ
|
||||
O.Remove(organ)
|
||||
C.update_body(TRUE)
|
||||
C.update_hair()
|
||||
C.update_body_parts()
|
||||
C.update_appearance()
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/limbgrower/RefreshParts()
|
||||
reagents.maximum_volume = 0
|
||||
for(var/obj/item/reagent_containers/glass/our_beaker in component_parts)
|
||||
|
||||
@@ -45,7 +45,8 @@ Buildable meters
|
||||
if(make_from)
|
||||
make_from_existing(make_from)
|
||||
else
|
||||
pipe_type = _pipe_type
|
||||
if(!initial(src.pipe_type))
|
||||
pipe_type = _pipe_type
|
||||
setDir(_dir)
|
||||
|
||||
update()
|
||||
|
||||
@@ -374,7 +374,7 @@ GLOBAL_LIST_EMPTY(allConsoles)
|
||||
radio_freq = FREQ_ENGINEERING
|
||||
if("security")
|
||||
radio_freq = FREQ_SECURITY
|
||||
if("cargobay" || "mining")
|
||||
if("cargobay", "mining")
|
||||
radio_freq = FREQ_SUPPLY
|
||||
Radio.set_frequency(radio_freq)
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/obj/mecha/combat/five_stars
|
||||
desc = "A state of the art tank deployed by the Spinward Stellar Coalition National Guard."
|
||||
name = "\improper Tank"
|
||||
icon = 'icons/mecha/mecha_96x96.dmi'
|
||||
icon_state = "five_stars"
|
||||
armor = list("melee" = 100, "bullet" = 50, "laser" = 35, "energy" = 35, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
|
||||
step_in = 4
|
||||
dir_in = 1 //Facing North.
|
||||
max_integrity = 800
|
||||
pixel_x = -32
|
||||
pixel_y = -32
|
||||
|
||||
/obj/mecha/combat/five_stars/Initialize()
|
||||
. = ..()
|
||||
var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/spacecops(src)
|
||||
ME.attach(src)
|
||||
ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg(src)
|
||||
ME.attach(src)
|
||||
ME = new /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay(src)
|
||||
ME.attach(src)
|
||||
max_ammo()
|
||||
@@ -130,7 +130,11 @@
|
||||
if(isalien(target))
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter/xenosplatter(target.drop_location(), splatter_dir)
|
||||
else
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(target.drop_location(), splatter_dir)
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/H = target
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(target.drop_location(), splatter_dir, H.dna.species.exotic_blood_color)
|
||||
else
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(target.drop_location(), splatter_dir)
|
||||
|
||||
//organs go everywhere
|
||||
if(target_part && prob(10 * drill_level))
|
||||
|
||||
@@ -342,6 +342,9 @@
|
||||
harmful = TRUE
|
||||
ammo_type = "missiles_he"
|
||||
|
||||
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/spacecops
|
||||
projectiles = 420
|
||||
|
||||
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/breaching
|
||||
name = "\improper BRM-6 missile rack"
|
||||
desc = "A weapon for combat exosuits. Launches low-explosive breaching missiles designed to explode only when striking a sturdy target."
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
var/gibs_reagent_id = /datum/reagent/liquidgibs
|
||||
var/gibs_bloodtype = "A+"
|
||||
|
||||
/obj/effect/decal/cleanable/blood/gibs/Initialize(mapload, list/datum/disease/diseases)
|
||||
/obj/effect/decal/cleanable/blood/gibs/Initialize(mapload, list/datum/disease/diseases, list/blood_data)
|
||||
. = ..()
|
||||
if(random_icon_states && (icon_state == initial(icon_state)) && length(random_icon_states) > 0)
|
||||
icon_state = pick(random_icon_states)
|
||||
if(gibs_reagent_id)
|
||||
reagents.add_reagent(gibs_reagent_id, 5)
|
||||
reagents.add_reagent(gibs_reagent_id, 5, blood_data)
|
||||
if(gibs_bloodtype)
|
||||
add_blood_DNA(list("Non-human DNA" = gibs_bloodtype), diseases)
|
||||
update_icon()
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
GLOBAL_LIST(gang_tags)
|
||||
|
||||
/obj/effect/decal/cleanable/crayon
|
||||
name = "rune"
|
||||
desc = "Graffiti. Damn kids."
|
||||
@@ -45,6 +47,8 @@
|
||||
data["pixel_x"] = pixel_x
|
||||
if(pixel_y != initial(pixel_y))
|
||||
data["pixel_y"] = pixel_y
|
||||
/obj/effect/decal/cleanable/crayon/NeverShouldHaveComeHere(turf/T)
|
||||
return isgroundlessturf(T)
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/PersistenceLoad(list/data)
|
||||
. = ..()
|
||||
@@ -63,3 +67,18 @@
|
||||
pixel_x = data["pixel_x"]
|
||||
if(data["pixel_y"])
|
||||
pixel_y = data["pixel_y"]
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/gang
|
||||
name = "Leet Like Jeff K gang tag"
|
||||
desc = "Looks like someone's claimed this area for Leet Like Jeff K."
|
||||
icon = 'icons/obj/gang/tags.dmi'
|
||||
layer = BELOW_MOB_LAYER
|
||||
var/datum/team/gang/my_gang
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/gang/Initialize(mapload, main, type, e_name, graf_rot, alt_icon = null)
|
||||
. = ..()
|
||||
LAZYADD(GLOB.gang_tags, src)
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/gang/Destroy()
|
||||
LAZYREMOVE(GLOB.gang_tags, src)
|
||||
..()
|
||||
|
||||
@@ -31,7 +31,10 @@
|
||||
|
||||
var/list/dna_to_add //find the dna to pass to the spawned gibs. do note this can be null if the mob doesn't have blood. add_blood_DNA() has built in null handling.
|
||||
var/body_coloring = ""
|
||||
|
||||
var/list/blood_data_to_add
|
||||
if(source_mob)
|
||||
blood_data_to_add = source_mob.get_blood_data()
|
||||
if(!issilicon(source_mob))
|
||||
dna_to_add = blood_dna || source_mob.get_blood_dna_list() //ez pz
|
||||
if(ishuman(source_mob))
|
||||
@@ -65,7 +68,7 @@
|
||||
if(gibamounts[i])
|
||||
for(var/j = 1, j<= gibamounts[i], j++)
|
||||
var/gibType = gibtypes[i]
|
||||
gib = new gibType(loc, diseases)
|
||||
gib = new gibType(loc, diseases, blood_data_to_add)
|
||||
if(iscarbon(loc))
|
||||
var/mob/living/carbon/digester = loc
|
||||
digester.stomach_contents += gib
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
duration = 5
|
||||
randomdir = FALSE
|
||||
layer = BELOW_MOB_LAYER
|
||||
color = BLOOD_COLOR_HUMAN // set it to red by default because the actual icons are white
|
||||
var/splatter_type = "splatter"
|
||||
|
||||
/obj/effect/temp_visual/dir_setting/bloodsplatter/Initialize(mapload, set_dir, new_color)
|
||||
|
||||
+125
-47
@@ -46,11 +46,23 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
||||
max_integrity = 200
|
||||
|
||||
obj_flags = NONE
|
||||
///Item flags for the item
|
||||
var/item_flags = NONE
|
||||
|
||||
var/hitsound = null
|
||||
var/usesound = null
|
||||
var/throwhitsound = null
|
||||
///Sound played when you hit something with the item
|
||||
var/hitsound
|
||||
///Played when the item is used, for example tools
|
||||
var/usesound
|
||||
///Used when yate into a mob
|
||||
var/mob_throw_hit_sound
|
||||
///Sound used when equipping the item into a valid slot
|
||||
var/equip_sound
|
||||
///Sound uses when picking the item up (into your hands)
|
||||
var/pickup_sound
|
||||
///Sound uses when dropping the item, or when its thrown.
|
||||
var/drop_sound
|
||||
///Whether or not we use stealthy audio levels for this item's attack sounds
|
||||
var/stealthy_audio = FALSE
|
||||
|
||||
/// Weight class for how much storage capacity it uses and how big it physically is meaning storages can't hold it if their maximum weight class isn't as high as it.
|
||||
var/w_class = WEIGHT_CLASS_NORMAL
|
||||
@@ -129,7 +141,8 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
||||
var/datum/dog_fashion/dog_fashion = null
|
||||
|
||||
//Tooltip vars
|
||||
var/force_string //string form of an item's force. Edit this var only to set a custom force string
|
||||
///string form of an item's force. Edit this var only to set a custom force string
|
||||
var/force_string
|
||||
var/last_force_string_check = 0
|
||||
|
||||
var/trigger_guard = TRIGGER_GUARD_NONE
|
||||
@@ -331,15 +344,20 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
||||
return ..()
|
||||
|
||||
/obj/item/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if(!user)
|
||||
return
|
||||
if(anchored)
|
||||
return
|
||||
if(loc == user && current_equipped_slot && current_equipped_slot != SLOT_HANDS)
|
||||
if(loc == user && current_equipped_slot && current_equipped_slot != ITEM_SLOT_HANDS)
|
||||
if(current_equipped_slot in user.check_obscured_slots())
|
||||
to_chat(user, "<span class='warning'>You are unable to unequip that while wearing other garments over it!</span>")
|
||||
return FALSE
|
||||
|
||||
. = TRUE
|
||||
|
||||
if(resistance_flags & ON_FIRE)
|
||||
var/mob/living/carbon/C = user
|
||||
var/can_handle_hot = FALSE
|
||||
@@ -374,6 +392,8 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
||||
|
||||
//If the item is in a storage item, take it out
|
||||
SEND_SIGNAL(loc, COMSIG_TRY_STORAGE_TAKE, src, user.loc, TRUE)
|
||||
if(QDELETED(src)) //moving it out of the storage to the floor destroyed it.
|
||||
return
|
||||
|
||||
if(throwing)
|
||||
throwing.finalize(FALSE)
|
||||
@@ -381,11 +401,12 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
||||
if(!allow_attack_hand_drop(user) || !user.temporarilyRemoveItemFromInventory(src))
|
||||
return
|
||||
|
||||
remove_outline()
|
||||
. = FALSE
|
||||
pickup(user)
|
||||
add_fingerprint(user)
|
||||
if(!user.put_in_active_hand(src, FALSE, FALSE))
|
||||
user.dropItemToGround(src)
|
||||
return TRUE
|
||||
|
||||
/obj/item/proc/allow_attack_hand_drop(mob/user)
|
||||
return TRUE
|
||||
@@ -395,7 +416,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
||||
return
|
||||
if(anchored)
|
||||
return
|
||||
if(loc == user && current_equipped_slot && current_equipped_slot != SLOT_HANDS)
|
||||
if(loc == user && current_equipped_slot && current_equipped_slot != ITEM_SLOT_HANDS)
|
||||
if(current_equipped_slot in user.check_obscured_slots())
|
||||
to_chat(user, "<span class='warning'>You are unable to unequip that while wearing other garments over it!</span>")
|
||||
return FALSE
|
||||
@@ -453,9 +474,8 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
||||
item_flags &= ~(IN_INVENTORY)
|
||||
item_flags &= ~(IN_STORAGE)
|
||||
SEND_SIGNAL(src, COMSIG_ITEM_DROPPED,user)
|
||||
remove_outline()
|
||||
// if(!silent)
|
||||
// playsound(src, drop_sound, DROP_SOUND_VOLUME, ignore_walls = FALSE)
|
||||
if(!silent)
|
||||
playsound(src, drop_sound, DROP_SOUND_VOLUME, ignore_walls = FALSE)
|
||||
user?.update_equipment_speed_mods()
|
||||
|
||||
// called just as an item is picked up (loc is not yet changed)
|
||||
@@ -548,7 +568,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
||||
|
||||
//sometimes we only want to grant the item's action if it's equipped in a specific slot.
|
||||
/obj/item/proc/item_action_slot_check(slot, mob/user, datum/action/A)
|
||||
if(slot == SLOT_IN_BACKPACK || slot == SLOT_LEGCUFFED) //these aren't true slots, so avoid granting actions there
|
||||
if(slot == ITEM_SLOT_BACKPACK || slot == ITEM_SLOT_LEGCUFFED) //these aren't true slots, so avoid granting actions there
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
@@ -702,8 +722,8 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
||||
if(isliving(hit_atom)) //Living mobs handle hit sounds differently.
|
||||
var/volume = get_volume_by_throwforce_and_or_w_class()
|
||||
if (throwforce > 0)
|
||||
if (throwhitsound)
|
||||
playsound(hit_atom, throwhitsound, volume, TRUE, -1)
|
||||
if (mob_throw_hit_sound)
|
||||
playsound(hit_atom, mob_throw_hit_sound, volume, TRUE, -1)
|
||||
else if(hitsound)
|
||||
playsound(hit_atom, hitsound, volume, TRUE, -1)
|
||||
else
|
||||
@@ -711,8 +731,8 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
||||
else
|
||||
playsound(hit_atom, 'sound/weapons/throwtap.ogg', 1, volume, -1)
|
||||
|
||||
// else
|
||||
// playsound(src, drop_sound, YEET_SOUND_VOLUME, ignore_walls = FALSE)
|
||||
else if (drop_sound)
|
||||
playsound(src, drop_sound, YEET_SOUND_VOLUME, ignore_walls = FALSE)
|
||||
return hit_atom.hitby(src, 0, itempush, throwingdatum=throwingdatum)
|
||||
|
||||
/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, messy_throw = TRUE)
|
||||
@@ -797,7 +817,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
||||
if(ismob(location))
|
||||
var/mob/M = location
|
||||
var/success = FALSE
|
||||
if(src == M.get_item_by_slot(SLOT_WEAR_MASK))
|
||||
if(src == M.get_item_by_slot(ITEM_SLOT_MASK))
|
||||
success = TRUE
|
||||
if(success)
|
||||
location = get_turf(M)
|
||||
@@ -886,47 +906,55 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
||||
openToolTip(user,src,params,title = name,content = "[desc]<br><b>Force:</b> [force_string]",theme = "")
|
||||
|
||||
/obj/item/MouseEntered(location, control, params)
|
||||
. = ..()
|
||||
SEND_SIGNAL(src, COMSIG_ITEM_MOUSE_ENTER, location, control, params)
|
||||
if((item_flags & IN_INVENTORY || item_flags & IN_STORAGE) && usr?.client.prefs.enable_tips && !QDELETED(src))
|
||||
var/timedelay = max(usr.client.prefs.tip_delay * 0.01, 0.01) // I heard multiplying is faster, also runtimes from very low/negative numbers
|
||||
usr.client.tip_timer = addtimer(CALLBACK(src, .proc/openTip, location, control, params, usr), timedelay, TIMER_STOPPABLE)//timer takes delay in deciseconds, but the pref is in milliseconds. dividing by 100 converts it.
|
||||
var/mob/living/L = usr
|
||||
if(istype(L) && (L.incapacitated() || (current_equipped_slot in L.check_obscured_slots()) || !L.canUnEquip(src)))
|
||||
apply_outline(_size = 3)
|
||||
else
|
||||
apply_outline()
|
||||
if(get(src, /mob) == usr && !QDELETED(src))
|
||||
var/mob/living/L = usr
|
||||
if(usr.client.prefs.enable_tips)
|
||||
var/timedelay = usr.client.prefs.tip_delay/100
|
||||
usr.client.tip_timer = addtimer(CALLBACK(src, .proc/openTip, location, control, params, usr), timedelay, TIMER_STOPPABLE)//timer takes delay in deciseconds, but the pref is in milliseconds. dividing by 100 converts it.
|
||||
if(usr.client.prefs.outline_enabled)
|
||||
if(istype(L) && L.incapacitated())
|
||||
apply_outline(COLOR_RED_GRAY) //if they're dead or handcuffed, let's show the outline as red to indicate that they can't interact with that right now
|
||||
else
|
||||
apply_outline(usr.client.prefs.outline_color) //if the player's alive and well we send the command with no color set, so it uses the theme's color
|
||||
|
||||
/obj/item/MouseDrop(atom/over, src_location, over_location, src_control, over_control, params)
|
||||
. = ..()
|
||||
remove_outline()
|
||||
remove_filter("hover_outline") //get rid of the hover effect in case the mouse exit isn't called if someone drags and drops an item and somthing goes wrong
|
||||
|
||||
/obj/item/MouseExited(location,control,params)
|
||||
/obj/item/MouseExited(location, control, params)
|
||||
SEND_SIGNAL(src, COMSIG_ITEM_MOUSE_EXIT, location, control, params)
|
||||
deltimer(usr.client.tip_timer) //delete any in-progress timer if the mouse is moved off the item before it finishes
|
||||
closeToolTip(usr)
|
||||
remove_outline()
|
||||
remove_filter("hover_outline")
|
||||
|
||||
/obj/item/proc/apply_outline(colour = null, _size=1)
|
||||
if(!(item_flags & IN_INVENTORY || item_flags & IN_STORAGE) || QDELETED(src) || isobserver(usr))
|
||||
/obj/item/proc/apply_outline(outline_color = null)
|
||||
if(get(src, /mob) != usr || QDELETED(src) || isobserver(usr)) //cancel if the item isn't in an inventory, is being deleted, or if the person hovering is a ghost (so that people spectating you don't randomly make your items glow)
|
||||
return
|
||||
if(usr.client)
|
||||
if(!usr.client.prefs.outline_enabled)
|
||||
return
|
||||
if(!colour)
|
||||
if(usr.client)
|
||||
colour = usr.client.prefs.outline_color
|
||||
if(!colour)
|
||||
colour = COLOR_BLUE_GRAY
|
||||
else
|
||||
colour = COLOR_BLUE_GRAY
|
||||
if(outline_filter)
|
||||
filters -= outline_filter
|
||||
outline_filter = filter(type="outline", size=_size, color=colour)
|
||||
filters += outline_filter
|
||||
var/theme = lowertext(usr.client.prefs.UI_style)
|
||||
if(!outline_color) //if we weren't provided with a color, take the theme's color
|
||||
switch(theme) //yeah it kinda has to be this way
|
||||
if("midnight")
|
||||
outline_color = COLOR_THEME_MIDNIGHT
|
||||
if("plasmafire")
|
||||
outline_color = COLOR_THEME_PLASMAFIRE
|
||||
if("retro")
|
||||
outline_color = COLOR_THEME_RETRO //just as garish as the rest of this theme
|
||||
if("slimecore")
|
||||
outline_color = COLOR_THEME_SLIMECORE
|
||||
if("operative")
|
||||
outline_color = COLOR_THEME_OPERATIVE
|
||||
if("clockwork")
|
||||
outline_color = COLOR_THEME_CLOCKWORK //if you want free gbp go fix the fact that clockwork's tooltip css is glass'
|
||||
if("glass")
|
||||
outline_color = COLOR_THEME_GLASS
|
||||
else //this should never happen, hopefully
|
||||
outline_color = COLOR_WHITE
|
||||
if(color)
|
||||
outline_color = COLOR_WHITE //if the item is recolored then the outline will be too, let's make the outline white so it becomes the same color instead of some ugly mix of the theme and the tint
|
||||
|
||||
/obj/item/proc/remove_outline()
|
||||
if(outline_filter)
|
||||
filters -= outline_filter
|
||||
outline_filter = null
|
||||
add_filter("hover_outline", 1, list("type" = "outline", "size" = 1, "color" = outline_color))
|
||||
|
||||
// Called when a mob tries to use the item as a tool.
|
||||
// Handles most checks.
|
||||
@@ -1063,6 +1091,19 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
||||
. = ..()
|
||||
if(var_name == NAMEOF(src, slowdown))
|
||||
set_slowdown(var_value) //don't care if it's a duplicate edit as slowdown'll be set, do it anyways to force normal behavior.
|
||||
|
||||
/obj/item/proc/canStrip(mob/stripper, mob/owner)
|
||||
SHOULD_BE_PURE(TRUE)
|
||||
return !HAS_TRAIT(src, TRAIT_NODROP) && !(item_flags & ABSTRACT)
|
||||
|
||||
/obj/item/proc/doStrip(mob/stripper, mob/owner)
|
||||
if(owner.dropItemToGround(src))
|
||||
if(stripper.can_hold_items())
|
||||
stripper.put_in_hands(src)
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/**
|
||||
* Does the current embedding var meet the criteria for being harmless? Namely, does it explicitly define the pain multiplier and jostle pain mult to be 0? If so, return true.
|
||||
*
|
||||
@@ -1195,3 +1236,40 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
||||
pain_stam_pct = (!isnull(embedding["pain_stam_pct"]) ? embedding["pain_stam_pct"] : EMBEDDED_PAIN_STAM_PCT),\
|
||||
embed_chance_turf_mod = (!isnull(embedding["embed_chance_turf_mod"]) ? embedding["embed_chance_turf_mod"] : EMBED_CHANCE_TURF_MOD))
|
||||
return TRUE
|
||||
|
||||
|
||||
/**
|
||||
* * An interrupt for offering an item to other people, called mainly from [/mob/living/carbon/proc/give], in case you want to run your own offer behavior instead.
|
||||
*
|
||||
* * Return TRUE if you want to interrupt the offer.
|
||||
*
|
||||
* * Arguments:
|
||||
* * offerer - the person offering the item
|
||||
*/
|
||||
/obj/item/proc/on_offered(mob/living/carbon/offerer)
|
||||
if(SEND_SIGNAL(src, COMSIG_ITEM_OFFERING, offerer) & COMPONENT_OFFER_INTERRUPT)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* * An interrupt for someone trying to accept an offered item, called mainly from [/mob/living/carbon/proc/take], in case you want to run your own take behavior instead.
|
||||
*
|
||||
* * Return TRUE if you want to interrupt the taking.
|
||||
*
|
||||
* * Arguments:
|
||||
* * offerer - the person offering the item
|
||||
* * taker - the person trying to accept the offer
|
||||
*/
|
||||
/obj/item/proc/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker)
|
||||
if(SEND_SIGNAL(src, COMSIG_ITEM_OFFER_TAKEN, offerer, taker) & COMPONENT_OFFER_INTERRUPT)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Updates all action buttons associated with this item
|
||||
*
|
||||
* Arguments:
|
||||
* * status_only - Update only current availability status of the buttons to show if they are ready or not to use
|
||||
* * force - Force buttons update even if the given button icon state has not changed
|
||||
*/
|
||||
/obj/item/proc/update_action_buttons(status_only = FALSE, force = FALSE)
|
||||
for(var/datum/action/current_action as anything in actions)
|
||||
current_action.UpdateButtonIcon(status_only, force)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
var/response_timer_id = null
|
||||
var/approval_time = 600
|
||||
var/allow_unicode = FALSE
|
||||
var/admin_approved = FALSE
|
||||
|
||||
var/static/regex/standard_station_regex
|
||||
|
||||
@@ -62,8 +63,32 @@
|
||||
|
||||
to_chat(user, "Your name has been sent to your employers for approval.")
|
||||
// Autoapproves after a certain time
|
||||
response_timer_id = addtimer(CALLBACK(src, .proc/rename_station, new_name, user.name, user.real_name, key_name(user)), approval_time, TIMER_STOPPABLE)
|
||||
to_chat(GLOB.admins, "<span class='adminnotice'><b><font color=orange>CUSTOM STATION RENAME:</font></b>[ADMIN_LOOKUPFLW(user)] proposes to rename the [name_type] to [html_encode(new_name)] (will autoapprove in [DisplayTimeText(approval_time)]). [ADMIN_SMITE(user)] (<A HREF='?_src_=holder;[HrefToken(TRUE)];reject_custom_name=[REF(src)]'>REJECT</A>) [ADMIN_CENTCOM_REPLY(user)]</span>")
|
||||
var/requires_approval = CONFIG_GET(flag/station_name_needs_approval)
|
||||
response_timer_id = addtimer(CALLBACK(src, .proc/check_state, new_name, user.name, user.real_name, key_name(user)), approval_time, TIMER_STOPPABLE)
|
||||
to_chat(GLOB.admins, "<span class='adminnotice'><b><font color=orange>CUSTOM STATION RENAME:</font></b>[ADMIN_LOOKUPFLW(user)] proposes to rename the [name_type] to [html_encode(new_name)] ([requires_approval ? "REQUIRES ADMIN APPROVAL and will autodeny" : "will autoapprove"] in [DisplayTimeText(approval_time)]). [ADMIN_SMITE(user)] (<A HREF='?_src_=holder;[HrefToken(TRUE)];reject_custom_name=[REF(src)]'>REJECT</A>)[requires_approval ? " (<A HREF='?_src_=holder;[HrefToken(TRUE)];approve_custom_name=[REF(src)]'>APPROVE</A>)" : ""] [ADMIN_CENTCOM_REPLY(user)]</span>")
|
||||
|
||||
/obj/item/station_charter/proc/check_state(designation, uname, ureal_name, ukey)
|
||||
var/requires_approval = CONFIG_GET(flag/station_name_needs_approval)
|
||||
if(requires_approval && !admin_approved)
|
||||
var/turf/T = get_turf(src)
|
||||
T.visible_message("<span class='warning'>A note appears on [src], stating this sector requires central command approval for its station names, which was not performed in time for this request. Looks like the change has been auto-rejected.</span>")
|
||||
var/m = "Station rename has been autorejected due to config requiring admin approval."
|
||||
message_admins(m)
|
||||
log_admin(m)
|
||||
else
|
||||
rename_station(designation, uname, ureal_name, ukey)
|
||||
response_timer_id = null
|
||||
admin_approved = FALSE
|
||||
|
||||
/obj/item/station_charter/proc/allow_pass(user)
|
||||
if(!user)
|
||||
return
|
||||
if(!response_timer_id)
|
||||
return
|
||||
admin_approved = TRUE
|
||||
var/m = "[key_name(user)] has approved the proposed station name. It can still be denied prior to the timer expiring."
|
||||
message_admins(m)
|
||||
log_admin(m)
|
||||
|
||||
/obj/item/station_charter/proc/reject_proposed(user)
|
||||
if(!user)
|
||||
@@ -80,6 +105,7 @@
|
||||
|
||||
deltimer(response_timer_id)
|
||||
response_timer_id = null
|
||||
admin_approved = FALSE
|
||||
|
||||
/obj/item/station_charter/proc/rename_station(designation, uname, ureal_name, ukey)
|
||||
set_station_name(designation)
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
user.put_in_hands(PA)
|
||||
|
||||
/obj/item/chrono_eraser/item_action_slot_check(slot, mob/user, datum/action/A)
|
||||
if(slot == SLOT_BACK)
|
||||
if(slot == ITEM_SLOT_BACK)
|
||||
return 1
|
||||
|
||||
/obj/item/gun/energy/chrono_gun
|
||||
|
||||
@@ -89,7 +89,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
|
||||
..()
|
||||
|
||||
/obj/item/proc/help_light_cig(mob/living/M)
|
||||
var/mask_item = M.get_item_by_slot(SLOT_WEAR_MASK)
|
||||
var/mask_item = M.get_item_by_slot(ITEM_SLOT_MASK)
|
||||
if(istype(mask_item, /obj/item/clothing/mask/cigarette))
|
||||
return mask_item
|
||||
|
||||
@@ -130,7 +130,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
|
||||
reagents.add_reagent_list(list_reagents)
|
||||
if(starts_lit)
|
||||
light()
|
||||
AddComponent(/datum/component/knockoff,90,list(BODY_ZONE_PRECISE_MOUTH),list(SLOT_WEAR_MASK))//90% to knock off when wearing a mask
|
||||
AddComponent(/datum/component/knockoff,90,list(BODY_ZONE_PRECISE_MOUTH),list(ITEM_SLOT_MASK))//90% to knock off when wearing a mask
|
||||
|
||||
/obj/item/clothing/mask/cigarette/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
@@ -819,7 +819,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
|
||||
|
||||
/obj/item/clothing/mask/vape/equipped(mob/user, slot)
|
||||
. = ..()
|
||||
if(slot == SLOT_WEAR_MASK)
|
||||
if(slot == ITEM_SLOT_MASK)
|
||||
if(!screw)
|
||||
to_chat(user, "<span class='notice'>You start puffing on the vape.</span>")
|
||||
reagents.reagents_holder_flags &= ~(NO_REACT)
|
||||
@@ -830,7 +830,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
|
||||
/obj/item/clothing/mask/vape/dropped(mob/user)
|
||||
. = ..()
|
||||
var/mob/living/carbon/C = user
|
||||
if(C.get_item_by_slot(SLOT_WEAR_MASK) == src)
|
||||
if(C.get_item_by_slot(ITEM_SLOT_MASK) == src)
|
||||
reagents.reagents_holder_flags |= NO_REACT
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
|
||||
|
||||
@@ -298,7 +298,7 @@
|
||||
|
||||
/obj/item/toy/crayon/proc/draw_on(atom/target, mob/user, proximity, params)
|
||||
var/static/list/punctuation = list("!","?",".",",","/","+","-","=","%","#","&")
|
||||
|
||||
var/istagger = HAS_TRAIT(user, TRAIT_TAGGER)
|
||||
var/cost = 1
|
||||
if(paint_mode == PAINT_LARGE_HORIZONTAL)
|
||||
cost = 5
|
||||
@@ -355,14 +355,14 @@
|
||||
else if(drawing in graffiti|oriented)
|
||||
temp = "graffiti"
|
||||
|
||||
// If a gang member is using a gang spraycan, it'll behave differently
|
||||
var/gang_mode = FALSE
|
||||
if(gang && user.mind && user.mind.has_antag_datum(/datum/antagonist/gang)) //Heres a check.
|
||||
gang_mode = TRUE // No more runtimes if a non-gang member sprays a gang can, it just works like normal cans.
|
||||
// discontinue if the area isn't valid for tagging because gang "honour"
|
||||
var/gang_mode
|
||||
if(user.mind)
|
||||
gang_mode = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
|
||||
if(gang_mode && (!can_claim_for_gang(user, target)))
|
||||
return
|
||||
|
||||
|
||||
var/graf_rot
|
||||
if(drawing in oriented)
|
||||
switch(user.dir)
|
||||
@@ -390,16 +390,12 @@
|
||||
audible_message("<span class='notice'>You hear spraying.</span>")
|
||||
playsound(user.loc, 'sound/effects/spray.ogg', 5, 1, 5)
|
||||
|
||||
var/takes_time = !instant //For order purposes, since I'm maximum bad.
|
||||
if(gang_mode)
|
||||
takes_time = TRUE
|
||||
|
||||
var/wait_time = 50
|
||||
if(paint_mode == PAINT_LARGE_HORIZONTAL)
|
||||
wait_time *= 3
|
||||
|
||||
if(takes_time) //This is what deteremines the time it takes to spray a tag in gang mode. 50 is Default.
|
||||
if(!do_after(user, gang_tag_delay, target = target)) //25 is a good number, but we have gang_tag_delay var now.
|
||||
if(gang_mode || !instant)
|
||||
if(!do_after(user, 50, target = target))
|
||||
return
|
||||
|
||||
if(length(text_buffer))
|
||||
@@ -410,16 +406,15 @@
|
||||
|
||||
|
||||
if(actually_paints)
|
||||
var/obj/effect/decal/cleanable/crayon/C = new(target, paint_color, drawing, temp, graf_rot)
|
||||
if(gang_mode)
|
||||
// Double check it wasn't tagged in the meanwhile.
|
||||
if(!can_claim_for_gang(user, target))
|
||||
return
|
||||
tag_for_gang(user, target)
|
||||
tag_for_gang(user, target, gang_mode)
|
||||
affected_turfs += target
|
||||
else
|
||||
switch(paint_mode)
|
||||
if(PAINT_NORMAL)
|
||||
var/obj/effect/decal/cleanable/crayon/C = new(target, paint_color, drawing, temp, graf_rot)
|
||||
C.add_hiddenprint(user)
|
||||
if(precision_mode)
|
||||
C.pixel_x = clamp(precision_x, -(world.icon_size/2), world.icon_size/2)
|
||||
@@ -432,14 +427,18 @@
|
||||
var/turf/left = locate(target.x-1,target.y,target.z)
|
||||
var/turf/right = locate(target.x+1,target.y,target.z)
|
||||
if(isValidSurface(left) && isValidSurface(right))
|
||||
var/obj/effect/decal/cleanable/crayon/C = new(left, paint_color, drawing, temp, graf_rot, PAINT_LARGE_HORIZONTAL_ICON)
|
||||
C.add_hiddenprint(user)
|
||||
C = new(left, paint_color, drawing, temp, graf_rot, PAINT_LARGE_HORIZONTAL_ICON)
|
||||
affected_turfs += left
|
||||
affected_turfs += right
|
||||
affected_turfs += target
|
||||
else
|
||||
to_chat(user, "<span class='warning'>There isn't enough space to paint!</span>")
|
||||
return
|
||||
C.add_hiddenprint(user)
|
||||
if(istagger)
|
||||
C.AddComponent(/datum/element/art, GOOD_ART)
|
||||
else
|
||||
C.AddComponent(/datum/element/art, BAD_ART)
|
||||
|
||||
if(!instant)
|
||||
to_chat(user, "<span class='notice'>You finish drawing \the [temp].</span>")
|
||||
@@ -462,52 +461,6 @@
|
||||
reagents.trans_to(t, ., volume_multiplier)
|
||||
check_empty(user)
|
||||
|
||||
|
||||
//////////////Gang mode stuff/////////////////
|
||||
/obj/item/toy/crayon/proc/can_claim_for_gang(mob/user, atom/target)
|
||||
// Check area validity.
|
||||
// Reject space, player-created areas, and non-station z-levels.
|
||||
var/area/A = get_base_area(target)
|
||||
if(!A || (!is_station_level(A.z)) || !(A.area_flags & VALID_TERRITORY))
|
||||
to_chat(user, "<span class='warning'>[A] is unsuitable for tagging.</span>")
|
||||
return FALSE
|
||||
|
||||
var/spraying_over = FALSE
|
||||
for(var/G in target)
|
||||
var/obj/effect/decal/cleanable/crayon/gang/gangtag = G
|
||||
if(istype(gangtag))
|
||||
var/datum/antagonist/gang/GA = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(gangtag.gang != GA.gang)
|
||||
spraying_over = TRUE
|
||||
break
|
||||
|
||||
var/occupying_gang = territory_claimed(A, user)
|
||||
if(occupying_gang && !spraying_over)
|
||||
to_chat(user, "<span class='danger'>[A] has already been tagged by the [occupying_gang] gang! You must get rid of or spray over the old tag first!</span>")
|
||||
return FALSE
|
||||
|
||||
// If you pass the gauntlet of checks, you're good to proceed
|
||||
return TRUE
|
||||
|
||||
/obj/item/toy/crayon/proc/territory_claimed(area/territory, mob/user)
|
||||
for(var/datum/team/gang/G in GLOB.gangs)
|
||||
if(territory.type in (G.territories|G.new_territories))
|
||||
. = G.name
|
||||
break
|
||||
|
||||
/obj/item/toy/crayon/proc/tag_for_gang(mob/user, atom/target)
|
||||
//Delete any old markings on this tile, including other gang tags
|
||||
for(var/obj/effect/decal/cleanable/crayon/old_marking in target)
|
||||
qdel(old_marking)
|
||||
|
||||
var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
var/area/territory = get_base_area(target)
|
||||
|
||||
new /obj/effect/decal/cleanable/crayon/gang(target,G.gang,"graffiti",0,user) // Heres the gang tag.
|
||||
to_chat(user, "<span class='notice'>You tagged [territory] for your gang!</span>")
|
||||
/////////////////Gang end////////////////////
|
||||
|
||||
|
||||
/obj/item/toy/crayon/attack(mob/M, mob/user)
|
||||
if(edible && (M == user))
|
||||
to_chat(user, "You take a bite of the [src.name]. Delicious!")
|
||||
@@ -521,6 +474,49 @@
|
||||
else
|
||||
..()
|
||||
|
||||
//////////////Gang mode stuff/////////////////
|
||||
/obj/item/toy/crayon/proc/can_claim_for_gang(mob/user, atom/target)
|
||||
var/area/A = get_area(target)
|
||||
if(!A || (!is_station_level(A.z)))
|
||||
to_chat(user, "<span class='warning'>[A] is unsuitable for tagging.</span>")
|
||||
return FALSE
|
||||
|
||||
var/spraying_over = FALSE
|
||||
for(var/obj/effect/decal/cleanable/crayon/gang/G in target)
|
||||
spraying_over = TRUE
|
||||
|
||||
for(var/obj/machinery/power/apc in target)
|
||||
to_chat(user, "<span class='warning'>You can't tag an APC.</span>")
|
||||
return FALSE
|
||||
|
||||
var/occupying_gang = territory_claimed(A, user)
|
||||
if(occupying_gang && !spraying_over)
|
||||
to_chat(user, "<span class='danger'>[A] has already been tagged by a gang! You must find and spray over the old tag first!</span>")
|
||||
return FALSE
|
||||
|
||||
// stolen from oldgang lmao
|
||||
return TRUE
|
||||
|
||||
/obj/item/toy/crayon/proc/tag_for_gang(mob/user, atom/target, datum/antagonist/gang/user_gang)
|
||||
for(var/obj/effect/decal/cleanable/crayon/old_marking in target)
|
||||
qdel(old_marking)
|
||||
|
||||
var/area/territory = get_area(target)
|
||||
|
||||
var/obj/effect/decal/cleanable/crayon/gang/tag = new /obj/effect/decal/cleanable/crayon/gang(target)
|
||||
tag.my_gang = user_gang.my_gang
|
||||
tag.icon_state = "[user_gang.gang_id]_tag"
|
||||
tag.name = "[tag.my_gang.name] gang tag"
|
||||
tag.desc = "Looks like someone's claimed this area for [tag.my_gang.name]."
|
||||
to_chat(user, "<span class='notice'>You tagged [territory] for [tag.my_gang.name]!</span>")
|
||||
|
||||
/obj/item/toy/crayon/proc/territory_claimed(area/territory, mob/user)
|
||||
for(var/obj/effect/decal/cleanable/crayon/gang/G in GLOB.gang_tags)
|
||||
if(get_area(G) == territory)
|
||||
return G
|
||||
|
||||
/////////////////Gang end////////////////////
|
||||
|
||||
/obj/item/toy/crayon/red
|
||||
icon_state = "crayonred"
|
||||
paint_color = "#DA0000"
|
||||
@@ -761,7 +757,7 @@
|
||||
|
||||
return
|
||||
|
||||
if(isobj(target))
|
||||
if(isobj(target) && !istype(target, /obj/effect/decal/cleanable/crayon/gang))
|
||||
if(actually_paints)
|
||||
if(istype(target, /obj/item/canvas)) //dont color our canvas neon green when im trying to paint please
|
||||
return
|
||||
@@ -866,26 +862,6 @@
|
||||
post_noise = FALSE
|
||||
reagent_contents = list(/datum/reagent/consumable/nothing = 1, /datum/reagent/toxin/mutetoxin = 1)
|
||||
|
||||
/obj/item/toy/crayon/spraycan/gang
|
||||
charges = 20 // Charges back to 20, which is the default value for them.
|
||||
gang = TRUE
|
||||
gang_tag_delay = 15 //Its 50% faster than a regular spraycan, for tagging. After-all they did spend points/meet the boss.
|
||||
|
||||
pre_noise = FALSE
|
||||
post_noise = TRUE // Its even more stealthy just a tad.
|
||||
|
||||
/obj/item/toy/crayon/spraycan/gang/Initialize(loc, datum/team/gang/G)
|
||||
..()
|
||||
if(G)
|
||||
gang = G
|
||||
paint_color = G.color
|
||||
update_icon()
|
||||
|
||||
/obj/item/toy/crayon/spraycan/gang/examine(mob/user)
|
||||
. = ..()
|
||||
if(user.mind && user.mind.has_antag_datum(/datum/antagonist/gang) || isobserver(user))
|
||||
. += "This spraycan has been specially modified with a stage 2 nozzle kit, making it faster."
|
||||
|
||||
/obj/item/toy/crayon/spraycan/infinite
|
||||
name = "infinite spraycan"
|
||||
charges = -1
|
||||
|
||||
@@ -80,13 +80,13 @@
|
||||
/obj/item/defibrillator/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
|
||||
if(loc == user)
|
||||
if(slot_flags == ITEM_SLOT_BACK)
|
||||
if(user.get_item_by_slot(SLOT_BACK) == src)
|
||||
if(user.get_item_by_slot(ITEM_SLOT_BACK) == src)
|
||||
ui_action_click()
|
||||
else
|
||||
to_chat(user, "<span class='warning'>Put the defibrillator on your back first!</span>")
|
||||
|
||||
else if(slot_flags == ITEM_SLOT_BELT)
|
||||
if(user.get_item_by_slot(SLOT_BELT) == src)
|
||||
if(user.get_item_by_slot(ITEM_SLOT_BELT) == src)
|
||||
ui_action_click()
|
||||
else
|
||||
to_chat(user, "<span class='warning'>Strap the defibrillator's belt on first!</span>")
|
||||
@@ -182,7 +182,7 @@
|
||||
|
||||
/obj/item/defibrillator/equipped(mob/user, slot)
|
||||
..()
|
||||
if((slot_flags == ITEM_SLOT_BACK && slot != SLOT_BACK) || (slot_flags == ITEM_SLOT_BELT && slot != SLOT_BELT))
|
||||
if((slot_flags == ITEM_SLOT_BACK && slot != ITEM_SLOT_BACK) || (slot_flags == ITEM_SLOT_BELT && slot != ITEM_SLOT_BELT))
|
||||
remove_paddles(user)
|
||||
update_power()
|
||||
|
||||
@@ -423,7 +423,7 @@
|
||||
if((!req_defib && grab_ghost) || (req_defib && defib.grab_ghost))
|
||||
H.notify_ghost_cloning("Your heart is being defibrillated!")
|
||||
H.grab_ghost() // Shove them back in their body.
|
||||
else if(H.can_defib())
|
||||
else if(H.can_revive())
|
||||
H.notify_ghost_cloning("Your heart is being defibrillated. Re-enter your corpse if you want to be revived!", source = src)
|
||||
|
||||
do_help(H, user)
|
||||
|
||||
@@ -369,6 +369,10 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
dat += "<li><a href='byond://?src=[REF(src)];choice=Toggle Door'>[PDAIMG(rdoor)]Toggle Remote Door</a></li>"
|
||||
if (cartridge.access & CART_DRONEPHONE)
|
||||
dat += "<li><a href='byond://?src=[REF(src)];choice=Drone Phone'>[PDAIMG(dronephone)]Drone Phone</a></li>"
|
||||
if (cartridge.access & CART_BARTENDER)
|
||||
dat += "<li><a href='byond://?src=[REF(src)];choice=Drink Recipe Browser'>[PDAIMG(bucket)]Drink Recipe Browser</a></li>"
|
||||
if (cartridge.access & CART_CHEMISTRY)
|
||||
dat += "<li><a href='byond://?src=[REF(src)];choice=Chemistry Recipe Browser'>[PDAIMG(bucket)]Chemistry Recipe Browser</a></li>"
|
||||
dat += "<li><a href='byond://?src=[REF(src)];choice=3'>[PDAIMG(atmos)]Atmospheric Scan</a></li>"
|
||||
dat += "<li><a href='byond://?src=[REF(src)];choice=Light'>[PDAIMG(flashlight)][fon ? "Disable" : "Enable"] Flashlight</a></li>"
|
||||
if (pai)
|
||||
@@ -705,6 +709,16 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
if(T)
|
||||
pai.forceMove(T)
|
||||
|
||||
//DRINK RECIPE BROWSER=============================
|
||||
if("Drink Recipe Browser")
|
||||
if(cartridge && cartridge.access & CART_BARTENDER)
|
||||
recipe_search(U, GLOB.drink_reactions_list)
|
||||
|
||||
//CHEMISTRY RECIPE BROWSER
|
||||
if("Chemistry Recipe Browser")
|
||||
if(cartridge && cartridge.access & CART_CHEMISTRY)
|
||||
recipe_search(U, GLOB.normalized_chemical_reactions_list)
|
||||
|
||||
//LINK FUNCTIONS===================================
|
||||
|
||||
else//Cartridge menu linking
|
||||
|
||||
@@ -222,6 +222,7 @@
|
||||
/obj/item/pda/bar
|
||||
name = "bartender PDA"
|
||||
icon_state = "pda-bartender"
|
||||
default_cartridge = /obj/item/cartridge/bartender
|
||||
inserted_item = /obj/item/pen/fountain
|
||||
|
||||
/obj/item/pda/atmos
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#define CART_QUARTERMASTER (1<<12)
|
||||
#define CART_HYDROPONICS (1<<13)
|
||||
#define CART_DRONEPHONE (1<<14)
|
||||
#define CART_BARTENDER (1<<15)
|
||||
#define CART_CHEMISTRY (1<<16)
|
||||
|
||||
|
||||
/obj/item/cartridge
|
||||
@@ -77,7 +79,7 @@
|
||||
/obj/item/cartridge/chemistry
|
||||
name = "\improper ChemWhiz cartridge"
|
||||
icon_state = "cart-chem"
|
||||
access = CART_REAGENT_SCANNER
|
||||
access = CART_REAGENT_SCANNER | CART_CHEMISTRY
|
||||
bot_access_flags = MED_BOT
|
||||
|
||||
/obj/item/cartridge/security
|
||||
@@ -190,6 +192,12 @@
|
||||
bot_access_flags = SEC_BOT | MULE_BOT | FLOOR_BOT | CLEAN_BOT | MED_BOT | FIRE_BOT
|
||||
spam_enabled = 1
|
||||
|
||||
/obj/item/cartridge/bartender
|
||||
name = "\improper B.O.O.Z.E cartridge"
|
||||
desc = "Now with 12% alcohol!"
|
||||
icon_state = "cart-bar"
|
||||
access = CART_BARTENDER
|
||||
|
||||
/obj/item/cartridge/captain/New()
|
||||
..()
|
||||
radio = new(src)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
rad_flags = RAD_NO_CONTAMINATE
|
||||
item_flags = NOBLUDGEON
|
||||
custom_materials = list(/datum/material/iron = 150, /datum/material/glass = 150)
|
||||
|
||||
var/grace = RAD_GRACE_PERIOD
|
||||
@@ -35,17 +36,15 @@
|
||||
. = ..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
soundloop = new(list(src), FALSE)
|
||||
soundloop = new(src, FALSE)
|
||||
|
||||
/obj/item/geiger_counter/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
QDEL_NULL(soundloop)
|
||||
|
||||
return ..()
|
||||
|
||||
/obj/item/geiger_counter/process()
|
||||
update_icon()
|
||||
update_sound()
|
||||
|
||||
/obj/item/geiger_counter/process(delta_time)
|
||||
if(!scanning)
|
||||
current_tick_amount = 0
|
||||
return
|
||||
@@ -64,49 +63,55 @@
|
||||
|
||||
current_tick_amount = 0
|
||||
|
||||
update_appearance()
|
||||
update_sound()
|
||||
|
||||
/obj/item/geiger_counter/examine(mob/user)
|
||||
. = ..()
|
||||
if(!scanning)
|
||||
return
|
||||
. += "<span class='info'>Alt-click it to clear stored radiation levels.</span>"
|
||||
. += span_info("Alt-click it to clear stored radiation levels.")
|
||||
if(obj_flags & EMAGGED)
|
||||
. += "<span class='warning'>The display seems to be incomprehensible.</span>"
|
||||
. += span_warning("The display seems to be incomprehensible.")
|
||||
return
|
||||
switch(radiation_count)
|
||||
if(-INFINITY to RAD_LEVEL_NORMAL)
|
||||
. += "<span class='notice'>Ambient radiation level count reports that all is well.</span>"
|
||||
. += span_notice("Ambient radiation level count reports that all is well.")
|
||||
if(RAD_LEVEL_NORMAL + 1 to RAD_LEVEL_MODERATE)
|
||||
. += "<span class='disarm'>Ambient radiation levels slightly above average.</span>"
|
||||
. += span_alert("Ambient radiation levels slightly above average.")
|
||||
if(RAD_LEVEL_MODERATE + 1 to RAD_LEVEL_HIGH)
|
||||
. += "<span class='warning'>Ambient radiation levels above average.</span>"
|
||||
. += span_warning("Ambient radiation levels above average.")
|
||||
if(RAD_LEVEL_HIGH + 1 to RAD_LEVEL_VERY_HIGH)
|
||||
. += "<span class='danger'>Ambient radiation levels highly above average.</span>"
|
||||
. += span_danger("Ambient radiation levels highly above average.")
|
||||
if(RAD_LEVEL_VERY_HIGH + 1 to RAD_LEVEL_CRITICAL)
|
||||
. += "<span class='suicide'>Ambient radiation levels nearing critical level.</span>"
|
||||
. += span_suicide("Ambient radiation levels nearing critical level.")
|
||||
if(RAD_LEVEL_CRITICAL + 1 to INFINITY)
|
||||
. += "<span class='boldannounce'>Ambient radiation levels above critical level!</span>"
|
||||
. += span_boldannounce("Ambient radiation levels above critical level!")
|
||||
|
||||
. += "<span class='notice'>The last radiation amount detected was [last_tick_amount]</span>"
|
||||
. += span_notice("The last radiation amount detected was [last_tick_amount]")
|
||||
|
||||
/obj/item/geiger_counter/update_icon_state()
|
||||
if(!scanning)
|
||||
icon_state = "geiger_off"
|
||||
else if(obj_flags & EMAGGED)
|
||||
return ..()
|
||||
if(obj_flags & EMAGGED)
|
||||
icon_state = "geiger_on_emag"
|
||||
else
|
||||
switch(radiation_count)
|
||||
if(-INFINITY to RAD_LEVEL_NORMAL)
|
||||
icon_state = "geiger_on_1"
|
||||
if(RAD_LEVEL_NORMAL + 1 to RAD_LEVEL_MODERATE)
|
||||
icon_state = "geiger_on_2"
|
||||
if(RAD_LEVEL_MODERATE + 1 to RAD_LEVEL_HIGH)
|
||||
icon_state = "geiger_on_3"
|
||||
if(RAD_LEVEL_HIGH + 1 to RAD_LEVEL_VERY_HIGH)
|
||||
icon_state = "geiger_on_4"
|
||||
if(RAD_LEVEL_VERY_HIGH + 1 to RAD_LEVEL_CRITICAL)
|
||||
icon_state = "geiger_on_4"
|
||||
if(RAD_LEVEL_CRITICAL + 1 to INFINITY)
|
||||
icon_state = "geiger_on_5"
|
||||
return ..()
|
||||
|
||||
switch(radiation_count)
|
||||
if(-INFINITY to RAD_LEVEL_NORMAL)
|
||||
icon_state = "geiger_on_1"
|
||||
if(RAD_LEVEL_NORMAL + 1 to RAD_LEVEL_MODERATE)
|
||||
icon_state = "geiger_on_2"
|
||||
if(RAD_LEVEL_MODERATE + 1 to RAD_LEVEL_HIGH)
|
||||
icon_state = "geiger_on_3"
|
||||
if(RAD_LEVEL_HIGH + 1 to RAD_LEVEL_VERY_HIGH)
|
||||
icon_state = "geiger_on_4"
|
||||
if(RAD_LEVEL_VERY_HIGH + 1 to RAD_LEVEL_CRITICAL)
|
||||
icon_state = "geiger_on_4"
|
||||
if(RAD_LEVEL_CRITICAL + 1 to INFINITY)
|
||||
icon_state = "geiger_on_5"
|
||||
return ..()
|
||||
|
||||
/obj/item/geiger_counter/proc/update_sound()
|
||||
var/datum/looping_sound/geiger/loop = soundloop
|
||||
@@ -124,21 +129,21 @@
|
||||
if(amount <= RAD_BACKGROUND_RADIATION || !scanning)
|
||||
return
|
||||
current_tick_amount += amount
|
||||
update_icon()
|
||||
update_appearance()
|
||||
|
||||
/obj/item/geiger_counter/attack_self(mob/user)
|
||||
scanning = !scanning
|
||||
update_icon()
|
||||
to_chat(user, "<span class='notice'>[icon2html(src, user)] You switch [scanning ? "on" : "off"] [src].</span>")
|
||||
update_appearance()
|
||||
to_chat(user, span_notice("[icon2html(src, user)] You switch [scanning ? "on" : "off"] [src]."))
|
||||
|
||||
/obj/item/geiger_counter/afterattack(atom/target, mob/user)
|
||||
/obj/item/geiger_counter/afterattack(atom/target, mob/living/user)
|
||||
. = ..()
|
||||
if(user.a_intent == INTENT_HELP)
|
||||
if(!(obj_flags & EMAGGED))
|
||||
user.visible_message("<span class='notice'>[user] scans [target] with [src].</span>", "<span class='notice'>You scan [target]'s radiation levels with [src]...</span>")
|
||||
user.visible_message(span_notice("[user] scans [target] with [src]."), span_notice("You scan [target]'s radiation levels with [src]..."))
|
||||
addtimer(CALLBACK(src, .proc/scan, target, user), 20, TIMER_UNIQUE) // Let's not have spamming GetAllContents
|
||||
else
|
||||
user.visible_message("<span class='notice'>[user] scans [target] with [src].</span>", "<span class='danger'>You project [src]'s stored radiation into [target]!</span>")
|
||||
user.visible_message(span_notice("[user] scans [target] with [src]."), span_danger("You project [src]'s stored radiation into [target]!"))
|
||||
target.rad_act(radiation_count)
|
||||
radiation_count = 0
|
||||
return TRUE
|
||||
@@ -156,57 +161,61 @@
|
||||
if(isliving(A))
|
||||
var/mob/living/M = A
|
||||
if(!M.radiation)
|
||||
to_chat(user, "<span class='notice'>[icon2html(src, user)] Radiation levels within normal boundaries.</span>")
|
||||
to_chat(user, span_notice("[icon2html(src, user)] Radiation levels within normal boundaries."))
|
||||
else
|
||||
to_chat(user, "<span class='boldannounce'>[icon2html(src, user)] Subject is irradiated. Radiation levels: [M.radiation] rad.</span>")
|
||||
to_chat(user, span_boldannounce("[icon2html(src, user)] Subject is irradiated. Radiation levels: [M.radiation]."))
|
||||
|
||||
if(rad_strength)
|
||||
to_chat(user, "<span class='boldannounce'>[icon2html(src, user)] Target contains radioactive contamination. Radioactive strength: [rad_strength]</span>")
|
||||
to_chat(user, span_boldannounce("[icon2html(src, user)] Target contains radioactive contamination. Radioactive strength: [rad_strength]"))
|
||||
else
|
||||
to_chat(user, "<span class='notice'>[icon2html(src, user)] Target is free of radioactive contamination.</span>")
|
||||
to_chat(user, span_notice("[icon2html(src, user)] Target is free of radioactive contamination."))
|
||||
|
||||
/obj/item/geiger_counter/attackby(obj/item/I, mob/user, params)
|
||||
if(I.tool_behaviour == TOOL_SCREWDRIVER && (obj_flags & EMAGGED))
|
||||
if(scanning)
|
||||
to_chat(user, "<span class='warning'>Turn off [src] before you perform this action!</span>")
|
||||
return 0
|
||||
user.visible_message("<span class='notice'>[user] unscrews [src]'s maintenance panel and begins fiddling with its innards...</span>", "<span class='notice'>You begin resetting [src]...</span>")
|
||||
to_chat(user, span_warning("Turn off [src] before you perform this action!"))
|
||||
return FALSE
|
||||
user.visible_message(span_notice("[user] unscrews [src]'s maintenance panel and begins fiddling with its innards..."), span_notice("You begin resetting [src]..."))
|
||||
if(!I.use_tool(src, user, 40, volume=50))
|
||||
return 0
|
||||
user.visible_message("<span class='notice'>[user] refastens [src]'s maintenance panel!</span>", "<span class='notice'>You reset [src] to its factory settings!</span>")
|
||||
return FALSE
|
||||
user.visible_message(span_notice("[user] refastens [src]'s maintenance panel!"), span_notice("You reset [src] to its factory settings!"))
|
||||
obj_flags &= ~EMAGGED
|
||||
radiation_count = 0
|
||||
update_icon()
|
||||
return 1
|
||||
update_appearance()
|
||||
return TRUE
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/geiger_counter/AltClick(mob/living/user)
|
||||
. = ..()
|
||||
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE))
|
||||
return
|
||||
return ..()
|
||||
if(!scanning)
|
||||
to_chat(usr, "<span class='warning'>[src] must be on to reset its radiation level!</span>")
|
||||
return TRUE
|
||||
to_chat(usr, span_warning("[src] must be on to reset its radiation level!"))
|
||||
return
|
||||
radiation_count = 0
|
||||
to_chat(usr, "<span class='notice'>You flush [src]'s radiation counts, resetting it to normal.</span>")
|
||||
update_icon()
|
||||
return TRUE
|
||||
to_chat(usr, span_notice("You flush [src]'s radiation counts, resetting it to normal."))
|
||||
update_appearance()
|
||||
|
||||
/obj/item/geiger_counter/emag_act(mob/user)
|
||||
. = ..()
|
||||
if(obj_flags & EMAGGED)
|
||||
return
|
||||
if(scanning)
|
||||
to_chat(user, "<span class='warning'>Turn off [src] before you perform this action!</span>")
|
||||
to_chat(user, span_warning("Turn off [src] before you perform this action!"))
|
||||
return
|
||||
to_chat(user, "<span class='warning'>You override [src]'s radiation storing protocols. It will now generate small doses of radiation, and stored rads are now projected into creatures you scan.</span>")
|
||||
to_chat(user, span_warning("You override [src]'s radiation storing protocols. It will now generate small doses of radiation, and stored rads are now projected into creatures you scan."))
|
||||
obj_flags |= EMAGGED
|
||||
return TRUE
|
||||
|
||||
/obj/item/geiger_counter/cyborg
|
||||
var/mob/listeningTo
|
||||
|
||||
/obj/item/geiger_counter/cyborg/cyborg_unequip(mob/user)
|
||||
if(!scanning)
|
||||
return
|
||||
scanning = FALSE
|
||||
update_appearance()
|
||||
|
||||
/obj/item/geiger_counter/cyborg/equipped(mob/user)
|
||||
. = ..()
|
||||
if(listeningTo == user)
|
||||
@@ -217,6 +226,7 @@
|
||||
listeningTo = user
|
||||
|
||||
/obj/item/geiger_counter/cyborg/proc/redirect_rad_act(datum/source, amount)
|
||||
SIGNAL_HANDLER
|
||||
rad_act(amount)
|
||||
|
||||
/obj/item/geiger_counter/cyborg/dropped(mob/user)
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
/obj/item/megaphone/equipped(mob/M, slot)
|
||||
. = ..()
|
||||
if (slot == SLOT_HANDS)
|
||||
if (slot == ITEM_SLOT_HANDS)
|
||||
RegisterSignal(M, COMSIG_MOB_SAY, .proc/handle_speech)
|
||||
else
|
||||
UnregisterSignal(M, COMSIG_MOB_SAY)
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
category = list("hacked", "Misc")
|
||||
|
||||
/obj/item/electropack/shockcollar/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
|
||||
if(loc == user && user.get_item_by_slot(SLOT_NECK))
|
||||
if(loc == user && user.get_item_by_slot(ITEM_SLOT_NECK))
|
||||
to_chat(user, "<span class='warning'>The collar is fastened tight! You'll need help taking this off!</span>")
|
||||
return
|
||||
return ..()
|
||||
@@ -167,7 +167,7 @@
|
||||
|
||||
if(isliving(loc) && on) //the "on" arg is currently useless
|
||||
var/mob/living/L = loc
|
||||
if(!L.get_item_by_slot(SLOT_NECK)) //**properly** stops pocket shockers
|
||||
if(!L.get_item_by_slot(ITEM_SLOT_NECK)) //**properly** stops pocket shockers
|
||||
return
|
||||
if(shock_cooldown == TRUE)
|
||||
return
|
||||
|
||||
@@ -56,7 +56,7 @@ GLOBAL_LIST_INIT(channel_tokens, list(
|
||||
/obj/item/radio/headset/ComponentInitialize()
|
||||
. = ..()
|
||||
if (bowman)
|
||||
AddComponent(/datum/component/wearertargeting/earprotection, list(SLOT_EARS))
|
||||
AddComponent(/datum/component/wearertargeting/earprotection, list(ITEM_SLOT_EARS))
|
||||
|
||||
/obj/item/radio/headset/Initialize()
|
||||
. = ..()
|
||||
@@ -373,4 +373,4 @@ GLOBAL_LIST_INIT(channel_tokens, list(
|
||||
name = replacetext(name,"headset", "bowman headset")
|
||||
desc = "[desc] Protects ears from flashbangs."
|
||||
bowman = TRUE
|
||||
AddComponent(/datum/component/wearertargeting/earprotection, list(SLOT_EARS))
|
||||
AddComponent(/datum/component/wearertargeting/earprotection, list(ITEM_SLOT_EARS))
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
|
||||
/obj/item/reverse_bear_trap/Initialize()
|
||||
. = ..()
|
||||
soundloop = new(list(src))
|
||||
soundloop2 = new(list(src))
|
||||
soundloop = new(src)
|
||||
soundloop2 = new(src)
|
||||
|
||||
/obj/item/reverse_bear_trap/Destroy()
|
||||
QDEL_NULL(soundloop)
|
||||
@@ -33,22 +33,22 @@
|
||||
STOP_PROCESSING(SSprocessing, src)
|
||||
return ..()
|
||||
|
||||
/obj/item/reverse_bear_trap/process()
|
||||
/obj/item/reverse_bear_trap/process(delta_time)
|
||||
if(!ticking)
|
||||
return
|
||||
time_left--
|
||||
time_left -= delta_time
|
||||
soundloop2.mid_length = max(0.5, time_left - 5) //beepbeepbeepbeepbeep
|
||||
if(!time_left || !isliving(loc))
|
||||
if(time_left <= 0 || !isliving(loc))
|
||||
playsound(src, 'sound/machines/microwave/microwave-end.ogg', 100, FALSE)
|
||||
soundloop.stop()
|
||||
soundloop2.stop()
|
||||
to_chat(loc, "<span class='userdanger'>*ding*</span>")
|
||||
to_chat(loc, span_userdanger("*ding*"))
|
||||
addtimer(CALLBACK(src, .proc/snap), 2)
|
||||
|
||||
/obj/item/reverse_bear_trap/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
|
||||
if(iscarbon(user))
|
||||
var/mob/living/carbon/C = user
|
||||
if(C.get_item_by_slot(SLOT_HEAD) == src)
|
||||
if(C.get_item_by_slot(ITEM_SLOT_HEAD) == src)
|
||||
if(HAS_TRAIT_FROM(src, TRAIT_NODROP, REVERSE_BEAR_TRAP_TRAIT) && !struggling)
|
||||
struggling = TRUE
|
||||
var/fear_string
|
||||
@@ -82,26 +82,26 @@
|
||||
..()
|
||||
|
||||
/obj/item/reverse_bear_trap/attack(mob/living/target, mob/living/user)
|
||||
if(target.get_item_by_slot(SLOT_HEAD))
|
||||
if(target.get_item_by_slot(ITEM_SLOT_HEAD))
|
||||
to_chat(user, "<span class='warning'>Remove [target.p_their()] headgear first!</span>")
|
||||
return
|
||||
target.visible_message("<span class='warning'>[user] starts forcing [src] onto [target]'s head!</span>", \
|
||||
"<span class='userdanger'>[target] starts forcing [src] onto your head!</span>", "<i>You hear clanking.</i>")
|
||||
to_chat(user, "<span class='danger'>You start forcing [src] onto [target]'s head...</span>")
|
||||
if(!do_after(user, 30, target = target) || target.get_item_by_slot(SLOT_HEAD))
|
||||
if(!do_after(user, 30, target = target) || target.get_item_by_slot(ITEM_SLOT_HEAD))
|
||||
return
|
||||
target.visible_message("<span class='warning'>[user] forces and locks [src] onto [target]'s head!</span>", \
|
||||
"<span class='userdanger'>[target] locks [src] onto your head!</span>", "<i>You hear a click, and then a timer ticking down.</i>")
|
||||
to_chat(user, "<span class='danger'>You force [src] onto [target]'s head and click the padlock shut.</span>")
|
||||
user.dropItemToGround(src)
|
||||
target.equip_to_slot_if_possible(src, SLOT_HEAD)
|
||||
target.equip_to_slot_if_possible(src, ITEM_SLOT_HEAD)
|
||||
arm()
|
||||
notify_ghosts("[user] put a reverse bear trap on [target]!", source = src, action = NOTIFY_ORBIT, ghost_sound = 'sound/machines/beep.ogg')
|
||||
|
||||
/obj/item/reverse_bear_trap/proc/snap()
|
||||
reset()
|
||||
var/mob/living/carbon/human/H = loc
|
||||
if(!istype(H) || H.get_item_by_slot(SLOT_HEAD) != src)
|
||||
if(!istype(H) || H.get_item_by_slot(ITEM_SLOT_HEAD) != src)
|
||||
visible_message("<span class='warning'>[src]'s jaws snap open with an ear-piercing crack!</span>")
|
||||
playsound(src, 'sound/effects/snap.ogg', 75, TRUE)
|
||||
else
|
||||
@@ -116,13 +116,22 @@
|
||||
|
||||
/obj/item/reverse_bear_trap/proc/reset()
|
||||
ticking = FALSE
|
||||
update_appearance(UPDATE_OVERLAYS)
|
||||
REMOVE_TRAIT(src, TRAIT_NODROP, REVERSE_BEAR_TRAP_TRAIT)
|
||||
soundloop.stop()
|
||||
soundloop2.stop()
|
||||
STOP_PROCESSING(SSprocessing, src)
|
||||
|
||||
/obj/item/reverse_bear_trap/update_overlays()
|
||||
. = ..()
|
||||
if(ticking != TRUE)
|
||||
return
|
||||
/// note: this timer overlay increments one frame every second (to simulate a clock ticking). If you want to instead have it do a full cycle in a minute, set the 'delay' of each frame of the icon overlay to 75 rather than 10, and the worn overlay to twice that.
|
||||
// . += "rbt_ticking"
|
||||
|
||||
/obj/item/reverse_bear_trap/proc/arm() //hulen
|
||||
ticking = TRUE
|
||||
update_appearance(UPDATE_OVERLAYS)
|
||||
escape_chance = initial(escape_chance) //we keep these vars until re-arm, for tracking purposes
|
||||
time_left = initial(time_left)
|
||||
ADD_TRAIT(src, TRAIT_NODROP, REVERSE_BEAR_TRAP_TRAIT)
|
||||
|
||||
@@ -325,7 +325,7 @@ GENETICS SCANNER
|
||||
var/breathes = TRUE
|
||||
var/blooded = TRUE
|
||||
if(C.dna && C.dna.species)
|
||||
if(HAS_TRAIT_FROM(C, TRAIT_NOBREATH, SPECIES_TRAIT))
|
||||
if(!HAS_TRAIT_FROM(C, TRAIT_AUXILIARY_LUNGS, SPECIES_TRAIT) && HAS_TRAIT_FROM(C, TRAIT_NOBREATH, SPECIES_TRAIT))
|
||||
breathes = FALSE
|
||||
if(NOBLOOD in C.dna.species.species_traits)
|
||||
blooded = FALSE
|
||||
@@ -380,6 +380,8 @@ GENETICS SCANNER
|
||||
mutant = TRUE
|
||||
else if (S.mutantstomach != initial(S.mutantstomach))
|
||||
mutant = TRUE
|
||||
else if (S.flying_species != initial(S.flying_species))
|
||||
mutant = TRUE
|
||||
|
||||
msg += "\t<span class='info'>Reported Species: [H.spec_trait_examine_font()][H.dna.custom_species ? H.dna.custom_species : S.name]</font></span>\n"
|
||||
msg += "\t<span class='info'>Base Species: [H.spec_trait_examine_font()][S.name]</font></span>\n"
|
||||
@@ -434,12 +436,13 @@ GENETICS SCANNER
|
||||
if(R)
|
||||
blood_type = R.name
|
||||
|
||||
|
||||
if((C.scan_blood_volume() + C.integrating_blood) <= (BLOOD_VOLUME_SAFE * C.blood_ratio) && (C.scan_blood_volume() + C.integrating_blood) > (BLOOD_VOLUME_OKAY*C.blood_ratio))
|
||||
msg += "<span class='danger'>LOW blood level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""].</span> <span class='info'>type: [blood_type]</span>\n"
|
||||
msg += "<span class='danger'>LOW [HAS_TRAIT(C, TRAIT_ROBOTIC_ORGANISM) ? "coolant" : "blood"] level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""].</span> <span class='info'>type: [blood_type]</span>\n"
|
||||
else if((C.scan_blood_volume() + C.integrating_blood) <= (BLOOD_VOLUME_OKAY * C.blood_ratio))
|
||||
msg += "<span class='danger'>CRITICAL blood level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""].</span> <span class='info'>type: [blood_type]</span>\n"
|
||||
msg += "<span class='danger'>CRITICAL [HAS_TRAIT(C, TRAIT_ROBOTIC_ORGANISM) ? "coolant" : "blood"] level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""].</span> <span class='info'>type: [blood_type]</span>\n"
|
||||
else
|
||||
msg += "<span class='info'>Blood level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""]. type: [blood_type]</span>\n"
|
||||
msg += "<span class='info'>[HAS_TRAIT(C, TRAIT_ROBOTIC_ORGANISM) ? "Coolant" : "Blood"] level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""]. type: [blood_type]</span>\n"
|
||||
|
||||
|
||||
var/cyberimp_detect
|
||||
|
||||
@@ -208,7 +208,7 @@ effective or pretty fucking useless.
|
||||
actions_types = list(/datum/action/item_action/toggle)
|
||||
|
||||
/obj/item/shadowcloak/ui_action_click(mob/user)
|
||||
if(user.get_item_by_slot(SLOT_BELT) == src)
|
||||
if(user.get_item_by_slot(ITEM_SLOT_BELT) == src)
|
||||
if(!on)
|
||||
Activate(usr)
|
||||
else
|
||||
@@ -216,7 +216,7 @@ effective or pretty fucking useless.
|
||||
return
|
||||
|
||||
/obj/item/shadowcloak/item_action_slot_check(slot, mob/user, datum/action/A)
|
||||
if(slot == SLOT_BELT)
|
||||
if(slot == ITEM_SLOT_BELT)
|
||||
return 1
|
||||
|
||||
/obj/item/shadowcloak/proc/Activate(mob/living/carbon/human/user)
|
||||
@@ -238,11 +238,11 @@ effective or pretty fucking useless.
|
||||
|
||||
/obj/item/shadowcloak/dropped(mob/user)
|
||||
..()
|
||||
if(user && user.get_item_by_slot(SLOT_BELT) != src)
|
||||
if(user && user.get_item_by_slot(ITEM_SLOT_BELT) != src)
|
||||
Deactivate()
|
||||
|
||||
/obj/item/shadowcloak/process()
|
||||
if(user.get_item_by_slot(SLOT_BELT) != src)
|
||||
if(user.get_item_by_slot(ITEM_SLOT_BELT) != src)
|
||||
Deactivate()
|
||||
return
|
||||
var/turf/T = get_turf(src)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// 1) Tennis balls, lines 39 - 99
|
||||
// 2) Chew bones, lines 101 - 138
|
||||
// 3) Frisbee, lines 140 - 166
|
||||
*/
|
||||
*/
|
||||
|
||||
/obj/item/toy/fluff
|
||||
name = "Fluff Item"
|
||||
|
||||
@@ -336,7 +336,7 @@
|
||||
var/obj/item/clothing/magichead = new /obj/item/clothing/mask/horsehead/cursed(user.drop_location())
|
||||
if(!user.dropItemToGround(user.wear_mask))
|
||||
qdel(user.wear_mask)
|
||||
user.equip_to_slot_if_possible(magichead, SLOT_WEAR_MASK, TRUE, TRUE)
|
||||
user.equip_to_slot_if_possible(magichead, ITEM_SLOT_MASK, TRUE, TRUE)
|
||||
qdel(src)
|
||||
else
|
||||
to_chat(user,"<span class='notice'>I say thee neigh</span>") //It still lives here
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
if(target.mind.has_antag_datum(ANTAG_DATUM_VASSAL))
|
||||
SSticker.mode.remove_vassal(target.mind)
|
||||
|
||||
if(target.mind.has_antag_datum(/datum/antagonist/rev/head) || target.mind.unconvertable || target.mind.has_antag_datum(/datum/antagonist/gang/boss))
|
||||
if(target.mind.has_antag_datum(/datum/antagonist/rev/head) || (target.mind.unconvertable))
|
||||
if(!silent)
|
||||
target.visible_message("<span class='warning'>[target] seems to resist the implant!</span>", "<span class='warning'>You feel something interfering with your mental conditioning, but you resist it!</span>")
|
||||
var/obj/item/implanter/I = loc
|
||||
|
||||
@@ -20,4 +20,7 @@
|
||||
imp_type = /obj/item/implant/uplink/precharged
|
||||
|
||||
/obj/item/implant/uplink/precharged
|
||||
starting_tc = 10
|
||||
starting_tc = TELECRYSTALS_PRELOADED_IMPLANT
|
||||
|
||||
/obj/item/implant/uplink/starting
|
||||
starting_tc = TELECRYSTALS_DEFAULT - UPLINK_IMPLANT_TELECRYSTAL_COST
|
||||
|
||||
@@ -189,5 +189,7 @@
|
||||
return FALSE
|
||||
brainwash(C, objective)
|
||||
message_admins("[ADMIN_LOOKUPFLW(user)] brainwashed [key_name_admin(C)] with objective '[objective]'.")
|
||||
user.log_message("has brainwashed [key_name(C)] with the objective '[objective]' using \the [src]", LOG_ATTACK)
|
||||
C.log_message("has been brainwashed with the objective '[objective]' by [key_name(user)] using \the [src]", LOG_VICTIM, log_globally = FALSE)
|
||||
log_game("[key_name(user)] brainwashed [key_name(C)] with objective '[objective]'.")
|
||||
return TRUE
|
||||
|
||||
@@ -188,7 +188,7 @@
|
||||
icon_state = "inducer-combat"
|
||||
item_state = "inducer-combat"
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
slot_flags = SLOT_BELT
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
desc = "A tool for inductively charging internal power cells. This one has been modified and upgraded to be able to charge into guns as well as normal electronics."
|
||||
cell_type = /obj/item/stock_parts/cell/hyper
|
||||
powertransfer = 1300
|
||||
|
||||
@@ -193,7 +193,7 @@
|
||||
|
||||
/obj/item/melee/transforming/energy/sword/saber
|
||||
possible_colors = list("red" = LIGHT_COLOR_RED, "blue" = LIGHT_COLOR_LIGHT_CYAN, "green" = LIGHT_COLOR_GREEN, "purple" = LIGHT_COLOR_LAVENDER)
|
||||
unique_reskin = list("Sword" = "sword0", "saber" = "esaber0")
|
||||
unique_reskin = list("Sword" = "sword0", "Saber" = "esaber0")
|
||||
var/hacked = FALSE
|
||||
var/saber = FALSE
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
/obj/item/target/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
|
||||
if(pinnedLoc)
|
||||
pinnedLoc.removeTarget(user)
|
||||
. = ..()
|
||||
|
||||
/obj/item/target/syndicate
|
||||
icon_state = "target_s"
|
||||
|
||||
@@ -488,6 +488,9 @@
|
||||
icon_state = "nanogel"
|
||||
var/being_applied = FALSE //No doafter stacking.
|
||||
|
||||
/obj/item/stack/medical/nanogel/one
|
||||
amount = 1
|
||||
|
||||
/obj/item/stack/medical/nanogel/try_heal(mob/living/M, mob/user, silent = FALSE)
|
||||
if(being_applied)
|
||||
to_chat(user, "<span class='warning'>You are already applying [src]!</span>")
|
||||
|
||||
@@ -53,7 +53,6 @@
|
||||
desc = "A satchel that opens into a localized pocket of Blue Space."
|
||||
icon_state = "holdingsat"
|
||||
item_state = "holdingsat"
|
||||
species_exception = list(/datum/species/angel)
|
||||
|
||||
/obj/item/storage/backpack/holding/duffel
|
||||
name = "duffel bag of holding"
|
||||
@@ -190,7 +189,6 @@
|
||||
name = "satchel"
|
||||
desc = "A trendy looking satchel."
|
||||
icon_state = "satchel-norm"
|
||||
species_exception = list(/datum/species/angel) //satchels can be equipped since they are on the side, not back
|
||||
|
||||
/obj/item/storage/backpack/satchel/leather
|
||||
name = "leather satchel"
|
||||
@@ -657,3 +655,9 @@
|
||||
desc = "Worn by snails as armor and storage compartment."
|
||||
icon_state = "snailshell"
|
||||
item_state = "snailshell"
|
||||
|
||||
/obj/item/storage/backpack/henchmen
|
||||
name = "wings"
|
||||
desc = "Granted to the henchmen who deserve it. This probably doesn't include you."
|
||||
icon_state = "henchmen"
|
||||
item_state = "henchmen"
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
desc = "This little bugger can be used to store and transport ores."
|
||||
icon = 'icons/obj/mining.dmi'
|
||||
icon_state = "satchel"
|
||||
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_POCKET
|
||||
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_POCKETS
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
component_type = /datum/component/storage/concrete/stack
|
||||
var/spam_protection = FALSE //If this is TRUE, the holder won't receive any messages when they fail to pick up ore through crossing it
|
||||
@@ -400,7 +400,7 @@
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "bag"
|
||||
desc = "A bag for storing pills, patches, and bottles."
|
||||
slot_flags = ITEM_SLOT_BELT|ITEM_SLOT_POCKET
|
||||
slot_flags = ITEM_SLOT_BELT|ITEM_SLOT_POCKETS
|
||||
resistance_flags = FLAMMABLE
|
||||
|
||||
/obj/item/storage/bag/chemistry/ComponentInitialize()
|
||||
@@ -420,7 +420,7 @@
|
||||
icon = 'icons/obj/chemical.dmi'
|
||||
icon_state = "biobag"
|
||||
desc = "A bag for the safe transportation and disposal of biowaste and other biological materials."
|
||||
slot_flags = ITEM_SLOT_BELT|ITEM_SLOT_POCKET
|
||||
slot_flags = ITEM_SLOT_BELT|ITEM_SLOT_POCKETS
|
||||
resistance_flags = FLAMMABLE
|
||||
|
||||
/obj/item/storage/bag/bio/ComponentInitialize()
|
||||
@@ -451,7 +451,7 @@
|
||||
desc = "A pouch for your ammo that goes in your pocket."
|
||||
icon = 'icons/obj/items_and_weapons.dmi'
|
||||
icon_state = "ammopouch"
|
||||
slot_flags = ITEM_SLOT_POCKET
|
||||
slot_flags = ITEM_SLOT_POCKETS
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
resistance_flags = FLAMMABLE
|
||||
|
||||
@@ -472,7 +472,7 @@ Bag for holding materials
|
||||
desc = "A pouch for sheets and RCD ammunition that manages to hang where you would normally put things in your pocket."
|
||||
icon = 'icons/obj/items_and_weapons.dmi'
|
||||
icon_state = "materialpouch"
|
||||
slot_flags = ITEM_SLOT_POCKET
|
||||
slot_flags = ITEM_SLOT_POCKETS
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
resistance_flags = FLAMMABLE
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list("bible", "koran", "scrapbook", "bible",
|
||||
var/mob/living/carbon/human/H = usr
|
||||
H.dna.add_mutation(CLOWNMUT)
|
||||
H.dna.add_mutation(SMILE)
|
||||
H.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/clown_hat(H), SLOT_WEAR_MASK)
|
||||
H.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/clown_hat(H), ITEM_SLOT_MASK)
|
||||
|
||||
GLOB.bible_icon_state = icon_state
|
||||
GLOB.bibleitemstates = item_state
|
||||
|
||||
@@ -248,10 +248,10 @@
|
||||
return ..()
|
||||
var/obj/item/clothing/mask/cigarette/cig = locate(/obj/item/clothing/mask/cigarette) in contents
|
||||
if(cig)
|
||||
if(!user.wear_mask && !(SLOT_WEAR_MASK in M.check_obscured_slots()))
|
||||
if(!user.wear_mask && !(ITEM_SLOT_MASK in M.check_obscured_slots()))
|
||||
var/obj/item/clothing/mask/cigarette/W = cig
|
||||
SEND_SIGNAL(src, COMSIG_TRY_STORAGE_TAKE, W, M)
|
||||
M.equip_to_slot_if_possible(W, SLOT_WEAR_MASK)
|
||||
M.equip_to_slot_if_possible(W, ITEM_SLOT_MASK)
|
||||
contents -= W
|
||||
to_chat(user, "<span class='notice'>You take \a [W] out of the pack.</span>")
|
||||
else
|
||||
|
||||
@@ -0,0 +1,539 @@
|
||||
/// doing nothing/orbiting idly
|
||||
#define STATE_IDLE 0
|
||||
/// performing reset animation
|
||||
#define STATE_RESET 1
|
||||
/// performing attack animation
|
||||
#define STATE_ATTACK 2
|
||||
/// performing animation between attacks
|
||||
#define STATE_RECOVER 3
|
||||
|
||||
/**
|
||||
* Simple summon weapon code in this file
|
||||
*
|
||||
* tl;dr latch onto target, repeatedly proc attacks, animate using transforms,
|
||||
* no real hitboxes/collisions, think of /datum/component/orbit-adjacent
|
||||
*/
|
||||
/obj/item/summon
|
||||
name = "a horrifying mistake"
|
||||
desc = "Why does this exist?"
|
||||
/// datum type
|
||||
var/host_type
|
||||
/// number of summons
|
||||
var/summon_count = 6
|
||||
/// how long it takes for a "stack" to fall off by itself
|
||||
var/stack_duration = 5 SECONDS
|
||||
/// our summon weapon host
|
||||
var/datum/summon_weapon_host/host
|
||||
/// range summons will chase to
|
||||
var/range = 7
|
||||
/// are we a ranged weapon?
|
||||
var/melee_only = TRUE
|
||||
|
||||
/obj/item/summon/Initialize()
|
||||
. = ..()
|
||||
if(host_type)
|
||||
host = new host_type(src, summon_count, range)
|
||||
|
||||
/obj/item/summon/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
if(!host)
|
||||
return
|
||||
if(!proximity_flag && melee_only)
|
||||
return
|
||||
Target(target)
|
||||
|
||||
/obj/item/summon/dropped(mob/user, silent)
|
||||
. = ..()
|
||||
addtimer(CALLBACK(src, .proc/check_activation), 0, TIMER_UNIQUE)
|
||||
|
||||
/obj/item/summon/equipped(mob/user, slot)
|
||||
. = ..()
|
||||
addtimer(CALLBACK(src, .proc/check_activation), 0, TIMER_UNIQUE)
|
||||
|
||||
/obj/item/summon/proc/check_activation()
|
||||
if(!host)
|
||||
return
|
||||
if(!isliving(loc))
|
||||
host.SetMaster(null)
|
||||
var/mob/living/L = loc
|
||||
if(!istype(L))
|
||||
return
|
||||
if(!L.is_holding(src))
|
||||
host.SetMaster(src)
|
||||
host.Suppress()
|
||||
host.SetMaster(L)
|
||||
host.Wake()
|
||||
|
||||
/obj/item/summon/proc/Target(atom/victim)
|
||||
if(!host?.CheckTarget(victim))
|
||||
return
|
||||
host.AutoTarget(victim, stack_duration)
|
||||
|
||||
/obj/item/summon/sword
|
||||
name = "spectral blade"
|
||||
desc = "An eldritch blade that summons phantasms to attack one's enemies."
|
||||
icon = 'icons/obj/items_and_weapons.dmi'
|
||||
icon_state = "spectral"
|
||||
item_state = "spectral"
|
||||
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
|
||||
host_type = /datum/summon_weapon_host/sword
|
||||
force = 15
|
||||
sharpness = SHARP_EDGED
|
||||
|
||||
/**
|
||||
* Serves as the master datum for summon weapons
|
||||
*/
|
||||
/datum/summon_weapon_host
|
||||
/// master atom
|
||||
var/atom/master
|
||||
/// suppressed?
|
||||
var/active = TRUE
|
||||
/// actual projectiles
|
||||
var/list/datum/summon_weapon/controlled
|
||||
/// active projectiles - refreshing a projectile reorders the list, so if they all have the same stack durations, you can trust the list to have last-refreshed at [1]
|
||||
var/list/datum/summon_weapon/attacking
|
||||
/// idle projectiles
|
||||
var/list/datum/summon_weapon/idle
|
||||
/// projectile type
|
||||
var/weapon_type
|
||||
/// default stack time
|
||||
var/stack_time = 5 SECONDS
|
||||
/// range
|
||||
var/range = 7
|
||||
|
||||
/datum/summon_weapon_host/New(atom/master, count, range)
|
||||
src.master = master
|
||||
src.range = range
|
||||
controlled = list()
|
||||
attacking = list()
|
||||
idle = list()
|
||||
Create(count)
|
||||
|
||||
/datum/summon_weapon_host/Destroy()
|
||||
QDEL_LIST(controlled)
|
||||
master = null
|
||||
return ..()
|
||||
|
||||
/datum/summon_weapon_host/proc/SetMaster(atom/master, reset_on_failure = TRUE)
|
||||
var/changed = src.master != master
|
||||
src.master = master
|
||||
if(changed)
|
||||
for(var/datum/summon_weapon/weapon as anything in idle)
|
||||
weapon.Reset()
|
||||
if(!master && reset_on_failure)
|
||||
for(var/datum/summon_weapon/weapon as anything in attacking)
|
||||
weapon.Reset()
|
||||
|
||||
/datum/summon_weapon_host/proc/Create(count)
|
||||
if(!weapon_type)
|
||||
return
|
||||
for(var/i in 1 to min(count, clamp(20 - controlled.len - count, 0, 20)))
|
||||
var/datum/summon_weapon/weapon = new weapon_type
|
||||
Associate(weapon)
|
||||
|
||||
/datum/summon_weapon_host/proc/Associate(datum/summon_weapon/linking)
|
||||
if(linking.host && linking.host != src)
|
||||
linking.host.Disassociate(linking)
|
||||
linking.host = src
|
||||
controlled |= linking
|
||||
linking.Reset()
|
||||
|
||||
/datum/summon_weapon_host/proc/Disassociate(datum/summon_weapon/unlinking, reset = TRUE, autodel)
|
||||
if(unlinking.host == src)
|
||||
unlinking.host = null
|
||||
controlled -= unlinking
|
||||
if(reset)
|
||||
unlinking.Reset(del_no_host = autodel)
|
||||
idle -= unlinking
|
||||
attacking -= unlinking
|
||||
|
||||
/datum/summon_weapon_host/proc/AutoTarget(atom/victim, duration = stack_time)
|
||||
if(!active)
|
||||
return
|
||||
var/datum/summon_weapon/weapon = (idle.len && idle[1]) || (attacking.len && attacking[1])
|
||||
if(!weapon)
|
||||
return
|
||||
if(!CheckTarget(victim))
|
||||
return
|
||||
weapon.Target(victim)
|
||||
if(duration)
|
||||
weapon.ResetIn(duration)
|
||||
|
||||
/datum/summon_weapon_host/proc/OnTarget(datum/summon_weapon/weapon, atom/victim)
|
||||
attacking -= weapon
|
||||
idle -= weapon
|
||||
attacking |= weapon
|
||||
|
||||
/datum/summon_weapon_host/proc/OnReset(datum/summon_weapon/weapon, atom/victim)
|
||||
attacking -= weapon
|
||||
idle |= weapon
|
||||
|
||||
/datum/summon_weapon_host/proc/CheckTarget(atom/victim)
|
||||
if(isitem(victim))
|
||||
return FALSE
|
||||
if(QDELETED(victim))
|
||||
return FALSE
|
||||
if(victim == master)
|
||||
return FALSE
|
||||
if(isliving(victim))
|
||||
var/mob/living/L = victim
|
||||
if(L.stat == DEAD)
|
||||
return FALSE
|
||||
return TRUE
|
||||
if(isobj(victim))
|
||||
var/obj/O = victim
|
||||
return (O.obj_flags & CAN_BE_HIT)
|
||||
return FALSE
|
||||
|
||||
/datum/summon_weapon_host/proc/Suppress()
|
||||
active = FALSE
|
||||
for(var/datum/summon_weapon/weapon as anything in controlled)
|
||||
weapon.Reset()
|
||||
|
||||
/datum/summon_weapon_host/proc/Wake()
|
||||
active = TRUE
|
||||
for(var/datum/summon_weapon/weapon as anything in controlled)
|
||||
weapon.Reset()
|
||||
|
||||
/datum/summon_weapon_host/sword
|
||||
weapon_type = /datum/summon_weapon/sword
|
||||
|
||||
/**
|
||||
* A singular summoned object
|
||||
*
|
||||
* How summon weapons work:
|
||||
*
|
||||
* Reset() - makes it go back to its master.
|
||||
* Target() - locks onto a target for a duration
|
||||
*
|
||||
* The biggest challenge is synchronizing animations.
|
||||
* Variables keep track of when things tick, but,
|
||||
* animations are client-timed, and not server-timed
|
||||
*
|
||||
* Animations:
|
||||
* The weapon can only track its "intended" angle and dist
|
||||
* "Current" pixel x/y are always calculated relative to a target from the current orbiting atom the physical effect is on
|
||||
* There's 3 animations,
|
||||
* MoveTo(location, angle, dist, rotation)
|
||||
* Orbit(location)
|
||||
* Rotate(degrees)
|
||||
*
|
||||
* And an non-animation that just snaps it to a location,
|
||||
* HardReset(location)
|
||||
*/
|
||||
/datum/summon_weapon
|
||||
/// name
|
||||
var/name = "summoned weapon"
|
||||
/// host
|
||||
var/datum/summon_weapon_host/host
|
||||
/// icon file
|
||||
var/icon = 'icons/effects/summon.dmi'
|
||||
/// icon state
|
||||
var/icon_state
|
||||
/// mutable_appearance to use, will skip making from icon/icon state if so
|
||||
var/mutable_appearance/appearance
|
||||
/// the actual effect
|
||||
var/atom/movable/summon_weapon_effect/atom
|
||||
/// currently locked attack target
|
||||
var/atom/victim
|
||||
/// current angle from victim - clockwise from 0. null if not attacking.
|
||||
var/angle
|
||||
/// current distance from victim - pixels
|
||||
var/dist
|
||||
/// current rotation - angles clockwise from north
|
||||
var/rotation
|
||||
/// rand dist to rotate during reattack phase
|
||||
var/angle_vary = 45
|
||||
/// orbit distance from victim - pixels
|
||||
var/orbit_dist = 72
|
||||
/// orbit distance variation from victim
|
||||
var/orbit_dist_vary = 24
|
||||
/// attack delay in deciseconds - this is time spent between attacks
|
||||
var/attack_speed = 1.5
|
||||
/// attack length in deciseconds - this is the attack animation speed in total
|
||||
var/attack_length = 1.5
|
||||
/// attack damage
|
||||
var/attack_damage = 5
|
||||
/// reset animation duration
|
||||
var/reset_speed = 2
|
||||
/// attack damtype
|
||||
var/attack_type = BRUTE
|
||||
/// attack sound
|
||||
var/attack_sound = list(
|
||||
'sound/weapons/bladeslice.ogg',
|
||||
'sound/weapons/bladesliceb.ogg'
|
||||
)
|
||||
/// attack verb
|
||||
var/attack_verb = list(
|
||||
"rended",
|
||||
"pierced",
|
||||
"penetrated",
|
||||
"sliced"
|
||||
)
|
||||
/// current state
|
||||
var/state = STATE_IDLE
|
||||
/// animation locked until
|
||||
var/animation_lock
|
||||
/// animation lock timer
|
||||
var/animation_timerid
|
||||
/// reset timerid
|
||||
var/reset_timerid
|
||||
|
||||
/datum/summon_weapon/New(mutable_appearance/appearance_override)
|
||||
if(appearance_override)
|
||||
appearance = appearance_override
|
||||
Setup()
|
||||
attack_verb = typelist(NAMEOF(src, attack_verb), attack_verb)
|
||||
attack_sound = typelist(NAMEOF(src, attack_sound), attack_sound)
|
||||
|
||||
/datum/summon_weapon/Destroy()
|
||||
host.Disassociate(src, autodel = FALSE)
|
||||
QDEL_NULL(atom)
|
||||
QDEL_NULL(appearance)
|
||||
return ..()
|
||||
|
||||
/datum/summon_weapon/proc/Setup()
|
||||
atom = new
|
||||
if(!appearance)
|
||||
GenerateAppearance()
|
||||
atom.appearance = appearance
|
||||
atom.moveToNullspace()
|
||||
if(host)
|
||||
Reset()
|
||||
|
||||
/datum/summon_weapon/proc/GenerateAppearance()
|
||||
if(!appearance)
|
||||
appearance = new
|
||||
appearance.icon = icon
|
||||
appearance.icon_state = icon_state
|
||||
appearance.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
appearance.opacity = FALSE
|
||||
appearance.plane = GAME_PLANE
|
||||
appearance.layer = ABOVE_MOB_LAYER
|
||||
appearance.appearance_flags = KEEP_TOGETHER
|
||||
appearance.overlays = list(
|
||||
emissive_appearance(icon, icon_state)
|
||||
)
|
||||
|
||||
/datum/summon_weapon/proc/Reset(immediate = FALSE, del_no_host = TRUE)
|
||||
angle = null
|
||||
victim = null
|
||||
if(reset_timerid)
|
||||
deltimer(reset_timerid)
|
||||
reset_timerid = null
|
||||
host?.OnReset(src)
|
||||
atom.Release()
|
||||
state = STATE_RESET
|
||||
if(!host)
|
||||
if(del_no_host)
|
||||
qdel(src)
|
||||
return
|
||||
HardReset(null)
|
||||
atom.moveToNullspace()
|
||||
return
|
||||
if(immediate)
|
||||
if(animation_timerid)
|
||||
deltimer(animation_timerid)
|
||||
Act()
|
||||
else
|
||||
Wake()
|
||||
|
||||
/datum/summon_weapon/proc/ResetIn(ds)
|
||||
reset_timerid = addtimer(CALLBACK(src, .proc/Reset), ds, TIMER_STOPPABLE)
|
||||
|
||||
/datum/summon_weapon/proc/Target(atom/victim)
|
||||
if(!istype(victim) || !isturf(victim.loc) || (host && !host.CheckTarget(victim)))
|
||||
Reset()
|
||||
return
|
||||
src.victim = victim
|
||||
host.OnTarget(src, victim)
|
||||
state = STATE_ATTACK
|
||||
Wake()
|
||||
|
||||
/datum/summon_weapon/proc/Wake()
|
||||
if(!animation_timerid)
|
||||
Act()
|
||||
|
||||
/datum/summon_weapon/proc/AnimationLock(duration)
|
||||
if(animation_timerid)
|
||||
deltimer(animation_timerid)
|
||||
animation_timerid = addtimer(CALLBACK(src, .proc/Act), duration, TIMER_CLIENT_TIME | TIMER_STOPPABLE)
|
||||
|
||||
/datum/summon_weapon/proc/Act()
|
||||
animation_timerid = null
|
||||
switch(state)
|
||||
if(STATE_IDLE)
|
||||
return
|
||||
if(STATE_ATTACK)
|
||||
if(!isturf(victim.loc) || (host && !host.CheckTarget(victim)))
|
||||
Reset(TRUE)
|
||||
return
|
||||
state = STATE_RECOVER
|
||||
// register hit at the halfway mark
|
||||
// we can do better math to approximate when the attack will hit but i'm too tired to bother
|
||||
addtimer(CALLBACK(src, .proc/Hit, victim), attack_length / 2, TIMER_CLIENT_TIME)
|
||||
// we need to approximate our incoming angle - again, better math exists but why bother
|
||||
var/incoming_angle = angle
|
||||
if(isturf(atom.loc) && (atom.loc != victim.loc))
|
||||
incoming_angle = Get_Angle(atom.loc, victim.loc)
|
||||
// pierce through target
|
||||
// we do not want to turn while doing this so we pierce through them visually
|
||||
incoming_angle += 180
|
||||
var/outgoing_angle = SIMPLIFY_DEGREES(incoming_angle)
|
||||
AnimationLock(MoveTo(victim, null, outgoing_angle, orbit_dist + rand(-orbit_dist_vary, orbit_dist_vary), outgoing_angle, attack_length))
|
||||
if(STATE_RESET)
|
||||
state = STATE_IDLE
|
||||
if(!host || !host.active || !get_turf(host.master))
|
||||
atom.moveToNullspace()
|
||||
src.angle = null
|
||||
src.dist = null
|
||||
src.rotation = null
|
||||
return
|
||||
var/reset_angle = rand(0, 360)
|
||||
AnimationLock(MoveTo(host.master, null, reset_angle, 30, 90, reset_speed))
|
||||
addtimer(CALLBACK(src, .proc/Orbit, host.master, reset_angle, 30, 3 SECONDS), reset_speed, TIMER_CLIENT_TIME)
|
||||
if(STATE_RECOVER)
|
||||
state = STATE_ATTACK
|
||||
AnimationLock(Rotate(rand(-angle_vary, angle_vary), attack_speed, null))
|
||||
|
||||
/datum/summon_weapon/proc/Hit(atom/victim)
|
||||
if(!isobj(victim) && !isliving(victim))
|
||||
return FALSE
|
||||
if(isliving(victim))
|
||||
var/mob/living/L = victim
|
||||
L.apply_damage(attack_damage, attack_type)
|
||||
playsound(victim, pick(attack_sound), 75)
|
||||
else if(isobj(victim))
|
||||
var/obj/O = victim
|
||||
O.take_damage(attack_damage, attack_type)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* relative to defaults to current location
|
||||
*/
|
||||
/datum/summon_weapon/proc/MoveTo(atom/destination, atom/relative_to, angle = 0, dist = 64, rotation = 180, time)
|
||||
. = time
|
||||
// construct final transform
|
||||
var/matrix/dest = ConstructMatrix(angle, dist, rotation)
|
||||
|
||||
// move to
|
||||
atom.Lock(destination)
|
||||
|
||||
// get relative first positions
|
||||
relative_to = get_turf(relative_to || atom.locked)
|
||||
destination = get_turf(destination)
|
||||
// if none, move to immediately and end
|
||||
if(!relative_to)
|
||||
atom.transform = dest
|
||||
src.angle = angle
|
||||
src.dist = dist
|
||||
src.rotation = rotation
|
||||
// end animations
|
||||
animate(atom, time = 0, flags = ANIMATION_END_NOW)
|
||||
return 0
|
||||
|
||||
// grab source
|
||||
var/rel_x = (destination.x - relative_to.x) * world.icon_size + src.dist * sin(src.angle)
|
||||
var/rel_y = (destination.y - relative_to.y) * world.icon_size + src.dist * cos(src.angle)
|
||||
|
||||
// construct source matrix
|
||||
var/matrix/source = new
|
||||
|
||||
source.Turn((relative_to == get_turf(atom.locked))? src.rotation : Get_Angle(relative_to, destination))
|
||||
source.Translate(rel_x, rel_y)
|
||||
|
||||
// set vars
|
||||
src.angle = angle
|
||||
src.dist = dist
|
||||
src.rotation = rotation
|
||||
|
||||
// animate
|
||||
atom.transform = source
|
||||
animate(atom, transform = dest, time, FALSE, LINEAR_EASING, ANIMATION_LINEAR_TRANSFORM | ANIMATION_END_NOW)
|
||||
|
||||
/**
|
||||
* rotation defaults to facing towards locked atom
|
||||
*/
|
||||
/datum/summon_weapon/proc/Rotate(degrees, time, rotation)
|
||||
. = time
|
||||
if(!dist)
|
||||
return 0
|
||||
var/matrix/M = ConstructMatrix(angle + degrees, dist, rotation || src.rotation)
|
||||
if(rotation)
|
||||
src.rotation = rotation
|
||||
angle += degrees
|
||||
animate(atom, transform = M, time, FALSE, LINEAR_EASING, ANIMATION_END_NOW | ANIMATION_LINEAR_TRANSFORM)
|
||||
|
||||
/datum/summon_weapon/proc/Orbit(atom/destination, initial_degrees = rand(0, 360), dist, speed)
|
||||
. = 0
|
||||
atom.Lock(destination)
|
||||
animate(atom, 0, FALSE, flags = ANIMATION_END_NOW)
|
||||
atom.transform = ConstructMatrix(initial_degrees, dist, 90)
|
||||
atom.SpinAnimation(speed, parallel = FALSE, segments = 10)
|
||||
// we can't predict dist/angle anymre because clienttime vs servertime.
|
||||
// well, we can, but, let's not be bothered with timeofday math eh.
|
||||
dist = 0
|
||||
angle = 0
|
||||
|
||||
/datum/summon_weapon/proc/ConstructMatrix(angle = 0, dist = 64, rotation = 0)
|
||||
var/matrix/M = new
|
||||
M.Turn(rotation)
|
||||
M.Translate(0, dist)
|
||||
M.Turn(angle)
|
||||
return M
|
||||
|
||||
/datum/summon_weapon/proc/HardReset(atom/snap_to)
|
||||
if(animation_timerid)
|
||||
deltimer(animation_timerid)
|
||||
atom.Release()
|
||||
atom.forceMove(snap_to)
|
||||
atom.transform = null
|
||||
|
||||
/datum/summon_weapon/sword
|
||||
name = "spectral blade"
|
||||
icon_state = "sword"
|
||||
attack_damage = 5
|
||||
attack_speed = 1.5
|
||||
attack_length = 1.5
|
||||
|
||||
/atom/movable/summon_weapon_effect
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
plane = GAME_PLANE
|
||||
layer = ABOVE_MOB_LAYER
|
||||
opacity = FALSE
|
||||
density = FALSE
|
||||
/// locked atom
|
||||
var/atom/locked
|
||||
|
||||
/atom/movable/summon_weapon_effect/Destroy()
|
||||
Release()
|
||||
return ..()
|
||||
|
||||
/atom/movable/summon_weapon_effect/proc/Lock(atom/target)
|
||||
if(locked == target)
|
||||
return
|
||||
if(locked)
|
||||
Release()
|
||||
if(!target)
|
||||
return
|
||||
locked = target
|
||||
forceMove(locked.loc)
|
||||
if(ismovable(locked))
|
||||
RegisterSignal(locked, COMSIG_MOVABLE_MOVED, .proc/Update)
|
||||
|
||||
/atom/movable/summon_weapon_effect/proc/Release()
|
||||
if(ismovable(locked))
|
||||
UnregisterSignal(locked, COMSIG_MOVABLE_MOVED)
|
||||
locked = null
|
||||
|
||||
/atom/movable/summon_weapon_effect/proc/Update()
|
||||
if(!locked)
|
||||
return
|
||||
if(loc != locked.loc)
|
||||
forceMove(locked.loc)
|
||||
|
||||
#undef STATE_IDLE
|
||||
#undef STATE_ATTACK
|
||||
#undef STATE_RECOVER
|
||||
#undef STATE_RESET
|
||||
@@ -291,8 +291,8 @@
|
||||
message_admins("Explosive tank rupture! Last key to touch the tank was [src.fingerprintslast].")
|
||||
log_game("Explosive tank rupture! Last key to touch the tank was [src.fingerprintslast].")
|
||||
//Give the gas a chance to build up more pressure through reacting
|
||||
air_contents.react(src)
|
||||
air_contents.react(src)
|
||||
for(var/i in 1 to TANK_POST_FRAGMENT_REACTIONS)
|
||||
air_contents.react(src)
|
||||
|
||||
pressure = air_contents.return_pressure()
|
||||
var/range = (pressure-TANK_FRAGMENT_PRESSURE)/TANK_FRAGMENT_SCALE
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
|
||||
/obj/item/watertank/equipped(mob/user, slot)
|
||||
..()
|
||||
if(slot != SLOT_BACK)
|
||||
if(slot != ITEM_SLOT_BACK)
|
||||
remove_noz()
|
||||
|
||||
/obj/item/watertank/proc/remove_noz()
|
||||
@@ -271,18 +271,7 @@
|
||||
if(resin_cooldown)
|
||||
to_chat(user, "<span class='warning'>Resin launcher is still recharging...</span>")
|
||||
return
|
||||
resin_cooldown = TRUE
|
||||
R.remove_any(100)
|
||||
var/obj/effect/resin_container/A = new (get_turf(src))
|
||||
log_game("[key_name(user)] used Resin Launcher at [AREACOORD(user)].")
|
||||
playsound(src,'sound/items/syringeproj.ogg',40,1)
|
||||
for(var/a=0, a<5, a++)
|
||||
step_towards(A, target)
|
||||
sleep(2)
|
||||
A.Smoke()
|
||||
spawn(100)
|
||||
if(src)
|
||||
resin_cooldown = FALSE
|
||||
launch_resin(target, user)
|
||||
return
|
||||
if(nozzle_mode == RESIN_FOAM)
|
||||
if(!Adj|| !isturf(target))
|
||||
@@ -301,6 +290,21 @@
|
||||
to_chat(user, "<span class='warning'>Resin foam mix is still being synthesized...</span>")
|
||||
return
|
||||
|
||||
/obj/item/extinguisher/mini/nozzle/proc/launch_resin(atom/target, mob/user)
|
||||
set waitfor = FALSE
|
||||
resin_cooldown = TRUE
|
||||
reagents.remove_any(100)
|
||||
var/obj/effect/resin_container/A = new (get_turf(src))
|
||||
log_game("[key_name(user)] used Resin Launcher at [AREACOORD(user)].")
|
||||
playsound(src,'sound/items/syringeproj.ogg',40,1)
|
||||
for(var/a=0, a<5, a++)
|
||||
step_towards(A, target)
|
||||
sleep(2)
|
||||
A.Smoke()
|
||||
spawn(100)
|
||||
if(src)
|
||||
resin_cooldown = FALSE
|
||||
|
||||
/obj/effect/resin_container
|
||||
name = "resin container"
|
||||
desc = "A compacted ball of expansive resin, used to repair the atmosphere in a room, or seal off breaches."
|
||||
@@ -344,14 +348,14 @@
|
||||
toggle_injection()
|
||||
|
||||
/obj/item/reagent_containers/chemtank/item_action_slot_check(slot, mob/user, datum/action/A)
|
||||
if(slot == SLOT_BACK)
|
||||
if(slot == ITEM_SLOT_BACK)
|
||||
return 1
|
||||
|
||||
/obj/item/reagent_containers/chemtank/proc/toggle_injection()
|
||||
var/mob/living/carbon/human/user = usr
|
||||
if(!istype(user))
|
||||
return
|
||||
if (user.get_item_by_slot(SLOT_BACK) != src)
|
||||
if (user.get_item_by_slot(ITEM_SLOT_BACK) != src)
|
||||
to_chat(user, "<span class='warning'>The chemtank needs to be on your back before you can activate it!</span>")
|
||||
return
|
||||
if(on)
|
||||
|
||||
@@ -46,6 +46,8 @@
|
||||
create_reagents(max_fuel)
|
||||
reagents.add_reagent(/datum/reagent/fuel, max_fuel)
|
||||
update_icon()
|
||||
if(can_off_process)
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/weldingtool/ComponentInitialize()
|
||||
. = ..()
|
||||
|
||||
@@ -1105,7 +1105,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
|
||||
icon_state = "madeyoulook"
|
||||
force = 0
|
||||
throwforce = 0
|
||||
item_flags = DROPDEL | ABSTRACT // | HAND_ITEM
|
||||
item_flags = DROPDEL | ABSTRACT | HAND_ITEM
|
||||
attack_verb = list("bopped")
|
||||
|
||||
/obj/item/circlegame/Initialize()
|
||||
@@ -1207,7 +1207,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
|
||||
item_state = "nothing"
|
||||
force = 0
|
||||
throwforce = 0
|
||||
item_flags = DROPDEL | ABSTRACT // | HAND_ITEM
|
||||
item_flags = DROPDEL | ABSTRACT | HAND_ITEM
|
||||
attack_verb = list("slapped")
|
||||
hitsound = 'sound/effects/snap.ogg'
|
||||
|
||||
@@ -1227,6 +1227,120 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/slapper/on_offered(mob/living/carbon/offerer)
|
||||
. = TRUE
|
||||
|
||||
if(!(locate(/mob/living/carbon) in orange(1, offerer)))
|
||||
visible_message(span_danger("[offerer] raises [offerer.p_their()] arm, looking around for a high-five, but there's no one around!"), \
|
||||
span_warning("You post up, looking for a high-five, but finding no one within range!"), null, 2)
|
||||
return
|
||||
|
||||
offerer.visible_message(span_notice("[offerer] raises [offerer.p_their()] arm, looking for a high-five!"), \
|
||||
span_notice("You post up, looking for a high-five!"), null, 2)
|
||||
offerer.apply_status_effect(STATUS_EFFECT_OFFERING, src, /atom/movable/screen/alert/give/highfive)
|
||||
|
||||
/// Yeah broh! This is where we do the high-fiving (or high-tenning :o)
|
||||
/obj/item/slapper/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker)
|
||||
. = TRUE
|
||||
|
||||
var/open_hands_taker
|
||||
var/slappers_giver
|
||||
for(var/i in taker.held_items) // see how many hands the taker has open for high'ing
|
||||
if(isnull(i))
|
||||
open_hands_taker++
|
||||
|
||||
if(!open_hands_taker)
|
||||
to_chat(taker, span_warning("You can't high-five [offerer] with no open hands!"))
|
||||
SEND_SIGNAL(taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five_full_hand) // not so successful now!
|
||||
return
|
||||
|
||||
for(var/i in offerer.held_items)
|
||||
var/obj/item/slapper/slap_check = i
|
||||
if(istype(slap_check))
|
||||
slappers_giver++
|
||||
|
||||
if(slappers_giver >= 2) // we only check this if it's already established the taker has 2+ hands free
|
||||
offerer.visible_message(span_notice("[taker] enthusiastically high-tens [offerer]!"), span_nicegreen("Wow! You're high-tenned [taker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), ignored_mobs=taker)
|
||||
to_chat(taker, span_nicegreen("You give high-tenning [offerer] your all!"))
|
||||
playsound(offerer, 'sound/weapons/slap.ogg', 100, TRUE, 1)
|
||||
SEND_SIGNAL(offerer, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_ten)
|
||||
SEND_SIGNAL(taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_ten)
|
||||
else
|
||||
offerer.visible_message(span_notice("[taker] high-fives [offerer]!"), span_nicegreen("All right! You're high-fived by [taker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), ignored_mobs=taker)
|
||||
to_chat(taker, span_nicegreen("You high-five [offerer]!"))
|
||||
playsound(offerer, 'sound/weapons/slap.ogg', 50, TRUE, -1)
|
||||
SEND_SIGNAL(offerer, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five)
|
||||
SEND_SIGNAL(taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five)
|
||||
qdel(src)
|
||||
|
||||
/// Gangster secret handshakes.
|
||||
/obj/item/slapper/secret_handshake
|
||||
name = "Secret Handshake"
|
||||
icon_state = "recruit"
|
||||
icon = 'icons/obj/gang/actions.dmi'
|
||||
/// References the active families gamemode handler (if one exists), for adding new family members to.
|
||||
var/datum/gang_handler/handler
|
||||
/// The typepath of the gang antagonist datum that the person who uses the package should have added to them -- remember that the distinction between e.g. Ballas and Grove Street is on the antag datum level, not the team datum level.
|
||||
var/gang_to_use
|
||||
/// The team datum that the person who uses this package should be added to.
|
||||
var/datum/team/gang/team_to_use
|
||||
|
||||
|
||||
/// Adds the user to the family that this package corresponds to, dispenses the free_clothes of that family, and adds them to the handler if it exists.
|
||||
/obj/item/slapper/secret_handshake/proc/add_to_gang(mob/living/user, original_name)
|
||||
var/datum/antagonist/gang/swappin_sides = new gang_to_use()
|
||||
swappin_sides.original_name = original_name
|
||||
swappin_sides.handler = handler
|
||||
user.mind.add_antag_datum(swappin_sides, team_to_use)
|
||||
var/policy = get_policy(ROLE_FAMILIES)
|
||||
if(policy)
|
||||
to_chat(user, policy)
|
||||
team_to_use.add_member(user.mind)
|
||||
swappin_sides.equip_gangster_in_inventory()
|
||||
if (!isnull(handler) && !handler.gangbangers.Find(user.mind)) // if we have a handler and they're not tracked by it
|
||||
handler.gangbangers += user.mind
|
||||
|
||||
/// Checks if the user is trying to use the package of the family they are in, and if not, adds them to the family, with some differing processing depending on whether the user is already a family member.
|
||||
/obj/item/slapper/secret_handshake/proc/attempt_join_gang(mob/living/user)
|
||||
if(!user?.mind)
|
||||
return
|
||||
var/datum/antagonist/gang/is_gangster = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
var/real_name_backup = user.real_name
|
||||
if(is_gangster)
|
||||
if(is_gangster.my_gang == team_to_use)
|
||||
return
|
||||
real_name_backup = is_gangster.original_name
|
||||
is_gangster.my_gang.remove_member(user.mind)
|
||||
user.mind.remove_antag_datum(/datum/antagonist/gang)
|
||||
add_to_gang(user, real_name_backup)
|
||||
|
||||
/obj/item/slapper/secret_handshake/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker)
|
||||
. = TRUE
|
||||
if (!(null in taker.held_items))
|
||||
to_chat(taker, span_warning("You can't get taught the secret handshake if [offerer] has no free hands!"))
|
||||
return
|
||||
|
||||
if(HAS_TRAIT(taker, TRAIT_MINDSHIELD))
|
||||
to_chat(taker, "You attended a seminar on not signing up for a gang and are not interested.")
|
||||
return
|
||||
|
||||
var/datum/antagonist/gang/is_gangster = taker.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(is_gangster?.starter_gangster)
|
||||
if(is_gangster.my_gang == team_to_use)
|
||||
to_chat(taker, "You started your family. You don't need to join it.")
|
||||
return
|
||||
to_chat(taker, "You started your family. You can't turn your back on it now.")
|
||||
return
|
||||
|
||||
offerer.visible_message(span_notice("[taker] is taught the secret handshake by [offerer]!"), span_nicegreen("All right! You've taught the secret handshake to [taker]!"), span_hear("You hear a bunch of weird shuffling and flesh slapping sounds!"), ignored_mobs=taker)
|
||||
to_chat(taker, span_nicegreen("You get taught the secret handshake by [offerer]!"))
|
||||
var/datum/antagonist/gang/owner_gang_datum = offerer.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
handler = owner_gang_datum.handler
|
||||
gang_to_use = owner_gang_datum.type
|
||||
team_to_use = owner_gang_datum.my_gang
|
||||
attempt_join_gang(taker)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/extendohand
|
||||
name = "extendo-hand"
|
||||
desc = "Futuristic tech has allowed these classic spring-boxing toys to essentially act as a fully functional hand-operated hand prosthetic."
|
||||
@@ -1261,7 +1375,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
|
||||
// attack_verb_simple = list("whack", "thwack", "wallop", "sock")
|
||||
// icon = 'icons/obj/items_and_weapons.dmi'
|
||||
// icon_state = "gohei"
|
||||
// inhand_icon_state = "gohei"
|
||||
// item_state = "gohei"
|
||||
// lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
|
||||
// righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
|
||||
|
||||
|
||||
@@ -238,7 +238,18 @@
|
||||
/obj/get_dumping_location(datum/component/storage/source,mob/user)
|
||||
return get_turf(src)
|
||||
|
||||
/obj/proc/CanAStarPass()
|
||||
/**
|
||||
* This proc is used for telling whether something can pass by this object in a given direction, for use by the pathfinding system.
|
||||
*
|
||||
* Trying to generate one long path across the station will call this proc on every single object on every single tile that we're seeing if we can move through, likely
|
||||
* multiple times per tile since we're likely checking if we can access said tile from multiple directions, so keep these as lightweight as possible.
|
||||
*
|
||||
* Arguments:
|
||||
* * ID- An ID card representing what access we have (and thus if we can open things like airlocks or windows to pass through them). The ID card's physical location does not matter, just the reference
|
||||
* * to_dir- What direction we're trying to move in, relevant for things like directional windows that only block movement in certain directions
|
||||
* * caller- The movable we're checking pass flags for, if we're making any such checks
|
||||
**/
|
||||
/obj/proc/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller)
|
||||
. = !density
|
||||
|
||||
/obj/proc/check_uplink_validity()
|
||||
|
||||
@@ -52,6 +52,8 @@
|
||||
var/author_ckey
|
||||
var/icon_generated = FALSE
|
||||
var/icon/generated_icon
|
||||
///boolean that blocks persistence from saving it. enabled from printing copies, because we do not want to save copies.
|
||||
var/no_save = FALSE
|
||||
|
||||
// Painting overlay offset when framed
|
||||
var/framed_offset_x = 11
|
||||
@@ -370,7 +372,7 @@
|
||||
update_icon()
|
||||
|
||||
/obj/structure/sign/painting/proc/save_persistent()
|
||||
if(!persistence_id || !current_canvas)
|
||||
if(!persistence_id || !current_canvas || current_canvas.no_save)
|
||||
return
|
||||
if(sanitize_filename(persistence_id) != persistence_id)
|
||||
stack_trace("Invalid persistence_id - [persistence_id]")
|
||||
|
||||
@@ -531,7 +531,7 @@
|
||||
|
||||
/obj/structure/closet/get_remote_view_fullscreens(mob/user)
|
||||
if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS)))
|
||||
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 1)
|
||||
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/scaled/impaired, 1)
|
||||
|
||||
/obj/structure/closet/emp_act(severity)
|
||||
. = ..()
|
||||
|
||||
@@ -397,7 +397,7 @@
|
||||
data["owner_name"] = payments_acc.account_holder
|
||||
if(showpiece)
|
||||
data["product_name"] = capitalize(showpiece.name)
|
||||
var/base64 = icon2base64(icon(showpiece.icon, showpiece.icon_state))
|
||||
var/base64 = icon2base64(icon(showpiece.icon, showpiece.icon_state, SOUTH, 1))
|
||||
data["product_icon"] = base64
|
||||
data["registered"] = register
|
||||
data["product_cost"] = sale_price
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Objects that spawn ghosts in as a certain role when they click on it, i.e. away mission bartenders.
|
||||
|
||||
#define spawnOverride TRUE
|
||||
//Preserved terrarium/seed vault: Spawns in seed vault structures in lavaland. Ghosts become plantpeople and are advised to begin growing plants in the room near them.
|
||||
/obj/effect/mob_spawn/human/seed_vault
|
||||
name = "preserved terrarium"
|
||||
@@ -36,6 +36,44 @@
|
||||
|
||||
//Ash walker eggs: Spawns in ash walker dens in lavaland. Ghosts become unbreathing lizards that worship the Necropolis and are advised to retrieve corpses to create more ash walkers.
|
||||
|
||||
/obj/structure/ash_walker_eggshell
|
||||
name = "ash walker egg"
|
||||
desc = "A man-sized yellow egg, spawned from some unfathomable creature. A humanoid silhouette lurks within. The egg shell looks resistant to temperature but otherwise rather brittle."
|
||||
icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
|
||||
icon_state = "large_egg"
|
||||
resistance_flags = LAVA_PROOF | FIRE_PROOF | FREEZE_PROOF
|
||||
max_integrity = 80
|
||||
var/obj/effect/mob_spawn/human/ash_walker/egg
|
||||
|
||||
/obj/structure/ash_walker_eggshell/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) //lifted from xeno eggs
|
||||
switch(damage_type)
|
||||
if(BRUTE)
|
||||
if(damage_amount)
|
||||
playsound(loc, 'sound/effects/attackblob.ogg', 100, TRUE)
|
||||
else
|
||||
playsound(src, 'sound/weapons/tap.ogg', 50, TRUE)
|
||||
if(BURN)
|
||||
if(damage_amount)
|
||||
playsound(loc, 'sound/items/welder.ogg', 100, TRUE)
|
||||
|
||||
/obj/structure/ash_walker_eggshell/attack_ghost(mob/user) //Pass on ghost clicks to the mob spawner
|
||||
if(egg)
|
||||
egg.attack_ghost(user)
|
||||
. = ..()
|
||||
|
||||
/obj/structure/ash_walker_eggshell/Destroy()
|
||||
if(!egg)
|
||||
return ..()
|
||||
var/mob/living/carbon/human/yolk = new /mob/living/carbon/human/(get_turf(src))
|
||||
yolk.fully_replace_character_name(null,random_unique_lizard_name(gender))
|
||||
yolk.set_species(/datum/species/lizard/ashwalker)
|
||||
yolk.underwear = "Nude"
|
||||
yolk.equipOutfit(/datum/outfit/ashwalker)//this is an authentic mess we're making
|
||||
yolk.update_body()
|
||||
yolk.gib()
|
||||
QDEL_NULL(egg)
|
||||
return ..()
|
||||
|
||||
/obj/effect/mob_spawn/human/ash_walker
|
||||
name = "ash walker egg"
|
||||
desc = "A man-sized yellow egg, spawned from some unfathomable creature. A humanoid silhouette lurks within."
|
||||
@@ -55,12 +93,25 @@
|
||||
You have seen lights in the distance... they foreshadow the arrival of outsiders to your domain. \
|
||||
Ensure your nest remains protected at all costs."
|
||||
assignedrole = "Ash Walker"
|
||||
var/datum/team/ashwalkers/team
|
||||
var/obj/structure/ash_walker_eggshell/eggshell
|
||||
|
||||
/obj/effect/mob_spawn/human/ash_walker/Destroy()
|
||||
eggshell = null
|
||||
return ..()
|
||||
|
||||
/obj/effect/mob_spawn/human/ash_walker/allow_spawn(mob/user, silent = FALSE)
|
||||
if(!(user.key in team.players_spawned) || spawnOverride)//one per person unless you get a bonus spawn
|
||||
return TRUE
|
||||
to_chat(user, span_warning("<b>You have exhausted your usefulness to the Necropolis</b>."))
|
||||
return FALSE
|
||||
|
||||
/obj/effect/mob_spawn/human/ash_walker/special(mob/living/new_spawn)
|
||||
new_spawn.real_name = random_unique_lizard_name(gender)
|
||||
if(is_mining_level(z))
|
||||
to_chat(new_spawn, "<b>Drag the corpses of men and beasts to your nest. It will absorb them to create more of your kind. Glory to the Necropolis!</b>")
|
||||
to_chat(new_spawn, "<b>You can expand the weather proof area provided by your shelters by using the 'New Area' key near the bottom right of your HUD.</b>")
|
||||
to_chat(new_spawn, "<b>Dragging injured ashwalkers to the tentacle or using the sleep verb next to it youself causes the body to remade whole after a short delay!</b>")
|
||||
else
|
||||
to_chat(new_spawn, "<span class='userdanger'>You have been born outside of your natural home! Whether you decide to return home, or make due with your new home is your own decision.</span>")
|
||||
|
||||
@@ -72,10 +123,18 @@
|
||||
H.undershirt = "Nude"
|
||||
H.socks = "Nude"
|
||||
H.update_body()
|
||||
new_spawn.mind.add_antag_datum(/datum/antagonist/ashwalker, team)
|
||||
team.players_spawned += (new_spawn.key)
|
||||
eggshell.egg = null
|
||||
QDEL_NULL(eggshell)
|
||||
|
||||
/obj/effect/mob_spawn/human/ash_walker/Initialize(mapload)
|
||||
/obj/effect/mob_spawn/human/ash_walker/Initialize(mapload, datum/team/ashwalkers/ashteam)
|
||||
. = ..()
|
||||
var/area/A = get_area(src)
|
||||
team = ashteam
|
||||
eggshell = new /obj/structure/ash_walker_eggshell(get_turf(loc))
|
||||
eggshell.egg = src
|
||||
src.forceMove(eggshell)
|
||||
if(A)
|
||||
notify_ghosts("An ash walker egg is ready to hatch in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_ASHWALKER, ignore_dnr_observers = TRUE)
|
||||
|
||||
|
||||
@@ -305,11 +305,10 @@
|
||||
else
|
||||
return 0
|
||||
|
||||
/obj/structure/girder/CanAStarPass(ID, dir, caller)
|
||||
/obj/structure/girder/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller)
|
||||
. = !density
|
||||
if(ismovable(caller))
|
||||
var/atom/movable/mover = caller
|
||||
. = . || (mover.pass_flags & PASSGRILLE)
|
||||
if(istype(caller))
|
||||
. = . || (caller.pass_flags & PASSGRILLE)
|
||||
|
||||
/obj/structure/girder/deconstruct(disassembled = TRUE)
|
||||
if(!(flags_1 & NODECONSTRUCT_1))
|
||||
|
||||
@@ -133,11 +133,10 @@
|
||||
else
|
||||
return !density
|
||||
|
||||
/obj/structure/grille/CanAStarPass(ID, dir, caller)
|
||||
/obj/structure/grille/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller)
|
||||
. = !density
|
||||
if(ismovable(caller))
|
||||
var/atom/movable/mover = caller
|
||||
. = . || (mover.pass_flags & PASSGRILLE)
|
||||
if(istype(caller))
|
||||
. = . || (caller.pass_flags & PASSGRILLE)
|
||||
|
||||
/obj/structure/grille/attackby(obj/item/W, mob/user, params)
|
||||
user.DelayNextAction(CLICK_CD_MELEE)
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
/obj/machinery/manned_turret/Destroy()
|
||||
target = null
|
||||
target_turf = null
|
||||
..()
|
||||
return ..()
|
||||
|
||||
//BUCKLE HOOKS
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an
|
||||
|
||||
/obj/structure/bodycontainer/get_remote_view_fullscreens(mob/user)
|
||||
if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS)))
|
||||
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 2)
|
||||
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/scaled/impaired, 2)
|
||||
/*
|
||||
* Morgue
|
||||
*/
|
||||
@@ -371,8 +371,7 @@ GLOBAL_LIST_EMPTY(crematoriums)
|
||||
else
|
||||
return 0
|
||||
|
||||
/obj/structure/tray/m_tray/CanAStarPass(ID, dir, caller)
|
||||
/obj/structure/tray/m_tray/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller)
|
||||
. = !density
|
||||
if(ismovable(caller))
|
||||
var/atom/movable/mover = caller
|
||||
. = . || (mover.pass_flags & PASSTABLE)
|
||||
if(istype(caller))
|
||||
. = . || (caller.pass_flags & PASSTABLE)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#define MAX_NOTICES 5
|
||||
|
||||
/obj/structure/noticeboard
|
||||
name = "notice board"
|
||||
desc = "A board for pinning important notices upon."
|
||||
@@ -7,8 +9,25 @@
|
||||
density = FALSE
|
||||
anchored = TRUE
|
||||
max_integrity = 150
|
||||
/// Current number of a pinned notices
|
||||
var/notices = 0
|
||||
|
||||
/obj/structure/noticeboard/directional/north
|
||||
dir = SOUTH
|
||||
pixel_y = 32
|
||||
|
||||
/obj/structure/noticeboard/directional/south
|
||||
dir = NORTH
|
||||
pixel_y = -32
|
||||
|
||||
/obj/structure/noticeboard/directional/east
|
||||
dir = WEST
|
||||
pixel_x = 32
|
||||
|
||||
/obj/structure/noticeboard/directional/west
|
||||
dir = EAST
|
||||
pixel_x = -32
|
||||
|
||||
/obj/structure/noticeboard/Initialize(mapload)
|
||||
. = ..()
|
||||
|
||||
@@ -16,7 +35,7 @@
|
||||
return
|
||||
|
||||
for(var/obj/item/I in loc)
|
||||
if(notices > 4)
|
||||
if(notices >= MAX_NOTICES)
|
||||
break
|
||||
if(istype(I, /obj/item/paper))
|
||||
I.forceMove(src)
|
||||
@@ -27,67 +46,84 @@
|
||||
/obj/structure/noticeboard/attackby(obj/item/O, mob/user, params)
|
||||
if(istype(O, /obj/item/paper) || istype(O, /obj/item/photo))
|
||||
if(!allowed(user))
|
||||
to_chat(user, "<span class='info'>You are not authorized to add notices</span>")
|
||||
to_chat(user, span_warning("You are not authorized to add notices!"))
|
||||
return
|
||||
if(notices < 5)
|
||||
if(notices < MAX_NOTICES)
|
||||
if(!user.transferItemToLoc(O, src))
|
||||
return
|
||||
notices++
|
||||
icon_state = "nboard0[notices]"
|
||||
to_chat(user, "<span class='notice'>You pin the [O] to the noticeboard.</span>")
|
||||
to_chat(user, span_notice("You pin the [O] to the noticeboard."))
|
||||
else
|
||||
to_chat(user, "<span class='notice'>The notice board is full</span>")
|
||||
to_chat(user, span_warning("The notice board is full!"))
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/structure/noticeboard/interact(mob/user)
|
||||
ui_interact(user)
|
||||
/obj/structure/noticeboard/ui_state(mob/user)
|
||||
return GLOB.physical_state
|
||||
|
||||
/obj/structure/noticeboard/ui_interact(mob/user)
|
||||
/obj/structure/noticeboard/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "NoticeBoard", name)
|
||||
ui.open()
|
||||
|
||||
/obj/structure/noticeboard/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["allowed"] = allowed(user)
|
||||
data["items"] = list()
|
||||
for(var/obj/item/content in contents)
|
||||
var/list/content_data = list(
|
||||
name = content.name,
|
||||
ref = REF(content)
|
||||
)
|
||||
data["items"] += list(content_data)
|
||||
return data
|
||||
|
||||
/obj/structure/noticeboard/ui_act(action, params)
|
||||
. = ..()
|
||||
var/auth = allowed(user)
|
||||
var/dat = "<B>[name]</B><BR>"
|
||||
for(var/obj/item/P in src)
|
||||
if(istype(P, /obj/item/paper))
|
||||
dat += "<A href='?src=[REF(src)];read=[REF(P)]'>[P.name]</A> [auth ? "<A href='?src=[REF(src)];write=[REF(P)]'>Write</A> <A href='?src=[REF(src)];remove=[REF(P)]'>Remove</A>" : ""]<BR>"
|
||||
else
|
||||
dat += "<A href='?src=[REF(src)];read=[REF(P)]'>[P.name]</A> [auth ? "<A href='?src=[REF(src)];remove=[REF(P)]'>Remove</A>" : ""]<BR>"
|
||||
user << browse("<HEAD><TITLE>Notices</TITLE></HEAD>[dat]","window=noticeboard")
|
||||
onclose(user, "noticeboard")
|
||||
if(.)
|
||||
return
|
||||
|
||||
/obj/structure/noticeboard/Topic(href, href_list)
|
||||
..()
|
||||
usr.set_machine(src)
|
||||
if(href_list["remove"])
|
||||
if((usr.stat || usr.restrained())) //For when a player is handcuffed while they have the notice window open
|
||||
return
|
||||
var/obj/item/I = locate(href_list["remove"]) in contents
|
||||
if(istype(I) && I.loc == src)
|
||||
I.forceMove(usr.loc)
|
||||
usr.put_in_hands(I)
|
||||
notices--
|
||||
icon_state = "nboard0[notices]"
|
||||
var/obj/item/item = locate(params["ref"]) in contents
|
||||
if(!istype(item) || item.loc != src)
|
||||
return
|
||||
|
||||
if(href_list["write"])
|
||||
if((usr.stat || usr.restrained())) //For when a player is handcuffed while they have the notice window open
|
||||
return
|
||||
var/obj/item/P = locate(href_list["write"]) in contents
|
||||
if(istype(P) && P.loc == src)
|
||||
var/obj/item/I = usr.is_holding_item_of_type(/obj/item/pen)
|
||||
if(I)
|
||||
add_fingerprint(usr)
|
||||
P.attackby(I, usr)
|
||||
var/mob/user = usr
|
||||
|
||||
switch(action)
|
||||
if("examine")
|
||||
if(istype(item, /obj/item/paper))
|
||||
item.ui_interact(user)
|
||||
else
|
||||
to_chat(usr, "<span class='notice'>You'll need something to write with!</span>")
|
||||
user.examinate(item)
|
||||
return TRUE
|
||||
if("remove")
|
||||
if(!allowed(user))
|
||||
return
|
||||
remove_item(item, user)
|
||||
return TRUE
|
||||
|
||||
if(href_list["read"])
|
||||
var/obj/item/I = locate(href_list["read"]) in contents
|
||||
if(istype(I) && I.loc == src)
|
||||
usr.examinate(I)
|
||||
/**
|
||||
* Removes an item from the notice board
|
||||
*
|
||||
* Arguments:
|
||||
* * item - The item that is to be removed
|
||||
* * user - The mob that is trying to get the item removed, if there is one
|
||||
*/
|
||||
/obj/structure/noticeboard/proc/remove_item(obj/item/item, mob/user)
|
||||
item.forceMove(drop_location())
|
||||
if(user)
|
||||
user.put_in_hands(item)
|
||||
balloon_alert(user, "removed from board")
|
||||
notices--
|
||||
icon_state = "nboard0[notices]"
|
||||
|
||||
/obj/structure/noticeboard/deconstruct(disassembled = TRUE)
|
||||
if(!(flags_1 & NODECONSTRUCT_1))
|
||||
new /obj/item/stack/sheet/metal (loc, 1)
|
||||
for(var/obj/item/content in contents)
|
||||
remove_item(content)
|
||||
qdel(src)
|
||||
|
||||
// Notice boards for the heads of staff (plus the qm)
|
||||
@@ -131,3 +167,5 @@
|
||||
name = "Staff Notice Board"
|
||||
desc = "Important notices from the heads of staff."
|
||||
req_access = list(ACCESS_HEADS)
|
||||
|
||||
#undef MAX_NOTICES
|
||||
|
||||
@@ -56,18 +56,18 @@
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/structure/plasticflaps/CanAStarPass(ID, to_dir, caller)
|
||||
/obj/structure/plasticflaps/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller)
|
||||
if(isliving(caller))
|
||||
if(isbot(caller))
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
var/mob/living/M = caller
|
||||
if(!(SEND_SIGNAL(M, COMSIG_CHECK_VENTCRAWL)) && M.mob_size != MOB_SIZE_TINY)
|
||||
return 0
|
||||
var/atom/movable/M = caller
|
||||
if(M && M.pulling)
|
||||
return CanAStarPass(ID, to_dir, M.pulling)
|
||||
return 1 //diseases, stings, etc can pass
|
||||
var/mob/living/living_caller = caller
|
||||
if(!(SEND_SIGNAL(living_caller, COMSIG_CHECK_VENTCRAWL)) && living_caller.mob_size != MOB_SIZE_TINY)
|
||||
return FALSE
|
||||
|
||||
if(caller?.pulling)
|
||||
return CanAStarPass(ID, to_dir, caller.pulling)
|
||||
return TRUE //diseases, stings, etc can pass
|
||||
|
||||
/obj/structure/plasticflaps/CanPass(atom/movable/A, turf/T)
|
||||
if(istype(A) && (A.pass_flags & PASSGLASS))
|
||||
|
||||
@@ -109,11 +109,10 @@
|
||||
else
|
||||
return !density
|
||||
|
||||
/obj/structure/table/CanAStarPass(ID, dir, caller)
|
||||
/obj/structure/table/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller)
|
||||
. = !density
|
||||
if(ismovable(caller))
|
||||
var/atom/movable/mover = caller
|
||||
. = . || (mover.pass_flags & PASSTABLE)
|
||||
if(istype(caller))
|
||||
. = . || (caller.pass_flags & PASSTABLE)
|
||||
|
||||
/obj/structure/table/proc/tableplace(mob/living/user, mob/living/pushed_mob)
|
||||
pushed_mob.forceMove(src.loc)
|
||||
@@ -702,11 +701,10 @@
|
||||
else
|
||||
return 0
|
||||
|
||||
/obj/structure/rack/CanAStarPass(ID, dir, caller)
|
||||
/obj/structure/rack/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller)
|
||||
. = !density
|
||||
if(ismovable(caller))
|
||||
var/atom/movable/mover = caller
|
||||
. = . || (mover.pass_flags & PASSTABLE)
|
||||
if(istype(caller))
|
||||
. = . || (caller.pass_flags & PASSTABLE)
|
||||
|
||||
/obj/structure/rack/MouseDrop_T(obj/O, mob/user)
|
||||
. = ..()
|
||||
|
||||
@@ -58,13 +58,10 @@
|
||||
pinned_target.nullPinnedLoc()
|
||||
nullPinnedTarget()
|
||||
handle_density()
|
||||
if(ishuman(user))
|
||||
if(!user.get_active_held_item())
|
||||
user.put_in_hands(pinned_target)
|
||||
to_chat(user, "<span class='notice'>You take the target out of the stake.</span>")
|
||||
else
|
||||
pinned_target.forceMove(user.drop_location())
|
||||
to_chat(user, "<span class='notice'>You take the target out of the stake.</span>")
|
||||
pinned_target.forceMove(get_turf(src))
|
||||
to_chat(user, "<span class='notice'>You take the target out of the stake.</span>")
|
||||
if(user.can_hold_items())
|
||||
user.put_in_hands(pinned_target)
|
||||
|
||||
/obj/structure/target_stake/bullet_act(obj/item/projectile/P)
|
||||
if(pinned_target)
|
||||
|
||||
@@ -257,7 +257,7 @@
|
||||
|
||||
/obj/machinery/shower/Initialize()
|
||||
. = ..()
|
||||
soundloop = new(list(src), FALSE)
|
||||
soundloop = new(src, FALSE)
|
||||
|
||||
/obj/machinery/shower/Destroy()
|
||||
QDEL_NULL(soundloop)
|
||||
@@ -669,7 +669,7 @@
|
||||
icon_state = "well_3"
|
||||
return TRUE
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You need at least tweenty-five pieces of sandstone!</span>")
|
||||
to_chat(user, "<span class='warning'>You need at least twenty-five pieces of sandstone!</span>")
|
||||
return
|
||||
if(steps == 3 && S.tool_behaviour == TOOL_SHOVEL)
|
||||
S.use_tool(src, user, 80, volume=100)
|
||||
|
||||
@@ -90,13 +90,13 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup)
|
||||
. = ..()
|
||||
if(direct)
|
||||
setDir(direct)
|
||||
|
||||
|
||||
if(extra_reinforced && anchored)
|
||||
state = PRWINDOW_SECURE
|
||||
|
||||
else if(reinf && anchored)
|
||||
state = WINDOW_SCREWED_TO_FRAME
|
||||
|
||||
|
||||
|
||||
if(mapload && electrochromatic_id && electrochromatic_id[1] == "!")
|
||||
electrochromatic_id = SSmapping.get_obfuscated_id(electrochromatic_id)
|
||||
@@ -571,13 +571,13 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup)
|
||||
/obj/structure/window/get_dumping_location(obj/item/storage/source,mob/user)
|
||||
return null
|
||||
|
||||
/obj/structure/window/CanAStarPass(ID, to_dir)
|
||||
/obj/structure/window/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller)
|
||||
if(!density)
|
||||
return 1
|
||||
return TRUE
|
||||
if((dir == FULLTILE_WINDOW_DIR) || (dir == to_dir))
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/obj/structure/window/GetExplosionBlock()
|
||||
return reinf && fulltile ? real_explosion_block : 0
|
||||
@@ -719,7 +719,6 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup)
|
||||
icon = 'icons/obj/smooth_structures/rplasma_window.dmi'
|
||||
icon_state = "rplasmawindow"
|
||||
dir = FULLTILE_WINDOW_DIR
|
||||
state = PRWINDOW_SECURE
|
||||
max_integrity = 1000
|
||||
fulltile = TRUE
|
||||
flags_1 = PREVENT_CLICK_UNDER_1
|
||||
|
||||
@@ -34,11 +34,11 @@
|
||||
/turf/open/floor/plating/catwalk_floor/screwdriver_act(mob/living/user, obj/item/tool)
|
||||
. = ..()
|
||||
covered = !covered
|
||||
to_chat(user, span_notice("[!covered ? "You removed the cover!" : "You added the cover!"]"))
|
||||
user.balloon_alert(user, "[!covered ? "cover removed" : "cover added"]")
|
||||
update_icon(UPDATE_OVERLAYS)
|
||||
|
||||
/turf/open/floor/plating/catwalk_floor/pry_tile(obj/item/crowbar, mob/user, silent)
|
||||
if(covered)
|
||||
to_chat(user, span_notice("You need to remove the cover first!"))
|
||||
user.balloon_alert(user, "remove cover first!")
|
||||
return FALSE
|
||||
. = ..()
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
barefootstep = FOOTSTEP_HARD_BAREFOOT
|
||||
clawfootstep = FOOTSTEP_HARD_CLAW
|
||||
heavyfootstep = FOOTSTEP_GENERIC_HEAVY
|
||||
flags_1 = NO_SCREENTIPS_1
|
||||
|
||||
/// Minimum explosion power to break tile
|
||||
var/explosion_power_break_tile = EXPLOSION_POWER_FLOOR_TILE_BREAK
|
||||
|
||||
@@ -125,8 +125,12 @@
|
||||
if(istype(W, /obj/item/stack/tile/material))
|
||||
var/turf/newturf = PlaceOnTop(/turf/open/floor/material, flags = CHANGETURF_INHERIT_AIR)
|
||||
newturf.set_custom_materials(W.custom_materials)
|
||||
if(length(C.atom_colours) && C.atom_colours[WASHABLE_COLOUR_PRIORITY] != null)
|
||||
newturf.add_atom_colour(C.atom_colours[WASHABLE_COLOUR_PRIORITY], FIXED_COLOUR_PRIORITY)
|
||||
else if(W.turf_type)
|
||||
var/turf/open/floor/T = PlaceOnTop(W.turf_type, flags = CHANGETURF_INHERIT_AIR)
|
||||
if(length(C.atom_colours) && C.atom_colours[WASHABLE_COLOUR_PRIORITY] != null)
|
||||
T.add_atom_colour(C.atom_colours[WASHABLE_COLOUR_PRIORITY], FIXED_COLOUR_PRIORITY)
|
||||
if(istype(W, /obj/item/stack/tile/light)) //TODO: get rid of this ugly check somehow
|
||||
var/obj/item/stack/tile/light/L = W
|
||||
var/turf/open/floor/light/F = T
|
||||
|
||||
@@ -19,3 +19,11 @@
|
||||
|
||||
/turf/open/floor/plating/dirt/try_replace_tile(obj/item/stack/tile/T, mob/user, params)
|
||||
return
|
||||
|
||||
/turf/open/floor/plating/dirt/space
|
||||
baseturfs = /turf/baseturf_bottom
|
||||
planetary_atmos = FALSE
|
||||
desc = "Upon closer examination there's plating beneath the dirt."
|
||||
|
||||
/turf/open/floor/plating/dirt/space/airless
|
||||
initial_gas_mix = AIRLESS_ATMOS
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user