Merge remote-tracking branch 'upstream/master'

This commit is contained in:
raspyosu
2020-02-21 01:48:50 -05:00
313 changed files with 5420 additions and 2451 deletions

View File

@@ -714,7 +714,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
AI_Interact = !AI_Interact
if(mob && IsAdminGhost(mob))
mob.has_unlimited_silicon_privilege = AI_Interact
mob.silicon_privileges = AI_Interact ? ALL : NONE
log_admin("[key_name(usr)] has [AI_Interact ? "activated" : "deactivated"] Admin AI Interact")
message_admins("[key_name_admin(usr)] has [AI_Interact ? "activated" : "deactivated"] their AI interaction")

View File

@@ -14,7 +14,8 @@ GLOBAL_LIST_EMPTY(antagonists)
var/list/objectives = list()
var/antag_memory = ""//These will be removed with antag datum
var/antag_moodlet //typepath of moodlet that the mob will gain with their status
var/can_hijack = HIJACK_NEUTRAL //If these antags are alone on shuttle hijack happens.
/// If above 0, this is the multiplier for the speed at which we hijack the shuttle. Do not directly read, use hijack_speed().
var/hijack_speed = 0
//Antag panel properties
var/show_in_antagpanel = TRUE //This will hide adding this antag type in antag panel, use only for internal subtypes that shouldn't be added directly but still show if possessed by mind
@@ -229,6 +230,13 @@ GLOBAL_LIST_EMPTY(antagonists)
return
antag_memory = new_memo
/// Gets how fast we can hijack the shuttle, return 0 for can not hijack. Defaults to hijack_speed var, override for custom stuff like buffing hijack speed for hijack objectives or something.
/datum/antagonist/proc/hijack_speed()
var/datum/objective/hijack/H = locate() in objectives
if(!isnull(H?.hijack_speed_override))
return H.hijack_speed_override
return hijack_speed
//This one is created by admin tools for custom objectives
/datum/antagonist/custom
antagpanel_category = "Custom"

View File

@@ -286,10 +286,10 @@
var/mob/living/L = owner.current
level_bloodcost = maxBloodVolume * 0.2
//If the blood volume of the bloodsucker is lower than the cost to level up, return and inform the bloodsucker
//TODO: Make this into a radial, or perhaps a tgui next UI
// Purchase Power Prompt
var/list/options = list()
var/list/options = list()
for(var/pickedpower in typesof(/datum/action/bloodsucker))
var/datum/action/bloodsucker/power = pickedpower
// If I don't own it, and I'm allowed to buy it.

View File

@@ -170,7 +170,7 @@
// Warn Feeder about Witnesses...
var/was_unnoticed = TRUE
for(var/mob/living/M in viewers(notice_range, owner))
if(M != owner && M != target && iscarbon(M) && M.mind && !M.has_unlimited_silicon_privilege && !M.eye_blind && !M.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
if(M != owner && M != target && iscarbon(M) && M.mind && !M.silicon_privileges && !M.eye_blind && !M.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
was_unnoticed = FALSE
break
if(was_unnoticed)

View File

@@ -28,9 +28,9 @@
to_chat(owner, "<span class='warning'>Your coffin has been destroyed!</span>")
return FALSE
return TRUE
/datum/action/bloodsucker/gohome/proc/flicker_lights(var/flicker_range, var/beat_volume)
for(var/obj/machinery/light/L in view(flicker_range, get_turf(owner)))
for(var/obj/machinery/light/L in view(flicker_range, get_turf(owner)))
playsound(get_turf(owner), 'sound/effects/singlebeat.ogg', beat_volume, 1)
@@ -45,7 +45,7 @@
flicker_lights(4, 40)
sleep(50)
flicker_lights(4, 60)
for(var/obj/machinery/light/L in view(6, get_turf(owner)))
for(var/obj/machinery/light/L in view(6, get_turf(owner)))
L.flicker(5)
playsound(get_turf(owner), 'sound/effects/singlebeat.ogg', 60, 1)
// ( STEP TWO: Lights OFF? )
@@ -56,7 +56,7 @@
if(!owner)
return
// SEEN?: (effects ONLY if there are witnesses! Otherwise you just POOF)
var/am_seen = FALSE // Do Effects (seen by anyone)
var/drop_item = FALSE // Drop Stuff (seen by non-vamp)
if(isturf(owner.loc)) // Only check if I'm not in a Locker or something.
@@ -65,7 +65,7 @@
if(T && T.lighting_object && T.get_lumcount()>= 0.1)
// B) Check for Viewers
for(var/mob/living/M in viewers(get_turf(owner)))
if(M != owner && isliving(M) && M.mind && !M.has_unlimited_silicon_privilege && !M.eye_blind) // M.client <--- add this in after testing!
if(M != owner && isliving(M) && M.mind && !M.silicon_privileges && !M.eye_blind) // M.client <--- add this in after testing!
am_seen = TRUE
if (!M.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
drop_item = TRUE
@@ -95,12 +95,12 @@
puff.effect_type = /obj/effect/particle_effect/smoke/vampsmoke
puff.set_up(3, 0, get_turf(owner))
puff.start()
//STEP FIVE: Create animal at prev location
var/mob/living/simple_animal/SA = pick(/mob/living/simple_animal/mouse,/mob/living/simple_animal/mouse,/mob/living/simple_animal/mouse, /mob/living/simple_animal/hostile/retaliate/bat) //prob(300) /mob/living/simple_animal/mouse,
new SA (owner.loc)
// TELEPORT: Move to Coffin & Close it!
do_teleport(owner, bloodsuckerdatum.coffin, no_effects = TRUE, forced = TRUE, channel = TELEPORT_CHANNEL_QUANTUM)
do_teleport(owner, bloodsuckerdatum.coffin, no_effects = TRUE, forced = TRUE, channel = TELEPORT_CHANNEL_QUANTUM)
user.resting = TRUE
user.Stun(30,1)
// CLOSE LID: If fail, force me in.
@@ -112,4 +112,4 @@
bloodsuckerdatum.coffin.update_icon()
// Lock Coffin
bloodsuckerdatum.coffin.LockMe(owner)

View File

@@ -14,6 +14,9 @@
message_Trigger = ""//"Whom will you subvert to your will?"
bloodsucker_can_buy = TRUE
must_be_capacitated = TRUE
var/list/hit //current hit, set while power is in use as we can't pass the list as an extra calling argument in registersignal.
/// If set, uses this speed in deciseconds instead of world.tick_lag
var/speed_override
/datum/action/bloodsucker/targeted/haste/CheckCanUse(display_error)
. = ..()
@@ -43,43 +46,46 @@
return TRUE
/datum/action/bloodsucker/targeted/haste/FireTargetedPower(atom/A)
// set waitfor = FALSE <---- DONT DO THIS!We WANT this power to hold up ClickWithPower(), so that we can unlock the power when it's done.
// This is a non-async proc to make sure the power is "locked" until this finishes.
hit = list()
RegisterSignal(owner, COMSIG_MOVABLE_MOVED, .proc/on_move)
var/mob/living/user = owner
var/turf/T = isturf(A) ? A : get_turf(A)
// Pulled? Not anymore.
owner.pulledby = null
// Step One: Heatseek toward Target's Turf
walk_to(owner, T, 0, 0.01, 20) // NOTE: this runs in the background! to cancel it, you need to use walk(owner.current,0), or give them a new path.
user.pulledby?.stop_pulling()
// Go to target turf
// DO NOT USE WALK TO.
playsound(get_turf(owner), 'sound/weapons/punchmiss.ogg', 25, 1, -1)
var/safety = 20
while(get_turf(owner) != T && safety > 0 && !(isliving(target) && target.Adjacent(owner)))
user.canmove = FALSE //Dont move while doing the thing, or itll break
safety --
// Did I get knocked down?
if(owner && owner.incapacitated(ignore_restraints=TRUE, ignore_grab=TRUE))// owner.incapacitated())
// We're gonna cancel. But am I on the ground? Spin me!
if(user.resting)
var/send_dir = get_dir(owner, T)
new /datum/forced_movement(owner, get_ranged_target_turf(owner, send_dir, 1), 1, FALSE)
owner.spin(10)
var/safety = get_dist(user, T) * 3 + 1
var/consequetive_failures = 0
var/speed = isnull(speed_override)? world.tick_lag : speed_override
while(--safety && (get_turf(user) != T))
var/success = step_towards(user, T) //This does not try to go around obstacles.
if(!success)
success = step_to(user, T) //this does
if(!success)
if(++consequetive_failures >= 3) //if 3 steps don't work
break //just stop
else
consequetive_failures = 0
if(user.resting)
user.setDir(turn(user.dir, 90)) //down? spin2win :^)
if(user.incapacitated(ignore_restraints = TRUE, ignore_grab = TRUE)) //actually down? stop.
break
// Spin/Stun people we pass.
//var/mob/living/newtarget = locate(/mob/living) in oview(1, owner)
var/list/mob/living/foundtargets = list()
for(var/mob/living/newtarget in oview(1, owner))
if (newtarget && newtarget != target && !(newtarget in foundtargets))//!newtarget.IsKnockdown())
if (rand(0, 5) < level_current)
playsound(get_turf(newtarget), "sound/weapons/punch[rand(1,4)].ogg", 15, 1, -1)
newtarget.Knockdown(10 + level_current * 5)
if(newtarget.IsStun())
newtarget.spin(10,1)
if (rand(0,4))
newtarget.drop_all_held_items()
foundtargets += newtarget
sleep(1)
if(user)
user.update_canmove() //Let the poor guy move again
if(success) //don't sleep if we failed to move.
sleep(speed)
UnregisterSignal(owner, COMSIG_MOVABLE_MOVED)
hit = null
user.update_canmove()
/datum/action/bloodsucker/targeted/haste/DeactivatePower(mob/living/user = owner, mob/living/target)
..() // activate = FALSE
user.update_canmove()
/datum/action/bloodsucker/targeted/haste/proc/on_move()
for(var/mob/living/L in dview(1, get_turf(owner)))
if(!hit[L] && (L != owner))
hit[L] = TRUE
playsound(L, "sound/weapons/punch[rand(1,4)].ogg", 15, 1, -1)
L.Knockdown(10 + level_current * 5, override_hardstun = 0.1)
L.spin(10, 1)

View File

@@ -54,8 +54,8 @@
REMOVE_TRAIT(user, TRAIT_VIRUSIMMUNE, "bloodsucker")
var/obj/item/organ/heart/vampheart/H = user.getorganslot(ORGAN_SLOT_HEART)
var/obj/item/organ/eyes/vassal/bloodsucker/E = user.getorganslot(ORGAN_SLOT_EYES)
E.flash_protect = 0
E.flash_protect = 0
// WE ARE ALIVE! //
bloodsuckerdatum.poweron_masquerade = TRUE
while(bloodsuckerdatum && ContinueActive(user))

View File

@@ -5,7 +5,6 @@
var/special_role = ROLE_BROTHER
var/datum/team/brother_team/team
antag_moodlet = /datum/mood_event/focused
can_hijack = HIJACK_HIJACKER
/datum/antagonist/brother/create_team(datum/team/brother_team/new_team)
if(!new_team)

View File

@@ -120,7 +120,7 @@
hierophant_network.Grant(current)
current.throw_alert("clockinfo", /obj/screen/alert/clockwork/infodump)
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
if(G.active && ishuman(current))
if(G && G.active && ishuman(current))
current.add_overlay(mutable_appearance('icons/effects/genetics.dmi', "servitude", -MUTATIONS_LAYER))
/datum/antagonist/clockcult/remove_innate_effects(mob/living/mob_override)
@@ -174,9 +174,12 @@
log_admin("[key_name(admin)] has made [new_owner.current] into a servant of Ratvar.")
/datum/antagonist/clockcult/admin_remove(mob/user)
remove_servant_of_ratvar(owner.current, TRUE)
message_admins("[key_name_admin(user)] has removed clockwork servant status from [owner.current].")
log_admin("[key_name(user)] has removed clockwork servant status from [owner.current].")
var/mob/target = owner.current
if(!target)
return
remove_servant_of_ratvar(target, TRUE)
message_admins("[key_name_admin(user)] has removed clockwork servant status from [target].")
log_admin("[key_name(user)] has removed clockwork servant status from [target].")
/datum/antagonist/clockcult/get_admin_commands()
. = ..()

View File

@@ -5,49 +5,49 @@ is currently following.
*/
GLOBAL_LIST_INIT(disease_ability_singletons, list(
new /datum/disease_ability/action/cough,
new /datum/disease_ability/action/sneeze,
new /datum/disease_ability/action/infect,
new /datum/disease_ability/symptom/mild/cough,
new /datum/disease_ability/symptom/mild/sneeze,
new /datum/disease_ability/symptom/medium/shedding,
new /datum/disease_ability/symptom/medium/beard,
new /datum/disease_ability/symptom/medium/hallucigen,
new /datum/disease_ability/symptom/medium/choking,
new /datum/disease_ability/symptom/medium/confusion,
new /datum/disease_ability/symptom/medium/vomit,
new /datum/disease_ability/symptom/medium/voice_change,
new /datum/disease_ability/symptom/medium/visionloss,
new /datum/disease_ability/symptom/medium/deafness,
new /datum/disease_ability/symptom/powerful/narcolepsy,
new /datum/disease_ability/symptom/medium/fever,
new /datum/disease_ability/symptom/medium/shivering,
new /datum/disease_ability/symptom/medium/headache,
new /datum/disease_ability/symptom/medium/nano_boost,
new /datum/disease_ability/symptom/medium/nano_destroy,
new /datum/disease_ability/symptom/medium/viraladaptation,
new /datum/disease_ability/symptom/medium/viralevolution,
new /datum/disease_ability/symptom/medium/vitiligo,
new /datum/disease_ability/symptom/medium/revitiligo,
new /datum/disease_ability/symptom/medium/itching,
new /datum/disease_ability/symptom/medium/heal/weight_loss,
new /datum/disease_ability/symptom/medium/heal/sensory_restoration,
new /datum/disease_ability/symptom/medium/heal/mind_restoration,
new /datum/disease_ability/symptom/powerful/fire,
new /datum/disease_ability/symptom/powerful/flesh_eating,
// new /datum/disease_ability/symptom/powerful/genetic_mutation,
new /datum/disease_ability/symptom/powerful/inorganic_adaptation,
new /datum/disease_ability/symptom/powerful/heal/starlight,
new /datum/disease_ability/symptom/powerful/heal/oxygen,
new /datum/disease_ability/symptom/powerful/heal/chem,
new /datum/disease_ability/symptom/powerful/heal/metabolism,
new /datum/disease_ability/symptom/powerful/heal/dark,
new /datum/disease_ability/symptom/powerful/heal/water,
new /datum/disease_ability/symptom/powerful/heal/plasma,
new /datum/disease_ability/symptom/powerful/heal/radiation,
new /datum/disease_ability/symptom/powerful/heal/coma,
new /datum/disease_ability/symptom/powerful/youth
))
new /datum/disease_ability/action/cough,
new /datum/disease_ability/action/sneeze,
new /datum/disease_ability/action/infect,
new /datum/disease_ability/symptom/mild/cough,
new /datum/disease_ability/symptom/mild/sneeze,
new /datum/disease_ability/symptom/medium/shedding,
new /datum/disease_ability/symptom/medium/beard,
new /datum/disease_ability/symptom/medium/hallucigen,
new /datum/disease_ability/symptom/medium/choking,
new /datum/disease_ability/symptom/medium/confusion,
new /datum/disease_ability/symptom/medium/vomit,
new /datum/disease_ability/symptom/medium/voice_change,
new /datum/disease_ability/symptom/medium/visionloss,
new /datum/disease_ability/symptom/medium/deafness,
new /datum/disease_ability/symptom/powerful/narcolepsy,
new /datum/disease_ability/symptom/medium/fever,
new /datum/disease_ability/symptom/medium/shivering,
new /datum/disease_ability/symptom/medium/headache,
new /datum/disease_ability/symptom/medium/nano_boost,
new /datum/disease_ability/symptom/medium/nano_destroy,
new /datum/disease_ability/symptom/medium/viraladaptation,
new /datum/disease_ability/symptom/medium/viralevolution,
new /datum/disease_ability/symptom/medium/disfiguration,
new /datum/disease_ability/symptom/medium/polyvitiligo,
new /datum/disease_ability/symptom/medium/itching,
new /datum/disease_ability/symptom/medium/heal/weight_loss,
new /datum/disease_ability/symptom/medium/heal/sensory_restoration,
new /datum/disease_ability/symptom/medium/heal/mind_restoration,
new /datum/disease_ability/symptom/powerful/fire,
new /datum/disease_ability/symptom/powerful/flesh_eating,
new /datum/disease_ability/symptom/powerful/genetic_mutation,
new /datum/disease_ability/symptom/powerful/inorganic_adaptation,
new /datum/disease_ability/symptom/powerful/heal/starlight,
new /datum/disease_ability/symptom/powerful/heal/oxygen,
new /datum/disease_ability/symptom/powerful/heal/chem,
new /datum/disease_ability/symptom/powerful/heal/metabolism,
new /datum/disease_ability/symptom/powerful/heal/dark,
new /datum/disease_ability/symptom/powerful/heal/water,
new /datum/disease_ability/symptom/powerful/heal/plasma,
new /datum/disease_ability/symptom/powerful/heal/radiation,
new /datum/disease_ability/symptom/powerful/heal/coma,
new /datum/disease_ability/symptom/powerful/youth
))
/datum/disease_ability
var/name
@@ -57,7 +57,7 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
var/short_desc = ""
var/long_desc = ""
var/stat_block = ""
var/threshold_block = list()
var/threshold_block = ""
var/category = ""
var/list/symptoms
@@ -76,7 +76,7 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
resistance += initial(S.resistance)
stage_speed += initial(S.stage_speed)
transmittable += initial(S.transmittable)
threshold_block += initial(S.threshold_desc)
threshold_block += "<br><br>[initial(S.threshold_desc)]"
stat_block = "Resistance: [resistance]<br>Stealth: [stealth]<br>Stage Speed: [stage_speed]<br>Transmissibility: [transmittable]<br><br>"
if(symptoms.len == 1) //lazy boy's dream
name = initial(S.name)
@@ -106,10 +106,8 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
for(var/T in symptoms)
var/datum/symptom/S = new T()
SD.symptoms += S
S.OnAdd(SD)
if(SD.processing)
if(S.Start(SD))
S.next_activation = world.time + rand(S.symptom_delay_min * 10, S.symptom_delay_max * 10)
S.Start(SD)
SD.Refresh()
for(var/T in actions)
var/datum/action/A = new T()
@@ -136,7 +134,6 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
var/datum/symptom/S = locate(T) in SD.symptoms
if(S)
SD.symptoms -= S
S.OnRemove(SD)
if(SD.processing)
S.End(SD)
qdel(S)
@@ -296,7 +293,6 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
cost = 8
category = "Symptom (Strong+)"
/******MILD******/
/datum/disease_ability/symptom/mild/cough
@@ -377,11 +373,11 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
/datum/disease_ability/symptom/medium/viralevolution
symptoms = list(/datum/symptom/viralevolution)
/datum/disease_ability/symptom/medium/vitiligo
symptoms = list(/datum/symptom/vitiligo)
/datum/disease_ability/symptom/medium/polyvitiligo
symptoms = list(/datum/symptom/polyvitiligo)
/datum/disease_ability/symptom/medium/revitiligo
symptoms = list(/datum/symptom/revitiligo)
/datum/disease_ability/symptom/medium/disfiguration
symptoms = list(/datum/symptom/disfiguration)
/datum/disease_ability/symptom/medium/itching
symptoms = list(/datum/symptom/itching)
@@ -409,11 +405,9 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
/datum/disease_ability/symptom/powerful/flesh_eating
symptoms = list(/datum/symptom/flesh_eating)
/*
/datum/disease_ability/symptom/powerful/genetic_mutation
symptoms = list(/datum/symptom/genetic_mutation)
cost = 8
*/
/datum/disease_ability/symptom/powerful/inorganic_adaptation
symptoms = list(/datum/symptom/inorganic_adaptation)
@@ -457,4 +451,4 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
/datum/disease_ability/symptom/powerful/heal/coma
symptoms = list(/datum/symptom/heal/coma)
short_desc = "Cause victims to fall into a healing coma when hurt."
long_desc = "Cause victims to fall into a healing coma when hurt."
long_desc = "Cause victims to fall into a healing coma when hurt."

View File

@@ -12,7 +12,6 @@
var/list/name_source
show_in_antagpanel = FALSE
antag_moodlet = /datum/mood_event/focused
can_hijack = HIJACK_PREVENT
/datum/antagonist/ert/on_gain()
update_name()

View File

@@ -3,7 +3,7 @@
var/obj/item/claymore/highlander/sword
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
can_hijack = HIJACK_HIJACKER
hijack_speed = 2 //if you kill everyone and actually haev a hand to hijack with, you win??
/datum/antagonist/highlander/apply_innate_effects(mob/living/mob_override)
var/mob/living/L = owner.current || mob_override

View File

@@ -8,11 +8,6 @@
var/give_objectives = TRUE
var/give_equipment = TRUE
/datum/antagonist/ninja/New()
if(helping_station)
can_hijack = HIJACK_PREVENT
. = ..()
/datum/antagonist/ninja/apply_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_ninja_icons_added(M)
@@ -135,8 +130,6 @@
adj = "objectiveless"
else
return
if(helping_station)
can_hijack = HIJACK_PREVENT
new_owner.assigned_role = ROLE_NINJA
new_owner.special_role = ROLE_NINJA
new_owner.add_antag_datum(src)

View File

@@ -8,7 +8,6 @@
var/always_new_team = FALSE //If not assigned a team by default ops will try to join existing ones, set this to TRUE to always create new team.
var/send_to_spawnpoint = TRUE //Should the user be moved to default spawnpoint.
var/nukeop_outfit = /datum/outfit/syndicate
can_hijack = HIJACK_HIJACKER //Alternative way to wipe out the station.
/datum/antagonist/nukeop/proc/update_synd_icons_added(mob/living/M)
var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS]

