diff --git a/code/datums/wires/camera.dm b/code/datums/wires/camera.dm
index d03da5fa17..35bb07cbc5 100644
--- a/code/datums/wires/camera.dm
+++ b/code/datums/wires/camera.dm
@@ -58,7 +58,7 @@ var/const/CAMERA_WIRE_NOTHING2 = 32
C.setViewRange(new_range)
if(CAMERA_WIRE_POWER)
- C.deactivate(null) // Deactivate the camera
+ C.kick_viewers() // Kicks anyone watching the camera
if(CAMERA_WIRE_LIGHT)
C.light_disabled = !C.light_disabled
diff --git a/code/defines/procs/admin.dm b/code/defines/procs/admin.dm
index abf4c97dd3..2bfb8b9436 100644
--- a/code/defines/procs/admin.dm
+++ b/code/defines/procs/admin.dm
@@ -1,7 +1,13 @@
proc/log_and_message_admins(var/message as text)
- log_admin(usr ? "[usr]([usr.ckey]) [message]" : "EVENT [message]")
- message_admins(usr ? "[usr]([usr.ckey]) [message]" : "EVENT [message]")
+ log_admin(usr ? "[key_name(usr)] [message]" : "EVENT [message]")
+ message_admins(usr ? "[key_name(usr)] [message]" : "EVENT [message]")
proc/admin_log_and_message_admins(var/message as text)
- log_admin(usr ? "[key_name(usr)] [message]" : "EVENT [message]")
- message_admins(usr ? "[key_name(usr)] [message]" : "EVENT [message]", 1)
+ log_admin(usr ? "[key_name_admin(usr)] [message]" : "EVENT [message]")
+ message_admins(usr ? "[key_name_admin(usr)] [message]" : "EVENT [message]", 1)
+
+proc/admin_attack_log(var/mob/attacker, var/mob/victim, var/attacker_message, var/victim_message, var/admin_message)
+ victim.attack_log += text("\[[time_stamp()]\] [victim_message] [key_name(attacker)]")
+ attacker.attack_log += text("\[[time_stamp()]\] [attacker_message] [key_name(victim)]")
+
+ msg_admin_attack("[key_name(attacker)] [admin_message] [key_name(victim)] (INTENT: [uppertext(attacker.a_intent)]) (JMP)")
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index 71894c2983..a286b530ca 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -37,6 +37,10 @@ var/const/HOLOPAD_MODE = RANGE_BASED
icon_state = "holopad0"
layer = TURF_LAYER+0.1 //Preventing mice and drones from sneaking under them.
+
+ var/power_per_hologram = 500 //per usage per hologram
+ idle_power_usage = 5
+ use_power = 1
var/list/mob/living/silicon/ai/masters = new() //List of AIs that use the holopad
var/last_request = 0 //to prevent request spam. ~Carn
@@ -122,7 +126,6 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
SetLuminosity(2) //pad lighting
icon_state = "holopad1"
A.holo = src
- use_power += HOLOGRAM_POWER_USAGE
return 1
/obj/machinery/hologram/holopad/proc/clear_holo(mob/living/silicon/ai/user)
@@ -130,29 +133,31 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
user.holo = null
del(masters[user])//Get rid of user's hologram //qdel
masters -= user //Discard AI from the list of those who use holopad
- use_power = max(HOLOPAD_PASSIVE_POWER_USAGE, use_power - HOLOGRAM_POWER_USAGE)//Reduce power usage
if (!masters.len)//If no users left
SetLuminosity(0) //pad lighting (hologram lighting will be handled automatically since its owner was deleted)
icon_state = "holopad0"
- use_power = HOLOPAD_PASSIVE_POWER_USAGE
return 1
/obj/machinery/hologram/holopad/process()
for (var/mob/living/silicon/ai/master in masters)
- if(master && !master.stat && master.client && master.eyeobj)//If there is an AI attached, it's not incapacitated, it has a client, and the client eye is centered on the projector.
- if(!(stat & NOPOWER))//If the machine has power.
- if((HOLOPAD_MODE == RANGE_BASED && (get_dist(master.eyeobj, src) <= holo_range)))
- return 1
+ var/active_ai = (master && !master.stat && master.client && master.eyeobj)//If there is an AI attached, it's not incapacitated, it has a client, and the client eye is centered on the projector.
+ if((stat & NOPOWER) || !active_ai)
+ clear_holo(master)
+ continue
+
+ if((HOLOPAD_MODE == RANGE_BASED && (get_dist(master.eyeobj, src) > holo_range)))
+ clear_holo(master)
+ continue
+
+ if(HOLOPAD_MODE == AREA_BASED)
+ var/area/holo_area = get_area(src)
+ var/area/eye_area = get_area(master.eyeobj)
+
+ if(!(eye_area in holo_area.master.related))
+ clear_holo(master)
+ continue
- else if (HOLOPAD_MODE == AREA_BASED)
-
- var/area/holo_area = get_area(src)
- var/area/eye_area = get_area(master.eyeobj)
-
- if(eye_area in holo_area.master.related)
- return 1
-
- clear_holo(master)//If not, we want to get rid of the hologram.
+ use_power(power_per_hologram)
return 1
/obj/machinery/hologram/holopad/proc/move_hologram(mob/living/silicon/ai/user)
diff --git a/code/modules/clothing/under/accessories/storage.dm b/code/modules/clothing/under/accessories/storage.dm
index 77f32edb9a..6c6c683b27 100644
--- a/code/modules/clothing/under/accessories/storage.dm
+++ b/code/modules/clothing/under/accessories/storage.dm
@@ -6,6 +6,7 @@
slot = "utility"
var/slots = 3
var/obj/item/weapon/storage/internal/hold
+ w_class = 3.0
/obj/item/clothing/accessory/storage/New()
..()
@@ -48,7 +49,7 @@
/obj/item/clothing/accessory/storage/webbing
name = "webbing"
- desc = "Strudy mess of synthcotton belts and buckles, ready to share your burden."
+ desc = "Sturdy mess of synthcotton belts and buckles, ready to share your burden."
icon_state = "webbing"
item_color = "webbing"
@@ -83,4 +84,4 @@
/obj/item/weapon/kitchenknife/ritual)
new /obj/item/weapon/hatchet/unathiknife(hold)
- new /obj/item/weapon/hatchet/unathiknife(hold)
\ No newline at end of file
+ new /obj/item/weapon/hatchet/unathiknife(hold)
diff --git a/code/modules/holodeck/HolodeckControl.dm b/code/modules/holodeck/HolodeckControl.dm
index fdc882eee6..8ae14ea2db 100644
--- a/code/modules/holodeck/HolodeckControl.dm
+++ b/code/modules/holodeck/HolodeckControl.dm
@@ -28,7 +28,7 @@ var/global/list/holodeck_programs = list(
var/area/linkedholodeck = null
var/area/target = null
var/active = 0
- var/list/holographic_items = list()
+ var/list/holographic_objs = list()
var/list/holographic_mobs = list()
var/damaged = 0
var/safety_disabled = 0
@@ -208,7 +208,7 @@ var/global/list/holodeck_programs = list(
emergencyShutdown()
/obj/machinery/computer/HolodeckControl/process()
- for(var/item in holographic_items) // do this first, to make sure people don't take items out when power is down.
+ for(var/item in holographic_objs) // do this first, to make sure people don't take items out when power is down.
if(!(get_turf(item) in linkedholodeck))
derez(item, 0)
@@ -221,7 +221,7 @@ var/global/list/holodeck_programs = list(
if(!..())
return
if(active)
- use_power(item_power_usage * (holographic_items.len + holographic_mobs.len))
+ use_power(item_power_usage * (holographic_objs.len + holographic_mobs.len))
if(!checkInteg(linkedholodeck))
damaged = 1
@@ -243,7 +243,7 @@ var/global/list/holodeck_programs = list(
T.hotspot_expose(1000,500,1)
/obj/machinery/computer/HolodeckControl/proc/derez(var/obj/obj , var/silent = 1)
- holographic_items.Remove(obj)
+ holographic_objs.Remove(obj)
if(obj == null)
return
@@ -271,7 +271,7 @@ var/global/list/holodeck_programs = list(
if(toggleOn)
var/area/targetsource = locate(/area/holodeck/source_emptycourt)
- holographic_items = targetsource.copy_contents_to(linkedholodeck)
+ holographic_objs = targetsource.copy_contents_to(linkedholodeck)
spawn(30)
for(var/obj/effect/landmark/L in linkedholodeck)
@@ -288,7 +288,7 @@ var/global/list/holodeck_programs = list(
active = 1
use_power = 2
else
- for(var/item in holographic_items)
+ for(var/item in holographic_objs)
derez(item)
if(!linkedholodeck.has_gravity)
linkedholodeck.gravitychange(1,linkedholodeck)
@@ -313,7 +313,7 @@ var/global/list/holodeck_programs = list(
active = 1
use_power = 2
- for(var/item in holographic_items)
+ for(var/item in holographic_objs)
derez(item)
for(var/mob/living/simple_animal/hostile/carp/holodeck/C in holographic_mobs)
@@ -323,7 +323,9 @@ var/global/list/holodeck_programs = list(
for(var/obj/effect/decal/cleanable/blood/B in linkedholodeck)
del(B)
- holographic_items = A.copy_contents_to(linkedholodeck , 1)
+ holographic_objs = A.copy_contents_to(linkedholodeck , 1)
+ for(var/obj/holo_obj in holographic_objs)
+ holo_obj.alpha *= 0.8 //give holodeck objs a slight transparency
spawn(30)
for(var/obj/effect/landmark/L in linkedholodeck)
@@ -366,7 +368,7 @@ var/global/list/holodeck_programs = list(
/obj/machinery/computer/HolodeckControl/proc/emergencyShutdown()
//Get rid of any items
- for(var/item in holographic_items)
+ for(var/item in holographic_objs)
derez(item)
for(var/mob/living/simple_animal/hostile/carp/holodeck/C in holographic_mobs)
holographic_mobs -= C
diff --git a/code/modules/holodeck/HolodeckObjects.dm b/code/modules/holodeck/HolodeckObjects.dm
index 460f4422fb..7a7526b150 100644
--- a/code/modules/holodeck/HolodeckObjects.dm
+++ b/code/modules/holodeck/HolodeckObjects.dm
@@ -392,10 +392,15 @@
icon_state = "holo4"
icon_living = "holo4"
icon_dead = "holo4"
+ alpha = 127
icon_gib = null
meat_amount = 0
meat_type = null
+/mob/living/simple_animal/hostile/carp/holodeck/New()
+ ..()
+ SetLuminosity(2) //hologram lighting
+
/mob/living/simple_animal/hostile/carp/holodeck/proc/set_safety(var/safe)
if (safe)
faction = "neutral"
diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm
index 804b0347be..cbae0f8f40 100644
--- a/code/modules/reagents/reagent_containers/pill.dm
+++ b/code/modules/reagents/reagent_containers/pill.dm
@@ -196,7 +196,7 @@
icon_state = "pill8"
New()
..()
- reagents.add_reagent("dexalin", 15)
+ reagents.add_reagent("dexalinp", 15)
/obj/item/weapon/reagent_containers/pill/dermaline
name = "Dermaline pill"
diff --git a/code/modules/spells/mind_transfer.dm b/code/modules/spells/mind_transfer.dm
index 161ed3516d..a4f8258ff7 100644
--- a/code/modules/spells/mind_transfer.dm
+++ b/code/modules/spells/mind_transfer.dm
@@ -1,114 +1,116 @@
-/obj/effect/proc_holder/spell/targeted/mind_transfer
- name = "Mind Transfer"
- desc = "This spell allows the user to switch bodies with a target."
-
- school = "transmutation"
- charge_max = 600
- clothes_req = 0
- invocation = "GIN'YU CAPAN"
- invocation_type = "whisper"
- range = 1
- var/list/protected_roles = list("Wizard","Changeling","Cultist") //which roles are immune to the spell
- var/list/compatible_mobs = list(/mob/living/carbon/human) //which types of mobs are affected by the spell. NOTE: change at your own risk
- var/base_spell_loss_chance = 20 //base probability of the wizard losing a spell in the process
- var/spell_loss_chance_modifier = 7 //amount of probability of losing a spell added per spell (mind_transfer included)
- var/spell_loss_amount = 1 //the maximum amount of spells possible to lose during a single transfer
- var/msg_wait = 500 //how long in deciseconds it waits before telling that body doesn't feel right or mind swap robbed of a spell
- var/paralysis_amount_caster = 20 //how much the caster is paralysed for after the spell
- var/paralysis_amount_victim = 20 //how much the victim is paralysed for after the spell
-
-/*
-Urist: I don't feel like figuring out how you store object spells so I'm leaving this for you to do.
-Make sure spells that are removed from spell_list are actually removed and deleted when mind transfering.
-Also, you never added distance checking after target is selected. I've went ahead and did that.
-*/
-/obj/effect/proc_holder/spell/targeted/mind_transfer/cast(list/targets,mob/user = usr)
- if(!targets.len)
- user << "No mind found."
- return
-
- if(targets.len > 1)
- user << "Too many minds! You're not a hive damnit!"//Whaa...aat?
- return
-
- var/mob/living/target = targets[1]
-
- if(!(target in oview(range)))//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it.
- user << "They are too far away!"
- return
-
- if(!(target.type in compatible_mobs))
- user << "Their mind isn't compatible with yours."
- return
-
- if(target.stat == DEAD)
- user << "You didn't study necromancy back at the Space Wizard Federation academy."
- return
-
- if(!target.key || !target.mind)
- user << "They appear to be catatonic. Not even magic can affect their vacant mind."
- return
-
- if(target.mind.special_role in protected_roles)
- user << "Their mind is resisting your spell."
- return
-
- var/mob/living/victim = target//The target of the spell whos body will be transferred to.
- var/mob/caster = user//The wizard/whomever doing the body transferring.
-
- //SPELL LOSS BEGIN
- //NOTE: The caster must ALWAYS keep mind transfer, even when other spells are lost.
- var/obj/effect/proc_holder/spell/targeted/mind_transfer/m_transfer = locate() in user.spell_list//Find mind transfer directly.
- var/list/checked_spells = user.spell_list
- checked_spells -= m_transfer //Remove Mind Transfer from the list.
-
- if(caster.spell_list.len)//If they have any spells left over after mind transfer is taken out. If they don't, we don't need this.
- for(var/i=spell_loss_amount,(i>0&&checked_spells.len),i--)//While spell loss amount is greater than zero and checked_spells has spells in it, run this proc.
- for(var/j=checked_spells.len,(j>0&&checked_spells.len),j--)//While the spell list to check is greater than zero and has spells in it, run this proc.
- if(prob(base_spell_loss_chance))
- checked_spells -= pick(checked_spells)//Pick a random spell to remove.
- spawn(msg_wait)
- victim << "The mind transfer has robbed you of a spell."
- break//Spell lost. Break loop, going back to the previous for() statement.
- else//Or keep checking, adding spell chance modifier to increase chance of losing a spell.
- base_spell_loss_chance += spell_loss_chance_modifier
-
- checked_spells += m_transfer//Add back Mind Transfer.
- user.spell_list = checked_spells//Set user spell list to whatever the new list is.
- //SPELL LOSS END
-
- //MIND TRANSFER BEGIN
- if(caster.mind.special_verbs.len)//If the caster had any special verbs, remove them from the mob verb list.
- for(var/V in caster.mind.special_verbs)//Since the caster is using an object spell system, this is mostly moot.
- caster.verbs -= V//But a safety nontheless.
-
- if(victim.mind.special_verbs.len)//Now remove all of the victim's verbs.
- for(var/V in victim.mind.special_verbs)
- victim.verbs -= V
-
- var/mob/dead/observer/ghost = victim.ghostize(0)
- ghost.spell_list = victim.spell_list//If they have spells, transfer them. Now we basically have a backup mob.
-
- caster.mind.transfer_to(victim)
- victim.spell_list = caster.spell_list//Now they are inside the victim's body.
-
- if(victim.mind.special_verbs.len)//To add all the special verbs for the original caster.
- for(var/V in caster.mind.special_verbs)//Not too important but could come into play.
- caster.verbs += V
-
- ghost.mind.transfer_to(caster)
- caster.key = ghost.key //have to transfer the key since the mind was not active
- caster.spell_list = ghost.spell_list
-
- if(caster.mind.special_verbs.len)//If they had any special verbs, we add them here.
- for(var/V in caster.mind.special_verbs)
- caster.verbs += V
- //MIND TRANSFER END
-
- //Here we paralyze both mobs and knock them out for a time.
- caster.Paralyse(paralysis_amount_caster)
- victim.Paralyse(paralysis_amount_victim)
-
- //After a certain amount of time the victim gets a message about being in a different body.
- spawn(msg_wait)
- caster << "\red You feel woozy and lightheaded. Your body doesn't seem like your own."
+/obj/effect/proc_holder/spell/targeted/mind_transfer
+ name = "Mind Transfer"
+ desc = "This spell allows the user to switch bodies with a target."
+
+ school = "transmutation"
+ charge_max = 600
+ clothes_req = 0
+ invocation = "GIN'YU CAPAN"
+ invocation_type = "whisper"
+ range = 1
+ var/list/protected_roles = list("Wizard","Changeling","Cultist") //which roles are immune to the spell
+ var/list/compatible_mobs = list(/mob/living/carbon/human) //which types of mobs are affected by the spell. NOTE: change at your own risk
+ var/base_spell_loss_chance = 20 //base probability of the wizard losing a spell in the process
+ var/spell_loss_chance_modifier = 7 //amount of probability of losing a spell added per spell (mind_transfer included)
+ var/spell_loss_amount = 1 //the maximum amount of spells possible to lose during a single transfer
+ var/msg_wait = 500 //how long in deciseconds it waits before telling that body doesn't feel right or mind swap robbed of a spell
+ var/paralysis_amount_caster = 20 //how much the caster is paralysed for after the spell
+ var/paralysis_amount_victim = 20 //how much the victim is paralysed for after the spell
+
+/*
+Urist: I don't feel like figuring out how you store object spells so I'm leaving this for you to do.
+Make sure spells that are removed from spell_list are actually removed and deleted when mind transfering.
+Also, you never added distance checking after target is selected. I've went ahead and did that.
+*/
+/obj/effect/proc_holder/spell/targeted/mind_transfer/cast(list/targets,mob/user = usr)
+ if(!targets.len)
+ user << "No mind found."
+ return
+
+ if(targets.len > 1)
+ user << "Too many minds! You're not a hive damnit!"//Whaa...aat?
+ return
+
+ var/mob/living/target = targets[1]
+
+ if(!(target in oview(range)))//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it.
+ user << "They are too far away!"
+ return
+
+ if(!(target.type in compatible_mobs))
+ user << "Their mind isn't compatible with yours."
+ return
+
+ if(target.stat == DEAD)
+ user << "You didn't study necromancy back at the Space Wizard Federation academy."
+ return
+
+ if(!target.key || !target.mind)
+ user << "They appear to be catatonic. Not even magic can affect their vacant mind."
+ return
+
+ if(target.mind.special_role in protected_roles)
+ user << "Their mind is resisting your spell."
+ return
+
+ var/mob/living/victim = target//The target of the spell whos body will be transferred to.
+ var/mob/caster = user//The wizard/whomever doing the body transferring.
+
+ //SPELL LOSS BEGIN
+ admin_attack_log(caster, victim, "Used mind transfer on", "Had mind transfer used on him by", "used mind transfer on")
+
+ //NOTE: The caster must ALWAYS keep mind transfer, even when other spells are lost.
+ var/obj/effect/proc_holder/spell/targeted/mind_transfer/m_transfer = locate() in user.spell_list//Find mind transfer directly.
+ var/list/checked_spells = user.spell_list
+ checked_spells -= m_transfer //Remove Mind Transfer from the list.
+
+ if(caster.spell_list.len)//If they have any spells left over after mind transfer is taken out. If they don't, we don't need this.
+ for(var/i=spell_loss_amount,(i>0&&checked_spells.len),i--)//While spell loss amount is greater than zero and checked_spells has spells in it, run this proc.
+ for(var/j=checked_spells.len,(j>0&&checked_spells.len),j--)//While the spell list to check is greater than zero and has spells in it, run this proc.
+ if(prob(base_spell_loss_chance))
+ checked_spells -= pick(checked_spells)//Pick a random spell to remove.
+ spawn(msg_wait)
+ victim << "The mind transfer has robbed you of a spell."
+ break//Spell lost. Break loop, going back to the previous for() statement.
+ else//Or keep checking, adding spell chance modifier to increase chance of losing a spell.
+ base_spell_loss_chance += spell_loss_chance_modifier
+
+ checked_spells += m_transfer//Add back Mind Transfer.
+ user.spell_list = checked_spells//Set user spell list to whatever the new list is.
+ //SPELL LOSS END
+
+ //MIND TRANSFER BEGIN
+ if(caster.mind.special_verbs.len)//If the caster had any special verbs, remove them from the mob verb list.
+ for(var/V in caster.mind.special_verbs)//Since the caster is using an object spell system, this is mostly moot.
+ caster.verbs -= V//But a safety nontheless.
+
+ if(victim.mind.special_verbs.len)//Now remove all of the victim's verbs.
+ for(var/V in victim.mind.special_verbs)
+ victim.verbs -= V
+
+ var/mob/dead/observer/ghost = victim.ghostize(0)
+ ghost.spell_list = victim.spell_list//If they have spells, transfer them. Now we basically have a backup mob.
+
+ caster.mind.transfer_to(victim)
+ victim.spell_list = caster.spell_list//Now they are inside the victim's body.
+
+ if(victim.mind.special_verbs.len)//To add all the special verbs for the original caster.
+ for(var/V in caster.mind.special_verbs)//Not too important but could come into play.
+ caster.verbs += V
+
+ ghost.mind.transfer_to(caster)
+ caster.key = ghost.key //have to transfer the key since the mind was not active
+ caster.spell_list = ghost.spell_list
+
+ if(caster.mind.special_verbs.len)//If they had any special verbs, we add them here.
+ for(var/V in caster.mind.special_verbs)
+ caster.verbs += V
+ //MIND TRANSFER END
+
+ //Here we paralyze both mobs and knock them out for a time.
+ caster.Paralyse(paralysis_amount_caster)
+ victim.Paralyse(paralysis_amount_victim)
+
+ //After a certain amount of time the victim gets a message about being in a different body.
+ spawn(msg_wait)
+ caster << "\red You feel woozy and lightheaded. Your body doesn't seem like your own."