View File

@@ -4,7 +4,6 @@
show_in_antagpanel = FALSE
var/datum/objective/mission
var/datum/team/ert/ert_team
can_hijack = HIJACK_PREVENT
/datum/antagonist/official/greet()
to_chat(owner, "<B><font size=3 color=red>You are a CentCom Official.</font></B>")

View File

@@ -13,7 +13,7 @@
var/should_give_codewords = TRUE
var/should_equip = TRUE
var/traitor_kind = TRAITOR_HUMAN //Set on initial assignment
can_hijack = HIJACK_HIJACKER
hijack_speed = 0.5 //10 seconds per hijack stage by default
/datum/antagonist/traitor/on_gain()
if(owner.current && isAI(owner.current))
@@ -60,6 +60,7 @@
message = GLOB.syndicate_code_response_regex.Replace(message, "<span class='red'>$1</span>")
hearing_args[HEARING_RAW_MESSAGE] = message
// needs to be refactored to base /datum/antagonist sometime..
/datum/antagonist/traitor/proc/add_objective(datum/objective/O)
objectives += O

View File

@@ -2,7 +2,6 @@
name = "Wishgranter Avatar"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
can_hijack = HIJACK_HIJACKER
/datum/antagonist/wishgranter/proc/forge_objectives()
var/datum/objective/hijack/hijack = new

View File

@@ -12,7 +12,6 @@
var/move_to_lair = TRUE
var/outfit_type = /datum/outfit/wizard
var/wiz_age = WIZARD_AGE_MIN /* Wizards by nature cannot be too young. */
can_hijack = HIJACK_HIJACKER
/datum/antagonist/wizard/on_gain()
register()

View File

@@ -229,7 +229,7 @@
. += "<span class='notice'>Alt-click to [locked ? "unlock" : "lock"] the interface.</span>"
/obj/machinery/airalarm/ui_status(mob/user)
if(user.has_unlimited_silicon_privilege && aidisabled)
if(hasSiliconAccessInArea(user) && aidisabled)
to_chat(user, "AI control has been disabled.")
else if(!shorted)
return ..()
@@ -245,7 +245,7 @@
/obj/machinery/airalarm/ui_data(mob/user)
var/data = list(
"locked" = locked,
"siliconUser" = user.has_unlimited_silicon_privilege || hasSiliconAccessInArea(user),
"siliconUser" = hasSiliconAccessInArea(user),
"emagged" = (obj_flags & EMAGGED ? 1 : 0),
"danger_level" = danger_level,
)
@@ -288,7 +288,7 @@
"danger_level" = cur_tlv.get_danger_level(environment.gases[gas_id] * partial_pressure)
))
if(!locked || user.has_unlimited_silicon_privilege || hasSiliconAccessInArea(user))
if(!locked || hasSiliconAccessInArea(user, PRIVILEDGES_SILICON|PRIVILEDGES_DRONE))
data["vents"] = list()
for(var/id_tag in A.air_vent_names)
var/long_name = A.air_vent_names[id_tag]
@@ -368,12 +368,14 @@
/obj/machinery/airalarm/ui_act(action, params)
if(..() || buildstage != 2)
return
if((locked && !usr.has_unlimited_silicon_privilege && !hasSiliconAccessInArea(usr)) || (usr.has_unlimited_silicon_privilege && aidisabled))
var/silicon_access = hasSiliconAccessInArea(usr)
var/bot_priviledges = silicon_access || (usr.silicon_privileges & PRIVILEDGES_DRONE)
if((locked && !bot_priviledges) || (silicon_access && aidisabled))
return
var/device_id = params["id_tag"]
switch(action)
if("lock")
if(usr.has_unlimited_silicon_privilege && !wires.is_cut(WIRE_IDSCAN))
if(bot_priviledges && !wires.is_cut(WIRE_IDSCAN))
locked = !locked
. = TRUE
if("power", "toggle_filter", "widenet", "scrubbing")

View File

@@ -24,13 +24,13 @@
export_types = list(/obj/structure/ore_box)
/datum/export/large/crate/wood
cost = 140 //
cost = 140
unit_name = "wooden crate"
export_types = list(/obj/structure/closet/crate/wooden)
exclude_types = list()
/datum/export/large/barrel
cost = 500 //150 to make meaning proffit of 350
cost = 300 //double the wooden cost of a coffin.
unit_name = "wooden barrel"
export_types = list(/obj/structure/fermenting_barrel)
@@ -40,19 +40,11 @@
export_types = list(/obj/structure/closet/crate/coffin)
/datum/export/large/reagent_dispenser
cost = 100 // +0-400 depending on amount of reagents left
var/contents_cost = 400
/datum/export/large/reagent_dispenser/get_cost(obj/O)
var/obj/structure/reagent_dispensers/D = O
var/ratio = D.reagents.total_volume / D.reagents.maximum_volume
return ..() + round(contents_cost * ratio)
cost = 100
/datum/export/large/reagent_dispenser/water
unit_name = "watertank"
export_types = list(/obj/structure/reagent_dispensers/watertank)
contents_cost = 200
/datum/export/large/reagent_dispenser/fuel
unit_name = "fueltank"
@@ -60,7 +52,6 @@
/datum/export/large/reagent_dispenser/beer
unit_name = "beer keg"
contents_cost = 700
export_types = list(/obj/structure/reagent_dispensers/beerkeg)
/datum/export/large/pipedispenser

View File

@@ -65,11 +65,6 @@
material_id = MAT_TITANIUM
message = "cm3 of titanium"
/datum/export/material/plastitanium
cost = 165 // plasma + titanium costs
material_id = MAT_TITANIUM // code can only check for one material_id; plastitanium is half plasma, half titanium
message = "cm3 of plastitanium"
/datum/export/material/plastic
cost = 5
material_id = MAT_PLASTIC

View File

@@ -67,6 +67,16 @@
message = "of plasteel"
export_types = list(/obj/item/stack/sheet/plasteel)
/datum/export/material/plastitanium
cost = 165 // plasma + titanium costs
export_types = list(/obj/item/stack/sheet/mineral/plastitanium)
message = "of plastitanium"
/datum/export/material/plastitanium_glass
cost = 168 // plasma + titanium + glass costs
export_types = list(/obj/item/stack/sheet/plastitaniumglass)
message = "of plastitanium glass"
// 1 glass + 0.5 metal, cost is rounded up.
/datum/export/stack/rglass
cost = 6

View File

@@ -199,6 +199,31 @@
unit_name = "advanced shotgun shell"
export_types = list(/obj/item/ammo_casing/shotgun/dragonsbreath, /obj/item/ammo_casing/shotgun/meteorslug, /obj/item/ammo_casing/shotgun/pulseslug, /obj/item/ammo_casing/shotgun/frag12, /obj/item/ammo_casing/shotgun/ion, /obj/item/ammo_casing/shotgun/laserslug)
/////////////////////////
//Bow and Arrows/////////
/////////////////////////
/datum/export/weapon/bows
cost = 450
unit_name = "bow"
export_types = list(/obj/item/gun/ballistic/bow)
/datum/export/weapon/arrows
cost = 150
unit_name = "arrow"
export_types = list(/obj/item/ammo_casing/caseless/arrow, /obj/item/ammo_casing/caseless/arrow/bone, /obj/item/ammo_casing/caseless/arrow/ashen)
/datum/export/weapon/bow_teaching
cost = 500
unit_name = "stone tablets"
export_types = list(/obj/item/book/granter/crafting_recipe/bone_bow)
/datum/export/weapon/quiver
cost = 100
unit_name = "quiver"
export_types = list(/obj/item/storage/belt/quiver)
/////////////////////////
//The Traitor Sell Outs//
/////////////////////////

View File

@@ -97,7 +97,7 @@
var/canBeacon = beacon && (isturf(beacon.loc) || ismob(beacon.loc))//is the beacon in a valid location?
var/list/data = list()
data["locked"] = locked//swipe an ID to unlock
data["siliconUser"] = user.has_unlimited_silicon_privilege
data["siliconUser"] = hasSiliconAccessInArea(user)
data["beaconzone"] = beacon ? get_area(beacon) : ""//where is the beacon located? outputs in the tgui
data["usingBeacon"] = usingBeacon //is the mode set to deliver to the beacon or the cargobay?
data["canBeacon"] = !usingBeacon || canBeacon //is the mode set to beacon delivery, and is the beacon in a valid location?
@@ -183,6 +183,7 @@
LZ = pick(empty_turfs)
if (SO.pack.cost <= SSshuttle.points && LZ)//we need to call the cost check again because of the CHECK_TICK call
SSshuttle.points -= SO.pack.cost
SSblackbox.record_feedback("nested tally", "cargo_imports", 1, list("[SO.pack.cost]", "[SO.pack.name]"))
new /obj/effect/abstract/DPtarget(LZ, podType, SO)
. = TRUE
update_icon()
@@ -196,6 +197,7 @@
CHECK_TICK
if(empty_turfs && empty_turfs.len)
SSshuttle.points -= SO.pack.cost * (0.72*MAX_EMAG_ROCKETS)
SSblackbox.record_feedback("nested tally", "cargo_imports", MAX_EMAG_ROCKETS, list("[SO.pack.cost * 0.72]", "[SO.pack.name]"))
SO.generateRequisition(get_turf(src))
for(var/i in 1 to MAX_EMAG_ROCKETS)
var/LZ = pick(empty_turfs)

View File

@@ -2,7 +2,7 @@
var/name = "Crate"
var/group = ""
var/hidden = FALSE //Aka emag only
var/contraband = FALSE //Hacking the console with a multitool
var/contraband = FALSE //Requires a hacked console UNLESS DropPodOnly = TRUE, in which case it requires an emag
var/cost = 700 // Minimum cost, or infinite points are possible.
var/access = FALSE //What access does the Crate itself need?
var/access_any = FALSE //Do we care about access?

View File

@@ -178,7 +178,7 @@
/datum/supply_pack/security/armory/russian
name = "Russian Surplus Crate"
desc = "Hello Comrade, we have the most modern russian military equipment the black market can offer, for the right price of course. Sadly we couldnt remove the lock so it requires Armory access to open."
cost = 5000
cost = 7500
contraband = TRUE
contains = list(/obj/item/reagent_containers/food/snacks/rationpack,
/obj/item/ammo_box/a762,
@@ -192,12 +192,11 @@
/obj/item/clothing/mask/russian_balaclava,
/obj/item/clothing/head/helmet/rus_ushanka,
/obj/item/clothing/suit/armor/vest/russian_coat,
/obj/item/gun/ballistic/shotgun/boltaction,
/obj/item/gun/ballistic/shotgun/boltaction)
crate_name = "surplus military crate"
/datum/supply_pack/security/armory/russian/fill(obj/structure/closet/crate/C)
for(var/i in 1 to 10)
for(var/i in 1 to 5)
var/item = pick(contains)
new item(C)
@@ -218,7 +217,7 @@
crate_name = "swat crate"
/datum/supply_pack/security/armory/swattasers //Lesser AEG tbh
name = "SWAT tatical tasers Crate"
name = "SWAT tactical tasers Crate"
desc = "Contains two tactical energy gun, these guns are able to tase, disable and lethal as well as hold a seclight. Requires Armory access to open."
cost = 7000
contains = list(/obj/item/gun/energy/e_gun/stun,

View File

@@ -63,6 +63,7 @@
/obj/item/toy/cards/deck/syndicate,
/obj/item/reagent_containers/food/drinks/bottle/absinthe,
/obj/item/clothing/under/syndicate/tacticool,
/obj/item/clothing/under/syndicate/skirt,
/obj/item/clothing/under/syndicate,
/obj/item/suppressor,
/obj/item/storage/fancy/cigarettes/cigpack_syndicate,

View File

@@ -130,7 +130,7 @@
/datum/supply_pack/emergency/bomb
name = "Explosive Emergency Crate"
desc = "Science gone bonkers? Beeping behind the airlock? Buy now and be the hero the station des... I mean needs! (Time not included.)"
desc = "Science gone bonkers? Beeping behind the airlock? Buy now and become the hero the station des... I mean needs! Time not included, but a full bomb suit and hood, as well as a mask and defusal kit are! Non-Nuclear ordnances only."
cost = 1500
contains = list(/obj/item/clothing/head/bomb_hood,
/obj/item/clothing/suit/bomb_suit,

View File

@@ -88,6 +88,7 @@
/obj/item/clothing/mask/gas,
/obj/item/clothing/suit/space/hardsuit/medical)
crate_name = "medical hardsuit"
crate_type = /obj/structure/closet/crate/secure/medical
/datum/supply_pack/medical/supplies
name = "Medical Supplies Crate"
@@ -125,7 +126,7 @@
/obj/item/reagent_containers/medspray/synthflesh,
/obj/item/reagent_containers/medspray/sterilizine)
crate_name = "medco surgery tools"
crate_type = /obj/structure/closet/crate/medical
crate_type = /obj/structure/closet/crate/secure/medical
/datum/supply_pack/medical/surgery
name = "Surgical Supplies Crate"

View File

@@ -193,6 +193,7 @@
/obj/item/reagent_containers/food/snacks/meat/slab/bear,
/obj/item/reagent_containers/food/snacks/meat/slab/xeno,
/obj/item/reagent_containers/food/snacks/meat/slab/spider,
/obj/item/reagent_containers/food/snacks/meat/rawcrab,
/obj/item/reagent_containers/food/snacks/meat/rawbacon,
/obj/item/reagent_containers/food/snacks/spiderleg,
/obj/item/reagent_containers/food/snacks/carpmeat,
@@ -327,7 +328,7 @@
/datum/supply_pack/organic/seeds
name = "Seeds Crate"
desc = "Big things have small beginnings. Contains thirteen different seeds."
desc = "Big things have small beginnings. Contains fourteen different seeds."
cost = 1250
contains = list(/obj/item/seeds/chili,
/obj/item/seeds/berry,

View File

@@ -84,7 +84,7 @@
/datum/supply_pack/science/glasswork
name = "Glass Blower Kit Crate"
desc = "Learn and make glassworks of usefull things for a profit! Contains glassworking tools and blowing rods. Glass not included."
desc = "Learn and make glassworks of useful things for a profit! Contains glassworking tools and blowing rods. Glass not included."
cost = 1000
contains = list(/obj/item/glasswork/glasskit,
/obj/item/glasswork/glasskit,
@@ -111,9 +111,9 @@
/datum/supply_pack/science/nuke_b_gone
name = "Nuke Defusal Kit"
desc = "Contains set of tools to defuse a nuke."
cost = 7500 //Usefull for traitors/nukies that fucked up
cost = 7500 //Useful for traitors/nukies that fucked up
dangerous = TRUE
DropPodOnly = TRUE
hidden = TRUE
contains = list(/obj/item/nuke_core_container/nt,
/obj/item/screwdriver/nuke/nt,
/obj/item/paper/guides/nt/nuke_instructions)
@@ -194,7 +194,7 @@
/datum/supply_pack/science/supermater
name = "Supermatter Extraction Tools Crate"
desc = "Contains a set of tools to extract a sliver of supermatter. Consult your CE today!"
cost = 7500 //Usefull for traitors that fucked up
cost = 7500 //Useful for traitors that fucked up
hidden = TRUE
contains = list(/obj/item/nuke_core_container/supermatter,
/obj/item/scalpel/supermatter,

View File

@@ -103,7 +103,7 @@
desc = "An old russian Minutemen crate, comes with a full russian outfit, a mosin and a stripper clip."
contraband = TRUE
access = FALSE
cost = 5500 //
cost = 6500 //
contains = list(/obj/item/clothing/suit/armor/navyblue/russian,
/obj/item/clothing/shoes/combat,
/obj/item/clothing/head/ushanka,

View File

@@ -737,13 +737,13 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
message_admins("<span class='adminnotice'>Proxy Detection: [key_name_admin(src)] IP intel rated [res.intel*100]% likely to be a Proxy/VPN.</span>")
ip_intel = res.intel
/client/Click(atom/object, atom/location, control, params)
/client/Click(atom/object, atom/location, control, params, ignore_spam = FALSE)
var/ab = FALSE
var/list/L = params2list(params)
if (object && object == middragatom && L["left"])
ab = max(0, 5 SECONDS-(world.time-middragtime)*0.1)
var/mcl = CONFIG_GET(number/minute_click_limit)
if (!holder && mcl)
if (!holder && !ignore_spam && mcl)
var/minute = round(world.time, 600)
if (!clicklimiter)
clicklimiter = new(LIMITER_SIZE)
@@ -768,7 +768,7 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
return
var/scl = CONFIG_GET(number/second_click_limit)
if (!holder && scl)
if (!holder && !ignore_spam && scl)
var/second = round(world.time, 10)
if (!clicklimiter)
clicklimiter = new(LIMITER_SIZE)

View File

@@ -53,7 +53,8 @@
H.adjust_blurriness(1)
H.visible_message("<span class='warning'>[H] is creamed by [src]!</span>", "<span class='userdanger'>You've been creamed by [src]!</span>")
playsound(H, "desceration", 50, TRUE)
reagents.trans_to(H,15) //Cream pie combat
if(!H.is_mouth_covered())
reagents.trans_to(H,15) //Cream pie combat
if(!H.creamed) // one layer at a time
H.add_overlay(creamoverlay)
H.creamed = TRUE

View File

@@ -319,7 +319,7 @@
/obj/machinery/icecream_vat/proc/replace_beaker(mob/living/user, obj/item/reagent_containers/new_beaker)
if(beaker)
beaker.forceMove(drop_location())
if(user && Adjacent(user) && !issiliconoradminghost(user))
if(user && Adjacent(user) && user.can_hold_items())
user.put_in_hands(beaker)
if(new_beaker)
beaker = new_beaker

View File

@@ -122,13 +122,10 @@
to_chat(usr, "<span class='warning'>Error: Insufficient credits for [prize.equipment_name] on [I]!</span>")
flick(icon_deny, src)
else
if (I.mining_points -= prize.cost)
to_chat(usr, "<span class='notice'>[src] clanks to life briefly before vending [prize.equipment_name]!</span>")
new prize.equipment_path(src.loc)
SSblackbox.record_feedback("nested tally", "mining_equipment_bought", 1, list("[type]", "[prize.equipment_path]"))
else
to_chat(usr, "<span class='warning'>Error: Transaction failure, please try again later!</span>")
flick(icon_deny, src)
I.mining_points -= prize.cost
to_chat(usr, "<span class='notice'>[src] clanks to life briefly before vending [prize.equipment_name]!</span>")
new prize.equipment_path(src.loc)
SSblackbox.record_feedback("nested tally", "mining_equipment_bought", 1, list("[type]", "[prize.equipment_path]"))
else
to_chat(usr, "<span class='warning'>Error: An ID with a registered account is required!</span>")
flick(icon_deny, src)

View File

@@ -6,7 +6,7 @@
var/preferred_form = null
if(IsAdminGhost(src))
has_unlimited_silicon_privilege = 1
silicon_privileges = ALL
if(client.prefs.unlock_content)
preferred_form = client.prefs.ghost_form

View File

@@ -834,12 +834,31 @@
update_inv_handcuffed()
update_hud_handcuffed()
/mob/living/carbon/proc/can_defib()
var/tlimit = DEFIB_TIME_LIMIT * 10
var/obj/item/organ/heart = getorgan(/obj/item/organ/heart)
if(suiciding || hellbound || HAS_TRAIT(src, TRAIT_HUSK))
return
if((world.time - timeofdeath) > tlimit)
return
if((getBruteLoss() >= MAX_REVIVE_BRUTE_DAMAGE) || (getFireLoss() >= MAX_REVIVE_FIRE_DAMAGE))
return
if(!heart || (heart.organ_flags & ORGAN_FAILING))
return
var/obj/item/organ/brain/BR = getorgan(/obj/item/organ/brain)
if(QDELETED(BR) || BR.brain_death || (BR.organ_flags & ORGAN_FAILING) || suiciding)
return
return TRUE
/mob/living/carbon/fully_heal(admin_revive = FALSE)
if(reagents)
reagents.clear_reagents()
var/obj/item/organ/brain/B = getorgan(/obj/item/organ/brain)
if(B)
B.brain_death = FALSE
for(var/O in internal_organs)
var/obj/item/organ/organ = O
organ.setOrganDamage(0)
for(var/thing in diseases)
var/datum/disease/D = thing
if(D.severity != DISEASE_SEVERITY_POSITIVE)
@@ -852,7 +871,8 @@
qdel(R)
update_handcuffed()
if(reagents)
reagents.addiction_list = list()
for(var/addi in reagents.addiction_list)
reagents.remove_addiction(addi)
cure_all_traumas(TRAUMA_RESILIENCE_MAGIC)
..()
// heal ears after healing traits, since ears check TRAIT_DEAF trait
@@ -987,3 +1007,6 @@
return TRUE
if(isclothing(wear_mask) && (wear_mask.clothing_flags & SCAN_REAGENTS))
return TRUE
/mob/living/carbon/can_hold_items()
return TRUE

View File

@@ -803,7 +803,7 @@
hud_used.staminas?.update_icon_state()
hud_used.staminabuffer?.update_icon_state()
/mob/living/carbon/human/fully_heal(admin_revive = 0)
/mob/living/carbon/human/fully_heal(admin_revive = FALSE)
if(admin_revive)
regenerate_limbs()
regenerate_organs()
@@ -823,9 +823,6 @@
. += dna.species.check_weakness(weapon, attacker)
/mob/living/carbon/human/is_literate()
return 1
/mob/living/carbon/human/can_hold_items()
return TRUE
/mob/living/carbon/human/update_gravity(has_gravity,override = 0)
@@ -834,7 +831,7 @@
..()
/mob/living/carbon/human/vomit(lost_nutrition = 10, blood = 0, stun = 1, distance = 0, message = 1, toxic = 0)
if(blood && (NOBLOOD in dna.species.species_traits))
if(blood && dna?.species && (NOBLOOD in dna.species.species_traits))
if(message)
visible_message("<span class='warning'>[src] dry heaves!</span>", \
"<span class='userdanger'>You try to throw up, but there's nothing in your stomach!</span>")

View File

@@ -20,7 +20,7 @@
/mob/living/carbon/human/proc/checkarmor(obj/item/bodypart/def_zone, d_type)
if(!d_type)
if(!d_type || !def_zone)
return 0
var/protection = 0
var/list/body_parts = list(head, wear_mask, wear_suit, w_uniform, back, gloves, shoes, belt, s_store, glasses, ears, wear_id, wear_neck) //Everything but pockets. Pockets are l_store and r_store. (if pockets were allowed, putting something armored, gloves or hats for example, would double up on the armor)

View File

@@ -135,7 +135,7 @@
RegisterSignal(owner, COMSIG_CLICK_SHIFT, .proc/examinate_check)
RegisterSignal(src, COMSIG_ATOM_HEARER_IN_VIEW, .proc/include_owner)
RegisterSignal(owner, COMSIG_LIVING_REGENERATE_LIMBS, .proc/unlist_head)
RegisterSignal(owner, COMSIG_LIVING_FULLY_HEAL, .proc/retrieve_head)
RegisterSignal(owner, COMSIG_LIVING_REVIVE, .proc/retrieve_head)
/obj/item/dullahan_relay/proc/examinate_check(atom/source, mob/user)
if(user.client.eye == src)
@@ -148,8 +148,9 @@
/obj/item/dullahan_relay/proc/unlist_head(datum/source, noheal = FALSE, list/excluded_limbs)
excluded_limbs |= BODY_ZONE_HEAD // So we don't gib when regenerating limbs.
/obj/item/dullahan_relay/proc/retrieve_head(datum/source, admin_revive = FALSE)
if(admin_revive) //retrieving the owner's head for ahealing purposes.
//Retrieving the owner's head for better ahealing.
/obj/item/dullahan_relay/proc/retrieve_head(datum/source, full_heal, admin_revive)
if(admin_revive)
var/obj/item/bodypart/head/H = loc
var/turf/T = get_turf(owner)
if(H && istype(H) && T && !(H in owner.GetAllContents()))

View File

@@ -71,14 +71,42 @@
if(/obj/item/projectile/energy/florayield)
H.nutrition = min(H.nutrition+30, NUTRITION_LEVEL_FULL)
/datum/species/pod/pseudo_weak
name = "Anthromorphic Plant"
id = "podweak"
limbs_id = "pod"
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,MUTCOLORS)
mutant_bodyparts = list("mam_tail", "mam_ears", "mam_body_markings", "mam_snouts", "taur", "legs")
default_features = list("mcolor" = "FFF","mcolor2" = "FFF","mcolor3" = "FFF", "mam_snouts" = "Husky", "mam_tail" = "Husky", "mam_ears" = "Husky", "mam_body_markings" = "Husky", "taur" = "None", "legs" = "Normal Legs")
limbs_id = "pod"
light_nutrition_gain_factor = 7.5
light_bruteheal = 0.2
light_burnheal = 0.2
light_toxheal = 0.7
/datum/species/pod/pseudo_weak/spec_death(gibbed, mob/living/carbon/human/H)
if(H)
stop_wagging_tail(H)
/datum/species/pod/pseudo_weak/spec_stun(mob/living/carbon/human/H,amount)
if(H)
stop_wagging_tail(H)
. = ..()
/datum/species/pod/pseudo_weak/can_wag_tail(mob/living/carbon/human/H)
return ("mam_tail" in mutant_bodyparts) || ("mam_waggingtail" in mutant_bodyparts)
/datum/species/pod/pseudo_weak/is_wagging_tail(mob/living/carbon/human/H)
return ("mam_waggingtail" in mutant_bodyparts)
/datum/species/pod/pseudo_weak/start_wagging_tail(mob/living/carbon/human/H)
if("mam_tail" in mutant_bodyparts)
mutant_bodyparts -= "mam_tail"
mutant_bodyparts |= "mam_waggingtail"
H.update_body()
/datum/species/pod/pseudo_weak/stop_wagging_tail(mob/living/carbon/human/H)
if("mam_waggingtail" in mutant_bodyparts)
mutant_bodyparts -= "mam_waggingtail"
mutant_bodyparts |= "mam_tail"
H.update_body()

View File

@@ -213,6 +213,16 @@
PDA.f_lum = 0
PDA.update_icon()
visible_message("<span class='danger'>The light in [PDA] shorts out!</span>")
else if(istype(O, /obj/item/gun))
var/obj/item/gun/weapon = O
if(weapon.gun_light)
var/obj/item/flashlight/seclite/light = weapon.gun_light
light.forceMove(get_turf(weapon))
light.burn()
weapon.gun_light = null
weapon.update_gunlight()
QDEL_NULL(weapon.alight)
visible_message("<span class='danger'>[light] on [O] flickers out and disintegrates!</span>")
else
visible_message("<span class='danger'>[O] is disintegrated by [src]!</span>")
O.burn()

View File

@@ -72,7 +72,7 @@
put_in_hands(I)
update_inv_hands()
if(SLOT_IN_BACKPACK)
if(!SEND_SIGNAL(back, COMSIG_TRY_STORAGE_INSERT, I, src, TRUE))
if(!back || !SEND_SIGNAL(back, COMSIG_TRY_STORAGE_INSERT, I, src, TRUE))
not_handled = TRUE
else
not_handled = TRUE

View File

@@ -357,8 +357,8 @@
if(istype(loc, /obj/structure/closet/crate/coffin)|| istype(loc, /obj/structure/closet/body_bag) || istype(loc, /obj/structure/bodycontainer))
return
// No decay if formaldehyde in corpse or when the corpse is charred
if(reagents.has_reagent(/datum/reagent/toxin/formaldehyde, 1) || HAS_TRAIT(src, TRAIT_HUSK))
// No decay if formaldehyde/preservahyde in corpse or when the corpse is charred
if(reagents.has_reagent(/datum/reagent/toxin/formaldehyde, 1) || HAS_TRAIT(src, TRAIT_HUSK) || reagents.has_reagent(/datum/reagent/preservahyde, 1))
return
// Also no decay if corpse chilled or not organic/undead
@@ -397,7 +397,7 @@
if(O)
O.on_life()
else
if(reagents.has_reagent(/datum/reagent/toxin/formaldehyde, 1)) // No organ decay if the body contains formaldehyde.
if(reagents.has_reagent(/datum/reagent/toxin/formaldehyde, 1) || reagents.has_reagent(/datum/reagent/preservahyde, 1)) // No organ decay if the body contains formaldehyde. Or preservahyde.
return
for(var/V in internal_organs)
var/obj/item/organ/O = V

View File

@@ -25,7 +25,7 @@
/mob/living/proc/spawn_gibs(with_bodyparts, atom/loc_override)
var/location = loc_override ? loc_override.drop_location() : drop_location()
if((MOB_ROBOTIC) in (mob_biotypes))
if(mob_biotypes & MOB_ROBOTIC)
new /obj/effect/gibspawner/robot(location, src, get_static_viruses())
else
new /obj/effect/gibspawner/generic(location, src, get_static_viruses())

View File

@@ -482,7 +482,8 @@
med_hud_set_status()
//proc used to ressuscitate a mob
/mob/living/proc/revive(full_heal = 0, admin_revive = 0)
/mob/living/proc/revive(full_heal = FALSE, admin_revive = FALSE)
SEND_SIGNAL(src, COMSIG_LIVING_REVIVE, full_heal, admin_revive)
if(full_heal)
fully_heal(admin_revive)
if(stat == DEAD && can_be_revived()) //in some cases you can't revive (e.g. no brain)
@@ -528,11 +529,6 @@
fire_stacks = 0
confused = 0
update_canmove()
var/datum/component/mood/mood = GetComponent(/datum/component/mood)
if (mood)
QDEL_LIST_ASSOC_VAL(mood.mood_events)
mood.sanity = SANITY_GREAT
mood.update_mood()
//Heal all organs
if(iscarbon(src))
var/mob/living/carbon/C = src
@@ -540,8 +536,6 @@
for(var/organ in C.internal_organs)
var/obj/item/organ/O = organ
O.setOrganDamage(0)
SEND_SIGNAL(src, COMSIG_LIVING_FULLY_HEAL, admin_revive)
//proc called by revive(), to check if we can actually ressuscitate the mob (we don't want to revive him and have him instantly die again)
/mob/living/proc/can_be_revived()

View File

@@ -2,7 +2,6 @@
name = "pAI"
icon = 'icons/mob/pai.dmi'
icon_state = "repairbot"
mouse_opacity = MOUSE_OPACITY_OPAQUE
density = FALSE
pass_flags = PASSTABLE | PASSMOB
mob_size = MOB_SIZE_TINY
@@ -11,6 +10,7 @@
health = 500
maxHealth = 500
layer = BELOW_MOB_LAYER
silicon_privileges = PRIVILEDGES_PAI
var/datum/element/mob_holder/current_mob_holder //because only a few of their chassis can be actually held.
var/network = "ss13"
@@ -74,7 +74,7 @@
var/emitteroverloadcd = 100
var/radio_short = FALSE
var/radio_short_cooldown = 5 MINUTES
var/radio_short_cooldown = 3 MINUTES
var/radio_short_timerid
canmove = FALSE

View File

@@ -56,7 +56,8 @@
if(P.stun)
fold_in(force = TRUE)
visible_message("<span class='warning'>The electrically-charged projectile disrupts [src]'s holomatrix, forcing [src] to fold in!</span>")
return ..()
. = ..()
return BULLET_ACT_FORCE_PIERCE
/mob/living/silicon/pai/stripPanelUnequip(obj/item/what, mob/who, where) //prevents stripping
to_chat(src, "<span class='warning'>Your holochassis stutters and warps intensely as you attempt to interact with the object, forcing you to cease lest the field fail.</span>")

View File

@@ -596,8 +596,8 @@
/obj/item/robot_module/peacekeeper/do_transform_animation()
..()
to_chat(loc, "<span class='userdanger'>Under ASIMOV/CREWSIMOV, you are an enforcer of the PEACE and preventer of HUMAN/CREW HARM. \
You are not a security module and you are expected to follow orders and prevent harm above all else. Space law means nothing to you.</span>")
to_chat(loc, "<span class='userdanger'>Under ASIMOV/CREWSIMOV, you are an enforcer of the PEACE. \
You are not a security module and you are expected to follow orders to the best of your abilities without causing harm. Space law means nothing to you.</span>")
/obj/item/robot_module/peacekeeper/be_transformed_to(obj/item/robot_module/old_module)
var/mob/living/silicon/robot/R = loc

View File

@@ -1,6 +1,6 @@
/mob/living/silicon
gender = NEUTER
has_unlimited_silicon_privilege = 1
silicon_privileges = PRIVILEDGES_SILICON
verb_say = "states"
verb_ask = "queries"
verb_exclaim = "declares"

View File

@@ -15,7 +15,7 @@
maxbodytemp = INFINITY
minbodytemp = 0
blood_volume = 0
has_unlimited_silicon_privilege = 1
silicon_privileges = PRIVILEDGES_BOT
sentience_type = SENTIENCE_ARTIFICIAL
status_flags = NONE //no default canpush
verb_say = "states"
@@ -194,10 +194,12 @@
if(locked) //First emag application unlocks the bot's interface. Apply a screwdriver to use the emag again.
locked = FALSE
emagged = 1
to_chat(user, "<span class='notice'>You bypass [src]'s controls.</span>")
if(user)
to_chat(user, "<span class='notice'>You bypass [src]'s controls.</span>")
return TRUE
if(!open)
to_chat(user, "<span class='warning'>You need to open maintenance panel first!</span>")
if(user)
to_chat(user, "<span class='warning'>You need to open maintenance panel first!</span>")
return
emagged = 2
remote_disabled = 1 //Manually emagging the bot locks out the AI built in panel.
@@ -205,7 +207,8 @@
bot_reset()
turn_on() //The bot automatically turns on when emagged, unless recently hit with EMP.
to_chat(src, "<span class='userdanger'>(#$*#$^^( OVERRIDE DETECTED</span>")
log_combat(user, src, "emagged")
if(user)
log_combat(user, src, "emagged")
return TRUE
/mob/living/simple_animal/bot/examine(mob/user)

View File

@@ -123,7 +123,8 @@
emagged = TRUE
if(!open)
locked = !locked
to_chat(user, "<span class='notice'>You [locked ? "lock" : "unlock"] [src]'s controls!</span>")
if(user)
to_chat(user, "<span class='notice'>You [locked ? "lock" : "unlock"] [src]'s controls!</span>")
flick("mulebot-emagged", src)
playsound(src, "sparks", 100, FALSE)
@@ -180,7 +181,7 @@
var/list/data = list()
data["on"] = on
data["locked"] = locked
data["siliconUser"] = user.has_unlimited_silicon_privilege
data["siliconUser"] = hasSiliconAccessInArea(usr)
data["mode"] = mode ? mode_name[mode] : "Ready"
data["modeStatus"] = ""
switch(mode)
@@ -205,11 +206,12 @@
return data
/mob/living/simple_animal/bot/mulebot/ui_act(action, params)
if(..() || (locked && !usr.has_unlimited_silicon_privilege))
var/silicon_access = hasSiliconAccessInArea(usr)
if(..() || (locked && silicon_access))
return
switch(action)
if("lock")
if(usr.has_unlimited_silicon_privilege)
if(silicon_access)
locked = !locked
. = TRUE
if("power")
@@ -766,4 +768,3 @@
/obj/machinery/bot_core/mulebot
req_access = list(ACCESS_CARGO)

View File

@@ -41,7 +41,7 @@
bubble_icon = "machine"
initial_language_holder = /datum/language_holder/drone
mob_size = MOB_SIZE_SMALL
has_unlimited_silicon_privilege = 1
silicon_privileges = PRIVILEDGES_DRONE
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
hud_possible = list(DIAG_STAT_HUD, DIAG_HUD, ANTAG_HUD)
unique_name = TRUE

View File

@@ -0,0 +1,91 @@
//shameless copies of carps.
/mob/living/simple_animal/hostile/shark
name = "Space Shark"
desc = "The best terror of the seas, next to the kraken."
icon_state = "shark"
icon_living = "shark"
icon = 'icons/mob/sharks.dmi'
icon_dead = "shark_dead"
icon_gib = "carp_gib"
environment_smash = 0
speak_chance = 0
turns_per_move = 3
butcher_results = list(/obj/item/reagent_containers/food/snacks/carpmeat = 3)
response_help = "pets"
response_disarm = "gently pushes aside"
response_harm = "hits"
speed = 0
maxHealth = 75
health = 75
harm_intent_damage = 18
melee_damage_lower = 18
melee_damage_upper = 18
attacktext = "maims"
attack_sound = 'sound/weapons/bite.ogg'
gold_core_spawnable = 1
//Space shark aren't affected by cold.
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
maxbodytemp = 1500
faction = list("shark")
/mob/living/simple_animal/hostile/shark/Process_Spacemove(var/movement_dir = 0)
return 1 //No drifting in space for space sharks....either!
/mob/living/simple_animal/hostile/shark/FindTarget()
. = ..()
if(.)
emote("me", 1, "growls at [.]!")
/mob/living/simple_animal/hostile/shark/AttackingTarget()
. =..()
var/mob/living/carbon/L = .
if(istype(L))
if(prob(25))
L.Knockdown(20)
L.visible_message("<span class='danger'>\the [src] knocks down \the [L]!</span>")
/mob/living/simple_animal/hostile/shark/laser
name = "Laser-Shark"
desc = "NOW we've jumped the shark."
icon_state = "lasershark"
icon_living = "lasershark"
icon_dead = "lasershark_dead"
icon_gib = "carp_gib"
ranged = 1
retreat_distance = 3
minimum_distance = 0 //Between shots they can and will close in to nash
projectiletype = /obj/item/projectile/beam/laser/heavylaser
projectilesound = 'sound/weapons/lasercannonfire.ogg'
maxHealth = 50
health = 50
/mob/living/simple_animal/hostile/shark/kawaii
name = "Kawaii Shark"
desc = "Senpai~ Notice me.."
icon_state = "kawaiishark"
icon_living = "kawaiishark"
icon_dead = "kawaiishark_dead"
speak = list("Oh Senpai","Notice me senpai!","Oh my...","Kawaii~")
speak_emote = list("lovingly says","says")
speak_chance = 2
turns_per_move = 3
butcher_results = list(/mob/living/simple_animal/butterfly = 3)
maxHealth = 50
health = 50
maxbodytemp = INFINITY
harm_intent_damage = 0
melee_damage_lower = 0
melee_damage_upper = 0
attacktext = "violently hugs"
vision_range = 0
/mob/living/simple_animal/hostile/shark/kawaii/death()
say("Senpai, you noticed~!")
LoseAggro()
..()
walk(src, 0)

View File

@@ -126,7 +126,7 @@
// This loop will, at most, loop twice.
for(var/atom/check in check_list)
for(var/mob/living/M in viewers(world.view + 1, check) - src)
if(M.client && CanAttack(M) && !M.has_unlimited_silicon_privilege)
if(M.client && CanAttack(M) && !M.silicon_privileges)
if(!M.eye_blind)
return M
for(var/obj/mecha/M in view(world.view + 1, check)) //assuming if you can see them they can see you

View File

@@ -95,7 +95,7 @@
var/digitalinvis = 0 //Are they ivisible to the AI?
var/image/digitaldisguise = null //what does the AI see instead of them?
var/has_unlimited_silicon_privilege = 0 // Can they interact with station electronics
var/silicon_privileges = NONE // Can they interact with station electronics
var/obj/control_object //Used by admins to possess objects. All mobs should have this var
var/atom/movable/remote_control //Calls relaymove() to whatever it is

View File

@@ -406,8 +406,8 @@ It's fairly easy to fix if dealing with single letters but not so much with comp
return
return TRUE
/atom/proc/hasSiliconAccessInArea(mob/user)
return user && (issilicon(user) || (user.siliconaccesstoggle && (get_area(src) in user.siliconaccessareas)))
/atom/proc/hasSiliconAccessInArea(mob/user, flags = PRIVILEDGES_SILICON)
return user.silicon_privileges & (flags) || (user.siliconaccesstoggle && (get_area(src) in user.siliconaccessareas))
/mob/proc/toggleSiliconAccessArea(area/area)
if (area in siliconaccessareas)
@@ -555,4 +555,4 @@ It's fairly easy to fix if dealing with single letters but not so much with comp
//Can the mob see reagents inside of containers?
/mob/proc/can_see_reagents()
return stat == DEAD || has_unlimited_silicon_privilege //Dead guys and silicons can always see reagents
return stat == DEAD || silicon_privileges //Dead guys and silicons can always see reagents

View File

@@ -5,7 +5,7 @@
return "Default Implementation"
/datum/proc/oui_canuse(mob/user)
if(isobserver(user) && !user.has_unlimited_silicon_privilege)
if(isobserver(user) && !user.silicon_privileges)
return FALSE
return oui_canview(user)
@@ -35,7 +35,7 @@
return ..()
/obj/machinery/oui_canview(mob/user)
if(user.has_unlimited_silicon_privilege || hasSiliconAccessInArea(user))
if(hasSiliconAccessInArea(user, ALL))
return TRUE
if(!can_interact(user))
return FALSE

View File

@@ -0,0 +1,422 @@
#define POOL_NO_OVERDOSE_MEDICINE_MAX 5 //max units of no-overdose medicine to allow mobs to have through duplication
//Originally stolen from paradise. Credits to tigercat2000.
//Modified a lot by Kokojo and Tortellini Tony for hippiestation.
//Heavily refactored by tgstation
/obj/machinery/pool
icon = 'icons/obj/machines/pool.dmi'
anchored = TRUE
resistance_flags = INDESTRUCTIBLE
/obj/machinery/pool/controller
name = "\improper Pool Controller"
desc = "An advanced substance generation and fluid tank management system that can refill the contents of a pool to a completely different substance in minutes."
icon_state = "poolc_3"
density = TRUE
use_power = TRUE
idle_power_usage = 75
/// How far it scans for pool objects
var/scan_range = 6
/// Is pool mist currently on?
var/mist_state = FALSE
/// Linked mist effects
var/list/obj/effect/mist/linked_mist = list()
/// Pool turfs
var/list/turf/open/pool/linked_turfs = list()
/// All mobs in pool
var/list/mob/living/mobs_in_pool = list()
/// Is the pool bloody?
var/bloody = 0
/// Last time we process_reagents()'d
var/last_reagent_process = 0
/// Maximum amount we will take from a beaker
var/max_beaker_transfer = 100
/// Minimum amount of a reagent for it to work on us
var/min_reagent_amount = 10
/// ADMINBUS ONLY - WHETHER OR NOT WE HAVE NOREACT ;)
var/noreact_reagents = FALSE
/// how fast in deciseconds between reagent processes
var/reagent_tick_interval = 5
/// Can we use unsafe temperatures
var/temperature_unlocked = FALSE
/// See __DEFINES/pool.dm, temperature defines
var/temperature = POOL_NORMAL
/// Whether or not the pool can be drained
var/drainable = FALSE
// Whether or not the pool is drained
var/drained = FALSE
/// Pool drain
var/obj/machinery/pool/drain/linked_drain
/// Pool filter
var/obj/machinery/pool/filter/linked_filter
/// Next world.time you can interact with settings
var/interact_delay = 0
/// Airlock style shocks
var/shocked = FALSE
/// Old reagent color, used to determine if update_color needs to reset colors.
var/old_rcolor
/// Just to prevent spam
var/draining = FALSE
/// Reagent blacklisting
var/respect_reagent_blacklist = TRUE
/obj/machinery/pool/controller/examine(mob/user)
. = ..()
. += "<span class='boldnotice'>Alt click to drain reagents.</span>"
/obj/machinery/pool/controller/Initialize()
. = ..()
START_PROCESSING(SSfastprocess, src)
create_reagents(1000)
if(noreact_reagents)
reagents.reagents_holder_flags |= NO_REACT
wires = new /datum/wires/poolcontroller(src)
scan_things()
/obj/machinery/pool/controller/Destroy()
STOP_PROCESSING(SSprocessing, src)
linked_drain = null
linked_filter = null
linked_turfs.Cut()
mobs_in_pool.Cut()
return ..()
/obj/machinery/pool/controller/proc/scan_things()
var/list/cached = range(scan_range, src)
for(var/turf/open/pool/W in cached)
linked_turfs += W
W.controller = src
for(var/obj/machinery/pool/drain/pooldrain in cached)
linked_drain = pooldrain
linked_drain.controller = src
break
for(var/obj/machinery/pool/filter/F in cached)
linked_filter = F
linked_filter.controller = src
break
/obj/machinery/pool/controller/emag_act(mob/user)
. = ..()
if(!(obj_flags & EMAGGED)) //If it is not already emagged, emag it.
to_chat(user, "<span class='warning'>You disable the [src]'s safety features.</span>")
do_sparks(5, TRUE, src)
obj_flags |= EMAGGED
temperature_unlocked = TRUE
drainable = TRUE
log_game("[key_name(user)] emagged [src]")
message_admins("[key_name_admin(user)] emagged [src]")
else
to_chat(user, "<span class='warning'>The interface on [src] is already too damaged to short it again.</span>")
return
/obj/machinery/pool/controller/AltClick(mob/user)
. = ..()
if(!isliving(user) || !user.Adjacent(src) || !user.CanReach(src) || user.IsStun() || user.IsKnockdown() || user.incapacitated())
return FALSE
visible_message("<span class='boldwarning'>[user] starts to drain [src]!</span>")
draining = TRUE
if(!do_after(user, 50, target = src))
draining = FALSE
return TRUE
reagents.remove_all(INFINITY)
visible_message("<span class='boldnotice'>[user] drains [src].</span>")
say("Reagents cleared.")
update_color()
draining = FALSE
return TRUE
/obj/machinery/pool/controller/attackby(obj/item/W, mob/user)
if(shocked && !(stat & NOPOWER))
shock(user,50)
if(stat & (BROKEN))
return
if(istype(W,/obj/item/reagent_containers))
if(W.reagents.total_volume) //check if there's reagent
user.visible_message("<span class='boldwarning'>[user] is feeding [src] some chemicals from [W].</span>")
if(do_after(user, 50, target = src))
for(var/datum/reagent/R in W.reagents.reagent_list)
if(R.type in GLOB.blacklisted_pool_reagents)
to_chat(user, "[src] cannot accept [R.name].")
return
if(R.reagent_state == SOLID)
to_chat(user, "The pool cannot accept reagents in solid form!.")
return
reagents.clear_reagents()
// This also reacts them. No nitroglycerin deathpools, sorry gamers :(
W.reagents.trans_to(reagents, max_beaker_transfer)
user.visible_message("<span class='notice'>[src] makes a slurping noise.</span>", "<span class='notice'>All of the contents of [W] are quickly suctioned out by the machine!</span")
updateUsrDialog()
var/list/reagent_names = list()
var/list/rejected = list()
for(var/datum/reagent/R in reagents.reagent_list)
if((R.volume >= min_reagent_amount) && (!respect_reagent_blacklist || R.can_synth))
reagent_names += R.name
else
reagents.remove_reagent(R.type, INFINITY)
rejected += R.name
if(length(reagent_names))
reagent_names = english_list(reagent_names)
var/msg = "POOL: [key_name(user)] has changed [src]'s chems to [reagent_names]"
log_game(msg)
message_admins(msg)
if(length(rejected))
rejected = english_list(rejected)
to_chat(user, "<span class='warning'>[src] rejects the following chemicals as they do not have at least [min_reagent_amount] units of volume: [rejected]</span>")
update_color()
else
to_chat(user, "<span class='notice'>[src] beeps unpleasantly as it rejects the beaker. Why are you trying to feed it an empty beaker?</span>")
return
else if(panel_open && is_wire_tool(W))
wires.interact(user)
else
return ..()
/obj/machinery/pool/controller/screwdriver_act(mob/living/user, obj/item/W)
. = ..()
if(.)
return TRUE
cut_overlays()
panel_open = !panel_open
to_chat(user, "You [panel_open ? "open" : "close"] the maintenance panel.")
W.play_tool_sound(src)
if(panel_open)
add_overlay("wires")
return TRUE
//procs
/obj/machinery/pool/controller/proc/shock(mob/user, prb)
if(stat & (BROKEN|NOPOWER)) // unpowered, no shock
return FALSE
if(!prob(prb))
return FALSE
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
s.set_up(5, 1, src)
s.start()
if(electrocute_mob(user, get_area(src), src, 0.7))
return TRUE
else
return FALSE
/obj/machinery/pool/controller/proc/process_reagents()
if(last_reagent_process > world.time + reagent_tick_interval)
return
if(!length(reagents.reagent_list))
return
for(var/turf/open/pool/W in linked_turfs)
for(var/mob/living/carbon/human/swimee in W)
for(var/datum/reagent/R in reagents.reagent_list)
if(R.reagent_state == SOLID)
R.reagent_state = LIQUID
if(!swimee.reagents.has_reagent(POOL_NO_OVERDOSE_MEDICINE_MAX))
swimee.reagents.add_reagent(R.type, 0.5) //osmosis
reagents.reaction(swimee, VAPOR, 0.03) //3 percent. Need to find a way to prevent this from stacking chems at some point like the above.
for(var/obj/objects in W)
if(W.reagents)
W.reagents.reaction(objects, VAPOR, 1)
last_reagent_process = world.time
/obj/machinery/pool/controller/process()
updateUsrDialog()
if(stat & (NOPOWER|BROKEN))
return
if(drained)
return
process_pool()
process_reagents()
/obj/machinery/pool/controller/proc/process_pool()
if(drained)
return
for(var/mob/living/M in mobs_in_pool)
switch(temperature) //Apply different effects based on what the temperature is set to.
if(POOL_SCALDING) //Scalding
M.adjust_bodytemperature(50,0,500)
if(POOL_WARM) //Warm
M.adjust_bodytemperature(20,0,360) //Heats up mobs till the termometer shows up
//Normal temp does nothing, because it's just room temperature water.
if(POOL_COOL)
M.adjust_bodytemperature(-20,250) //Cools mobs till the termometer shows up
if(POOL_FRIGID) //Freezing
M.adjust_bodytemperature(-60) //cool mob at -35k per cycle, less would not affect the mob enough.
if(M.bodytemperature <= 50 && !M.stat)
M.apply_status_effect(/datum/status_effect/freon)
if(ishuman(M))
var/mob/living/carbon/human/drownee = M
if(!drownee || drownee.stat == DEAD)
return
if(drownee.resting && !drownee.internal)
if(drownee.stat != CONSCIOUS)
drownee.adjustOxyLoss(9)
else
drownee.adjustOxyLoss(4)
if(prob(35))
to_chat(drownee, "<span class='danger'>You're drowning!</span>")
/obj/machinery/pool/controller/proc/set_bloody(state)
if(bloody == state)
return
bloody = state
update_color()
/obj/machinery/pool/controller/proc/update_color()
if(drained)
return
var/rcolor
if(reagents.reagent_list.len)
rcolor = mix_color_from_reagents(reagents.reagent_list)
if(rcolor == old_rcolor)
return // small performance upgrade hopefully?
old_rcolor = rcolor
for(var/X in linked_turfs)
var/turf/open/pool/color1 = X
if(bloody)
if(rcolor)
var/thecolor = BlendRGB(rgb(150, 20, 20), rcolor, 0.5)
color1.watereffect.add_atom_colour(thecolor, FIXED_COLOUR_PRIORITY)
color1.watertop.add_atom_colour(thecolor, FIXED_COLOUR_PRIORITY)
else
var/thecolor = rgb(150, 20, 20)
color1.watereffect.add_atom_colour(thecolor, FIXED_COLOUR_PRIORITY)
color1.watertop.add_atom_colour(thecolor, FIXED_COLOUR_PRIORITY)
else if(!bloody && rcolor)
color1.watereffect.add_atom_colour(rcolor, FIXED_COLOUR_PRIORITY)
color1.watertop.add_atom_colour(rcolor, FIXED_COLOUR_PRIORITY)
else
color1.watereffect.remove_atom_colour(FIXED_COLOUR_PRIORITY)
color1.watertop.remove_atom_colour(FIXED_COLOUR_PRIORITY)
/obj/machinery/pool/controller/proc/update_temp()
if(mist_state)
if(temperature < POOL_SCALDING)
mist_off()
else
if(temperature == POOL_SCALDING)
mist_on()
update_icon()
/obj/machinery/pool/controller/update_icon()
. = ..()
icon_state = "poolc_[temperature]"
/obj/machinery/pool/controller/proc/CanUpTemp(mob/user)
if(temperature == POOL_WARM && (temperature_unlocked || issilicon(user) || IsAdminGhost(user)) || temperature < POOL_WARM)
return TRUE
return FALSE
/obj/machinery/pool/controller/proc/CanDownTemp(mob/user)
if(temperature == POOL_COOL && (temperature_unlocked || issilicon(user) || IsAdminGhost(user)) || temperature > POOL_COOL)
return TRUE
return FALSE
/obj/machinery/pool/controller/Topic(href, href_list)
if(..())
return
if(interact_delay > world.time)
return
if(href_list["IncreaseTemp"])
if(CanUpTemp(usr))
visible_message("<span class='warning'>[usr] presses a button on [src].</span>")
temperature++
update_temp()
var/msg = "POOL: [key_name(usr)] increased [src]'s pool temperature at [COORD(src)] to [temperature]"
log_game(msg)
message_admins(msg)
interact_delay = world.time + 15
if(href_list["DecreaseTemp"])
if(CanDownTemp(usr))
visible_message("<span class='warning'>[usr] presses a button on [src].</span>")
temperature--
update_temp()
var/msg = "POOL: [key_name(usr)] decreased [src]'s pool temperature at [COORD(src)] to [temperature]"
log_game(msg)
message_admins(msg)
interact_delay = world.time + 15
if(href_list["Activate Drain"])
if((drainable || issilicon(usr) || IsAdminGhost(usr)) && !linked_drain.active)
var/msg = "POOL: [key_name(usr)] activated [src]'s pool drain in [linked_drain.filling? "FILLING" : "DRAINING"] mode at [COORD(src)]"
log_game(msg)
message_admins(msg)
visible_message("<span class='warning'>[usr] presses a button on [src].</span>")
mist_off()
interact_delay = world.time + 60
linked_drain.active = TRUE
linked_drain.cycles_left = 75
if(!linked_drain.filling)
new /obj/effect/whirlpool(linked_drain.loc)
temperature = POOL_NORMAL
else
new /obj/effect/waterspout(linked_drain.loc)
temperature = POOL_NORMAL
update_temp()
bloody = FALSE
updateUsrDialog()
/obj/machinery/pool/controller/proc/temp2text()
switch(temperature)
if(POOL_FRIGID)
return "<span class='boldwarning'>Frigid</span>"
if(POOL_COOL)
return "<span class='boldnotice'>Cool</span>"
if(POOL_NORMAL)
return "<span class='notice'>Normal</span>"
if(POOL_WARM)
return "<span class='boldnotice'>Warm</span>"
if(POOL_SCALDING)
return "<span class='boldwarning'>Scalding</span>"
else
return "Outside of possible range."
/obj/machinery/pool/controller/ui_interact(mob/user)
. = ..()
if(.)
return
if(shocked && !(stat & NOPOWER))
shock(user,50)
if(panel_open && !isAI(user))
return wires.interact(user)
if(stat & (NOPOWER|BROKEN))
return
var/datum/browser/popup = new(user, "Pool Controller", name, 300, 450)
var/dat = ""
if(interact_delay > world.time)
dat += "<span class='notice'>[round((interact_delay - world.time)/10, 0.1)] seconds left until [src] can operate again.</span><BR>"
dat += text({"
<h3>Temperature</h3>
<div class='statusDisplay'>
<B>Current temperature:</B> [temp2text()]<BR>
[CanUpTemp(user) ? "<a href='?src=\ref[src];IncreaseTemp=1'>Increase Temperature</a><br>" : "<span class='linkOff'>Increase Temperature</span><br>"]
[CanDownTemp(user) ? "<a href='?src=\ref[src];DecreaseTemp=1'>Decrease Temperature</a><br>" : "<span class='linkOff'>Decrease Temperature</span><br>"]
</div>
<h3>Drain</h3>
<div class='statusDisplay'>
<B>Drain status:</B> [(issilicon(user) || IsAdminGhost(user) || drainable) ? "<span class='bad'>Enabled</span>" : "<span class='good'>Disabled</span>"]
<br><b>Pool status:</b> "})
if(!drained)
dat += "<span class='good'>Full</span><BR>"
else
dat += "<span class='bad'>Drained</span><BR>"
if((issilicon(user) || IsAdminGhost(user) || drainable) && !linked_drain.active)
dat += "<a href='?src=\ref[src];Activate Drain=1'>[drained ? "Fill" : "Drain"] Pool</a><br>"
popup.set_content(dat)
popup.open()
/obj/machinery/pool/controller/proc/reset(wire)
switch(wire)
if(WIRE_SHOCK)
if(!wires.is_cut(wire))
shocked = FALSE
/obj/machinery/pool/controller/proc/mist_on() //Spawn /obj/effect/mist (from the shower) on all linked pool tiles
if(mist_state)
return
mist_state = TRUE
for(var/X in linked_turfs)
var/turf/open/pool/W = X
if(W.filled)
var/M = new /obj/effect/mist(W)
linked_mist += M
/obj/machinery/pool/controller/proc/mist_off() //Delete all /obj/effect/mist from all linked pool tiles.
for(var/M in linked_mist)
qdel(M)
mist_state = FALSE

View File

@@ -0,0 +1,160 @@
/obj/machinery/pool/drain
name = "drain"
icon_state = "drain"
desc = "A suction system to remove the contents of the pool, and sometimes small objects. Do not insert fingers."
anchored = TRUE
/// Active/on?
var/active = FALSE
/// Filling or draining
var/filling = FALSE
/// Drain item suction range
var/item_suction_range = 2
/// Fill mode knock away range
var/fill_push_range = 6
/// Drain mode suction range
var/drain_suck_range = 6
/// Parent controller
var/obj/machinery/pool/controller/controller
/// Cycles left for fill/drain while active
var/cycles_left = 0
/// Mobs we are swirling around
var/list/whirling_mobs
/// Suck in once per x ticks
var/suck_in_once_per = 3
var/cooldown
/obj/machinery/pool/drain/Initialize()
START_PROCESSING(SSfastprocess, src)
whirling_mobs = list()
return ..()
/obj/machinery/pool/drain/Destroy()
STOP_PROCESSING(SSfastprocess, src)
controller.linked_drain = null
controller = null
whirling_mobs = null
return ..()
/obj/machinery/pool/drain/proc/is_in_our_pool(atom/A)
. = FALSE
if(istype(A.loc, /turf/open/pool))
var/turf/open/pool/P = A.loc
if(P.controller == controller)
. = TRUE
// This should probably start using move force sometime in the future but I'm lazy.
/obj/machinery/pool/drain/process()
if(!filling)
for(var/obj/item/I in range(min(item_suction_range, 10), src))
if(!I.anchored && (I.w_class <= WEIGHT_CLASS_SMALL))
step_towards(I, src)
if((I.w_class <= WEIGHT_CLASS_TINY) && (get_dist(I, src) == 0))
I.forceMove(controller.linked_filter)
if(active)
if(filling)
if(cycles_left-- > 0)
playsound(src, 'sound/effects/fillingwatter.ogg', 100, TRUE)
for(var/obj/O in orange(min(fill_push_range, 10), src))
if(!O.anchored && is_in_our_pool(O))
step_away(O, src)
for(var/mob/M in orange(min(fill_push_range, 10), src)) //compiler fastpath apparently?
if(!M.anchored && isliving(M) && is_in_our_pool(M))
step_away(M, src)
else
for(var/turf/open/pool/P in controller.linked_turfs)
P.filled = TRUE
P.update_icon()
for(var/obj/effect/waterspout/S in range(1, src))
qdel(S)
controller.drained = FALSE
if(controller.bloody < 1000)
controller.bloody /= 2
else
controller.bloody /= 4
controller.update_color()
filling = FALSE
active = FALSE
else
if(cycles_left-- > 0)
playsound(src, 'sound/effects/pooldrain.ogg', 100, TRUE)
playsound(src, "water_wade", 60, TRUE)
for(var/obj/O in orange(min(drain_suck_range, 10), src))
if(!O.anchored && is_in_our_pool(O))
step_towards(O, src)
for(var/mob/M in orange(min(drain_suck_range, 10), src))
if(isliving(M) && !M.anchored && is_in_our_pool(M))
if(!(cycles_left % suck_in_once_per))
step_towards(M, src)
whirl_mob(M)
if(ishuman(M) && (get_dist(M, src) <= 1))
var/mob/living/carbon/human/H = M
playsound(src, pick('sound/misc/crack.ogg','sound/misc/crunch.ogg'), 50, TRUE)
if(H.lying) //down for any reason
H.adjustBruteLoss(2)
to_chat(H, "<span class='danger'>You're caught in the drain!</span>")
else
H.apply_damage(2.5, BRUTE, pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) //drain should only target the legs
to_chat(H, "<span class='danger'>Your legs are caught in the drain!</span>")
else
for(var/turf/open/pool/P in controller.linked_turfs)
P.filled = FALSE
P.update_icon()
for(var/obj/effect/whirlpool/W in range(1, src))
qdel(W)
controller.drained = TRUE
controller.mist_off()
active = FALSE
filling = TRUE
/// dangerous proc don't fuck with, admins
/obj/machinery/pool/drain/proc/whirl_mob(mob/living/L, duration = 8, delay = 1)
set waitfor = FALSE
if(whirling_mobs[L])
return
whirling_mobs[L] = TRUE
for(var/i in 1 to min(duration, 100))
L.setDir(turn(L.dir, 90))
sleep(delay)
if(QDELETED(L))
break
if(QDELETED(src))
return
whirling_mobs -= L
/obj/machinery/pool/filter
name = "Filter"
icon_state = "filter"
desc = "The part of the pool where all the IDs, ATV keys, and pens, and other dangerous things get trapped."
var/obj/machinery/pool/controller/controller
/obj/machinery/pool/filter/Destroy()
controller.linked_filter = null
controller = null
return ..()
/obj/machinery/pool/filter/emag_act(mob/living/user)
. = ..()
if(!(obj_flags & EMAGGED))
to_chat(user, "<span class='warning'>You disable the [src]'s shark filter! Run!</span>")
obj_flags |= EMAGGED
do_sparks(5, TRUE, src)
icon_state = "filter_b"
addtimer(CALLBACK(src, /obj/machinery/pool/filter/proc/spawn_shark), 50)
var/msg = "[key_name(user)] emagged the pool filter and spawned a shark"
log_game(msg)
message_admins(msg)
/obj/machinery/pool/filter/proc/spawn_shark()
if(prob(50))
new /mob/living/simple_animal/hostile/shark(loc)
else
if(prob(50))
new /mob/living/simple_animal/hostile/shark/kawaii(loc)
else
new /mob/living/simple_animal/hostile/shark/laser(loc)
/obj/machinery/pool/filter/attack_hand(mob/user)
to_chat(user, "You search the filter.")
for(var/obj/O in contents)
O.forceMove(loc)

View File

@@ -0,0 +1,29 @@
/obj/effect/splash
name = "splash"
desc = "Wataaa!."
icon = 'icons/turf/pool.dmi'
icon_state = "splash"
layer = ABOVE_ALL_MOB_LAYER
/obj/effect/whirlpool
name = "Whirlpool"
icon = 'icons/effects/96x96.dmi'
icon_state = "whirlpool"
layer = 5
anchored = TRUE
mouse_opacity = 0
pixel_x = -32
pixel_y = -32
alpha = 90
/obj/effect/waterspout
name = "Waterspout"
icon = 'icons/effects/96x96.dmi'
icon_state = "waterspout"
color = "#3399AA"
layer = 5
anchored = TRUE
mouse_opacity = 0
pixel_x = -32
pixel_y = -32
alpha = 120

View File

@@ -0,0 +1,191 @@
/turf/open/pool
icon = 'icons/turf/pool.dmi'
name = "poolwater"
desc = "You're safer here than in the deep."
icon_state = "pool_tile"
heat_capacity = INFINITY
var/filled = TRUE
var/next_splash = 0
var/obj/machinery/pool/controller/controller
var/obj/effect/overlay/water/watereffect
var/obj/effect/overlay/water/top/watertop
/turf/open/pool/Initialize(mapload)
. = ..()
update_icon()
/turf/open/pool/Destroy()
if(controller)
controller.linked_turfs -= src
controller = null
QDEL_NULL(watereffect)
QDEL_NULL(watertop)
return ..()
/turf/open/pool/update_icon()
. = ..()
if(!filled)
name = "drained pool"
desc = "No diving!"
QDEL_NULL(watereffect)
QDEL_NULL(watertop)
else
name = "poolwater"
desc = "You're safer here than in the deep."
watereffect = new /obj/effect/overlay/water(src)
watertop = new /obj/effect/overlay/water/top(src)
/obj/effect/overlay/water
name = "water"
icon = 'icons/turf/pool.dmi'
icon_state = "bottom"
density = FALSE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
layer = ABOVE_MOB_LAYER
anchored = TRUE
resistance_flags = INDESTRUCTIBLE
/obj/effect/overlay/water/top
icon_state = "top"
layer = BELOW_MOB_LAYER
// Mousedrop hook to normal turfs to get out of pools.
/turf/open/MouseDrop_T(atom/from, mob/user)
// I could make this /open/floor and not have the !istype but ehh - kev
if(isliving(from) && HAS_TRAIT(from, TRAIT_SWIMMING) && isliving(user) && ((user == from) || user.CanReach(from)) && !user.IsStun() && !user.IsKnockdown() && !user.incapacitated() && !istype(src, /turf/open/pool))
var/mob/living/L = from
//The element only exists if you're on water and a living mob, so let's skip those checks.
var/pre_msg
var/post_msg
if(user == from)
pre_msg = "<span class='notice'>[L] is getting out of the pool.</span>"
post_msg = "<span class='notice'>[L] gets out of the pool.</span>"
else
pre_msg = "<span class='notice'>[L] is being pulled out of the pool by [user].</span>"
post_msg = "<span class='notice'>[user] pulls [L] out of the pool.</span>"
L.visible_message(pre_msg)
if(do_mob(user, L, 20))
L.visible_message(post_msg)
L.forceMove(src)
else
return ..()
// Exit check
/turf/open/pool/Exit(atom/movable/AM, atom/newloc)
if(!AM.has_gravity(src))
return ..()
if(isliving(AM) || isstructure(AM))
if(AM.throwing)
return ..() //WHEEEEEEEEEEE
if(istype(AM, /obj/structure) && isliving(AM.pulledby))
return ..() //people pulling stuff out of pool
if(!ishuman(AM))
return ..() //human weak, monkey (and anyone else) ook ook eek eek strong
if(isliving(AM) && (locate(/obj/structure/pool/ladder) in src))
return ..() //climbing out
return istype(newloc, /turf/open/pool)
return ..()
// Exited logic
/turf/open/pool/Exited(atom/A, atom/newLoc)
. = ..()
if(isliving(A))
var/turf/open/pool/P = newLoc
if(!istype(P) || (P.controller != controller))
controller?.mobs_in_pool -= A
// Entered logic
/turf/open/pool/Entered(atom/movable/AM, atom/oldloc)
if(istype(AM, /obj/effect/decal/cleanable))
var/obj/effect/decal/cleanable/C = AM
if(prob(C.bloodiness))
controller.set_bloody(TRUE)
QDEL_IN(AM, 25)
animate(AM, alpha = 10, time = 20)
return ..()
if(!AM.has_gravity(src))
return ..()
if(isliving(AM))
var/mob/living/victim = AM
if(!HAS_TRAIT(victim, TRAIT_SWIMMING)) //poor guy not swimming time to dunk them!
victim.AddElement(/datum/element/swimming)
controller.mobs_in_pool += victim
if(locate(/obj/structure/pool/ladder) in src) //safe climbing
return
if(iscarbon(AM)) //FUN TIME!
var/mob/living/carbon/H = victim
if(filled)
if (H.wear_mask && H.wear_mask.flags_cover & MASKCOVERSMOUTH)
H.visible_message("<span class='danger'>[H] falls in the water!</span>",
"<span class='userdanger'>You fall in the water!</span>")
playsound(src, 'sound/effects/splash.ogg', 60, TRUE, 1)
H.Knockdown(20)
return
else
H.Knockdown(60)
H.adjustOxyLoss(5)
H.emote("cough")
H.visible_message("<span class='danger'>[H] falls in and takes a drink!</span>",
"<span class='userdanger'>You fall in and swallow some water!</span>")
playsound(src, 'sound/effects/splash.ogg', 60, TRUE, 1)
else if(!H.head || !(H.head.armor.getRating("melee") > 20))
if(prob(75))
H.visible_message("<span class='danger'>[H] falls in the drained pool!</span>",
"<span class='userdanger'>You fall in the drained pool!</span>")
H.adjustBruteLoss(7)
H.Knockdown(80)
playsound(src, 'sound/effects/woodhit.ogg', 60, TRUE, 1)
else
H.visible_message("<span class='danger'>[H] falls in the drained pool, and cracks his skull!</span>",
"<span class='userdanger'>You fall in the drained pool, and crack your skull!</span>")
H.apply_damage(15, BRUTE, "head")
H.Knockdown(200) // This should hurt. And it does.
playsound(src, 'sound/effects/woodhit.ogg', 60, TRUE, 1)
playsound(src, 'sound/misc/crack.ogg', 100, TRUE)
else
H.visible_message("<span class='danger'>[H] falls in the drained pool, but had an helmet!</span>",
"<span class='userdanger'>You fall in the drained pool, but you had an helmet!</span>")
H.Knockdown(40)
playsound(src, 'sound/effects/woodhit.ogg', 60, TRUE, 1)
else if(filled)
victim.adjustStaminaLoss(1)
playsound(src, "water_wade", 20, TRUE)
return ..()
/turf/open/pool/MouseDrop_T(atom/from, mob/user)
. = ..()
if(!isliving(from))
return
var/mob/living/victim = from
if(user.stat || user.lying || !Adjacent(user) || !from.Adjacent(user) || !iscarbon(user) || !victim.has_gravity(src) || HAS_TRAIT(victim, TRAIT_SWIMMING))
return
var/victimname = victim == user? "themselves" : "[victim]"
var/starttext = victim == user? "[user] is descending into [src]." : "[user] is lowering [victim] into [src]."
user.visible_message("<span class='notice'>[starttext]</span>")
if(do_mob(user, victim, 20))
user.visible_message("<span class='notice'>[user] lowers [victimname] into [src].</span>")
victim.AddElement(/datum/element/swimming) //make sure they have it so they don't fall/whatever
victim.forceMove(src)
/turf/open/pool/attackby(obj/item/W, mob/living/user)
if(istype(W, /obj/item/mop) && filled)
W.reagents.add_reagent("water", 5)
to_chat(user, "<span class='notice'>You wet [W] in [src].</span>")
playsound(loc, 'sound/effects/slosh.ogg', 25, TRUE)
else
return ..()
/turf/open/pool/attack_hand(mob/living/user)
. = ..()
if(.)
return
if((user.loc != src) && !user.IsStun() && !user.IsKnockdown() && !user.incapacitated() && Adjacent(user) && HAS_TRAIT(user, TRAIT_SWIMMING) && filled && (next_splash < world.time))
playsound(src, 'sound/effects/watersplash.ogg', 8, TRUE, 1)
next_splash = world.time + 25
var/obj/effect/splash/S = new(src)
animate(S, alpha = 0, time = 8)
QDEL_IN(S, 10)
for(var/mob/living/carbon/human/H in src)
if(!H.wear_mask && (H.stat == CONSCIOUS))
H.emote("cough")
H.adjustStaminaLoss(4)

View File

@@ -0,0 +1,32 @@
//Pool noodles
/obj/item/toy/poolnoodle
icon = 'icons/obj/toy.dmi'
icon_state = "noodle"
name = "pool noodle"
desc = "A strange, bulky, bendable toy that can annoy people."
force = 0
color = "#000000"
w_class = WEIGHT_CLASS_SMALL
throwforce = 1
throw_speed = 10 //weeee
hitsound = 'sound/weapons/tap.ogg'
attack_verb = list("flogged", "poked", "jabbed", "slapped", "annoyed")
lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items_righthand.dmi'
/obj/item/toy/poolnoodle/attack(target as mob, mob/living/user as mob)
. = ..()
user.spin(prob(20)? 16 : 8, 1)
/obj/item/toy/poolnoodle/red
item_state = "noodlered"
color = "#ff4c4c"
/obj/item/toy/poolnoodle/blue
item_state = "noodleblue"
color = "#3232ff"
/obj/item/toy/poolnoodle/yellow
item_state = "noodleyellow"
color = "#ffff66"

View File

@@ -0,0 +1,159 @@
/obj/structure/pool
name = "pool"
icon = 'icons/obj/machines/pool.dmi'
anchored = TRUE
resistance_flags = UNACIDABLE|INDESTRUCTIBLE
/obj/structure/pool/ladder
name = "Ladder"
icon_state = "ladder"
desc = "Are you getting in or are you getting out?."
layer = ABOVE_MOB_LAYER
dir = EAST
/obj/structure/pool/ladder/attack_hand(mob/living/user)
. = ..()
if(.)
return
if(!HAS_TRAIT(user, TRAIT_SWIMMING))
if(user.CanReach(src))
user.AddElement(/datum/element/swimming)
user.forceMove(get_step(src, dir))
else
if(user.loc == loc)
user.forceMove(get_step(src, turn(dir, 180))) //If this moves them out the element cleans up after itself.
/obj/structure/pool/Rboard
name = "JumpBoard"
density = FALSE
icon_state = "boardright"
desc = "The less-loved portion of the jumping board."
dir = EAST
/obj/structure/pool/Lboard
name = "JumpBoard"
icon_state = "boardleft"
desc = "Get on there to jump!"
layer = FLY_LAYER
dir = WEST
var/jumping = FALSE
var/timer
/obj/structure/pool/Lboard/proc/backswim()
if(jumping)
for(var/mob/living/jumpee in loc) //hackzors.
playsound(jumpee, 'sound/effects/splash.ogg', 60, TRUE, 1)
if(!HAS_TRAIT(jumpee, TRAIT_SWIMMING))
jumpee.AddElement(/datum/element/swimming)
jumpee.Stun(2)
/obj/structure/pool/Lboard/proc/reset_position(mob/user, initial_layer, initial_px, initial_py)
user.layer = initial_layer
user.pixel_x = initial_px
user.pixel_y = initial_py
/obj/structure/pool/Lboard/attack_hand(mob/living/user)
if(iscarbon(user))
var/mob/living/carbon/jumper = user
if(jumping)
to_chat(user, "<span class='notice'>Someone else is already making a jump!</span>")
return
var/turf/T = get_turf(src)
if(HAS_TRAIT(user, TRAIT_SWIMMING))
return
else
if(Adjacent(jumper))
jumper.visible_message("<span class='notice'>[user] climbs up \the [src]!</span>", \
"<span class='notice'>You climb up \the [src] and prepares to jump!</span>")
jumper.Stun(40)
jumping = TRUE
var/original_layer = jumper.layer
var/original_px = jumper.pixel_x
var/original_py = jumper.pixel_y
jumper.layer = RIPPLE_LAYER
jumper.pixel_x = 3
jumper.pixel_y = 7
jumper.dir = WEST
jumper.AddElement(/datum/element/swimming)
sleep(1)
jumper.forceMove(T)
addtimer(CALLBACK(src, .proc/dive, jumper, original_layer, original_px, original_py), 10)
/obj/structure/pool/Lboard/proc/dive(mob/living/carbon/jumper, original_layer, original_px, original_py)
switch(rand(1, 100))
if(1 to 20)
jumper.visible_message("<span class='notice'>[jumper] goes for a small dive!</span>", \
"<span class='notice'>You go for a small dive.</span>")
sleep(15)
backswim()
var/atom/throw_target = get_edge_target_turf(src, dir)
jumper.throw_at(throw_target, 1, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
if(21 to 40)
jumper.visible_message("<span class='notice'>[jumper] goes for a dive!</span>", \
"<span class='notice'>You're going for a dive!</span>")
sleep(20)
backswim()
var/atom/throw_target = get_edge_target_turf(src, dir)
jumper.throw_at(throw_target, 2, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
if(41 to 60)
jumper.visible_message("<span class='notice'>[jumper] goes for a long dive! Stay far away!</span>", \
"<span class='notice'>You're going for a long dive!!</span>")
sleep(25)
backswim()
var/atom/throw_target = get_edge_target_turf(src, dir)
jumper.throw_at(throw_target, 3, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
if(61 to 80)
jumper.visible_message("<span class='notice'>[jumper] goes for an awesome dive! Don't stand in [jumper.p_their()] way!</span>", \
"<span class='notice'>You feel like this dive will be awesome</span>")
sleep(30)
backswim()
var/atom/throw_target = get_edge_target_turf(src, dir)
jumper.throw_at(throw_target, 4, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
if(81 to 91)
sleep(20)
backswim()
jumper.visible_message("<span class='danger'>[jumper] misses [jumper.p_their()] step!</span>", \
"<span class='userdanger'>You misstep!</span>")
var/atom/throw_target = get_edge_target_turf(src, dir)
jumper.throw_at(throw_target, 0, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
jumper.Knockdown(100)
jumper.adjustBruteLoss(10)
if(91 to 100)
jumper.visible_message("<span class='notice'>[jumper] is preparing for the legendary dive! Can [jumper.p_they()] make it?</span>", \
"<span class='userdanger'>You start preparing for a legendary dive!</span>")
jumper.SpinAnimation(7,1)
sleep(30)
if(prob(75))
backswim()
jumper.visible_message("<span class='notice'>[jumper] fails!</span>", \
"<span class='userdanger'>You can't quite do it!</span>")
var/atom/throw_target = get_edge_target_turf(src, dir)
jumper.throw_at(throw_target, 1, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
else
jumper.fire_stacks = min(1,jumper.fire_stacks + 1)
jumper.IgniteMob()
sleep(5)
backswim()
jumper.visible_message("<span class='danger'>[jumper] bursts into flames of pure awesomness!</span>", \
"<span class='userdanger'>No one can stop you now!</span>")
var/atom/throw_target = get_edge_target_turf(src, dir)
jumper.throw_at(throw_target, 6, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
addtimer(CALLBACK(src, .proc/togglejumping), 35)
reset_position(jumper, original_layer, original_px, original_py)
/obj/structure/pool/Lboard/proc/togglejumping()
jumping = FALSE
/obj/structure/pool/Lboard/proc/on_finish_jump(mob/living/victim)
if(istype(victim.loc, /turf/open/pool))
var/turf/open/pool/P = victim.loc
if(!P.filled) //you dun fucked up now
to_chat(victim, "<span class='warning'>That was stupid of you..</span>")
victim.visible_message("<span class='danger'>[victim] smashes into the ground!</span>")
victim.apply_damage(50)
victim.Knockdown(200)

View File

@@ -0,0 +1,59 @@
#define POOL_WIRE_DRAIN "drain"
#define POOL_WIRE_TEMP "temp"
/datum/wires/poolcontroller
holder_type = /obj/machinery/pool/controller
proper_name = "Pool"
/datum/wires/poolcontroller/New(atom/holder)
wires = list(
POOL_WIRE_DRAIN, WIRE_SHOCK, WIRE_ZAP, POOL_WIRE_TEMP
)
add_duds(3)
..()
/datum/wires/poolcontroller/interactable(mob/user)
var/obj/machinery/pool/controller/P = holder
if(P.panel_open)
return TRUE
/datum/wires/poolcontroller/get_status()
var/obj/machinery/pool/controller/P = holder
var/list/status = list()
status += "The blue light is [P.drainable ? "on" : "off"]."
status += "The red light is [P.temperature_unlocked ? "on" : "off"]."
status += "The yellow light is [P.shocked ? "on" : "off"]."
return status
/datum/wires/poolcontroller/on_pulse(wire)
var/obj/machinery/pool/controller/P = holder
switch(wire)
if(POOL_WIRE_DRAIN)
P.drainable = FALSE
if(POOL_WIRE_TEMP)
P.temperature_unlocked = FALSE
if(WIRE_SHOCK)
P.shocked = !P.shocked
addtimer(CALLBACK(P, /obj/machinery/autolathe.proc/reset, wire), 60)
/datum/wires/poolcontroller/on_cut(wire, mend)
var/obj/machinery/pool/controller/P = holder
switch(wire)
if(POOL_WIRE_DRAIN)
if(mend)
P.drainable = FALSE
else
P.drainable = TRUE
if(POOL_WIRE_TEMP)
if(mend)
P.temperature_unlocked = FALSE
else
P.temperature_unlocked = TRUE
if(WIRE_ZAP)
P.shock(usr, 50)
if(WIRE_SHOCK)
if(mend)
P.stat &= ~NOPOWER
else
P.stat |= NOPOWER

View File

@@ -846,7 +846,7 @@
return
/obj/machinery/power/apc/oui_canview(mob/user)
if(user.has_unlimited_silicon_privilege || area.hasSiliconAccessInArea(user))
if(area.hasSiliconAccessInArea(user)) //some APCs are mapped outside their assigned area, so this is required.
return TRUE
return ..()
@@ -864,7 +864,7 @@
if (H && !H.stealthmode && H.toggled)
abilitiesavail = TRUE
var/list/data = list(
"locked" = locked && !(integration_cog && is_servant_of_ratvar(user)) && !area.hasSiliconAccessInArea(user),
"locked" = locked && !(integration_cog && is_servant_of_ratvar(user)) && !area.hasSiliconAccessInArea(user, PRIVILEDGES_SILICON|PRIVILEDGES_DRONE),
"lock_nightshift" = nightshift_requires_auth,
"failTime" = failure_timer,
"isOperating" = operating,
@@ -874,7 +874,7 @@
"chargingStatus" = charging,
"totalLoad" = DisplayPower(lastused_total),
"coverLocked" = coverlocked,
"siliconUser" = user.has_unlimited_silicon_privilege || user.using_power_flow_console() || area.hasSiliconAccessInArea(user),
"siliconUser" = user.using_power_flow_console() || area.hasSiliconAccessInArea(user),
"malfStatus" = get_malf_status(user),
"emergencyLights" = !emergency_lights,
"nightshiftLights" = nightshift_lights,
@@ -951,7 +951,7 @@
return TRUE
if (user == hijacker || (area.hasSiliconAccessInArea(user) && !aidisabled))
return TRUE
if(user.has_unlimited_silicon_privilege)
if(user.silicon_privileges & PRIVILEDGES_SILICON)
var/mob/living/silicon/ai/AI = user
var/mob/living/silicon/robot/robot = user
if (src.aidisabled || malfhack && istype(malfai) && ((istype(AI) && (malfai!=AI && malfai != AI.parent)) || (istype(robot) && (robot in malfai.connected_robots))))
@@ -985,7 +985,7 @@
if (action == "hijack" && can_use(usr, 1)) //don't need auth for hijack button
hijack(usr)
return
var/authorized = (!locked || usr.has_unlimited_silicon_privilege || area.hasSiliconAccessInArea(usr) || (integration_cog && (is_servant_of_ratvar(usr))))
var/authorized = (!locked || area.hasSiliconAccessInArea(usr, PRIVILEDGES_SILICON|PRIVILEDGES_DRONE) || (integration_cog && (is_servant_of_ratvar(usr))))
if((action == "toggle_nightshift") && (!nightshift_requires_auth || authorized))
toggle_nightshift_lights()
return TRUE
@@ -993,7 +993,7 @@
return
switch(action)
if("lock")
if(usr.has_unlimited_silicon_privilege || area.hasSiliconAccessInArea(usr))
if(area.hasSiliconAccessInArea(usr))
if((obj_flags & EMAGGED) || (stat & (BROKEN|MAINT)))
to_chat(usr, "The APC does not respond to the command.")
else
@@ -1027,7 +1027,7 @@
update()
return TRUE
if("overload")
if(usr.has_unlimited_silicon_privilege || area.hasSiliconAccessInArea(usr))
if(area.hasSiliconAccessInArea(usr))
overload_lighting()
return TRUE
if("hack")

View File

@@ -357,16 +357,10 @@
maxcharge = 12000
chargerate = 600
/obj/item/stock_parts/cell/magnetic/empty
start_charged = FALSE
/obj/item/stock_parts/cell/magnetic/pistol
name = "magpistol power supply"
maxcharge = 6000
/obj/item/stock_parts/cell/magnetic/pistol/empty
start_charged = FALSE
/obj/item/stock_parts/cell/toymagburst
name = "toy mag burst rifle power supply"
maxcharge = 4000

View File

@@ -24,7 +24,16 @@
/obj/machinery/power/solar/Initialize(mapload, obj/item/solar_assembly/S)
. = ..()
Make(S)
if(!S)
assembly = new /obj/item/solar_assembly
assembly.glass_type = new /obj/item/stack/sheet/glass(null, 2)
assembly.anchored = TRUE
else
S.moveToNullspace()
assembly = S
assembly.glass_type.on_solar_construction(src)
obj_integrity = max_integrity
update_icon()
connect_to_network()
/obj/machinery/power/solar/Destroy()
@@ -45,17 +54,6 @@
control.connected_panels.Remove(src)
control = null
/obj/machinery/power/solar/proc/Make(obj/item/solar_assembly/S)
if(!S)
S = new /obj/item/solar_assembly
S.glass_type = new /obj/item/stack/sheet/glass(null, 2)
S.anchored = TRUE
else
S.moveToNullspace()
S.glass_type.on_solar_construction(src)
obj_integrity = max_integrity
update_icon()
/obj/machinery/power/solar/crowbar_act(mob/user, obj/item/I)
playsound(src.loc, 'sound/machines/click.ogg', 50, 1)
user.visible_message("[user] begins to take the glass off [src].", "<span class='notice'>You begin to take the glass off [src]...</span>")
@@ -201,7 +199,7 @@
new shard(Tsec)
new shard(Tsec)
else if(glass_type)
forceMove(glass_type, Tsec)
glass_type.forceMove(Tsec)
glass_type = null
/obj/item/solar_assembly/attackby(obj/item/W, mob/user, params)
@@ -226,7 +224,8 @@
var/obj/item/stack/sheet/G = S.change_stack(null, 2)
if(G)
glass_type = G
playsound(src.loc, 'sound/machines/click.ogg', 50, 1)
G.moveToNullspace()
playsound(loc, 'sound/machines/click.ogg', 50, 1)
user.visible_message("[user] places the glass on the solar assembly.", "<span class='notice'>You place the glass on the solar assembly.</span>")
if(tracker)
new /obj/machinery/power/tracker(get_turf(src), src)

View File

@@ -346,6 +346,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
// Pass all the gas related code an empty gas container
removed = new()
damage = min(damage_archived + (DAMAGE_HARDCAP * explosion_point),damage) // hardcap any direct damage taken before doing atmos damage
damage_archived = damage
if(!removed || !removed.total_moles() || isspaceturf(T)) //we're in space or there is no gas to process
if(takes_damage)

View File

@@ -20,7 +20,15 @@
/obj/machinery/power/tracker/Initialize(mapload, obj/item/solar_assembly/S)
. = ..()
Make(S)
if(!S)
assembly = new /obj/item/solar_assembly
assembly.glass_type = new /obj/item/stack/sheet/glass(null, 2)
assembly.tracker = TRUE
assembly.anchored = TRUE
else
S.moveToNullspace()
assembly = S
update_icon()
connect_to_network()
/obj/machinery/power/tracker/Destroy()
@@ -41,16 +49,6 @@
control.connected_tracker = null
control = null
/obj/machinery/power/tracker/proc/Make(obj/item/solar_assembly/S)
if(!S)
S = new /obj/item/solar_assembly
S.glass_type = new /obj/item/stack/sheet/glass(null, 2)
S.tracker = TRUE
S.anchored = TRUE
else
S.moveToNullspace()
update_icon()
//updates the tracker icon and the facing angle for the control computer
/obj/machinery/power/tracker/proc/set_angle(angle)
sun_angle = angle

View File

@@ -5,4 +5,22 @@
caliber = "arrow"
icon_state = "arrow"
throwforce = 3 //good luck hitting someone with the pointy end of the arrow
throw_speed = 3
throw_speed = 3
/obj/item/ammo_casing/caseless/arrow/ashen
name = "ashen arrow"
desc = "Fire harderned wooden arrow."
icon_state = "asharrow"
projectile_type = /obj/item/projectile/bullet/reusable/arrow/ashen
/obj/item/ammo_casing/caseless/arrow/bone
name = "bone arrow"
desc = "Arrow made of bone and sinew. The tip is sharp enough to pierce into a goliath plate."
icon_state = "bonearrow"
projectile_type = /obj/item/projectile/bullet/reusable/arrow/bone
/obj/item/ammo_casing/caseless/arrow/bronze
name = "bronze arrow"
desc = "Bronze tipped arrow."
icon_state = "bronzearrow"
projectile_type = /obj/item/projectile/bullet/reusable/arrow/bronze

View File

@@ -50,4 +50,19 @@
icon_state = "bow_[get_ammo() ? (chambered ? "firing" : "loaded") : "unloaded"]"
/obj/item/gun/ballistic/bow/can_shoot()
return chambered
return chambered
/obj/item/gun/ballistic/bow/ashen
name = "bone bow"
desc = "Some sort of primitive projectile weapon made of bone and sinew. Used to fire arrows."
icon_state = "ashenbow"
item_state = "ashenbow"
force = 8
/obj/item/gun/ballistic/bow/pipe
name = "pipe bow"
desc = "Some sort of pipe made projectile weapon made of a silk string and lots of bending. Used to fire arrows."
icon_state = "pipebow"
item_state = "pipebow"
inaccuracy_modifier = 1.1 //Made of pipe and in a rush
force = 0

View File

@@ -61,7 +61,6 @@
/obj/item/gun/ballistic/automatic/magrifle/nopin
pin = null
spawnwithmagazine = FALSE
cell_type = /obj/item/stock_parts/cell/magnetic/empty
/obj/item/gun/ballistic/automatic/magrifle/hyperburst
name = "\improper Hyper-Burst Rifle"
@@ -100,4 +99,3 @@
/obj/item/gun/ballistic/automatic/magrifle/pistol/nopin
pin = null
spawnwithmagazine = FALSE
cell_type = /obj/item/stock_parts/cell/magnetic/pistol/empty

View File

@@ -67,7 +67,8 @@
/obj/item/gun/energy/Destroy()
if(flags_1 & INITIALIZED_1)
QDEL_NULL(cell)
QDEL_LIST(ammo_type)
if(!(flags_1 & HOLOGRAM_1)) //holodeck stuff.
QDEL_LIST(ammo_type)
STOP_PROCESSING(SSobj, src)
return ..()

View File

@@ -11,6 +11,7 @@
/obj/item/gun/energy/laser/practice
name = "practice laser gun"
icon_state = "laser-p"
desc = "A modified version of the basic laser gun, this one fires less concentrated energy bolts designed for target practice."
ammo_type = list(/obj/item/ammo_casing/energy/laser/practice)
clumsy_check = 0

View File

@@ -52,7 +52,6 @@
var/hitscan = FALSE //Whether this is hitscan. If it is, speed is basically ignored.
var/list/beam_segments //assoc list of datum/point or datum/point/vector, start = end. Used for hitscan effect generation.
var/datum/point/beam_index
var/turf/hitscan_last //last turf touched during hitscanning.
var/tracer_type
var/muzzle_type
var/impact_type
@@ -492,8 +491,6 @@
process_homing()
var/forcemoved = FALSE
for(var/i in 1 to SSprojectiles.global_iterations_per_move)
if(QDELETED(src))
return
trajectory.increment(trajectory_multiplier)
var/turf/T = trajectory.return_turf()
if(!istype(T))
@@ -506,14 +503,16 @@
forceMove(T)
trajectory_ignore_forcemove = FALSE
after_z_change(old, loc)
forcemoved = TRUE
if(QDELETED(src))
return
if(!hitscanning)
pixel_x = trajectory.return_px()
pixel_y = trajectory.return_py()
forcemoved = TRUE
hitscan_last = loc
else if(T != loc)
step_towards(src, T)
hitscan_last = loc
if(QDELETED(src))
return
if(!hitscanning && !forcemoved)
pixel_x = trajectory.return_px() - trajectory.mpx * trajectory_multiplier * SSprojectiles.global_iterations_per_move
pixel_y = trajectory.return_py() - trajectory.mpy * trajectory_multiplier * SSprojectiles.global_iterations_per_move

View File

@@ -7,10 +7,11 @@
var/zap_range = 3
var/power = 10000
/obj/item/projectile/energy/tesla/fire(setAngle)
if(firer)
chain = firer.Beam(src, icon_state = "lightning[rand(1, 12)]", time = INFINITY, maxdistance = INFINITY)
..()
/obj/item/projectile/energy/tesla/fire(setAngle, atom/direct_target)
var/atom/source = fired_from || firer
if(source)
chain = source.Beam(src, icon_state = "lightning[rand(1, 12)]", time = INFINITY, maxdistance = INFINITY)
return ..()
/obj/item/projectile/energy/tesla/on_hit(atom/target)
. = ..()

View File

@@ -3,4 +3,23 @@
desc = "Woosh!"
damage = 15
icon_state = "arrow"
ammo_type = /obj/item/ammo_casing/caseless/arrow
ammo_type = /obj/item/ammo_casing/caseless/arrow
/obj/item/projectile/bullet/reusable/arrow/ashen
name = "ashen arrow"
desc = "Fire harderned arrow."
damage = 25
ammo_type = /obj/item/ammo_casing/caseless/arrow/ashen
/obj/item/projectile/bullet/reusable/arrow/bone //AP for ashwalkers
name = "bone arrow"
desc = "Arrow made of bone and sinew."
damage = 35
armour_penetration = 40
ammo_type = /obj/item/ammo_casing/caseless/arrow/bone
/obj/item/projectile/bullet/reusable/arrow/bronze //Just some AP shots
name = "bronze arrow"
desc = "Bronze tipped arrow."
armour_penetration = 10
ammo_type = /obj/item/ammo_casing/caseless/arrow/bronze

View File

@@ -43,4 +43,34 @@
explosion(target, 0, 1, 2, 4)
else
explosion(target, 0, 0, 2, 4)
return BULLET_ACT_HIT
return BULLET_ACT_HIT
/obj/item/projectile/bullet/a84mm_br
name ="\improper HE missile"
desc = "Boom."
icon_state = "missile"
damage = 30
ricochets_max = 0 //it's a MISSILE
var/sturdy = list(
/turf/closed,
/obj/mecha,
/obj/machinery/door/,
/obj/machinery/door/poddoor/shutters
)
/obj/item/broken_missile
name = "\improper broken missile"
desc = "A missile that did not detonate. The tail has snapped and it is in no way fit to be used again."
icon = 'icons/obj/projectiles.dmi'
icon_state = "missile_broken"
w_class = WEIGHT_CLASS_TINY
/obj/item/projectile/bullet/a84mm_br/on_hit(atom/target, blocked=0)
..()
for(var/i in sturdy)
if(istype(target, i))
explosion(target, 0, 1, 1, 2)
return BULLET_ACT_HIT
//if(istype(target, /turf/closed) || ismecha(target))
new /obj/item/broken_missile(get_turf(src), 1)

View File

@@ -77,6 +77,8 @@
/datum/reagents/Destroy()
. = ..()
//We're about to delete all reagents, so lets cleanup
addiction_list.Cut()
var/list/cached_reagents = reagent_list
for(var/reagent in cached_reagents)
var/datum/reagent/R = reagent
@@ -332,9 +334,7 @@
if(R.addiction_stage3_end to R.addiction_stage4_end)
need_mob_update += R.addiction_act_stage4(C)
if(R.addiction_stage4_end to INFINITY)
to_chat(C, "<span class='notice'>You feel like you've gotten over your need for [R.name].</span>")
SEND_SIGNAL(C, COMSIG_CLEAR_MOOD_EVENT, "[R.type]_addiction")
cached_addictions.Remove(R)
remove_addiction(R)
else
SEND_SIGNAL(C, COMSIG_CLEAR_MOOD_EVENT, "[R.type]_overdose")
addiction_tick++
@@ -344,6 +344,12 @@
C.update_stamina()
update_total()
/datum/reagents/proc/remove_addiction(datum/reagent/R)
to_chat(my_atom, "<span class='notice'>You feel like you've gotten over your need for [R.name].</span>")
SEND_SIGNAL(my_atom, COMSIG_CLEAR_MOOD_EVENT, "[R.type]_overdose")
addiction_list.Remove(R)
qdel(R)
//Signals that metabolization has stopped, triggering the end of trait-based effects
/datum/reagents/proc/end_metabolization(mob/living/carbon/C, keep_liverless = TRUE)
var/list/cached_reagents = reagent_list
@@ -762,6 +768,8 @@
R.metabolizing = FALSE
R.on_mob_end_metabolize(M)
R.on_mob_delete(M)
//Clear from relevant lists
addiction_list -= R
qdel(R)
reagent_list -= R
update_total()

View File

@@ -408,7 +408,7 @@
if(beaker)
var/obj/item/reagent_containers/B = beaker
B.forceMove(drop_location())
if(user && Adjacent(user) && !issiliconoradminghost(user))
if(user && Adjacent(user) && user.can_hold_items())
user.put_in_hands(B)
if(new_beaker)
beaker = new_beaker

View File

@@ -38,7 +38,7 @@
/obj/machinery/chem_heater/proc/replace_beaker(mob/living/user, obj/item/reagent_containers/new_beaker)
if(beaker)
beaker.forceMove(drop_location())
if(user && Adjacent(user) && !issiliconoradminghost(user))
if(user && Adjacent(user) && user.can_hold_items())
user.put_in_hands(beaker)
if(new_beaker)
beaker = new_beaker

View File

@@ -129,7 +129,7 @@
if(beaker)
var/obj/item/reagent_containers/B = beaker
B.forceMove(drop_location())
if(user && Adjacent(user) && !issiliconoradminghost(user))
if(user && Adjacent(user) && user.can_hold_items())
user.put_in_hands(B)
if(new_beaker)
beaker = new_beaker
@@ -139,7 +139,7 @@
if(bottle)
var/obj/item/storage/pill_bottle/B = bottle
B.forceMove(drop_location())
if(user && Adjacent(user) && !issiliconoradminghost(user))
if(user && Adjacent(user) && user.can_hold_items())
user.put_in_hands(B)
else
adjust_item_drop_location(B)

View File

@@ -192,7 +192,7 @@
update_icon()
var/turf/source_turf = get_turf(src)
log_virus("A culture bottle was printed for the virus [A.admin_details()] at [loc_name(source_turf)] by [key_name(usr)]")
. = TRUE
if("create_vaccine_bottle")
wait = TRUE
@@ -202,9 +202,9 @@
var/obj/item/reagent_containers/glass/bottle/B = new(drop_location())
B.name = "[D.name] vaccine bottle"
B.reagents.add_reagent(/datum/reagent/vaccine, 15, list(id))
update_icon()
. = TRUE
/obj/machinery/computer/pandemic/attackby(obj/item/I, mob/user, params)
@@ -229,7 +229,7 @@
/obj/machinery/computer/pandemic/proc/replace_beaker(mob/living/user, obj/item/reagent_containers/new_beaker)
if(beaker)
if(user && Adjacent(user) && !issiliconoradminghost(user))
if(user && Adjacent(user) && user.can_hold_items())
if(!user.put_in_hands(beaker))
beaker.forceMove(drop_location())
if(new_beaker)

View File

@@ -75,7 +75,7 @@
/obj/machinery/reagentgrinder/proc/replace_beaker(mob/living/user, obj/item/reagent_containers/new_beaker)
if(beaker)
beaker.forceMove(drop_location())
if(user && Adjacent(user) && !issiliconoradminghost(user))
if(user && Adjacent(user) && user.can_hold_items())
user.put_in_hands(beaker)
if(new_beaker)
beaker = new_beaker

View File

@@ -1482,14 +1482,14 @@ datum/reagent/medicine/styptic_powder/overdose_start(mob/living/M)
/datum/reagent/medicine/polypyr //This is intended to be an ingredient in advanced chems.
name = "Polypyrylium Oligomers"
description = "A<EFBFBD>purple mixture of short polyelectrolyte chains not easily synthesized in the laboratory. It is valued as an intermediate in the synthesis of the cutting edge pharmaceuticals."
description = "A purple mixture of short polyelectrolyte chains not easily synthesized in the laboratory. It is valued as an intermediate in the synthesis of the cutting edge pharmaceuticals."
reagent_state = SOLID
color = "#9423FF"
metabolization_rate = 0.25 * REAGENTS_METABOLISM
overdose_threshold = 50
taste_description = "numbing bitterness"
/datum/reagent/medicine/polypyr/on_mob_life(mob/living/carbon/M) //I w<EFBFBD>nted a collection of small positive effects, this is as hard to obtain as coniine after all.
/datum/reagent/medicine/polypyr/on_mob_life(mob/living/carbon/M) //I wanted a collection of small positive effects, this is as hard to obtain as coniine after all.
M.adjustOrganLoss(ORGAN_SLOT_LUNGS, -0.25)
M.adjustBruteLoss(-0.35, 0)
if(prob(50))
@@ -1511,4 +1511,3 @@ datum/reagent/medicine/styptic_powder/overdose_start(mob/living/M)
M.adjustOrganLoss(ORGAN_SLOT_LUNGS, 0.5)
..()
. = 1

View File

@@ -93,8 +93,6 @@
taste_description = "something spicy"
pH = 6.85
/datum/reagent/blood/on_merge(list/mix_data)
if(data && mix_data)
if(data["blood_DNA"] != mix_data["blood_DNA"])
@@ -131,16 +129,10 @@
/datum/reagent/blood/synthetics
data = list("donor"=null,"viruses"=null,"blood_DNA"="REPLICATED", "bloodcolor" = BLOOD_COLOR_SYNTHETIC, "blood_type"="SY","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null)
name = "Synthetic Blood"
description = "A synthetically produced imitation of blood."
taste_description = "oily"
color = BLOOD_COLOR_SYNTHETIC // rgb: 11, 7, 48
/datum/reagent/blood/lizard
data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_LIZARD, "blood_type"="L","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null)
name = "Lizard Blood"
taste_description = "spicy"
color = BLOOD_COLOR_LIZARD // rgb: 11, 7, 48
pH = 6.85
/datum/reagent/blood/jellyblood
data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_SLIME, "blood_type"="GEL","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null)
name = "Slime Jelly Blood"
@@ -150,29 +142,6 @@
taste_mult = 1.3
pH = 4
/datum/reagent/blood/xenomorph
data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_XENO, "blood_type"="X*","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null)
name = "Xenomorph Blood"
taste_description = "acidic heresy"
color = BLOOD_COLOR_XENO // greenish yellow ooze
shot_glass_icon_state = "shotglassgreen"
pH = 2.5
/datum/reagent/blood/oil
data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_OIL, "blood_type"="HF","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null)
name = "Hydraulic Blood"
taste_description = "burnt oil"
color = BLOOD_COLOR_OIL // dark, y'know, expected batman colors.
pH = 9.75
/datum/reagent/blood/insect
data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_BUG, "blood_type"="BUG","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null)
name = "Insectoid Blood"
taste_description = "waxy"
color = BLOOD_COLOR_BUG // Bug colored, I guess.
pH = 7.25
/datum/reagent/blood/jellyblood/on_mob_life(mob/living/carbon/M)
if(prob(10))
if(M.dna?.species?.exotic_bloodtype != "GEL")
@@ -2139,3 +2108,10 @@
to_chat(M, "<span class='userlove'>You feel like playing with your [G.name]!</span>")
..()
/datum/reagent/preservahyde
name = "Preservahyde"
description = "A powerful preservation agent, utilizing the preservative effects of formaldehyde with significantly less of the histamine."
reagent_state = LIQUID
color = "#f7685e"
metabolization_rate = REAGENTS_METABOLISM * 0.25

View File

@@ -504,7 +504,7 @@
toxpwr = 0
/datum/reagent/toxin/itching_powder/reaction_mob(mob/living/M, method=TOUCH, reac_volume)
if(method == TOUCH || method == VAPOR)
if((method == TOUCH || method == VAPOR) && M.reagents)
M.reagents.add_reagent(/datum/reagent/toxin/itching_powder, reac_volume)
/datum/reagent/toxin/itching_powder/on_mob_life(mob/living/carbon/M)

View File

@@ -47,6 +47,12 @@
results = list(/datum/reagent/consumable/sodiumchloride = 3)
required_reagents = list(/datum/reagent/water = 1, /datum/reagent/sodium = 1, /datum/reagent/chlorine = 1)
/datum/chemical_reaction/preservahyde
name = "Preservahyde"
id = "preservahyde"
results = list(/datum/reagent/preservahyde = 3)
required_reagents = list(/datum/reagent/water = 1, /datum/reagent/toxin/formaldehyde = 1, /datum/reagent/bromine = 1)
/datum/chemical_reaction/plasmasolidification
name = "Solid Plasma"
id = "solidplasma"
@@ -228,7 +234,6 @@
var/level_max = 2
/datum/chemical_reaction/mix_virus/on_reaction(datum/reagents/holder, multiplier)
var/datum/reagent/blood/B = locate(/datum/reagent/blood) in holder.reagent_list
if(B && B.data)
var/datum/disease/advance/D = locate(/datum/disease/advance) in B.data["viruses"]
@@ -236,94 +241,131 @@
for(var/i in 1 to min(multiplier, 5))
D.Evolve(level_min, level_max)
/datum/chemical_reaction/mix_virus/mix_virus_2
/datum/chemical_reaction/mix_virus/synth
id = "mixvirus_synth"
required_catalysts = list(/datum/reagent/blood/synthetics = 1)
/datum/chemical_reaction/mix_virus/mix_virus_2
name = "Mix Virus 2"
id = "mixvirus2"
required_reagents = list(/datum/reagent/toxin/mutagen = 1)
level_min = 2
level_max = 4
/datum/chemical_reaction/mix_virus/mix_virus_3
/datum/chemical_reaction/mix_virus/mix_virus_2/synth
id = "mixvirus2_synth"
required_catalysts = list(/datum/reagent/blood/synthetics = 1)
/datum/chemical_reaction/mix_virus/mix_virus_3
name = "Mix Virus 3"
id = "mixvirus3"
required_reagents = list(/datum/reagent/toxin/plasma = 1)
level_min = 4
level_max = 6
/datum/chemical_reaction/mix_virus/mix_virus_4
/datum/chemical_reaction/mix_virus/mix_virus_3/synth
id = "mixvirus3_synth"
required_catalysts = list(/datum/reagent/blood/synthetics = 1)
/datum/chemical_reaction/mix_virus/mix_virus_4
name = "Mix Virus 4"
id = "mixvirus4"
required_reagents = list(/datum/reagent/uranium = 1)
level_min = 5
level_max = 6
/datum/chemical_reaction/mix_virus/mix_virus_5
/datum/chemical_reaction/mix_virus/mix_virus_4/synth
id = "mixvirus4_synth"
required_catalysts = list(/datum/reagent/blood/synthetics = 1)
/datum/chemical_reaction/mix_virus/mix_virus_5
name = "Mix Virus 5"
id = "mixvirus5"
required_reagents = list(/datum/reagent/toxin/mutagen/mutagenvirusfood = 1)
level_min = 3
level_max = 3
/datum/chemical_reaction/mix_virus/mix_virus_6
/datum/chemical_reaction/mix_virus/mix_virus_5/synth
id = "mixvirus5_synth"
required_catalysts = list(/datum/reagent/blood/synthetics = 1)
/datum/chemical_reaction/mix_virus/mix_virus_6
name = "Mix Virus 6"
id = "mixvirus6"
required_reagents = list(/datum/reagent/toxin/mutagen/mutagenvirusfood/sugar = 1)
level_min = 4
level_max = 4
/datum/chemical_reaction/mix_virus/mix_virus_7
/datum/chemical_reaction/mix_virus/mix_virus_6/synth
id = "mixvirus6_synth"
required_catalysts = list(/datum/reagent/blood/synthetics = 1)
/datum/chemical_reaction/mix_virus/mix_virus_7
name = "Mix Virus 7"
id = "mixvirus7"
required_reagents = list(/datum/reagent/toxin/plasma/plasmavirusfood/weak = 1)
level_min = 5
level_max = 5
/datum/chemical_reaction/mix_virus/mix_virus_8
/datum/chemical_reaction/mix_virus/mix_virus_7/synth
id = "mixvirus7_synth"
required_catalysts = list(/datum/reagent/blood/synthetics = 1)
/datum/chemical_reaction/mix_virus/mix_virus_8
name = "Mix Virus 8"
id = "mixvirus8"
required_reagents = list(/datum/reagent/toxin/plasma/plasmavirusfood = 1)
level_min = 6
level_max = 6
/datum/chemical_reaction/mix_virus/mix_virus_9
/datum/chemical_reaction/mix_virus/mix_virus_8/synth
id = "mixvirus8_synth"
required_catalysts = list(/datum/reagent/blood/synthetics = 1)
/datum/chemical_reaction/mix_virus/mix_virus_9
name = "Mix Virus 9"
id = "mixvirus9"
required_reagents = list(/datum/reagent/medicine/synaptizine/synaptizinevirusfood = 1)
level_min = 1
level_max = 1
/datum/chemical_reaction/mix_virus/mix_virus_10
/datum/chemical_reaction/mix_virus/mix_virus_9/synth
id = "mixvirus9_synth"
required_catalysts = list(/datum/reagent/blood/synthetics = 1)
/datum/chemical_reaction/mix_virus/mix_virus_10
name = "Mix Virus 10"
id = "mixvirus10"
required_reagents = list(/datum/reagent/uranium/uraniumvirusfood = 1)
level_min = 6
level_max = 7
/datum/chemical_reaction/mix_virus/mix_virus_11
/datum/chemical_reaction/mix_virus/mix_virus_10/synth
id = "mixvirus10_synth"
required_catalysts = list(/datum/reagent/blood/synthetics = 1)
/datum/chemical_reaction/mix_virus/mix_virus_11
name = "Mix Virus 11"
id = "mixvirus11"
required_reagents = list(/datum/reagent/uranium/uraniumvirusfood/unstable = 1)
level_min = 7
level_max = 7
/datum/chemical_reaction/mix_virus/mix_virus_12
/datum/chemical_reaction/mix_virus/mix_virus_11/synth
id = "mixvirus11_synth"
required_catalysts = list(/datum/reagent/blood/synthetics = 1)
/datum/chemical_reaction/mix_virus/mix_virus_12
name = "Mix Virus 12"
id = "mixvirus12"
required_reagents = list(/datum/reagent/uranium/uraniumvirusfood/stable = 1)
level_min = 8
level_max = 8
/datum/chemical_reaction/mix_virus/mix_virus_12/synth
id = "mixvirus12_synth"
required_catalysts = list(/datum/reagent/blood/synthetics = 1)
/datum/chemical_reaction/mix_virus/rem_virus
name = "Devolve Virus"
id = "remvirus"
@@ -338,6 +380,10 @@
for(var/i in 1 to min(multiplier, 5))
D.Devolve()
/datum/chemical_reaction/mix_virus/rem_virus/synth
id = "remvirus_synth"
required_catalysts = list(/datum/reagent/blood/synthetics = 1)
/datum/chemical_reaction/mix_virus/neuter_virus
name = "Neuter Virus"
id = "neutervirus"
@@ -352,6 +398,10 @@
for(var/i in 1 to min(multiplier, 5))
D.Neuter()
/datum/chemical_reaction/mix_virus/neuter_virus/synth
id = "neutervirus_synth"
required_catalysts = list(/datum/reagent/blood/synthetics = 1)
////////////////////////////////// foam and foam precursor ///////////////////////////////////////////////////

View File

@@ -168,7 +168,7 @@ GLOBAL_LIST_INIT(food_reagents, build_reagents_to_food()) //reagentid = related
/obj/item/paper/secretrecipe
name = "old recipe"
var/recipe_id = "secretsauce"
var/recipe_id = /datum/reagent/consumable/secretsauce
/obj/item/paper/secretrecipe/examine(mob/user) //Extra secret
if(isobserver(user))

View File

@@ -431,4 +431,4 @@
/obj/item/reagent_containers/glass/bottle/hexacamphor
name = "Hexacamphor bottle"
desc = "A bottle of strong anaphrodisiac. Reduces libido."
list_reagents = list(/datum/reagent/drug/anaphrodisiacplus = 30)
list_reagents = list(/datum/reagent/drug/anaphrodisiacplus = 30)

View File

@@ -95,7 +95,7 @@
ignore_flags = 1 //so you can medipen through hardsuits
reagent_flags = DRAWABLE
flags_1 = null
list_reagents = list(/datum/reagent/medicine/epinephrine = 10, /datum/reagent/toxin/formaldehyde = 3)
list_reagents = list(/datum/reagent/medicine/epinephrine = 10, /datum/reagent/preservahyde = 3)
/obj/item/reagent_containers/hypospray/medipen/suicide_act(mob/living/carbon/user)
user.visible_message("<span class='suicide'>[user] begins to choke on \the [src]! It looks like [user.p_theyre()] trying to commit suicide!</span>")

View File

@@ -53,7 +53,7 @@
SEND_SIGNAL(A, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_MEDIUM)
return
/obj/item/reagent_containers/rag/pre_altattackby(mob/living/M, mob/living/user, params)
/obj/item/reagent_containers/rag/alt_pre_attack(mob/living/M, mob/living/user, params)
if(istype(M) && user.a_intent == INTENT_HELP)
user.changeNext_move(CLICK_CD_MELEE)
if(M.on_fire)

View File

@@ -3,7 +3,7 @@
///////////////////////////////////
/datum/design/milk
name = "10 Milk"
name = "10u Milk"
id = "milk"
build_type = BIOGENERATOR
materials = list(MAT_BIOMASS = 20)
@@ -11,7 +11,7 @@
category = list("initial","Food")
/datum/design/cream
name = "10 Cream"
name = "10u Cream"
id = "cream"
build_type = BIOGENERATOR
materials = list(MAT_BIOMASS = 30)
@@ -123,6 +123,22 @@
build_path = /obj/item/reagent_containers/glass/bottle/killer/pestkiller
category = list("initial","Botany Chemicals")
/datum/design/ammonia
name = "10u Ammonia"
id = "ammonia_biogen"
build_type = BIOGENERATOR
materials = list(MAT_BIOMASS = 25)
make_reagents = list(/datum/reagent/ammonia = 10)
category = list("initial","Botany Chemicals")
/datum/design/saltpetre
name = "10u Saltpetre"
id = "saltpetre_biogen"
build_type = BIOGENERATOR
materials = list(MAT_BIOMASS = 75)
make_reagents = list(/datum/reagent/saltpetre = 10)
category = list("initial","Botany Chemicals")
/datum/design/botany_bottle
name = "Empty Bottle"
id = "botany_bottle"

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