"
return out.Join("")
diff --git a/code/datums/skills/_skill_modifier.dm b/code/datums/skills/_skill_modifier.dm
new file mode 100644
index 0000000000..a28cf3aebd
--- /dev/null
+++ b/code/datums/skills/_skill_modifier.dm
@@ -0,0 +1,204 @@
+GLOBAL_LIST_EMPTY_TYPED(skill_modifiers, /datum/skill_modifier)
+GLOBAL_LIST_EMPTY(potential_skills_per_mod)
+GLOBAL_LIST_EMPTY(potential_mods_per_skill)
+
+/**
+ * Base skill modifier datum, used to modify a player skills without directly touching their values, levels and affinity
+ * and cause lots of edge cases. These are fairly simple overall... make a subtype though, don't use this one.
+ */
+/datum/skill_modifier
+ /// flags for this skill modifier.
+ var/modifier_flags = NONE
+ /// target skills, can be a specific skill typepath or a list of skill traits.
+ var/target_skills = /datum/skill
+ /// the GLOB.potential_skills_per_mod key generated on runtime. You shouldn't be var-editing it.
+ var/target_skills_key
+ /// The identifier key this skill modifier is associated with.
+ var/identifier
+ /// skill affinity modifier, can be a multiplier or addendum, depending on the modifier_flags.
+ var/affinity_mod = 1
+ /// skill value modifier, see above.
+ var/value_mod = 1
+ /// skill level modifier, see above.
+ var/level_mod = 1
+ /// Priority of this skill modifier compared to other ones.
+ var/priority = MODIFIER_SKILL_PRIORITY_DEF
+
+/datum/skill_modifier/New(id, register = FALSE)
+ identifier = GET_SKILL_MOD_ID(type, id)
+ if(id)
+ var/former_id = identifier
+ var/dupe = 0
+ while(GLOB.skill_modifiers[identifier])
+ identifier = "[former_id][++dupe]"
+
+ if(register)
+ register()
+
+/datum/skill_modifier/proc/register()
+ if(GLOB.skill_modifiers[identifier])
+ CRASH("Skill modifier identifier \"[identifier]\" already taken.")
+ GLOB.skill_modifiers[identifier] = src
+ if(ispath(target_skills))
+ target_skills_key = target_skills
+ var/list/mod_L = GLOB.potential_mods_per_skill[target_skills]
+ if(!mod_L)
+ mod_L = GLOB.potential_mods_per_skill[target_skills] = list()
+ else
+ BINARY_INSERT(identifier, mod_L, datum/skill_modifier, src, priority, COMPARE_VALUE)
+ mod_L[identifier] = src
+ GLOB.potential_skills_per_mod[target_skills_key] = list(target_skills)
+ else //Should be a list.
+ var/list/T = target_skills
+ T = sortTim(target_skills, /proc/cmp_text_asc) //Sort the list contents alphabetically.
+ target_skills_key = T.Join("-")
+ var/list/L = GLOB.potential_skills_per_mod[target_skills_key]
+ if(!L)
+ L = list()
+ for(var/path in GLOB.skill_datums)
+ if(GLOB.skill_datums[path].skill_traits & target_skills)
+ L += path
+ GLOB.potential_skills_per_mod[target_skills_key] = L
+ for(var/path in L)
+ var/list/mod_L = GLOB.potential_mods_per_skill[path]
+ if(!mod_L)
+ mod_L = GLOB.potential_mods_per_skill[path] = list()
+ else
+ BINARY_INSERT(identifier, mod_L, datum/skill_modifier, src, priority, COMPARE_VALUE)
+ mod_L[identifier] = src
+
+/datum/skill_modifier/Destroy()
+ for(var/path in GLOB.potential_skills_per_mod[target_skills_key])
+ var/mod_L = GLOB.potential_mods_per_skill[path]
+ mod_L -= identifier
+ if(!length(mod_L))
+ GLOB.potential_mods_per_skill -= path
+ GLOB.skill_modifiers -= identifier
+ return ..()
+
+#define ADD_MOD_STEP(L, P, O, G) \
+ var/__L = L[P];\
+ if(!__L){\
+ L[P] = list(id)\
+ } else {\
+ L[P] = GLOB.potential_mods_per_skill[P] & (__L + id)\
+ }\
+ if(M.modifier_flags & MODIFIER_SKILL_ORIGIN_DIFF){\
+ LAZYADDASSOC(O, id, "[P]" = G)\
+ }
+
+/datum/mind/proc/add_skill_modifier(id)
+ if(LAZYACCESS(skill_holder.all_current_skill_modifiers, id))
+ return
+ var/datum/skill_modifier/M = GLOB.skill_modifiers[id]
+ if(!M)
+ CRASH("Invalid add_skill_modifier id: [id].")
+ if(M.modifier_flags & MODIFIER_SKILL_BODYBOUND && !current)
+ CRASH("Body-bound skill modifier [M] was tried to be added to a mob-less mind.")
+
+ if(M.modifier_flags & MODIFIER_SKILL_VALUE)
+ LAZYINITLIST(skill_holder.skill_value_mods)
+ if(M.modifier_flags & MODIFIER_SKILL_AFFINITY)
+ LAZYINITLIST(skill_holder.skill_affinity_mods)
+ if(M.modifier_flags & MODIFIER_SKILL_LEVEL)
+ LAZYINITLIST(skill_holder.skill_level_mods)
+ for(var/path in GLOB.potential_skills_per_mod[M.target_skills_key])
+ if(M.modifier_flags & MODIFIER_SKILL_VALUE)
+ ADD_MOD_STEP(skill_holder.skill_value_mods, path, skill_holder.original_values, get_skill_value(path, FALSE))
+ if(M.modifier_flags & MODIFIER_SKILL_AFFINITY)
+ ADD_MOD_STEP(skill_holder.skill_affinity_mods, path, skill_holder.original_affinities, get_skill_affinity(path, FALSE))
+ if(M.modifier_flags & MODIFIER_SKILL_LEVEL)
+ ADD_MOD_STEP(skill_holder.skill_level_mods, path, skill_holder.original_levels, get_skill_level(path, FALSE))
+ LAZYSET(skill_holder.all_current_skill_modifiers, id, TRUE)
+
+ if(M.modifier_flags & MODIFIER_SKILL_BODYBOUND)
+ M.RegisterSignal(src, COMSIG_MIND_TRANSFER, /datum/skill_modifier.proc/on_mind_transfer)
+ M.RegisterSignal(current, COMSIG_MOB_ON_NEW_MIND, /datum/skill_modifier.proc/on_mob_new_mind, TRUE)
+ RegisterSignal(M, COMSIG_PARENT_PREQDELETED, .proc/on_skill_modifier_deletion)
+
+#undef ADD_MOD_STEP
+
+#define REMOVE_MOD_STEP(L, P, O)\
+ LAZYREMOVEASSOC(L, P, id);\
+ if(M.modifier_flags & MODIFIER_SKILL_ORIGIN_DIFF){\
+ LAZYREMOVEASSOC(O, id, "[P]")\
+ }
+
+/datum/mind/proc/remove_skill_modifier(id, mind_transfer = FALSE)
+ if(!LAZYACCESS(skill_holder.all_current_skill_modifiers, id))
+ return
+ var/datum/skill_modifier/M = GLOB.skill_modifiers[id]
+ if(!M)
+ CRASH("Invalid remove_skill_modifier id: [id].")
+
+ if(!skill_holder.skill_value_mods && !skill_holder.skill_affinity_mods && !skill_holder.skill_level_mods)
+ return
+ for(var/path in GLOB.potential_skills_per_mod[M.target_skills_key])
+ if(M.modifier_flags & MODIFIER_SKILL_VALUE && skill_holder.skill_value_mods)
+ REMOVE_MOD_STEP(skill_holder.skill_value_mods, path, skill_holder.original_values)
+ if(M.modifier_flags & MODIFIER_SKILL_AFFINITY && skill_holder.skill_affinity_mods)
+ REMOVE_MOD_STEP(skill_holder.skill_affinity_mods, path, skill_holder.original_affinities)
+ if(M.modifier_flags & MODIFIER_SKILL_LEVEL && skill_holder.skill_level_mods)
+ REMOVE_MOD_STEP(skill_holder.skill_level_mods, path, skill_holder.original_levels)
+ LAZYREMOVE(skill_holder.all_current_skill_modifiers, id)
+
+ if(!mind_transfer && M.modifier_flags & MODIFIER_SKILL_BODYBOUND)
+ M.UnregisterSignal(src, COMSIG_MIND_TRANSFER)
+ M.UnregisterSignal(current, list(COMSIG_MOB_ON_NEW_MIND))
+ UnregisterSignal(M, COMSIG_PARENT_PREQDELETED)
+
+#undef REMOVE_MOD_STEP
+
+/datum/mind/proc/on_skill_modifier_deletion(datum/skill_modifier/source)
+ remove_skill_modifier(source.identifier)
+
+/datum/skill_modifier/proc/apply_modifier(value, skillpath, datum/skill_holder/H, method = MODIFIER_TARGET_VALUE)
+ . = value
+ var/mod = value_mod
+ switch(method)
+ if(MODIFIER_TARGET_LEVEL)
+ mod = level_mod
+ if(MODIFIER_TARGET_AFFINITY)
+ mod = affinity_mod
+
+ if(modifier_flags & MODIFIER_USE_THRESHOLDS && istext(mod))
+ var/datum/skill/S = GLOB.skill_datums[skillpath]
+ if(method == MODIFIER_TARGET_VALUE && S.progression_type == SKILL_PROGRESSION_LEVEL)
+ var/datum/skill/level/L = S
+ switch(L.level_up_method)
+ if(STANDARD_LEVEL_UP)
+ mod = XP_LEVEL(L.standard_xp_lvl_up, L.xp_lvl_multiplier, S.competency_thresholds[mod])
+ if(DWARFY_LEVEL_UP)
+ mod = DORF_XP_LEVEL(L.standard_xp_lvl_up, L.xp_lvl_multiplier, S.competency_thresholds[mod])
+ else
+ mod = S.competency_thresholds[mod]
+
+ var/diff = 0
+ if(modifier_flags & (MODIFIER_SKILL_VIRTUE|MODIFIER_SKILL_HANDICAP))
+ if(modifier_flags & MODIFIER_SKILL_VIRTUE)
+ . = max(., mod)
+ if(modifier_flags & MODIFIER_SKILL_HANDICAP)
+ . = min(., mod)
+ diff = . - mod
+ else if(modifier_flags & MODIFIER_SKILL_MULT)
+ . *= mod
+ else
+ . += mod
+
+ if(modifier_flags & MODIFIER_SKILL_ORIGIN_DIFF)
+ var/list/to_access = H.original_values
+ switch(method)
+ if(MODIFIER_TARGET_LEVEL)
+ to_access = H.original_levels
+ if(MODIFIER_TARGET_AFFINITY)
+ to_access = H.original_affinities
+ . += value - diff - LAZYACCESS(to_access[identifier], "[skillpath]")
+
+///Body bound modifier signal procs.
+/datum/skill_modifier/proc/on_mind_transfer(datum/mind/source, mob/new_character, mob/old_character)
+ source.remove_skill_modifier(identifier, TRUE)
+ UnregisterSignal(source, COMSIG_MIND_TRANSFER)
+
+/datum/skill_modifier/proc/on_mob_new_mind(mob/source)
+ source.mind.add_skill_modifier(identifier)
+ RegisterSignal(source.mind, COMSIG_MIND_TRANSFER, /datum/skill_modifier.proc/on_mind_transfer)
diff --git a/code/datums/skills/engineering.dm b/code/datums/skills/engineering.dm
index 59c9436e46..db7b33450c 100644
--- a/code/datums/skills/engineering.dm
+++ b/code/datums/skills/engineering.dm
@@ -2,5 +2,4 @@
name = "Wiring"
desc = "How proficient and knowledged you are at wiring beyond laying cables on the floor."
name_color = COLOR_PALE_ORANGE
- competency_thresholds = list(JOB_SKILL_BASIC, JOB_SKILL_EXPERT, JOB_SKILL_MASTER)
- skill_flags = SKILL_USE_MOOD|SKILL_TRAIN_MOOD|SKILL_USE_TOOL|SKILL_TRAINING_TOOL
+ skill_traits = list(SKILL_SANITY, SKILL_INTELLIGENCE, SKILL_USE_TOOL, SKILL_TRAINING_TOOL)
diff --git a/code/datums/skills/medical.dm b/code/datums/skills/medical.dm
index aaae1236a6..404c141157 100644
--- a/code/datums/skills/medical.dm
+++ b/code/datums/skills/medical.dm
@@ -2,4 +2,4 @@
name = "Surgery"
desc = "How proficient you are at doing surgery."
name_color = COLOR_PALE_BLUE_GRAY
- competency_mults = list(0.025, 0.025, 0.025) // 60% surgery speed up at max value of 100.
+ competency_multiplier = 1.5 // 60% surgery speed up at max value of 100, considering the base multiplier.
diff --git a/code/datums/skills/modifiers/job.dm b/code/datums/skills/modifiers/job.dm
new file mode 100644
index 0000000000..7d79ae89b3
--- /dev/null
+++ b/code/datums/skills/modifiers/job.dm
@@ -0,0 +1,35 @@
+/// Jobbie skill modifiers.
+
+/datum/skill_modifier/job
+ modifier_flags = MODIFIER_SKILL_VALUE|MODIFIER_SKILL_VIRTUE|MODIFIER_SKILL_ORIGIN_DIFF
+ priority = MODIFIER_SKILL_PRIORITY_MAX
+
+/datum/skill_modifier/job/surgery
+ target_skills = /datum/skill/numerical/surgery
+ value_mod = STARTING_SKILL_SURGERY_MEDICAL
+
+/datum/skill_modifier/job/affinity
+ modifier_flags = MODIFIER_SKILL_AFFINITY|MODIFIER_SKILL_VIRTUE
+ affinity_mod = STARTING_SKILL_AFFINITY_DEF_JOB
+
+/datum/skill_modifier/job/affinity/surgery
+ target_skills = /datum/skill/numerical/surgery
+
+/datum/skill_modifier/job/affinity/wiring
+ target_skills = /datum/skill/level/job/wiring
+
+/// Level skill modifiers below.
+/datum/skill_modifier/job/level
+ modifier_flags = MODIFIER_SKILL_VALUE|MODIFIER_SKILL_LEVEL|MODIFIER_SKILL_VIRTUE|MODIFIER_SKILL_ORIGIN_DIFF
+ level_mod = JOB_SKILL_TRAINED
+
+/datum/skill_modifier/job/level/New(id)
+ if(level_mod)
+ value_mod = GET_STANDARD_LVL(level_mod)
+ ..()
+
+/datum/skill_modifier/job/level/wiring
+ target_skills = /datum/skill/level/job/wiring
+
+/datum/skill_modifier/job/level/wiring/basic
+ level_mod = JOB_SKILL_BASIC
diff --git a/code/datums/skills/modifiers/mood.dm b/code/datums/skills/modifiers/mood.dm
new file mode 100644
index 0000000000..30f24afcc4
--- /dev/null
+++ b/code/datums/skills/modifiers/mood.dm
@@ -0,0 +1,8 @@
+/datum/skill_modifier/bad_mood
+ modifier_flags = MODIFIER_SKILL_VALUE|MODIFIER_SKILL_LEVEL|MODIFIER_SKILL_MULT|MODIFIER_SKILL_BODYBOUND
+ target_skills = list(SKILL_SANITY)
+
+/datum/skill_modifier/great_mood
+ modifier_flags = MODIFIER_SKILL_AFFINITY|MODIFIER_SKILL_MULT|MODIFIER_SKILL_BODYBOUND
+ target_skills = list(SKILL_SANITY)
+ affinity_mod = 1.2
diff --git a/code/datums/skills/modifiers/organs.dm b/code/datums/skills/modifiers/organs.dm
new file mode 100644
index 0000000000..13ebaf0658
--- /dev/null
+++ b/code/datums/skills/modifiers/organs.dm
@@ -0,0 +1,13 @@
+/datum/skill_modifier/brain_damage
+ target_skills = list(SKILL_INTELLIGENCE)
+ modifier_flags = MODIFIER_SKILL_VALUE|MODIFIER_SKILL_AFFINITY|MODIFIER_SKILL_LEVEL|MODIFIER_SKILL_MULT|MODIFIER_SKILL_BODYBOUND
+ value_mod = 0.85
+ level_mod = 0.85
+ affinity_mod = 0.85
+
+/datum/skill_modifier/heavy_brain_damage
+ target_skills = list(SKILL_INTELLIGENCE)
+ modifier_flags = MODIFIER_SKILL_VALUE|MODIFIER_SKILL_AFFINITY|MODIFIER_SKILL_LEVEL|MODIFIER_SKILL_BODYBOUND|MODIFIER_SKILL_HANDICAP|MODIFIER_USE_THRESHOLDS
+ priority = MODIFIER_SKILL_PRIORITY_LOW
+ value_mod = THRESHOLD_COMPETENT
+ level_mod = THRESHOLD_COMPETENT
diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm
index 4c200f110d..b976ac0fc8 100644
--- a/code/datums/status_effects/debuffs.dm
+++ b/code/datums/status_effects/debuffs.dm
@@ -120,11 +120,13 @@
/datum/status_effect/mesmerize/on_creation(mob/living/new_owner, set_duration)
. = ..()
ADD_TRAIT(owner, TRAIT_MUTE, "mesmerize")
+ ADD_TRAIT(owner, TRAIT_COMBAT_MODE_LOCKED, "mesmerize")
owner.add_movespeed_modifier(/datum/movespeed_modifier/status_effect/mesmerize)
/datum/status_effect/mesmerize/on_remove()
. = ..()
REMOVE_TRAIT(owner, TRAIT_MUTE, "mesmerize")
+ REMOVE_TRAIT(owner, TRAIT_COMBAT_MODE_LOCKED, "mesmerize")
owner.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/mesmerize)
/datum/status_effect/mesmerize/on_creation(mob/living/new_owner, set_duration)
diff --git a/code/datums/tgs_event_handler.dm b/code/datums/tgs_event_handler.dm
new file mode 100644
index 0000000000..a3324121bf
--- /dev/null
+++ b/code/datums/tgs_event_handler.dm
@@ -0,0 +1,20 @@
+/datum/tgs_event_handler/impl/HandleEvent(event_code, ...)
+ switch(event_code)
+ if(TGS_EVENT_REBOOT_MODE_CHANGE)
+ var/list/reboot_mode_lookup = list ("[TGS_REBOOT_MODE_NORMAL]" = "be normal", "[TGS_REBOOT_MODE_SHUTDOWN]" = "shutdown the server", "[TGS_REBOOT_MODE_RESTART]" = "hard restart the server")
+ var old_reboot_mode = args[2]
+ var new_reboot_mode = args[3]
+ message_admins("TGS: Reboot will no longer [reboot_mode_lookup["[old_reboot_mode]"]], it will instead [reboot_mode_lookup["[new_reboot_mode]"]]")
+ if(TGS_EVENT_PORT_SWAP)
+ message_admins("TGS: Changing port from [world.port] to [args[2]]")
+ if(TGS_EVENT_INSTANCE_RENAMED)
+ message_admins("TGS: Instance renamed to from [world.TgsInstanceName()] to [args[2]]")
+ if(TGS_EVENT_COMPILE_START)
+ message_admins("TGS: Deployment started, new game version incoming...")
+ if(TGS_EVENT_COMPILE_CANCELLED)
+ message_admins("TGS: Deployment cancelled!")
+ if(TGS_EVENT_COMPILE_FAILURE)
+ message_admins("TGS: Deployment failed!")
+ if(TGS_EVENT_DEPLOYMENT_COMPLETE)
+ message_admins("TGS: Deployment complete!")
+ to_chat(world, "Server updated, changes will be applied on the next round...")
diff --git a/code/datums/wires/_wires.dm b/code/datums/wires/_wires.dm
index 22bcf0a568..a96e14d4d7 100644
--- a/code/datums/wires/_wires.dm
+++ b/code/datums/wires/_wires.dm
@@ -138,7 +138,7 @@
if(current_users[user])
return FALSE
if(req_skill && user?.mind)
- var/level_diff = req_skill - user.mind.skill_holder.get_skill_level(/datum/skill/level/job/wiring)
+ var/level_diff = req_skill - user.mind.get_skill_level(/datum/skill/level/job/wiring, round = TRUE)
if(level_diff > 0)
LAZYSET(current_users, user, TRUE)
to_chat(user, "You begin cutting [holder]'s [color] wire...")
@@ -167,7 +167,7 @@
if(current_users[user])
return FALSE
if(req_skill && user?.mind)
- var/level_diff = req_skill - user.mind.skill_holder.get_skill_level(/datum/skill/level/job/wiring)
+ var/level_diff = req_skill - user.mind.get_skill_level(/datum/skill/level/job/wiring, round = TRUE)
if(level_diff > 0)
LAZYSET(current_users, user, TRUE)
to_chat(user, "You begin pulsing [holder]'s [color] wire...")
@@ -255,7 +255,7 @@
var/reveal_wires = FALSE
// Admin ghost can see a purpose of each wire.
- if(IsAdminGhost(user) || user.mind.skill_holder.get_skill_level(/datum/skill/level/job/wiring) >= req_knowledge)
+ if(IsAdminGhost(user) || user.mind.get_skill_level(/datum/skill/level/job/wiring) >= req_knowledge)
reveal_wires = TRUE
// Same for anyone with an abductor multitool.
diff --git a/code/game/area/areas/ruins/space.dm b/code/game/area/areas/ruins/space.dm
index 59c195ded1..a9f48af64e 100644
--- a/code/game/area/areas/ruins/space.dm
+++ b/code/game/area/areas/ruins/space.dm
@@ -129,7 +129,26 @@
icon_state = "crew_quarters"
+//Ruin of Space Diner
+/area/ruin/space/diner
+ name = "Space Diner"
+
+/area/ruin/space/diner/interior
+ name = "Space Diner"
+ icon_state = "maintbar"
+ has_gravity = STANDARD_GRAVITY
+ blob_allowed = FALSE //Nope, no winning in the diner as a blob. Gotta eat the main station.
+
+/area/ruin/space/diner/solars
+ requires_power = FALSE
+ dynamic_lighting = DYNAMIC_LIGHTING_IFSTARLIGHT
+ valid_territory = FALSE
+ blob_allowed = FALSE
+ flags_1 = NONE
+ ambientsounds = ENGINEERING
+ name = "Space Diner Solar Array"
+ icon_state = "yellow"
//Ruin of Derelict Oupost
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 98027acc61..1ff27bafa9 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -581,6 +581,14 @@
SEND_SIGNAL(src, COMSIG_ATOM_RCD_ACT, user, the_rcd, passed_mode)
return FALSE
+/**
+ * Respond to a electric bolt action on our item
+ *
+ * Default behaviour is to return, we define here to allow for cleaner code later on
+ */
+/atom/proc/zap_act(power, zap_flags, shocked_targets)
+ return
+
/atom/proc/storage_contents_dump_act(obj/item/storage/src_object, mob/user)
if(GetComponent(/datum/component/storage))
return component_storage_contents_dump_act(src_object, user)
@@ -927,14 +935,14 @@ Proc for attack log creation, because really why not
target.log_message(reverse_message, LOG_ATTACK, color="orange", log_globally=FALSE)
// Filter stuff
-/atom/movable/proc/add_filter(name,priority,list/params)
+/atom/proc/add_filter(name,priority,list/params)
LAZYINITLIST(filter_data)
var/list/p = params.Copy()
p["priority"] = priority
filter_data[name] = p
update_filters()
-/atom/movable/proc/update_filters()
+/atom/proc/update_filters()
filters = null
filter_data = sortTim(filter_data, /proc/cmp_filter_data_priority, TRUE)
for(var/f in filter_data)
@@ -943,11 +951,11 @@ Proc for attack log creation, because really why not
arguments -= "priority"
filters += filter(arglist(arguments))
-/atom/movable/proc/get_filter(name)
+/atom/proc/get_filter(name)
if(filter_data && filter_data[name])
return filters[filter_data.Find(name)]
-/atom/movable/proc/remove_filter(name)
+/atom/proc/remove_filter(name)
if(filter_data && filter_data[name])
filter_data -= name
update_filters()
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index fe617ddb0f..9736f473e8 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -37,6 +37,8 @@
var/throwforce = 0
var/datum/component/orbiter/orbiting
var/can_be_z_moved = TRUE
+ ///If we were without gravity and another animation happened, the bouncing will stop, and we need to restart it in next life().
+ var/floating_need_update = FALSE
var/zfalling = FALSE
@@ -56,10 +58,6 @@
em_block = new(src, render_target)
vis_contents += em_block
-/atom/movable/Destroy()
- QDEL_NULL(em_block)
- return ..()
-
/atom/movable/proc/update_emissive_block()
if(blocks_emissive != EMISSIVE_BLOCK_GENERIC)
return
@@ -183,14 +181,15 @@
return TRUE
/atom/movable/proc/stop_pulling()
- if(pulling)
- pulling.pulledby = null
- var/mob/living/ex_pulled = pulling
- pulling = null
- setGrabState(0)
- if(isliving(ex_pulled))
- var/mob/living/L = ex_pulled
- L.update_mobility()// mob gets up if it was lyng down in a chokehold
+ if(!pulling)
+ return
+ pulling.pulledby = null
+ var/mob/living/ex_pulled = pulling
+ pulling = null
+ setGrabState(0)
+ if(isliving(ex_pulled))
+ var/mob/living/L = ex_pulled
+ L.update_mobility()// mob gets up if it was lyng down in a chokehold
/atom/movable/proc/Move_Pulled(atom/A)
if(!pulling)
@@ -232,10 +231,12 @@
/atom/movable/Destroy(force)
QDEL_NULL(proximity_monitor)
QDEL_NULL(language_holder)
+ QDEL_NULL(em_block)
unbuckle_all_mobs(force=1)
. = ..()
+
if(loc)
//Restore air flow if we were blocking it (movables with ATMOS_PASS_PROC will need to do this manually if necessary)
if(((CanAtmosPass == ATMOS_PASS_DENSITY && density) || CanAtmosPass == ATMOS_PASS_NO) && isturf(loc))
@@ -493,15 +494,16 @@
/atom/movable/proc/float(on)
if(throwing)
return
- if(on && !(movement_type & FLOATING))
+ if(on && (!(movement_type & FLOATING) || floating_need_update))
animate(src, pixel_y = pixel_y + 2, time = 10, loop = -1)
sleep(10)
animate(src, pixel_y = pixel_y - 2, time = 10, loop = -1)
- setMovetype(movement_type | FLOATING)
- else if (!on && (movement_type & FLOATING))
+ if(!(movement_type & FLOATING))
+ setMovetype(movement_type | FLOATING)
+ else if (!on && movement_type & FLOATING)
animate(src, pixel_y = initial(pixel_y), time = 10)
setMovetype(movement_type & ~FLOATING)
-
+ floating_need_update = FALSE
/* Language procs
* Unless you are doing something very specific, these are the ones you want to use.
diff --git a/code/game/gamemodes/gangs/dominator.dm b/code/game/gamemodes/gangs/dominator.dm
index dca50be5b9..af75315ded 100644
--- a/code/game/gamemodes/gangs/dominator.dm
+++ b/code/game/gamemodes/gangs/dominator.dm
@@ -48,9 +48,6 @@
/obj/machinery/dominator/hulk_damage()
return (max_integrity - integrity_failure) / DOM_HULK_HITS_REQUIRED
-/obj/machinery/dominator/tesla_act()
- qdel(src)
-
/obj/machinery/dominator/update_icon()
cut_overlays()
if(stat & BROKEN)
diff --git a/code/game/gamemodes/gangs/gang_decals.dm b/code/game/gamemodes/gangs/gang_decals.dm
index 7aaed769d9..a37b4cb63b 100644
--- a/code/game/gamemodes/gangs/gang_decals.dm
+++ b/code/game/gamemodes/gangs/gang_decals.dm
@@ -10,7 +10,7 @@
/obj/effect/decal/cleanable/crayon/gang
icon = 'icons/effects/crayondecal.dmi'
layer = ABOVE_NORMAL_TURF_LAYER //Harder to hide
- plane = GAME_PLANE
+ plane = ABOVE_WALL_PLANE
do_icon_rotate = FALSE //These are designed to always face south, so no rotation please.
var/datum/team/gang/gang
diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm
index 3f2edd7174..0416813f4f 100644
--- a/code/game/machinery/Sleeper.dm
+++ b/code/game/machinery/Sleeper.dm
@@ -37,6 +37,11 @@
reset_chem_buttons()
RefreshParts()
add_inital_chems()
+ new_occupant_dir = dir
+
+/obj/machinery/sleeper/setDir(newdir)
+ . = ..()
+ new_occupant_dir = dir
/obj/machinery/sleeper/on_deconstruction()
var/obj/item/reagent_containers/sleeper_buffer/buffer = new (loc)
diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm
index 93121a0753..e2f26d6617 100644
--- a/code/game/machinery/_machinery.dm
+++ b/code/game/machinery/_machinery.dm
@@ -110,7 +110,8 @@ Class Procs:
var/state_open = FALSE
var/critical_machine = FALSE //If this machine is critical to station operation and should have the area be excempted from power failures.
var/list/occupant_typecache //if set, turned into typecache in Initialize, other wise, defaults to mob/living typecache
- var/atom/movable/occupant = null
+ var/atom/movable/occupant
+ var/new_occupant_dir = SOUTH //The direction the occupant will be set to look at when entering the machine.
var/speed_process = FALSE // Process as fast as possible?
var/obj/item/circuitboard/circuit // Circuit to be created and inserted when the machinery is created
// For storing and overriding ui id and dimensions
@@ -217,6 +218,7 @@ Class Procs:
if(target && !target.has_buckled_mobs() && (!isliving(target) || !mobtarget.buckled))
occupant = target
target.forceMove(src)
+ target.setDir(new_occupant_dir)
updateUsrDialog()
update_icon()
@@ -520,11 +522,11 @@ Class Procs:
/obj/machinery/proc/can_be_overridden()
. = 1
-/obj/machinery/tesla_act(power, tesla_flags, shocked_objects)
- ..()
- if(prob(85) && (tesla_flags & TESLA_MACHINE_EXPLOSIVE))
+/obj/machinery/zap_act(power, zap_flags, shocked_objects)
+ . = ..()
+ if(prob(85) && (zap_flags & ZAP_MACHINE_EXPLOSIVE))
explosion(src, 1, 2, 4, flame_range = 2, adminlog = FALSE, smoke = FALSE)
- if(tesla_flags & TESLA_OBJ_DAMAGE)
+ else if(zap_flags & ZAP_OBJ_DAMAGE)
take_damage(power/2000, BURN, "energy")
if(prob(40))
emp_act(EMP_LIGHT)
diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm
index 3a90707bbe..8819020e3a 100644
--- a/code/game/machinery/buttons.dm
+++ b/code/game/machinery/buttons.dm
@@ -3,6 +3,7 @@
desc = "A remote control switch."
icon = 'icons/obj/stationobjs.dmi'
icon_state = "doorctrl"
+ plane = ABOVE_WALL_PLANE
var/skin = "doorctrl"
power_channel = ENVIRON
var/obj/item/assembly/device
@@ -17,7 +18,7 @@
/obj/machinery/button/Initialize(mapload, ndir = 0, built = 0)
if(istext(id) && mapload)
- if(copytext(id, 1, 2) == "!")
+ if(id[1] == "!")
id = SSmapping.get_obfuscated_id(id)
. = ..()
if(built)
diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm
index eb7f194229..967ee02f82 100644
--- a/code/game/machinery/computer/camera.dm
+++ b/code/game/machinery/computer/camera.dm
@@ -169,6 +169,7 @@
desc = "Used for watching an empty arena."
icon = 'icons/obj/stationobjs.dmi'
icon_state = "telescreen"
+ plane = ABOVE_WALL_PLANE
network = list("thunder")
density = FALSE
circuit = null
diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm
index 07f95c17ff..9330a30555 100644
--- a/code/game/machinery/cryopod.dm
+++ b/code/game/machinery/cryopod.dm
@@ -242,6 +242,7 @@
if((isnull(user) || istype(user)) && state_open && !panel_open)
..(user)
var/mob/living/mob_occupant = occupant
+ investigate_log("Cryogenics machine closed with occupant [key_name(occupant)] by user [key_name(user)].", INVESTIGATE_CRYOGENICS)
if(mob_occupant && mob_occupant.stat != DEAD)
to_chat(occupant, "You feel cool air surround you. You go numb as your senses turn inward.")
if(mob_occupant.client)//if they're logged in
@@ -251,12 +252,15 @@
icon_state = "cryopod"
/obj/machinery/cryopod/open_machine()
+ if(occupant)
+ investigate_log("Cryogenics machine opened with occupant [key_name(occupant)] inside.", INVESTIGATE_CRYOGENICS)
..()
icon_state = "cryopod-open"
density = TRUE
name = initial(name)
/obj/machinery/cryopod/container_resist(mob/living/user)
+ investigate_log("Cryogenics machine container resisted by [key_name(user)] with occupant [key_name(occupant)].", INVESTIGATE_CRYOGENICS)
visible_message("[occupant] emerges from [src]!",
"You climb out of [src]!")
open_machine()
@@ -304,6 +308,8 @@
var/mob/living/mob_occupant = occupant
var/list/obj/item/cryo_items = list()
+
+ investigate_log("Despawning [key_name(mob_occupant)].", INVESTIGATE_CRYOGENICS)
//Handle Borg stuff first
if(iscyborg(mob_occupant))
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index 3bc8aff809..4c12809184 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -470,15 +470,15 @@
if(welded)
weld_overlay = get_airlock_overlay("welded", overlays_file)
if(obj_integrity < integrity_failure * max_integrity)
- damag_overlay = get_airlock_overlay("sparks_broken", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
+ damag_overlay = get_airlock_overlay("sparks_broken", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE)
else if(obj_integrity < (0.75 * max_integrity))
- damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
+ damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE)
if(lights && hasPower())
if(locked)
- lights_overlay = get_airlock_overlay("lights_bolts", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
+ lights_overlay = get_airlock_overlay("lights_bolts", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE)
else if(emergency)
- lights_overlay = get_airlock_overlay("lights_emergency", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
+ lights_overlay = get_airlock_overlay("lights_emergency", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE)
if(note)
note_overlay = get_airlock_overlay(notetype, note_overlay_file)
@@ -496,18 +496,18 @@
else
panel_overlay = get_airlock_overlay("panel_closed", overlays_file)
if(obj_integrity < integrity_failure * max_integrity)
- damag_overlay = get_airlock_overlay("sparks_broken", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
+ damag_overlay = get_airlock_overlay("sparks_broken", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE)
else if(obj_integrity < (0.75 * max_integrity))
- damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
+ damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE)
if(welded)
weld_overlay = get_airlock_overlay("welded", overlays_file)
- lights_overlay = get_airlock_overlay("lights_denied", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
+ lights_overlay = get_airlock_overlay("lights_denied", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE)
if(note)
note_overlay = get_airlock_overlay(notetype, note_overlay_file)
if(AIRLOCK_EMAG)
frame_overlay = get_airlock_overlay("closed", icon)
- sparks_overlay = get_airlock_overlay("sparks", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
+ sparks_overlay = get_airlock_overlay("sparks", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE)
if(airlock_material)
filling_overlay = get_airlock_overlay("[airlock_material]_closed", overlays_file)
else
@@ -518,9 +518,9 @@
else
panel_overlay = get_airlock_overlay("panel_closed", overlays_file)
if(obj_integrity < integrity_failure * max_integrity)
- damag_overlay = get_airlock_overlay("sparks_broken", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
+ damag_overlay = get_airlock_overlay("sparks_broken", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE)
else if(obj_integrity < (0.75 * max_integrity))
- damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
+ damag_overlay = get_airlock_overlay("sparks_damaged", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE)
if(welded)
weld_overlay = get_airlock_overlay("welded", overlays_file)
if(note)
@@ -533,7 +533,7 @@
else
filling_overlay = get_airlock_overlay("fill_closing", icon)
if(lights && hasPower())
- lights_overlay = get_airlock_overlay("lights_closing", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
+ lights_overlay = get_airlock_overlay("lights_closing", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE)
if(panel_open)
if(security_level)
panel_overlay = get_airlock_overlay("panel_closing_protected", overlays_file)
@@ -554,7 +554,7 @@
else
panel_overlay = get_airlock_overlay("panel_open", overlays_file)
if(obj_integrity < (0.75 * max_integrity))
- damag_overlay = get_airlock_overlay("sparks_open", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
+ damag_overlay = get_airlock_overlay("sparks_open", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE)
if(note)
note_overlay = get_airlock_overlay("[notetype]_open", note_overlay_file)
@@ -565,7 +565,7 @@
else
filling_overlay = get_airlock_overlay("fill_opening", icon)
if(lights && hasPower())
- lights_overlay = get_airlock_overlay("lights_opening", overlays_file, ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE)
+ lights_overlay = get_airlock_overlay("lights_opening", overlays_file, EMISSIVE_UNBLOCKABLE_LAYER, EMISSIVE_UNBLOCKABLE_PLANE)
if(panel_open)
if(security_level)
panel_overlay = get_airlock_overlay("panel_opening_protected", overlays_file)
diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm
index cd22b2dc05..1d39372dec 100644
--- a/code/game/machinery/doors/brigdoors.dm
+++ b/code/game/machinery/doors/brigdoors.dm
@@ -21,6 +21,7 @@
icon = 'icons/obj/status_display.dmi'
icon_state = "frame"
desc = "A remote control for a door."
+ plane = ABOVE_WALL_PLANE
req_access = list(ACCESS_SECURITY)
density = FALSE
var/id // id of linked machinery/lockers
diff --git a/code/game/machinery/embedded_controller/simple_vent_controller.dm b/code/game/machinery/embedded_controller/simple_vent_controller.dm
index 6c8467dbd3..6416de70f1 100644
--- a/code/game/machinery/embedded_controller/simple_vent_controller.dm
+++ b/code/game/machinery/embedded_controller/simple_vent_controller.dm
@@ -34,7 +34,7 @@
/obj/machinery/embedded_controller/radio/simple_vent_controller
icon = 'icons/obj/airlock_machines.dmi'
icon_state = "airlock_control_standby"
-
+ plane = ABOVE_WALL_PLANE
name = "vent controller"
density = FALSE
diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm
index 6501a5b45e..8869ea396e 100644
--- a/code/game/machinery/firealarm.dm
+++ b/code/game/machinery/firealarm.dm
@@ -17,6 +17,7 @@
desc = "\"Pull this in case of emergency\". Thus, keep pulling it forever."
icon = 'icons/obj/monitors.dmi'
icon_state = "fire0"
+ plane = ABOVE_WALL_PLANE
max_integrity = 250
integrity_failure = 0.4
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 30)
diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm
index 7bef255aff..f4f1aa0637 100644
--- a/code/game/machinery/flasher.dm
+++ b/code/game/machinery/flasher.dm
@@ -5,6 +5,7 @@
desc = "A wall-mounted flashbulb device."
icon = 'icons/obj/stationobjs.dmi'
icon_state = "mflash1"
+ plane = ABOVE_WALL_PLANE
max_integrity = 250
integrity_failure = 0.4
light_color = LIGHT_COLOR_WHITE
@@ -20,6 +21,7 @@
name = "portable flasher"
desc = "A portable flashing device. Wrench to activate and deactivate. Cannot detect slow movements."
icon_state = "pflash1-p"
+ plane = GAME_PLANE
strength = 80
anchored = FALSE
base_state = "pflash"
diff --git a/code/game/machinery/harvester.dm b/code/game/machinery/harvester.dm
index 244c300905..141f261688 100644
--- a/code/game/machinery/harvester.dm
+++ b/code/game/machinery/harvester.dm
@@ -20,6 +20,11 @@
. = ..()
if(prob(1))
name = "auto-autopsy"
+ new_occupant_dir = dir
+
+/obj/machinery/harvester/setDir(newdir)
+ . = ..()
+ new_occupant_dir = dir
/obj/machinery/harvester/RefreshParts()
interval = 0
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index 3a79ff3c82..621e486e90 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -34,7 +34,7 @@ GLOBAL_LIST_EMPTY(network_holopads)
desc = "It's a floor-mounted device for projecting holographic images."
icon_state = "holopad0"
layer = LOW_OBJ_LAYER
- plane = FLOOR_PLANE
+ plane = ABOVE_WALL_PLANE
flags_1 = HEAR_1
use_power = IDLE_POWER_USE
idle_power_usage = 5
diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm
index b2a35dcf41..421e3433ca 100644
--- a/code/game/machinery/lightswitch.dm
+++ b/code/game/machinery/lightswitch.dm
@@ -5,6 +5,7 @@
name = "light switch"
icon = 'icons/obj/power.dmi'
icon_state = "light1"
+ plane = ABOVE_WALL_PLANE
desc = "Make dark."
var/on = TRUE
var/area/area = null
diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm
index 000585a4be..d55faf2343 100644
--- a/code/game/machinery/requests_console.dm
+++ b/code/game/machinery/requests_console.dm
@@ -16,6 +16,7 @@ GLOBAL_LIST_EMPTY(allConsoles)
desc = "A console intended to send requests to different departments on the station."
icon = 'icons/obj/terminals.dmi'
icon_state = "req_comp0"
+ plane = ABOVE_WALL_PLANE
var/department = "Unknown" //The list of all departments on the station (Determined from this variable on each unit) Set this to the same thing if you want several consoles in one department
var/list/messages = list() //List of all messages
var/departmentType = 0
diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm
index f9f4eb3e80..1730569e10 100644
--- a/code/game/machinery/spaceheater.dm
+++ b/code/game/machinery/spaceheater.dm
@@ -115,8 +115,8 @@
return PROCESS_KILL
/obj/machinery/space_heater/RefreshParts()
- var/laser = 0
- var/cap = 0
+ var/laser = 2
+ var/cap = 1
for(var/obj/item/stock_parts/micro_laser/M in component_parts)
laser += M.rating
for(var/obj/item/stock_parts/capacitor/M in component_parts)
@@ -166,6 +166,11 @@
else
return ..()
+/obj/machinery/space_heater/wrench_act(mob/living/user, obj/item/I)
+ ..()
+ default_unfasten_wrench(user, I, 5)
+ return TRUE
+
/obj/machinery/space_heater/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm
index 746c17f225..02bb07b8ed 100644
--- a/code/game/machinery/status_display.dm
+++ b/code/game/machinery/status_display.dm
@@ -21,6 +21,7 @@
desc = null
icon = 'icons/obj/status_display.dmi'
icon_state = "frame"
+ plane = ABOVE_WALL_PLANE
density = FALSE
use_power = IDLE_POWER_USE
idle_power_usage = 10
diff --git a/code/game/machinery/wishgranter.dm b/code/game/machinery/wishgranter.dm
index f618888d98..ee28f118fa 100644
--- a/code/game/machinery/wishgranter.dm
+++ b/code/game/machinery/wishgranter.dm
@@ -11,9 +11,6 @@
var/insisting = 0
/obj/machinery/wish_granter/attack_hand(mob/living/carbon/user)
- . = ..()
- if(.)
- return
if(charges <= 0)
to_chat(user, "The Wish Granter lies silent.")
return
@@ -22,22 +19,116 @@
to_chat(user, "You feel a dark stirring inside of the Wish Granter, something you want nothing of. Your instincts are better than any man's.")
return
- else if(is_special_character(user))
- to_chat(user, "Even to a heart as dark as yours, you know nothing good will come of this. Something instinctual makes you pull away.")
-
else if (!insisting)
to_chat(user, "Your first touch makes the Wish Granter stir, listening to you. Are you really sure you want to do this?")
insisting++
else
- to_chat(user, "You speak. [pick("I want the station to disappear","Humanity is corrupt, mankind must be destroyed","I want to be rich", "I want to rule the world","I want immortality.")]. The Wish Granter answers.")
- to_chat(user, "Your head pounds for a moment, before your vision clears. You are the avatar of the Wish Granter, and your power is LIMITLESS! And it's all yours. You need to make sure no one can take it from you. No one can know, first.")
+ if(is_special_character(user))
+ to_chat(user, "You speak. [pick("I want power","Humanity is corrupt, mankind must be destroyed", "I want to rule the world","I want immortality")]. The Wish Granter answers.")
+ to_chat(user, "Your head pounds for a moment, before your vision clears. The Wish Granter, sensing the darkness in your heart, has given you limitless power, and it's all yours!")
+ user.dna.add_mutation(HULK)
+ user.dna.add_mutation(XRAY)
+ user.dna.add_mutation(SPACEMUT)
+ user.dna.add_mutation(TK)
+ user.next_move_modifier *= 0.5 //half the delay between attacks!
+ to_chat(user, "Things around you feel slower!")
+ charges--
+ insisting = FALSE
+ to_chat(user, "You have a very great feeling about this!")
+ else
+ to_chat(user, "The Wish Granter awaits your wish.")
+ var/wish = input("You want...","Wish") as null|anything in list("Power","Wealth","The Station To Disappear","To Kill","Nothing")
+ switch(wish)
+ if("Power") //Gives infinite power in exchange for infinite power going off in your face!
+ if(charges <= 0)
+ return
+ to_chat(user, "Your wish is granted, but at a terrible cost...")
+ to_chat(user, "The Wish Granter punishes you for your selfishness, warping itself into a delaminating supermatter shard!")
+ var/obj/item/stock_parts/cell/infinite/powah = new /obj/item/stock_parts/cell/infinite(get_turf(user))
+ if(user.put_in_hands(powah))
+ to_chat(user, "[powah] materializes into your hands!")
+ else
+ to_chat(user, "[powah] materializes onto the floor.")
+ var/obj/machinery/power/supermatter_crystal/powerwish = new /obj/machinery/power/supermatter_crystal(loc)
+ powerwish.damage = 700 //right at the emergency threshold
+ powerwish.produces_gas = FALSE
+ charges--
+ insisting = FALSE
+ if(!charges)
+ qdel(src)
+ if("Wealth") //Gives 1 million space bucks in exchange for being turned into gold!
+ if(charges <= 0)
+ return
+ to_chat(user, "Your wish is granted, but at a cost...")
+ to_chat(user, "The Wish Granter punishes you for your selfishness, warping your body to match the greed in your heart.")
+ new /obj/structure/closet/crate/trashcart/moneywish(loc)
+ new /obj/structure/closet/crate/trashcart/moneywish(loc)
+ user.set_species(/datum/species/golem/gold)
+ charges--
+ insisting = FALSE
+ if(!charges)
+ qdel(src)
+ if("The Station To Disappear") //teleports you to the station and makes you blind, making the station disappear for you!
+ if(charges <= 0)
+ return
+ to_chat(user, "Your wish is 'granted', but at a terrible cost...")
+ to_chat(user, "The Wish Granter punishes you for your selfishness, claiming your soul and warping your eyes to match the darkness in your heart.")
+ user.dna.add_mutation(BLINDMUT)
+ var/obj/item/organ/eyes/eyes = user.getorganslot(ORGAN_SLOT_EYES)
+ if(eyes)
+ eyes.applyOrganDamage(eyes.maxHealth)
+ var/list/destinations = list()
+ for(var/obj/item/beacon/B in GLOB.teleportbeacons)
+ var/turf/T = get_turf(B)
+ if(is_station_level(T.z))
+ destinations += B
+ var/chosen_beacon = pick(destinations)
+ var/obj/effect/portal/jaunt_tunnel/J = new (get_turf(src), src, 100, null, FALSE, get_turf(chosen_beacon))
+ try_move_adjacent(J)
+ playsound(src,'sound/effects/sparks4.ogg',50,1)
+ charges--
+ insisting = FALSE
+ if(!charges)
+ qdel(src)
+ if("To Kill") //Makes you kill things in exchange for rewards!
+ if(charges <= 0)
+ return
+ to_chat(user, "Your wish is granted, but at a terrible cost...")
+ to_chat(user, "The Wish Granter punishes you for your wickedness, warping itself into a dastardly creature for you to kill! ...but it almost seems to reward you for this.")
+ var/obj/item/melee/transforming/energy/sword/cx/killreward = new /obj/item/melee/transforming/energy/sword/cx(get_turf(user))
+ if(user.put_in_hands(killreward))
+ to_chat(user, "[killreward] materializes into your hands!")
+ else
+ to_chat(user, "[killreward] materializes onto the floor.")
+ user.next_move_modifier *= 0.8 //20% less delay between attacks!
+ to_chat(user, "Things around you feel slightly slower!")
+ var/mob/living/simple_animal/hostile/venus_human_trap/killwish = new /mob/living/simple_animal/hostile/venus_human_trap(loc)
+ killwish.maxHealth = 1500
+ killwish.health = killwish.maxHealth
+ killwish.vine_grab_distance = 6
+ killwish.melee_damage_upper = 30
+ killwish.loot = list(/obj/item/twohanded/dualsaber/hypereutactic)
+ charges--
+ insisting = FALSE
+ if(!charges)
+ qdel(src)
+ if("Nothing") //Makes the wish granter disappear
+ if(charges <= 0)
+ return
+ to_chat(user, "The Wish Granter vanishes from sight!")
+ to_chat(user, "You feel as if you just narrowly avoided a terrible fate...")
+ charges--
+ insisting = FALSE
+ qdel(src)
- charges--
- insisting = 0
+//ITEMS THAT IT USES
- user.mind.add_antag_datum(/datum/antagonist/wishgranter)
+/obj/structure/closet/crate/trashcart/moneywish
+ desc = "A heavy, metal trashcart with wheels. Filled with cash."
+ name = "loaded trash cart"
- to_chat(user, "You have a very bad feeling about this.")
-
- return
+/obj/structure/closet/crate/trashcart/moneywish/PopulateContents() //25*20*1000=500,000
+ for(var/i in 1 to 25)
+ var/obj/item/stack/spacecash/c1000/lodsamoney = new /obj/item/stack/spacecash/c1000(src)
+ lodsamoney.amount = lodsamoney.max_amount
diff --git a/code/game/mecha/combat/neovgre.dm b/code/game/mecha/combat/neovgre.dm
index c678912a21..fdcc4df151 100644
--- a/code/game/mecha/combat/neovgre.dm
+++ b/code/game/mecha/combat/neovgre.dm
@@ -62,18 +62,21 @@
/obj/mecha/combat/neovgre/process()
..()
- if(GLOB.ratvar_awakens) // At this point only timley intervention by lord singulo could hople to stop the superweapon
+ if(GLOB.ratvar_awakens) // At this point only timley intervention by lord singulo could hope to stop the superweapon
cell.charge = INFINITY
max_integrity = INFINITY
obj_integrity = max_integrity
CHECK_TICK //Just to be on the safe side lag wise
- else if(cell.charge < cell.maxcharge)
- for(var/obj/effect/clockwork/sigil/transmission/T in range(SIGIL_ACCESS_RANGE, src))
- var/delta = min(recharge_rate, cell.maxcharge - cell.charge)
- if (get_clockwork_power() <= delta)
- cell.charge += delta
- adjust_clockwork_power(-delta)
- CHECK_TICK
+ else
+ if(cell.charge < cell.maxcharge)
+ for(var/obj/effect/clockwork/sigil/transmission/T in range(SIGIL_ACCESS_RANGE, src))
+ var/delta = min(recharge_rate, cell.maxcharge - cell.charge)
+ if (get_clockwork_power() >= delta)
+ cell.charge += delta
+ adjust_clockwork_power(-delta)
+ if(obj_integrity < max_integrity && istype(loc, /turf/open/floor/clockwork))
+ obj_integrity += min(max_integrity - obj_integrity, max_integrity / 200)
+ CHECK_TICK
/obj/mecha/combat/neovgre/Initialize()
.=..()
@@ -90,7 +93,7 @@
energy_drain = 30
name = "Aribter Laser Cannon"
desc = "Please re-attach this to neovgre and stop asking questions about why it looks like a normal Nanotrasen issue Solaris laser cannon - Nezbere"
- fire_sound = "sound/weapons/neovgre_laser.ogg"
+ fire_sound = 'sound/weapons/neovgre_laser.ogg'
/obj/item/mecha_parts/mecha_equipment/weapon/energy/laser/heavy/neovgre/can_attach(obj/mecha/combat/neovgre/M)
if(istype(M))
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index 02115d3e30..32be825489 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -23,11 +23,11 @@
layer = BELOW_MOB_LAYER//icon draw layer
infra_luminosity = 15 //byond implementation is bugged.
force = 5
- flags_1 = HEAR_1
+ flags_1 = HEAR_1|BLOCK_FACE_ATOM_1
var/can_move = 0 //time of next allowed movement
- var/mob/living/carbon/occupant = null
+ var/mob/living/occupant = null
var/step_in = 10 //make a step in step_in/10 sec.
- var/dir_in = 2//What direction will the mech face when entered/powered on? Defaults to South.
+ var/dir_in = SOUTH //What direction will the mech face when entered/powered on? Defaults to South.
var/normal_step_energy_drain = 10 //How much energy the mech will consume each time it moves. This variable is a backup for when leg actuators affect the energy drain.
var/step_energy_drain = 10
var/melee_energy_drain = 15
@@ -495,6 +495,10 @@
occupant_message("Air port connection teared off!")
mecha_log_message("Lost connection to gas port.")
+/obj/mecha/setDir(newdir)
+ . = ..()
+ occupant?.setDir(newdir)
+
/obj/mecha/Process_Spacemove(var/movement_dir = 0)
. = ..()
if(.)
diff --git a/code/game/mecha/mecha_defense.dm b/code/game/mecha/mecha_defense.dm
index 53768fe9a9..1fe7f74dec 100644
--- a/code/game/mecha/mecha_defense.dm
+++ b/code/game/mecha/mecha_defense.dm
@@ -77,7 +77,7 @@
/obj/mecha/attack_animal(mob/living/simple_animal/user)
mecha_log_message("Attack by simple animal. Attacker - [user].", color="red")
if(!user.melee_damage_upper && !user.obj_damage)
- user.emote("custom", message = "[user.friendly] [src].")
+ user.emote("custom", message = "[user.friendly_verb_continuous] [src].")
return 0
else
var/play_soundeffect = 1
diff --git a/code/game/mecha/mecha_topic.dm b/code/game/mecha/mecha_topic.dm
index 8d6328cf08..b1ab944b49 100644
--- a/code/game/mecha/mecha_topic.dm
+++ b/code/game/mecha/mecha_topic.dm
@@ -333,10 +333,13 @@
send_byjax(occupant,"exosuit.browser","t_port_connection","[internal_tank.connected_port?"Disconnect from":"Connect to"] gas port")
if(href_list["dna_lock"])
- if(occupant && !iscarbon(occupant))
- to_chat(occupant, " You do not have any DNA!")
+ if(!occupant)
return
- dna_lock = occupant.dna.unique_enzymes
+ var/mob/living/carbon/C = occupant
+ if(!istype(C) || !C.dna)
+ to_chat(C, " You do not have any DNA!")
+ return
+ dna_lock = C.dna.unique_enzymes
occupant_message("You feel a prick as the needle takes your DNA sample.")
if(href_list["reset_dna"])
diff --git a/code/game/objects/effects/contraband.dm b/code/game/objects/effects/contraband.dm
index 6b61eb2ebd..69aa3fb654 100644
--- a/code/game/objects/effects/contraband.dm
+++ b/code/game/objects/effects/contraband.dm
@@ -49,6 +49,7 @@
var/original_name
desc = "A large piece of space-resistant printed paper."
icon = 'icons/obj/contraband.dmi'
+ plane = ABOVE_WALL_PLANE
anchored = TRUE
var/ruined = FALSE
var/random_basetype
diff --git a/code/game/objects/effects/decals/crayon.dm b/code/game/objects/effects/decals/crayon.dm
index 955b9935dd..8cfdad0432 100644
--- a/code/game/objects/effects/decals/crayon.dm
+++ b/code/game/objects/effects/decals/crayon.dm
@@ -3,7 +3,7 @@
desc = "Graffiti. Damn kids."
icon = 'icons/effects/crayondecal.dmi'
icon_state = "rune1"
- plane = GAME_PLANE //makes the graffiti visible over a wall.
+ plane = ABOVE_WALL_PLANE //makes the graffiti visible over a wall.
gender = NEUTER
mergeable_decal = FALSE
var/do_icon_rotate = TRUE
diff --git a/code/game/objects/effects/decals/decal.dm b/code/game/objects/effects/decals/decal.dm
index 3e7706282a..5dbd18aa0c 100644
--- a/code/game/objects/effects/decals/decal.dm
+++ b/code/game/objects/effects/decals/decal.dm
@@ -35,6 +35,7 @@
icon = 'icons/turf/decals.dmi'
icon_state = "warningline"
layer = TURF_DECAL_LAYER
+ plane = ABOVE_WALL_PLANE
/obj/effect/turf_decal/Initialize()
..()
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index 992e9c84a7..6910bb1db0 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -469,6 +469,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/start/new_player)
/obj/effect/landmark/stationroom
var/list/templates = list()
layer = BULLET_HOLE_LAYER
+ plane = ABOVE_WALL_PLANE
/obj/effect/landmark/stationroom/New()
..()
diff --git a/code/game/objects/effects/spawners/structure.dm b/code/game/objects/effects/spawners/structure.dm
index cdf3f3e6b4..7a151c4312 100644
--- a/code/game/objects/effects/spawners/structure.dm
+++ b/code/game/objects/effects/spawners/structure.dm
@@ -24,6 +24,23 @@ again.
name = "window spawner"
spawn_list = list(/obj/structure/grille, /obj/structure/window/fulltile)
dir = SOUTH
+ var/electrochromatic
+ var/electrochromatic_id
+
+/obj/effect/spawner/structure/window/Initialize()
+ . = ..()
+ if(!electrochromatic)
+ return
+ if(!electrochromatic_id)
+ stack_trace("Electrochromatic window spawner set without electromatic id.")
+ return
+ if(electrochromatic_id[1] == "!")
+ electrochromatic_id = SSmapping.get_obfuscated_id(electrochromatic_id)
+ for(var/obj/structure/window/W in get_turf(src))
+ W.electrochromatic_id = electrochromatic_id
+ W.make_electrochromatic()
+ if(electrochromatic == ELECTROCHROMATIC_DIMMED)
+ W.electrochromatic_dim()
/obj/effect/spawner/structure/window/hollow
name = "hollow window spawner"
@@ -140,13 +157,16 @@ again.
spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/spawner/north, /obj/structure/window/reinforced/spawner/west)
. = ..()
-//tinted
+//tinted and electrochromatic
/obj/effect/spawner/structure/window/reinforced/tinted
name = "tinted reinforced window spawner"
icon_state = "twindow_spawner"
spawn_list = list(/obj/structure/grille, /obj/structure/window/reinforced/tinted/fulltile)
+/obj/effect/spawner/structure/window/reinforced/tinted/electrochromatic
+ name = "electrochromatic reinforced window spawner"
+ electrochromatic = ELECTROCHROMATIC_DIMMED
//shuttle window
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 949ed050f5..2a4b6d7df5 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -135,7 +135,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
///Skills vars
//list of skill PATHS exercised when using this item. An associated bitfield can be set to indicate additional ways the skill is used by this specific item.
var/list/datum/skill/used_skills
- var/skill_difficulty = THRESHOLD_COMPETENT //how difficult it's to use this item in general.
+ var/skill_difficulty = THRESHOLD_UNTRAINED //how difficult it's to use this item in general.
var/skill_gain = DEF_SKILL_GAIN //base skill value gain from using this item.
/obj/item/Initialize()
@@ -782,10 +782,13 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
var/user = usr
tip_timer = addtimer(CALLBACK(src, .proc/openTip, location, control, params, user), timedelay, TIMER_STOPPABLE)//timer takes delay in deciseconds, but the pref is in milliseconds. dividing by 100 converts it.
-/obj/item/MouseExited()
+/obj/item/MouseExited(location,control,params)
+ SEND_SIGNAL(src, COMSIG_ITEM_MOUSE_EXIT, location, control, params)
deltimer(tip_timer)//delete any in-progress timer if the mouse is moved off the item before it finishes
closeToolTip(usr)
+/obj/item/MouseEntered(location,control,params)
+ SEND_SIGNAL(src, COMSIG_ITEM_MOUSE_ENTER, location, control, params)
// Called when a mob tries to use the item as a tool.
// Handles most checks.
@@ -802,7 +805,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
if(delay)
if(user.mind && used_skills)
- delay = user.mind.skill_holder.item_action_skills_mod(src, delay, skill_difficulty, SKILL_USE_TOOL, NONE, FALSE)
+ delay = user.mind.item_action_skills_mod(src, delay, skill_difficulty, SKILL_USE_TOOL, null, FALSE)
// Create a callback with checks that would be called every tick by do_after.
var/datum/callback/tool_check = CALLBACK(src, .proc/tool_check_callback, user, amount, extra_checks)
@@ -828,11 +831,13 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
if(delay >= MIN_TOOL_SOUND_DELAY)
play_tool_sound(target, volume)
+
if(user.mind && used_skills && skill_gain_mult)
+ var/gain = skill_gain + delay/SKILL_GAIN_DELAY_DIVISOR
for(var/skill in used_skills)
- if(!(used_skills[skill] & SKILL_TRAINING_TOOL))
+ if(!(SKILL_TRAINING_TOOL in used_skills[skill]))
continue
- user.mind.skill_holder.auto_gain_experience(skill, skill_gain*skill_gain_mult, GET_STANDARD_LVL(max_level))
+ user.mind.auto_gain_experience(skill, gain*skill_gain_mult, GET_STANDARD_LVL(max_level))
return TRUE
diff --git a/code/game/objects/items/RCD.dm b/code/game/objects/items/RCD.dm
index 7f6e561f55..cb44b82ad2 100644
--- a/code/game/objects/items/RCD.dm
+++ b/code/game/objects/items/RCD.dm
@@ -143,8 +143,8 @@ RLD
//if user can't be seen from A (only checks surroundings' opaqueness) and can't see A.
//jarring, but it should stop people from targetting atoms they can't see...
//excluding darkness, to allow RLD to be used to light pitch black dark areas.
- if(!((user in view(view_range, A)) || (user in viewers(view_range, A))))
- to_chat(user, "You focus, pointing \the [src] at whatever outside your field of vision in the given direction... to no avail.")
+ if(!((user in view(view_range, A)) || (user in fov_viewers(view_range, A))))
+ to_chat(user, "You focus, pointing \the [src] at whatever outside your field of vision in that direction... to no avail.")
return FALSE
return TRUE
diff --git a/code/game/objects/items/apc_frame.dm b/code/game/objects/items/apc_frame.dm
index 1f6dff490d..3640db2d8a 100644
--- a/code/game/objects/items/apc_frame.dm
+++ b/code/game/objects/items/apc_frame.dm
@@ -58,18 +58,22 @@
/obj/item/wallframe/proc/after_attach(var/obj/O)
transfer_fingerprints_to(O)
-/obj/item/wallframe/attackby(obj/item/W, mob/user, params)
- ..()
- if(istype(W, /obj/item/screwdriver))
- // For camera-building borgs
- var/turf/T = get_step(get_turf(user), user.dir)
- if(iswallturf(T))
- T.attackby(src, user, params)
+/obj/item/wallframe/screwdriver_act(mob/user, obj/item/I)
+ . = ..()
+ if(.)
+ return
+ // For camera-building borgs
+ var/turf/T = get_step(get_turf(user), user.dir)
+ if(iswallturf(T))
+ T.attackby(src, user)
+/obj/item/wallframe/wrench_act(mob/user, obj/item/I)
+ if(!custom_materials)
+ return
var/metal_amt = round(custom_materials[SSmaterials.GetMaterialRef(/datum/material/iron)]/MINERAL_MATERIAL_AMOUNT)
var/glass_amt = round(custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)]/MINERAL_MATERIAL_AMOUNT)
- if(istype(W, /obj/item/wrench) && (metal_amt || glass_amt))
+ if(metal_amt || glass_amt)
to_chat(user, "You dismantle [src].")
if(metal_amt)
new /obj/item/stack/sheet/metal(get_turf(src), metal_amt)
diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm
index 8a5723e894..7036a78bc4 100644
--- a/code/game/objects/items/cards_ids.dm
+++ b/code/game/objects/items/cards_ids.dm
@@ -311,20 +311,19 @@
registered_account.bank_card_talk("ERROR: UNABLE TO LOGIN DUE TO SCHEDULED MAINTENANCE. MAINTENANCE IS SCHEDULED TO COMPLETE IN [(registered_account.withdrawDelay - world.time)/10] SECONDS.", TRUE)
return
- var/amount_to_remove = FLOOR(input(user, "How much do you want to withdraw? Current Balance: [registered_account.account_balance]", "Withdraw Funds", 5) as num|null, 1)
+ var/amount_to_remove = input(user, "How much do you want to withdraw? Current Balance: [registered_account.account_balance]", "Withdraw Funds", 5) as num|null
if(!amount_to_remove || amount_to_remove < 0)
return
if(!alt_click_can_use_id(user))
return
- if(registered_account.adjust_money(-amount_to_remove))
+ amount_to_remove = FLOOR(min(amount_to_remove, registered_account.account_balance), 1)
+ if(amount_to_remove && registered_account.adjust_money(-amount_to_remove))
var/obj/item/holochip/holochip = new (user.drop_location(), amount_to_remove)
user.put_in_hands(holochip)
to_chat(user, "You withdraw [amount_to_remove] credits into a holochip.")
return
- else
- var/difference = amount_to_remove - registered_account.account_balance
- registered_account.bank_card_talk("ERROR: The linked account requires [difference] more credit\s to perform that withdrawal.", TRUE)
+ registered_account.bank_card_talk("ERROR: The linked account has no sufficient credits to perform that withdrawal.", TRUE)
/obj/item/card/id/examine(mob/user)
. = ..()
diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm
index f60edf2917..a4ed8dedd1 100644
--- a/code/game/objects/items/crayons.dm
+++ b/code/game/objects/items/crayons.dm
@@ -91,6 +91,11 @@
refill()
+/obj/item/toy/crayon/examine(mob/user)
+ . = ..()
+ if(can_change_colour)
+ . += "Ctrl-click [src] while it's on your person to quickly recolour it."
+
/obj/item/toy/crayon/proc/refill()
if(charges == -1)
charges_left = 100
@@ -160,6 +165,12 @@
update_icon()
return TRUE
+/obj/item/toy/crayon/CtrlClick(mob/user)
+ if(can_change_colour && !isturf(loc) && user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
+ select_colour(user)
+ else
+ return ..()
+
/obj/item/toy/crayon/proc/staticDrawables()
. = list()
@@ -237,14 +248,7 @@
else
paint_mode = PAINT_NORMAL
if("select_colour")
- if(can_change_colour)
- var/chosen_colour = input(usr,"","Choose Color",paint_color) as color|null
-
- if (!isnull(chosen_colour))
- paint_color = chosen_colour
- . = TRUE
- else
- . = FALSE
+ . = can_change_colour && select_colour(usr)
if("enter_text")
var/txt = stripped_input(usr,"Choose what to write.",
"Scribbles",default = text_buffer)
@@ -254,6 +258,13 @@
drawtype = "a"
update_icon()
+/obj/item/toy/crayon/proc/select_colour(mob/user)
+ var/chosen_colour = input(user, "", "Choose Color", paint_color) as color|null
+ if (!isnull(chosen_colour) && user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
+ paint_color = chosen_colour
+ return TRUE
+ return FALSE
+
/obj/item/toy/crayon/proc/crayon_text_strip(text)
var/static/regex/crayon_r = new /regex(@"[^\w!?,.=%#&+\/\-]")
return replacetext(lowertext(text), crayon_r, "")
diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/defib.dm
index 3ac3347222..7cc76da312 100644
--- a/code/game/objects/items/defib.dm
+++ b/code/game/objects/items/defib.dm
@@ -433,7 +433,7 @@
H.visible_message("The defibrillator safely discharges the excessive charge into the floor!")
return
var/mob/living/M = H.pulledby
- if(M.electrocute_act(30, src))
+ if(M.electrocute_act(30, H))
M.visible_message("[M] is electrocuted by [M.p_their()] contact with [H]!")
M.emote("scream")
diff --git a/code/game/objects/items/devices/laserpointer.dm b/code/game/objects/items/devices/laserpointer.dm
index 9c94348fc2..c931d92379 100644
--- a/code/game/objects/items/devices/laserpointer.dm
+++ b/code/game/objects/items/devices/laserpointer.dm
@@ -135,7 +135,8 @@
outmsg = "You miss the lens of [C] with [src]!"
//catpeople
- for(var/mob/living/carbon/human/H in view(1,targloc))
+ var/list/viewers = fov_viewers(1,targloc)
+ for(var/mob/living/carbon/human/H in viewers)
if(!iscatperson(H) || H.incapacitated() || H.eye_blind )
continue
if(!H.lying)
@@ -150,7 +151,7 @@
H.visible_message("[H] stares at the light"," You stare at the light... ")
//cats!
- for(var/mob/living/simple_animal/pet/cat/C in view(1,targloc))
+ for(var/mob/living/simple_animal/pet/cat/C in viewers)
if(prob(50))
C.visible_message("[C] pounces on the light!","LIGHT!")
C.Move(targloc)
diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm
index ada598866b..f4c317c8ba 100644
--- a/code/game/objects/items/devices/radio/intercom.dm
+++ b/code/game/objects/items/devices/radio/intercom.dm
@@ -2,6 +2,7 @@
name = "station intercom"
desc = "Talk through this."
icon_state = "intercom"
+ plane = ABOVE_WALL_PLANE
anchored = TRUE
w_class = WEIGHT_CLASS_BULKY
canhear_range = 2
diff --git a/code/game/objects/items/flamethrower.dm b/code/game/objects/items/flamethrower.dm
index e2140bd0fd..c785c22813 100644
--- a/code/game/objects/items/flamethrower.dm
+++ b/code/game/objects/items/flamethrower.dm
@@ -196,9 +196,8 @@
sleep(1)
previousturf = T
operating = FALSE
- for(var/mob/M in viewers(1, loc))
- if((M.client && M.machine == src))
- attack_self(M)
+ if(usr.machine == src)
+ attack_self(usr)
/obj/item/flamethrower/proc/default_ignite(turf/target, release_amount = 0.05)
diff --git a/code/game/objects/items/implants/implantpad.dm b/code/game/objects/items/implants/implantpad.dm
index 7c863017a1..4766e534c9 100644
--- a/code/game/objects/items/implants/implantpad.dm
+++ b/code/game/objects/items/implants/implantpad.dm
@@ -66,7 +66,7 @@
if(ismob(loc))
attack_self(loc)
else
- for(var/mob/M in viewers(1, src))
+ for(var/mob/M in fov_viewers(1, src))
if(M.client)
attack_self(M)
add_fingerprint(usr)
diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm
index fc7b196bd2..1459afeb2d 100644
--- a/code/game/objects/items/shields.dm
+++ b/code/game/objects/items/shields.dm
@@ -197,10 +197,10 @@
if(obj_integrity >= max_integrity)
to_chat(user, "[src] is already in perfect condition.")
else
- var/obj/item/stack/sheet/mineral/titanium/T = W
- T.use(1)
+ var/obj/item/stack/S = W
+ S.use(1)
obj_integrity = max_integrity
- to_chat(user, "You repair [src] with [T].")
+ to_chat(user, "You repair [src] with [S].")
else
return ..()
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index f19dbb2a6b..aebc5121cd 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -5,7 +5,6 @@
* Wood
* Bamboo
* Cloth
- * Silk
* Durathread
* Cardboard
* Runed Metal (cult)
@@ -268,6 +267,7 @@ GLOBAL_LIST_INIT(wood_recipes, list ( \
new/datum/stack_recipe("apiary", /obj/structure/beebox, 40, time = 50),\
null, \
new/datum/stack_recipe("picture frame", /obj/item/wallframe/picture, 1, time = 10),\
+ new/datum/stack_recipe("painting frame", /obj/item/wallframe/painting, 1, time = 10),\
new/datum/stack_recipe("mortar", /obj/item/reagent_containers/glass/mortar, 3), \
new/datum/stack_recipe("honey frame", /obj/item/honey_frame, 5, time = 10),\
))
@@ -388,6 +388,9 @@ GLOBAL_LIST_INIT(cloth_recipes, list ( \
null, \
new/datum/stack_recipe("blindfold", /obj/item/clothing/glasses/sunglasses/blindfold, 2), \
null, \
+ new/datum/stack_recipe("19x19 canvas", /obj/item/canvas/nineteenXnineteen, 3), \
+ new/datum/stack_recipe("23x19 canvas", /obj/item/canvas/twentythreeXnineteen, 4), \
+ new/datum/stack_recipe("23x23 canvas", /obj/item/canvas/twentythreeXtwentythree, 5), \
))
/obj/item/stack/sheet/cloth
@@ -401,7 +404,6 @@ GLOBAL_LIST_INIT(cloth_recipes, list ( \
throwforce = 0
pull_effort = 90
is_fabric = TRUE
- loom_result = /obj/item/stack/sheet/silk
merge_type = /obj/item/stack/sheet/cloth
/obj/item/stack/sheet/cloth/get_main_recipes()
@@ -414,30 +416,6 @@ GLOBAL_LIST_INIT(cloth_recipes, list ( \
/obj/item/stack/sheet/cloth/thirty
amount = 30
-/*
- * Silk
- */
-
- GLOBAL_LIST_INIT(silk_recipes, list ( \
- new/datum/stack_recipe("white jumpsuit", /obj/item/clothing/under/color/white, 4, time = 40), \
- new/datum/stack_recipe("white gloves", /obj/item/clothing/gloves/color/white, 2, time = 40), \
- null, \
- new/datum/stack_recipe("silk string", /obj/item/weaponcrafting/silkstring, 1, time = 40), \
- ))
-
-/obj/item/stack/sheet/silk
- name = "silk"
- desc = "A long, soft material. Made out of refined cotton, instead of relying on the habits of spiders or silkworms."
- singular_name = "silk sheet"
- icon_state = "sheet-silk"
- item_state = "sheet-cloth"
- novariants = TRUE
- merge_type = /obj/item/stack/sheet/silk
-
-/obj/item/stack/sheet/silk/get_main_recipes()
- . = ..()
- . += GLOB.silk_recipes
-
/*
* Durathread
*/
@@ -446,6 +424,7 @@ GLOBAL_LIST_INIT(durathread_recipes, list ( \
new/datum/stack_recipe("durathread beret", /obj/item/clothing/head/beret/durathread, 2, time = 40), \
new/datum/stack_recipe("durathread beanie", /obj/item/clothing/head/beanie/durathread, 2, time = 40), \
new/datum/stack_recipe("durathread bandana", /obj/item/clothing/mask/bandana/durathread, 1, time = 25), \
+ new/datum/stack_recipe("durathread string", /obj/item/weaponcrafting/durathread_string, 1, time = 40) \
))
/obj/item/stack/sheet/durathread
diff --git a/code/game/objects/items/storage/storage.dm b/code/game/objects/items/storage/_storage.dm
similarity index 100%
rename from code/game/objects/items/storage/storage.dm
rename to code/game/objects/items/storage/_storage.dm
diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm
index a4d9636414..3d5f0dc924 100644
--- a/code/game/objects/items/storage/backpack.dm
+++ b/code/game/objects/items/storage/backpack.dm
@@ -24,9 +24,9 @@
/obj/item/storage/backpack/ComponentInitialize()
. = ..()
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 21
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.max_items = 21
+ STR.storage_flags = STORAGE_FLAGS_VOLUME_DEFAULT
+ STR.max_volume = STORAGE_VOLUME_BACKPACK
+ STR.max_w_class = MAX_WEIGHT_CLASS_BACKPACK
/*
* Backpack Types
@@ -64,9 +64,9 @@
/obj/item/storage/backpack/holding/ComponentInitialize()
. = ..()
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.allow_big_nesting = TRUE
- STR.max_w_class = WEIGHT_CLASS_BULKY
- STR.max_combined_w_class = 35
+ STR.max_w_class = MAX_WEIGHT_CLASS_BAG_OF_HOLDING
+ STR.storage_flags = STORAGE_FLAGS_VOLUME_DEFAULT
+ STR.max_volume = STORAGE_VOLUME_BAG_OF_HOLDING
/obj/item/storage/backpack/holding/suicide_act(mob/living/user)
user.visible_message("[user] is jumping into [src]! It looks like [user.p_theyre()] trying to commit suicide.")
@@ -344,7 +344,7 @@
/obj/item/storage/backpack/duffelbag/ComponentInitialize()
. = ..()
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 30
+ STR.max_volume = STORAGE_VOLUME_DUFFLEBAG
/obj/item/storage/backpack/duffelbag/captain
name = "captain's duffel bag"
diff --git a/code/game/objects/items/storage/secure.dm b/code/game/objects/items/storage/secure.dm
index e177a9f9fd..ef70bd201d 100644
--- a/code/game/objects/items/storage/secure.dm
+++ b/code/game/objects/items/storage/secure.dm
@@ -105,10 +105,7 @@
if (length(code) > 5)
code = "ERROR"
add_fingerprint(usr)
- for(var/mob/M in viewers(1, loc))
- if ((M.client && M.machine == src))
- attack_self(M)
- return
+ attack_self(usr)
return
@@ -158,6 +155,7 @@
/obj/item/storage/secure/safe
name = "secure safe"
icon = 'icons/obj/storage.dmi'
+ plane = ABOVE_WALL_PLANE
icon_state = "safe"
icon_opened = "safe0"
icon_locking = "safeb"
diff --git a/code/game/objects/items/storage/toolbox.dm b/code/game/objects/items/storage/toolbox.dm
index 64e0a6b492..7e89ddbd8d 100644
--- a/code/game/objects/items/storage/toolbox.dm
+++ b/code/game/objects/items/storage/toolbox.dm
@@ -16,12 +16,17 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
attack_verb = list("robusted")
hitsound = 'sound/weapons/smash.ogg'
custom_materials = list(/datum/material/iron = 500)
- material_flags = MATERIAL_COLOR
var/latches = "single_latch"
var/has_latches = TRUE
var/can_rubberify = TRUE
rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE //very protecc too
+/obj/item/storage/toolbox/greyscale
+ icon_state = "toolbox_default"
+ item_state = "toolbox_default"
+ can_rubberify = FALSE
+ material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS | MATERIAL_EFFECTS
+
/obj/item/storage/toolbox/Initialize(mapload)
if(has_latches)
if(prob(10))
@@ -48,7 +53,6 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
name = "emergency toolbox"
icon_state = "red"
item_state = "toolbox_red"
- material_flags = NONE
/obj/item/storage/toolbox/emergency/PopulateContents()
new /obj/item/crowbar/red(src)
@@ -73,7 +77,6 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
name = "mechanical toolbox"
icon_state = "blue"
item_state = "toolbox_blue"
- material_flags = NONE
/obj/item/storage/toolbox/mechanical/PopulateContents()
new /obj/item/screwdriver(src)
@@ -102,7 +105,6 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
name = "electrical toolbox"
icon_state = "yellow"
item_state = "toolbox_yellow"
- material_flags = NONE
/obj/item/storage/toolbox/electrical/PopulateContents()
var/pickedcolor = pick("red","yellow","green","blue","pink","orange","cyan","white")
@@ -124,7 +126,6 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
desc = "A toolbox painted black with a red stripe. It looks more heavier than normal toolboxes."
force = 15
throwforce = 18
- material_flags = NONE
/obj/item/storage/toolbox/syndicate/ComponentInitialize()
. = ..()
@@ -144,7 +145,6 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
name = "mechanical toolbox"
icon_state = "blue"
item_state = "toolbox_blue"
- material_flags = NONE
/obj/item/storage/toolbox/drone/PopulateContents()
var/pickedcolor = pick("red","yellow","green","blue","pink","orange","cyan","white")
@@ -166,7 +166,6 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
w_class = WEIGHT_CLASS_HUGE
attack_verb = list("robusted", "crushed", "smashed")
can_rubberify = FALSE
- material_flags = NONE
var/fabricator_type = /obj/item/clockwork/replica_fabricator/scarab
/obj/item/storage/toolbox/brass/ComponentInitialize()
@@ -209,7 +208,6 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
w_class = WEIGHT_CLASS_HUGE //heyo no bohing this!
force = 18 //spear damage
can_rubberify = FALSE
- material_flags = NONE
/obj/item/storage/toolbox/plastitanium/afterattack(atom/A, mob/user, proximity)
. = ..()
@@ -225,7 +223,6 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
icon_state = "green"
item_state = "toolbox_green"
w_class = WEIGHT_CLASS_GIGANTIC //Holds more than a regular toolbox!
- material_flags = NONE
/obj/item/storage/toolbox/artistic/ComponentInitialize()
. = ..()
@@ -302,7 +299,6 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
icon_state = "gold"
item_state = "toolbox_gold"
has_latches = FALSE
- material_flags = NONE
/obj/item/storage/toolbox/gold_real/PopulateContents()
new /obj/item/screwdriver/nuke(src)
@@ -328,7 +324,6 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
force = 0
throwforce = 0
can_rubberify = FALSE
- material_flags = NONE
/obj/item/storage/toolbox/proc/rubberify()
name = "rubber [name]"
@@ -364,7 +359,6 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
throwforce = 15
attack_verb = list("robusted", "bounced")
can_rubberify = FALSE //we are already the future.
- material_flags = NONE
/obj/item/storage/toolbox/rubber/Initialize()
icon_state = pick("blue", "red", "yellow", "green")
diff --git a/code/game/objects/items/tanks/watertank.dm b/code/game/objects/items/tanks/watertank.dm
index 57e9ea9aaa..de5f332f43 100644
--- a/code/game/objects/items/tanks/watertank.dm
+++ b/code/game/objects/items/tanks/watertank.dm
@@ -210,6 +210,7 @@
power = 8
force = 10
precision = 1
+ total_mass = 0.2
cooling_power = 5
w_class = WEIGHT_CLASS_HUGE
item_flags = ABSTRACT // don't put in storage
diff --git a/code/game/objects/items/teleportation.dm b/code/game/objects/items/teleportation.dm
index b5edb27704..881e4d5f1a 100644
--- a/code/game/objects/items/teleportation.dm
+++ b/code/game/objects/items/teleportation.dm
@@ -104,7 +104,7 @@
if (ismob(src.loc))
attack_self(src.loc)
else
- for(var/mob/M in viewers(1, src))
+ for(var/mob/M in fov_viewers(1, src))
if (M.client)
src.attack_self(M)
return
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index d34daf5c75..aa0aa70e03 100644
--- a/code/game/objects/items/weaponry.dm
+++ b/code/game/objects/items/weaponry.dm
@@ -478,6 +478,20 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
throw_range = 2
attack_verb = list("busted")
+/obj/item/statuebust/attack_self(mob/living/user)
+ add_fingerprint(user)
+ user.examinate(src)
+
+/obj/item/statuebust/examine(mob/living/user)
+ . = ..()
+ if(.)
+ return
+ if (!isliving(user))
+ return
+ user.visible_message("[user] stops to admire [src].", \
+ "You take in [src], admiring its fine craftsmanship.")
+ SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "artgood", /datum/mood_event/artgood)
+
/obj/item/tailclub
name = "tail club"
desc = "For the beating to death of lizards with their own tails."
diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm
index 8dac972d04..e64b7d8f8d 100644
--- a/code/game/objects/obj_defense.dm
+++ b/code/game/objects/obj_defense.dm
@@ -112,7 +112,7 @@
/obj/attack_animal(mob/living/simple_animal/M)
if(!M.melee_damage_upper && !M.obj_damage)
- M.emote("custom", message = "[M.friendly] [src].")
+ M.emote("custom", message = "[M.friendly_verb_continuous] [src].")
return 0
else
var/play_soundeffect = 1
@@ -232,15 +232,16 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e
cut_overlay(GLOB.fire_overlay, TRUE)
SSfire_burning.processing -= src
-/obj/proc/tesla_act(power, tesla_flags, shocked_targets)
+/obj/zap_act(power, zap_flags, shocked_targets)
+ if(QDELETED(src))
+ return 0
obj_flags |= BEING_SHOCKED
- var/power_bounced = power / 2
- tesla_zap(src, 3, power_bounced, tesla_flags, shocked_targets)
addtimer(CALLBACK(src, .proc/reset_shocked), 10)
+ return power / 2
//The surgeon general warns that being buckled to certain objects receiving powerful shocks is greatly hazardous to your health
-//Only tesla coils and grounding rods currently call this because mobs are already targeted over all other objects, but this might be useful for more things later.
-/obj/proc/tesla_buckle_check(var/strength)
+//Only tesla coils, vehicles, and grounding rods currently call this because mobs are already targeted over all other objects, but this might be useful for more things later.
+/obj/proc/zap_buckle_check(var/strength)
if(has_buckled_mobs())
for(var/m in buckled_mobs)
var/mob/living/buckled_mob = m
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 199f0c51e2..0b2fe5ac2a 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -123,7 +123,7 @@
/obj/proc/updateUsrDialog()
if((obj_flags & IN_USE) && !(obj_flags & USES_TGUI))
var/is_in_use = FALSE
- var/list/nearby = viewers(1, src)
+ var/list/nearby = fov_viewers(1, src)
for(var/mob/M in nearby)
if ((M.client && M.machine == src))
is_in_use = TRUE
@@ -152,7 +152,7 @@
if(obj_flags & IN_USE)
var/is_in_use = FALSE
if(update_viewers)
- for(var/mob/M in viewers(1, src))
+ for(var/mob/M in fov_viewers(1, src))
if ((M.client && M.machine == src))
is_in_use = TRUE
src.interact(M)
diff --git a/code/game/objects/structures/artstuff.dm b/code/game/objects/structures/artstuff.dm
index 405e697d3b..98f772a116 100644
--- a/code/game/objects/structures/artstuff.dm
+++ b/code/game/objects/structures/artstuff.dm
@@ -35,101 +35,368 @@
else
painting = null
-
-//////////////
-// CANVASES //
-//////////////
-
-#define AMT_OF_CANVASES 4 //Keep this up to date or shit will break.
-
-//To safe memory on making /icons we cache the blanks..
-GLOBAL_LIST_INIT(globalBlankCanvases, new(AMT_OF_CANVASES))
-
/obj/item/canvas
name = "canvas"
desc = "Draw out your soul on this canvas!"
icon = 'icons/obj/artstuff.dmi'
icon_state = "11x11"
resistance_flags = FLAMMABLE
- var/whichGlobalBackup = 1 //List index
+ var/width = 11
+ var/height = 11
+ var/list/grid
+ var/canvas_color = "#ffffff" //empty canvas color
+ var/ui_x = 400
+ var/ui_y = 400
+ var/used = FALSE
+ var/painting_name //Painting name, this is set after framing.
+ var/finalized = FALSE //Blocks edits
+ var/author_ckey
+ var/icon_generated = FALSE
+ var/icon/generated_icon
-/obj/item/canvas/nineteenXnineteen
- icon_state = "19x19"
- whichGlobalBackup = 2
+ // Painting overlay offset when framed
+ var/framed_offset_x = 11
+ var/framed_offset_y = 10
-/obj/item/canvas/twentythreeXnineteen
- icon_state = "23x19"
- whichGlobalBackup = 3
+ pixel_x = 10
+ pixel_y = 9
-/obj/item/canvas/twentythreeXtwentythree
- icon_state = "23x23"
- whichGlobalBackup = 4
-
-//HEY YOU
-//ARE YOU READING THE CODE FOR CANVASES?
-//ARE YOU AWARE THEY CRASH HALF THE SERVER WHEN SOMEONE DRAWS ON THEM...
-//...AND NOBODY CAN FIGURE OUT WHY?
-//THEN GO ON BRAVE TRAVELER
-//TRY TO FIX THEM AND REMOVE THIS CODE
/obj/item/canvas/Initialize()
- ..()
- return INITIALIZE_HINT_QDEL //Delete on creation
+ . = ..()
+ reset_grid()
-//Find the right size blank canvas
-/obj/item/canvas/proc/getGlobalBackup()
- . = null
- if(GLOB.globalBlankCanvases[whichGlobalBackup])
- . = GLOB.globalBlankCanvases[whichGlobalBackup]
- else
- var/icon/I = icon(initial(icon),initial(icon_state))
- GLOB.globalBlankCanvases[whichGlobalBackup] = I
- . = I
+/obj/item/canvas/proc/reset_grid()
+ grid = new/list(width,height)
+ for(var/x in 1 to width)
+ for(var/y in 1 to height)
+ grid[x][y] = canvas_color
+/obj/item/canvas/attack_self(mob/user)
+ . = ..()
+ ui_interact(user)
+/obj/item/canvas/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
+ datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
-//One pixel increments
-/obj/item/canvas/attackby(obj/item/I, mob/user, params)
- //Click info
- var/list/click_params = params2list(params)
- var/pixX = text2num(click_params["icon-x"])
- var/pixY = text2num(click_params["icon-y"])
+ ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "canvas", name, ui_x, ui_y, master_ui, state)
+ ui.set_autoupdate(FALSE)
+ ui.open()
- //Should always be true, otherwise you didn't click the object, but let's check because SS13~
- if(!click_params || !click_params["icon-x"] || !click_params["icon-y"])
- return
-
- //Cleaning one pixel with a soap or rag
- if(istype(I, /obj/item/soap) || istype(I, /obj/item/reagent_containers/rag))
- //Pixel info created only when needed
- var/icon/masterpiece = icon(icon,icon_state)
- var/thePix = masterpiece.GetPixel(pixX,pixY)
- var/icon/Ico = getGlobalBackup()
- if(!Ico)
- qdel(masterpiece)
- return
-
- var/theOriginalPix = Ico.GetPixel(pixX,pixY)
- if(thePix != theOriginalPix) //colour changed
- DrawPixelOn(theOriginalPix,pixX,pixY)
- qdel(masterpiece)
-
- //Drawing one pixel with a crayon
- else if(istype(I, /obj/item/toy/crayon))
- var/obj/item/toy/crayon/C = I
- DrawPixelOn(C.paint_color, pixX, pixY)
+/obj/item/canvas/attackby(obj/item/I, mob/living/user, params)
+ if(user.a_intent == INTENT_HELP)
+ ui_interact(user)
else
return ..()
+/obj/item/canvas/ui_data(mob/user)
+ . = ..()
+ .["grid"] = grid
+ .["name"] = painting_name
+ .["finalized"] = finalized
-//Clean the whole canvas
-/obj/item/canvas/attack_self(mob/user)
- if(!user)
+/obj/item/canvas/examine(mob/user)
+ . = ..()
+ ui_interact(user)
+
+/obj/item/canvas/ui_act(action, params)
+ . = ..()
+ if(. || finalized)
return
- var/icon/blank = getGlobalBackup()
- if(blank)
- //it's basically a giant etch-a-sketch
- icon = blank
- user.visible_message("[user] cleans the canvas.","You clean the canvas.")
+ var/mob/user = usr
+ switch(action)
+ if("paint")
+ var/obj/item/I = user.get_active_held_item()
+ var/color = get_paint_tool_color(I)
+ if(!color)
+ return FALSE
+ var/x = text2num(params["x"])
+ var/y = text2num(params["y"])
+ grid[x][y] = color
+ used = TRUE
+ update_icon()
+ . = TRUE
+ if("finalize")
+ . = TRUE
+ if(!finalized)
+ finalize(user)
+
+/obj/item/canvas/proc/finalize(mob/user)
+ finalized = TRUE
+ author_ckey = user.ckey
+ generate_proper_overlay()
+ try_rename(user)
+
+/obj/item/canvas/update_overlays()
+ . = ..()
+ if(!icon_generated)
+ if(used)
+ var/mutable_appearance/detail = mutable_appearance(icon,"[icon_state]wip")
+ detail.pixel_x = 1
+ detail.pixel_y = 1
+ . += detail
+ else
+ var/mutable_appearance/detail = mutable_appearance(generated_icon)
+ detail.pixel_x = 1
+ detail.pixel_y = 1
+ . += detail
+
+/obj/item/canvas/proc/generate_proper_overlay()
+ if(icon_generated)
+ return
+ var/png_filename = "data/paintings/temp_painting.png"
+ var/result = rustg_dmi_create_png(png_filename,"[width]","[height]",get_data_string())
+ if(result)
+ CRASH("Error generating painting png : [result]")
+ generated_icon = new(png_filename)
+ icon_generated = TRUE
+ update_icon()
+
+/obj/item/canvas/proc/get_data_string()
+ var/list/data = list()
+ for(var/y in 1 to height)
+ for(var/x in 1 to width)
+ data += grid[x][y]
+ return data.Join("")
+
+//Todo make this element ?
+/obj/item/canvas/proc/get_paint_tool_color(obj/item/I)
+ if(!I)
+ return
+ if(istype(I, /obj/item/toy/crayon))
+ var/obj/item/toy/crayon/C = I
+ return C.paint_color
+ else if(istype(I, /obj/item/pen))
+ var/obj/item/pen/P = I
+ switch(P.colour)
+ if("black")
+ return "#000000"
+ if("blue")
+ return "#0000ff"
+ if("red")
+ return "#ff0000"
+ return P.colour
+ else if(istype(I, /obj/item/soap) || istype(I, /obj/item/reagent_containers/rag))
+ return canvas_color
+
+/obj/item/canvas/proc/try_rename(mob/user)
+ var/new_name = stripped_input(user,"What do you want to name the painting?")
+ if(!painting_name && new_name && user.canUseTopic(src,BE_CLOSE))
+ painting_name = new_name
+ SStgui.update_uis(src)
+
+/obj/item/canvas/nineteenXnineteen
+ icon_state = "19x19"
+ width = 19
+ height = 19
+ ui_x = 600
+ ui_y = 600
+ pixel_x = 6
+ pixel_y = 9
+ framed_offset_x = 8
+ framed_offset_y = 9
+
+/obj/item/canvas/twentythreeXnineteen
+ icon_state = "23x19"
+ width = 23
+ height = 19
+ ui_x = 800
+ ui_y = 600
+ pixel_x = 4
+ pixel_y = 10
+ framed_offset_x = 6
+ framed_offset_y = 8
+
+/obj/item/canvas/twentythreeXtwentythree
+ icon_state = "23x23"
+ width = 23
+ height = 23
+ ui_x = 800
+ ui_y = 800
+ pixel_x = 5
+ pixel_y = 9
+ framed_offset_x = 5
+ framed_offset_y = 6
+
+/obj/item/wallframe/painting
+ name = "painting frame"
+ desc = "The perfect showcase for your favorite deathtrap memories."
+ icon = 'icons/obj/decals.dmi'
+ custom_materials = null
+ flags_1 = 0
+ icon_state = "frame-empty"
+ result_path = /obj/structure/sign/painting
+
+/obj/structure/sign/painting
+ name = "Painting"
+ desc = "Art or \"Art\"? You decide."
+ icon = 'icons/obj/decals.dmi'
+ icon_state = "frame-empty"
+ buildable_sign = FALSE
+ var/obj/item/canvas/C
+ var/persistence_id
+
+/obj/structure/sign/painting/Initialize(mapload, dir, building)
+ . = ..()
+ SSpersistence.painting_frames += src
+ AddComponent(/datum/component/art, 20)
+ if(dir)
+ setDir(dir)
+ if(building)
+ pixel_x = (dir & 3)? 0 : (dir == 4 ? -30 : 30)
+ pixel_y = (dir & 3)? (dir ==1 ? -30 : 30) : 0
+
+/obj/structure/sign/painting/Destroy()
+ . = ..()
+ SSpersistence.painting_frames -= src
+
+/obj/structure/sign/painting/attackby(obj/item/I, mob/user, params)
+ if(!C && istype(I, /obj/item/canvas))
+ frame_canvas(user,I)
+ else if(C && !C.painting_name && istype(I,/obj/item/pen))
+ try_rename(user)
+ else
+ return ..()
+
+/obj/structure/sign/painting/examine(mob/user)
+ . = ..()
+ if(C)
+ C.ui_interact(user,state = GLOB.physical_obscured_state)
+
+/obj/structure/sign/painting/wirecutter_act(mob/living/user, obj/item/I)
+ . = ..()
+ if(C)
+ C.forceMove(drop_location())
+ C = null
+ to_chat(user, "You remove the painting from the frame.")
+ update_icon()
+ return TRUE
+
+/obj/structure/sign/painting/proc/frame_canvas(mob/user,obj/item/canvas/new_canvas)
+ if(user.transferItemToLoc(new_canvas,src))
+ C = new_canvas
+ if(!C.finalized)
+ C.finalize(user)
+ to_chat(user,"You frame [C].")
+ update_icon()
+
+/obj/structure/sign/painting/proc/try_rename(mob/user)
+ if(!C.painting_name)
+ C.try_rename(user)
+
+/obj/structure/sign/painting/update_icon_state()
+ . = ..()
+ if(C && C.generated_icon)
+ icon_state = null
+ else
+ icon_state = "frame-empty"
-#undef AMT_OF_CANVASES
+/obj/structure/sign/painting/update_overlays()
+ . = ..()
+ if(C && C.generated_icon)
+ var/mutable_appearance/MA = mutable_appearance(C.generated_icon)
+ MA.pixel_x = C.framed_offset_x
+ MA.pixel_y = C.framed_offset_y
+ . += MA
+ var/mutable_appearance/frame = mutable_appearance(C.icon,"[C.icon_state]frame")
+ frame.pixel_x = C.framed_offset_x - 1
+ frame.pixel_y = C.framed_offset_y - 1
+ . += frame
+
+/obj/structure/sign/painting/proc/load_persistent()
+ if(!persistence_id)
+ return
+ if(!SSpersistence.paintings || !SSpersistence.paintings[persistence_id] || !length(SSpersistence.paintings[persistence_id]))
+ return
+ var/list/chosen = pick(SSpersistence.paintings[persistence_id])
+ var/title = chosen["title"]
+ var/author = chosen["ckey"]
+ var/png = "data/paintings/[persistence_id]/[chosen["md5"]].png"
+ if(!fexists(png))
+ stack_trace("Persistent painting [chosen["md5"]].png was not found in [persistence_id] directory.")
+ return
+ var/icon/I = new(png)
+ var/obj/item/canvas/new_canvas
+ var/w = I.Width()
+ var/h = I.Height()
+ for(var/T in typesof(/obj/item/canvas))
+ new_canvas = T
+ if(initial(new_canvas.width) == w && initial(new_canvas.height) == h)
+ new_canvas = new T(src)
+ break
+ new_canvas.fill_grid_from_icon(I)
+ new_canvas.generated_icon = I
+ new_canvas.icon_generated = TRUE
+ new_canvas.finalized = TRUE
+ new_canvas.painting_name = title
+ new_canvas.author_ckey = author
+ C = new_canvas
+ update_icon()
+
+/obj/structure/sign/painting/proc/save_persistent()
+ if(!persistence_id || !C)
+ return
+ if(sanitize_filename(persistence_id) != persistence_id)
+ stack_trace("Invalid persistence_id - [persistence_id]")
+ return
+ var/data = C.get_data_string()
+ var/md5 = md5(data)
+ var/list/current = SSpersistence.paintings[persistence_id]
+ if(!current)
+ current = list()
+ for(var/list/entry in current)
+ if(entry["md5"] == md5)
+ return
+ var/png_directory = "data/paintings/[persistence_id]/"
+ var/png_path = png_directory + "[md5].png"
+ var/result = rustg_dmi_create_png(png_path,"[C.width]","[C.height]",data)
+ if(result)
+ CRASH("Error saving persistent painting: [result]")
+ current += list(list("title" = C.painting_name , "md5" = md5, "ckey" = C.author_ckey))
+ SSpersistence.paintings[persistence_id] = current
+
+/obj/item/canvas/proc/fill_grid_from_icon(icon/I)
+ var/h = I.Height() + 1
+ for(var/x in 1 to width)
+ for(var/y in 1 to height)
+ grid[x][y] = I.GetPixel(x,h-y)
+
+//Presets for art gallery mapping, for paintings to be shared across stations
+/obj/structure/sign/painting/library
+ persistence_id = "library"
+
+/obj/structure/sign/painting/library_secure
+ persistence_id = "library_secure"
+
+/obj/structure/sign/painting/library_private // keep your smut away from prying eyes, or non-librarians at least
+ persistence_id = "library_private"
+
+/obj/structure/sign/painting/vv_get_dropdown()
+ . = ..()
+ VV_DROPDOWN_OPTION(VV_HK_REMOVE_PAINTING, "Remove Persistent Painting")
+
+/obj/structure/sign/painting/vv_do_topic(list/href_list)
+ . = ..()
+ if(href_list[VV_HK_REMOVE_PAINTING])
+ if(!check_rights(NONE))
+ return
+ var/mob/user = usr
+ if(!persistence_id || !C)
+ to_chat(user,"This is not a persistent painting.")
+ return
+ var/md5 = md5(C.get_data_string())
+ var/author = C.author_ckey
+ var/list/current = SSpersistence.paintings[persistence_id]
+ if(current)
+ for(var/list/entry in current)
+ if(entry["md5"] == md5)
+ current -= entry
+ var/png = "data/paintings/[persistence_id]/[md5].png"
+ fdel(png)
+ for(var/obj/structure/sign/painting/P in SSpersistence.painting_frames)
+ if(P.C && md5(P.C.get_data_string()) == md5)
+ QDEL_NULL(P.C)
+ log_admin("[key_name(user)] has deleted a persistent painting made by [author].")
+ message_admins("[key_name_admin(user)] has deleted persistent painting made by [author].")
diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm
index d3e8ecf0a6..2225c4c0c2 100644
--- a/code/game/objects/structures/beds_chairs/chair.dm
+++ b/code/game/objects/structures/beds_chairs/chair.dm
@@ -108,7 +108,9 @@
if(!istype(poordude))
return TRUE
user.visible_message("[user] pulls [src] out from under [poordude].", "You pull [src] out from under [poordude].")
- var/C = new item_chair(loc)
+ var/obj/item/chair/C = new item_chair(loc)
+ C.set_custom_materials(custom_materials)
+ TransferComponents(C)
user.put_in_hands(C)
poordude.DefaultCombatKnockdown(20)//rip in peace
user.adjustStaminaLoss(5)
diff --git a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm
index ae2e1a070a..500b8d6a49 100644
--- a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm
+++ b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm
@@ -24,6 +24,7 @@
move_delay = TRUE
var/oldloc = loc
step(src, direction)
+ user.setDir(direction)
if(oldloc != loc)
addtimer(CALLBACK(src, .proc/ResetMoveDelay), (use_mob_movespeed ? user.movement_delay() : CONFIG_GET(number/movedelay/walk_delay)) * move_speed_multiplier)
else
@@ -42,7 +43,7 @@
Snake = L
break
if(Snake)
- alerted = viewers(7,src)
+ alerted = fov_viewers(world.view,src)
..()
if(LAZYLEN(alerted))
egged = world.time + SNAKE_SPAM_TICKS
diff --git a/code/game/objects/structures/extinguisher.dm b/code/game/objects/structures/extinguisher.dm
index 9b736517be..3bc84deb4d 100644
--- a/code/game/objects/structures/extinguisher.dm
+++ b/code/game/objects/structures/extinguisher.dm
@@ -3,6 +3,7 @@
desc = "A small wall mounted cabinet designed to hold a fire extinguisher."
icon = 'icons/obj/wallmounts.dmi'
icon_state = "extinguisher_closed"
+ plane = ABOVE_WALL_PLANE
anchored = TRUE
density = FALSE
max_integrity = 200
diff --git a/code/game/objects/structures/false_walls.dm b/code/game/objects/structures/false_walls.dm
index e44698f96e..679a755321 100644
--- a/code/game/objects/structures/false_walls.dm
+++ b/code/game/objects/structures/false_walls.dm
@@ -7,6 +7,7 @@
anchored = TRUE
icon = 'icons/turf/walls/wall.dmi'
icon_state = "wall"
+ plane = WALL_PLANE
layer = LOW_OBJ_LAYER
density = TRUE
opacity = 1
diff --git a/code/game/objects/structures/fireaxe.dm b/code/game/objects/structures/fireaxe.dm
index 0f3a23dd0f..f4c1dd5ab9 100644
--- a/code/game/objects/structures/fireaxe.dm
+++ b/code/game/objects/structures/fireaxe.dm
@@ -3,6 +3,7 @@
desc = "There is a small label that reads \"For Emergency use only\" along with details for safe use of the axe. As if."
icon = 'icons/obj/wallmounts.dmi'
icon_state = "fireaxe"
+ plane = ABOVE_WALL_PLANE
anchored = TRUE
density = FALSE
armor = list("melee" = 50, "bullet" = 20, "laser" = 0, "energy" = 100, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 50)
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index 54e08db210..20151a0bdb 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -267,7 +267,7 @@
var/obj/structure/cable/C = T.get_cable_node()
if(C)
playsound(src, 'sound/magic/lightningshock.ogg', 100, 1, extrarange = 5)
- tesla_zap(src, 3, C.newavail() * 0.01, TESLA_MOB_DAMAGE | TESLA_OBJ_DAMAGE | TESLA_MOB_STUN | TESLA_ALLOW_DUPLICATES) //Zap for 1/100 of the amount of power. At a million watts in the grid, it will be as powerful as a tesla revolver shot.
+ tesla_zap(src, 3, C.newavail() * 0.01, ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE | ZAP_MOB_STUN | ZAP_ALLOW_DUPLICATES) //Zap for 1/100 of the amount of power. At a million watts in the grid, it will be as powerful as a tesla revolver shot.
C.add_delayedload(C.newavail() * 0.0375) // you can gain up to 3.5 via the 4x upgrades power is halved by the pole so thats 2x then 1X then .5X for 3.5x the 3 bounces shock.
return ..()
diff --git a/code/game/objects/structures/guillotine.dm b/code/game/objects/structures/guillotine.dm
index 611a6d024d..0a5ba6a68c 100644
--- a/code/game/objects/structures/guillotine.dm
+++ b/code/game/objects/structures/guillotine.dm
@@ -130,7 +130,7 @@
// The crowd is pleased
// The delay is to making large crowds have a longer laster applause
var/delay_offset = 0
- for(var/mob/M in viewers(src, 7))
+ for(var/mob/M in fov_viewers(world.view, src))
var/mob/living/carbon/human/C = M
if (ishuman(M))
addtimer(CALLBACK(C, /mob/.proc/emote, "clap"), delay_offset * 0.3)
diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm
index e32def727a..65e1b7dd9a 100644
--- a/code/game/objects/structures/mirror.dm
+++ b/code/game/objects/structures/mirror.dm
@@ -4,6 +4,7 @@
desc = "Mirror mirror on the wall, who's the most robust of them all?"
icon = 'icons/obj/watercloset.dmi'
icon_state = "mirror"
+ plane = ABOVE_WALL_PLANE
density = FALSE
anchored = TRUE
max_integrity = 200
diff --git a/code/game/objects/structures/noticeboard.dm b/code/game/objects/structures/noticeboard.dm
index f4bca1f900..68da53aa6c 100644
--- a/code/game/objects/structures/noticeboard.dm
+++ b/code/game/objects/structures/noticeboard.dm
@@ -3,6 +3,7 @@
desc = "A board for pinning important notices upon."
icon = 'icons/obj/stationobjs.dmi'
icon_state = "nboard00"
+ plane = ABOVE_WALL_PLANE
density = FALSE
anchored = TRUE
max_integrity = 150
diff --git a/code/game/objects/structures/signs/_signs.dm b/code/game/objects/structures/signs/_signs.dm
index 84add2f65f..a1de5fe6a3 100644
--- a/code/game/objects/structures/signs/_signs.dm
+++ b/code/game/objects/structures/signs/_signs.dm
@@ -3,6 +3,7 @@
anchored = TRUE
opacity = 0
density = FALSE
+ plane = ABOVE_WALL_PLANE
layer = SIGN_LAYER
max_integrity = 100
armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
diff --git a/code/game/objects/structures/statues.dm b/code/game/objects/structures/statues.dm
index 9a6b24b472..1f9491f4cf 100644
--- a/code/game/objects/structures/statues.dm
+++ b/code/game/objects/structures/statues.dm
@@ -8,12 +8,11 @@
max_integrity = 100
var/oreAmount = 5
var/material_drop_type = /obj/item/stack/sheet/metal
+ var/impressiveness = 15
CanAtmosPass = ATMOS_PASS_DENSITY
-
/obj/structure/statue/attackby(obj/item/W, mob/living/user, params)
add_fingerprint(user)
- user.changeNext_move(CLICK_CD_MELEE)
if(!(flags_1 & NODECONSTRUCT_1))
if(default_unfasten_wrench(user, W))
return
@@ -36,8 +35,22 @@
return
user.changeNext_move(CLICK_CD_MELEE)
add_fingerprint(user)
- user.visible_message("[user] rubs some dust off from the [name]'s surface.", \
- "You rub some dust off from the [name]'s surface.")
+ if(!do_after(user, 20, target = src))
+ return
+ user.visible_message("[user] rubs some dust off [src].", \
+ "You take in [src], rubbing some dust off its surface.")
+ if(!ishuman(user)) // only humans have the capacity to appreciate art
+ return
+ var/totalimpressiveness = (impressiveness *(obj_integrity/max_integrity))
+ switch(totalimpressiveness)
+ if(GREAT_ART to 100)
+ SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "artgreat", /datum/mood_event/artgreat)
+ if (GOOD_ART to GREAT_ART)
+ SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "artgood", /datum/mood_event/artgood)
+ if (BAD_ART to GOOD_ART)
+ SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "artok", /datum/mood_event/artok)
+ if (0 to BAD_ART)
+ SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "artbad", /datum/mood_event/artbad)
/obj/structure/statue/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
@@ -58,6 +71,7 @@
material_drop_type = /obj/item/stack/sheet/mineral/uranium
var/last_event = 0
var/active = null
+ impressiveness = 25 // radiation makes an impression
/obj/structure/statue/uranium/nuke
name = "statue of a nuclear fission explosive"
@@ -101,6 +115,7 @@
max_integrity = 200
material_drop_type = /obj/item/stack/sheet/mineral/plasma
desc = "This statue is suitably made from plasma."
+ impressiveness = 20
/obj/structure/statue/plasma/scientist
name = "statue of a scientist"
@@ -151,6 +166,7 @@
max_integrity = 300
material_drop_type = /obj/item/stack/sheet/mineral/gold
desc = "This is a highly valuable statue made from gold."
+ impressiveness = 30
/obj/structure/statue/gold/hos
name = "statue of the head of security"
@@ -178,6 +194,7 @@
max_integrity = 300
material_drop_type = /obj/item/stack/sheet/mineral/silver
desc = "This is a valuable statue made from silver."
+ impressiveness = 25
/obj/structure/statue/silver/md
name = "statue of a medical officer"
@@ -205,6 +222,7 @@
max_integrity = 1000
material_drop_type = /obj/item/stack/sheet/mineral/diamond
desc = "This is a very expensive diamond statue."
+ impressiveness = 60
/obj/structure/statue/diamond/captain
name = "statue of THE captain."
@@ -225,6 +243,7 @@
material_drop_type = /obj/item/stack/sheet/mineral/bananium
desc = "A bananium statue with a small engraving:'HOOOOOOONK'."
var/spam_flag = 0
+ impressiveness = 65
/obj/structure/statue/bananium/clown
name = "statue of a clown"
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 30e451aa14..3d552fa236 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -183,7 +183,7 @@
/obj/structure/table/alt_attack_hand(mob/user)
if(user && Adjacent(user) && !user.incapacitated())
- user.setClickCooldown(4)
+ user.changeNext_move(CLICK_CD_MELEE*0.5)
if(istype(user) && user.a_intent == INTENT_HARM)
user.visible_message("[user] slams [user.p_their()] palms down on [src].", "You slam your palms down on [src].")
playsound(src, 'sound/weapons/sonic_jackhammer.ogg', 50, 1)
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index e4d93ea8de..ba7c0d2fbe 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -1,7 +1,3 @@
-#define NOT_ELECTROCHROMATIC 0
-#define ELECTROCHROMATIC_OFF 1
-#define ELECTROCHROMATIC_DIMMED 2
-
GLOBAL_LIST_EMPTY(electrochromatic_window_lookup)
/proc/do_electrochromatic_toggle(new_status, id)
@@ -74,9 +70,8 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup)
if(reinf && anchored)
state = WINDOW_SCREWED_TO_FRAME
- if(mapload && electrochromatic_id)
- if(copytext(electrochromatic_id, 1, 2) == "!")
- electrochromatic_id = SSmapping.get_obfuscated_id(electrochromatic_id)
+ if(mapload && electrochromatic_id && electrochromatic_id[1] == "!")
+ electrochromatic_id = SSmapping.get_obfuscated_id(electrochromatic_id)
ini_dir = dir
air_update_turf(1)
@@ -885,7 +880,3 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup)
return
..()
update_icon()
-
-#undef NOT_ELECTROCHROMATIC
-#undef ELECTROCHROMATIC_OFF
-#undef ELECTROCHROMATIC_DIMMED
diff --git a/code/game/turfs/closed.dm b/code/game/turfs/closed.dm
index 3164836415..24af2ed5bd 100644
--- a/code/game/turfs/closed.dm
+++ b/code/game/turfs/closed.dm
@@ -1,5 +1,6 @@
/turf/closed
layer = CLOSED_TURF_LAYER
+ plane = WALL_PLANE
opacity = 1
density = TRUE
blocks_air = 1
diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm
index 268e8e9109..5243341ac1 100644
--- a/code/game/turfs/simulated/walls.dm
+++ b/code/game/turfs/simulated/walls.dm
@@ -290,7 +290,7 @@
if(LAZYLEN(dent_decals) >= MAX_DENT_DECALS)
return
- var/mutable_appearance/decal = mutable_appearance('icons/effects/effects.dmi', "", BULLET_HOLE_LAYER)
+ var/mutable_appearance/decal = mutable_appearance('icons/effects/effects.dmi', "", BULLET_HOLE_LAYER, ABOVE_WALL_PLANE)
switch(denttype)
if(WALL_DENT_SHOT)
decal.icon_state = "bullet_hole"
diff --git a/code/game/world.dm b/code/game/world.dm
index 10431e4af9..bd56ffdbf4 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -1,6 +1,7 @@
#define RESTART_COUNTER_PATH "data/round_counter.txt"
GLOBAL_VAR(restart_counter)
+GLOBAL_VAR_INIT(tgs_initialized, FALSE)
GLOBAL_VAR(topic_status_lastcache)
GLOBAL_LIST(topic_status_cache)
@@ -23,10 +24,11 @@ GLOBAL_LIST(topic_status_cache)
make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
- TgsNew(minimum_required_security_level = TGS_SECURITY_TRUSTED)
GLOB.revdata = new
+ InitTgs()
+
config.Load(params[OVERRIDE_CONFIG_DIRECTORY_PARAMETER])
//SetupLogs depends on the RoundID, so lets check
@@ -62,6 +64,15 @@ GLOBAL_LIST(topic_status_cache)
if(TEST_RUN_PARAMETER in params)
HandleTestRun()
+/world/proc/InitTgs()
+ TgsNew(new /datum/tgs_event_handler/impl, TGS_SECURITY_TRUSTED)
+ GLOB.revdata.load_tgs_info()
+#ifdef USE_CUSTOM_ERROR_HANDLER
+ if (TgsAvailable())
+ world.log = file("[GLOB.log_directory]/dd.log") //not all runtimes trigger world/Error, so this is the only way to ensure we can see all of them.
+#endif
+ GLOB.tgs_initialized = TRUE
+
/world/proc/HandleTestRun()
//trigger things to run the whole process
Master.sleep_offline_after_initializations = FALSE
@@ -223,16 +234,17 @@ GLOBAL_LIST(topic_status_cache)
qdel(src) //shut it down
/world/Reboot(reason = 0, fast_track = FALSE)
- TgsReboot()
if (reason || fast_track) //special reboot, do none of the normal stuff
if (usr)
log_admin("[key_name(usr)] Has requested an immediate world restart via client side debugging tools")
message_admins("[key_name_admin(usr)] Has requested an immediate world restart via client side debugging tools")
- to_chat(world, "Rebooting World immediately due to host request")
+ to_chat(world, "Rebooting World immediately due to host request.")
else
to_chat(world, "Rebooting world...")
Master.Shutdown() //run SS shutdowns
+ TgsReboot()
+
if(TEST_RUN_PARAMETER in params)
FinishTestRun()
return
diff --git a/code/modules/VR/vr_sleeper.dm b/code/modules/VR/vr_sleeper.dm
index e79784290b..26ae200990 100644
--- a/code/modules/VR/vr_sleeper.dm
+++ b/code/modules/VR/vr_sleeper.dm
@@ -22,6 +22,11 @@
sparks.set_up(2,0)
sparks.attach(src)
update_icon()
+ new_occupant_dir = dir
+
+/obj/machinery/vr_sleeper/setDir(newdir)
+ . = ..()
+ new_occupant_dir = dir
/obj/machinery/vr_sleeper/attackby(obj/item/I, mob/user, params)
if(!state_open && !occupant)
diff --git a/code/modules/admin/admin_investigate.dm b/code/modules/admin/admin_investigate.dm
index 55c55c42de..99bb988be6 100644
--- a/code/modules/admin/admin_investigate.dm
+++ b/code/modules/admin/admin_investigate.dm
@@ -4,7 +4,7 @@
var/F = file("[GLOB.log_directory]/[subject].html")
WRITE_FILE(F, "[TIME_STAMP("hh:mm:ss", FALSE)] [REF(src)] ([x],[y],[z]) || [src] [message] ")
-/client/proc/investigate_show(subject in list("notes, memos, watchlist", INVESTIGATE_RCD, INVESTIGATE_RESEARCH, INVESTIGATE_EXONET, INVESTIGATE_PORTAL, INVESTIGATE_SINGULO, INVESTIGATE_WIRES, INVESTIGATE_TELESCI, INVESTIGATE_GRAVITY, INVESTIGATE_RECORDS, INVESTIGATE_CARGO, INVESTIGATE_SUPERMATTER, INVESTIGATE_ATMOS, INVESTIGATE_EXPERIMENTOR, INVESTIGATE_BOTANY, INVESTIGATE_HALLUCINATIONS, INVESTIGATE_RADIATION, INVESTIGATE_CIRCUIT, INVESTIGATE_NANITES) )
+/client/proc/investigate_show(subject in list("notes, memos, watchlist", INVESTIGATE_RCD, INVESTIGATE_RESEARCH, INVESTIGATE_EXONET, INVESTIGATE_PORTAL, INVESTIGATE_SINGULO, INVESTIGATE_WIRES, INVESTIGATE_TELESCI, INVESTIGATE_GRAVITY, INVESTIGATE_RECORDS, INVESTIGATE_CARGO, INVESTIGATE_SUPERMATTER, INVESTIGATE_ATMOS, INVESTIGATE_EXPERIMENTOR, INVESTIGATE_BOTANY, INVESTIGATE_HALLUCINATIONS, INVESTIGATE_RADIATION, INVESTIGATE_CIRCUIT, INVESTIGATE_NANITES, INVESTIGATE_CRYOGENICS) )
set name = "Investigate"
set category = "Admin"
if(!holder)
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index e9b8c274a8..289abbe250 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -106,6 +106,7 @@ GLOBAL_LIST_INIT(admin_verbs_fun, list(
/client/proc/show_tip,
/client/proc/smite,
/client/proc/admin_away,
+ /client/proc/cmd_admin_toggle_fov,
/client/proc/roll_dices //CIT CHANGE - Adds dice verb
))
GLOBAL_PROTECT(admin_verbs_fun)
diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm
index a06b674e42..41662c1cad 100644
--- a/code/modules/admin/secrets.dm
+++ b/code/modules/admin/secrets.dm
@@ -69,11 +69,6 @@
The floor is lava! (DANGEROUS: extremely lame) Spawn a custom portal storm
- Flip client movement directions
- Randomize client movement directions
- Set each movement direction manually
- Reset movement directions to default
- Change bomb cap Mass Purrbation Mass Remove Purrbation
@@ -603,60 +598,6 @@
purrbation.")
log_admin("[key_name(usr)] has removed everyone from purrbation.")
- if("flipmovement")
- if(!check_rights(R_FUN))
- return
- if(alert("Flip all movement controls?","Confirm","Yes","Cancel") == "Cancel")
- return
- var/list/movement_keys = SSinput.movement_keys
- for(var/i in 1 to movement_keys.len)
- var/key = movement_keys[i]
- movement_keys[key] = turn(movement_keys[key], 180)
- message_admins("[key_name_admin(usr)] has flipped all movement directions.")
- log_admin("[key_name(usr)] has flipped all movement directions.")
-
- if("randommovement")
- if(!check_rights(R_FUN))
- return
- if(alert("Randomize all movement controls?","Confirm","Yes","Cancel") == "Cancel")
- return
- var/list/movement_keys = SSinput.movement_keys
- for(var/i in 1 to movement_keys.len)
- var/key = movement_keys[i]
- movement_keys[key] = turn(movement_keys[key], 45 * rand(1, 8))
- message_admins("[key_name_admin(usr)] has randomized all movement directions.")
- log_admin("[key_name(usr)] has randomized all movement directions.")
-
- if("custommovement")
- if(!check_rights(R_FUN))
- return
- if(alert("Are you sure you want to change every movement key?","Confirm","Yes","Cancel") == "Cancel")
- return
- var/list/movement_keys = SSinput.movement_keys
- var/list/new_movement = list()
- for(var/i in 1 to movement_keys.len)
- var/key = movement_keys[i]
-
- var/msg = "Please input the new movement direction when the user presses [key]. Ex. northeast"
- var/title = "New direction for [key]"
- var/new_direction = text2dir(input(usr, msg, title) as text|null)
- if(!new_direction)
- new_direction = movement_keys[key]
-
- new_movement[key] = new_direction
- SSinput.movement_keys = new_movement
- message_admins("[key_name_admin(usr)] has configured all movement directions.")
- log_admin("[key_name(usr)] has configured all movement directions.")
-
- if("resetmovement")
- if(!check_rights(R_FUN))
- return
- if(alert("Are you sure you want to reset movement keys to default?","Confirm","Yes","Cancel") == "Cancel")
- return
- SSinput.setup_default_movement_keys()
- message_admins("[key_name_admin(usr)] has reset all movement keys.")
- log_admin("[key_name(usr)] has reset all movement keys.")
-
if("customportal")
if(!check_rights(R_FUN))
return
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
index 26736ddff1..df54df5c7d 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
@@ -1025,8 +1025,6 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
v = SSnpcpool
if("SSmobs")
v = SSmobs
- if("SSmood")
- v = SSmood
if("SSquirks")
v = SSquirks
if("SSwet_floors")
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 5530225130..d335cfb171 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -1218,6 +1218,58 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggled Hub Visibility", "[GLOB.hub_visibility ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+/client/proc/cmd_admin_toggle_fov()
+ set category = "Fun"
+ set name = "Enable/Disable Field of Vision"
+
+ var/static/busy_toggling_fov = FALSE
+ if(!check_rights(R_ADMIN) || !check_rights(R_FUN))
+ return
+
+ var/on_off = CONFIG_GET(flag/use_field_of_vision)
+
+ if(busy_toggling_fov)
+ to_chat(usr, "A previous call of this function is still busy toggling FoV [on_off ? "on" : "off"]. Have some patiece.")
+ return
+ busy_toggling_fov = TRUE
+
+ log_admin("[key_name(usr)] has [on_off ? "disabled" : "enabled"] the Field of Vision configuration.")
+ CONFIG_SET(flag/use_field_of_vision, !on_off)
+
+ SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggled Field of Vision", "[on_off ? "Enabled" : "Disabled"]"))
+
+ if(on_off)
+ for(var/k in GLOB.mob_list)
+ if(!k)
+ continue
+ var/mob/M = k
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ if(!(H.dna?.species.has_field_of_vision))
+ continue
+ else if(!M.has_field_of_vision)
+ continue
+ var/datum/component/field_of_vision/FoV = M.GetComponent(/datum/component/field_of_vision)
+ if(FoV)
+ qdel(FoV)
+ CHECK_TICK
+ else
+ for(var/k in GLOB.clients)
+ if(!k)
+ continue
+ var/client/C = k
+ var/mob/M = C.mob
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ if(!(H.dna?.species.has_field_of_vision))
+ continue
+ else if(!M.has_field_of_vision)
+ continue
+ M.LoadComponent(/datum/component/field_of_vision, M.field_of_vision_type)
+ CHECK_TICK
+
+ busy_toggling_fov = FALSE
+
/client/proc/smite(mob/living/carbon/human/target as mob)
set name = "Smite"
set category = "Fun"
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index 4a7abda53c..3184a169fc 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -24,6 +24,8 @@ GLOBAL_LIST_EMPTY(antagonists)
var/list/blacklisted_quirks = list(/datum/quirk/nonviolent,/datum/quirk/mute) // Quirks that will be removed upon gaining this antag. Pacifist and mute are default.
var/threat = 0 // Amount of threat this antag poses, for dynamic mode
+ var/list/skill_modifiers
+
/datum/antagonist/New()
GLOB.antagonists += src
typecache_datum_blacklist = typecacheof(typecache_datum_blacklist)
@@ -68,15 +70,19 @@ GLOBAL_LIST_EMPTY(antagonists)
//Proc called when the datum is given to a mind.
/datum/antagonist/proc/on_gain()
- if(owner && owner.current)
- if(!silent)
- greet()
- apply_innate_effects()
- give_antag_moodies()
- remove_blacklisted_quirks()
- if(is_banned(owner.current) && replace_banned)
- replace_banned_player()
- SEND_SIGNAL(owner.current, COMSIG_MOB_ANTAG_ON_GAIN, src)
+ if(!(owner?.current))
+ return
+ if(!silent)
+ greet()
+ apply_innate_effects()
+ give_antag_moodies()
+ remove_blacklisted_quirks()
+ if(is_banned(owner.current) && replace_banned)
+ replace_banned_player()
+ if(skill_modifiers)
+ for(var/A in skill_modifiers)
+ ADD_SINGLETON_SKILL_MODIFIER(owner, A, type)
+ SEND_SIGNAL(owner.current, COMSIG_MOB_ANTAG_ON_GAIN, src)
/datum/antagonist/proc/is_banned(mob/M)
if(!M)
@@ -99,6 +105,8 @@ GLOBAL_LIST_EMPTY(antagonists)
clear_antag_moodies()
if(owner)
LAZYREMOVE(owner.antag_datums, src)
+ for(var/A in skill_modifiers)
+ owner.remove_skill_modifier(GET_SKILL_MOD_ID(A, type))
if(!silent && owner.current)
farewell()
var/datum/team/team = get_team()
diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm
index 44116dbe36..9132288415 100644
--- a/code/modules/antagonists/abductor/abductor.dm
+++ b/code/modules/antagonists/abductor/abductor.dm
@@ -21,6 +21,7 @@
landmark_type = /obj/effect/landmark/abductor/agent
greet_text = "Use your stealth technology and equipment to incapacitate humans for your scientist to retrieve."
show_in_antagpanel = TRUE
+ skill_modifiers = list(/datum/skill_modifier/job/level/wiring)
/datum/antagonist/abductor/scientist
name = "Abductor Scientist"
@@ -29,6 +30,7 @@
landmark_type = /obj/effect/landmark/abductor/scientist
greet_text = "Use your experimental console and surgical equipment to monitor your agent and experiment upon abducted humans."
show_in_antagpanel = TRUE
+ skill_modifiers = list(/datum/skill_modifier/job/affinity/surgery)
/datum/antagonist/abductor/create_team(datum/team/abductor_team/new_team)
if(!new_team)
diff --git a/code/modules/antagonists/abductor/equipment/glands/electric.dm b/code/modules/antagonists/abductor/equipment/glands/electric.dm
index 82ee77d192..7e52f6fcb6 100644
--- a/code/modules/antagonists/abductor/equipment/glands/electric.dm
+++ b/code/modules/antagonists/abductor/equipment/glands/electric.dm
@@ -23,5 +23,5 @@
addtimer(CALLBACK(src, .proc/zap), rand(30, 100))
/obj/item/organ/heart/gland/electric/proc/zap()
- tesla_zap(owner, 4, 8000, TESLA_MOB_DAMAGE | TESLA_OBJ_DAMAGE | TESLA_MOB_STUN)
+ tesla_zap(owner, 4, 8000, ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE | ZAP_MOB_STUN)
playsound(get_turf(owner), 'sound/magic/lightningshock.ogg', 50, TRUE)
\ No newline at end of file
diff --git a/code/modules/antagonists/blob/blob/blobs/blob_mobs.dm b/code/modules/antagonists/blob/blob/blobs/blob_mobs.dm
index fbdeea6b84..91fb538ca3 100644
--- a/code/modules/antagonists/blob/blob/blobs/blob_mobs.dm
+++ b/code/modules/antagonists/blob/blob/blobs/blob_mobs.dm
@@ -86,7 +86,8 @@
melee_damage_upper = 4
obj_damage = 20
environment_smash = ENVIRONMENT_SMASH_STRUCTURES
- attacktext = "hits"
+ attack_verb_continuous = "hits"
+ attack_verb_simple = "hit"
attack_sound = 'sound/weapons/genhit1.ogg'
movement_type = FLYING
del_on_death = 1
@@ -205,7 +206,8 @@
melee_damage_lower = 20
melee_damage_upper = 20
obj_damage = 60
- attacktext = "slams"
+ attack_verb_continuous = "slams"
+ attack_verb_simple = "slam"
attack_sound = 'sound/effects/blobattack.ogg'
verb_say = "gurgles"
verb_ask = "demands"
@@ -284,11 +286,11 @@
if(overmind) //if we have an overmind, we're doing chemical reactions instead of pure damage
melee_damage_lower = 4
melee_damage_upper = 4
- attacktext = overmind.blobstrain.blobbernaut_message
+ attack_verb_continuous = overmind.blobstrain.blobbernaut_message
else
melee_damage_lower = initial(melee_damage_lower)
melee_damage_upper = initial(melee_damage_upper)
- attacktext = initial(attacktext)
+ attack_verb_continuous = initial(attack_verb_continuous)
/mob/living/simple_animal/hostile/blob/blobbernaut/death(gibbed)
..(gibbed)
diff --git a/code/modules/antagonists/blob/blob/theblob.dm b/code/modules/antagonists/blob/blob/theblob.dm
index 5717dc557d..6a73dc579b 100644
--- a/code/modules/antagonists/blob/blob/theblob.dm
+++ b/code/modules/antagonists/blob/blob/theblob.dm
@@ -207,8 +207,8 @@
if(prob(100 - severity * 30))
new /obj/effect/temp_visual/emp(get_turf(src))
-/obj/structure/blob/tesla_act(power)
- ..()
+/obj/structure/blob/zap_act(power)
+ . = ..()
if(overmind)
if(overmind.blobstrain.tesla_reaction(src, power))
take_damage(power/400, BURN, "energy")
diff --git a/code/modules/antagonists/bloodsucker/bloodsucker_integration.dm b/code/modules/antagonists/bloodsucker/bloodsucker_integration.dm
index 98fb69e52c..330116a207 100644
--- a/code/modules/antagonists/bloodsucker/bloodsucker_integration.dm
+++ b/code/modules/antagonists/bloodsucker/bloodsucker_integration.dm
@@ -113,3 +113,5 @@
/mob/living/proc/StartFrenzy(inTime = 120)
set waitfor = FALSE
+
+
diff --git a/code/modules/antagonists/bloodsucker/bloodsucker_life.dm b/code/modules/antagonists/bloodsucker/bloodsucker_life.dm
index 8425309b6c..4e4b270c12 100644
--- a/code/modules/antagonists/bloodsucker/bloodsucker_life.dm
+++ b/code/modules/antagonists/bloodsucker/bloodsucker_life.dm
@@ -216,8 +216,8 @@
// for (var/datum/action/bloodsucker/masquerade/P in powers)
// P.Deactivate()
// TEMP DEATH
- var/total_brute = owner.current.getBruteLoss()
- var/total_burn = owner.current.getFireLoss()
+ var/total_brute = owner.current.getBruteLoss_nonProsthetic()
+ var/total_burn = owner.current.getFireLoss_nonProsthetic()
var/total_toxloss = owner.current.getToxLoss() //This is neater than just putting it in total_damage
var/total_damage = total_brute + total_burn + total_toxloss
// Died? Convert to Torpor (fake death)
diff --git a/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm b/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm
index 7998a33c7b..090ef45d89 100644
--- a/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm
+++ b/code/modules/antagonists/bloodsucker/objects/bloodsucker_crypt.dm
@@ -328,8 +328,8 @@
// to_chat(user, "The ritual has been interrupted!")
// useLock = FALSE
// return
- user.playsound_local(null, 'sound/effects/explosion_distant.ogg', 40, TRUE)
- target.playsound_local(null, 'sound/effects/explosion_distant.ogg', 40, TRUE)
+ user.playsound_local(null, 'sound/effects/explosion_distant.ogg', 40, TRUE)
+ target.playsound_local(null, 'sound/effects/explosion_distant.ogg', 40, TRUE)
target.playsound_local(null, 'sound/effects/singlebeat.ogg', 40, TRUE)
target.Jitter(25)
target.emote("laugh")
@@ -490,13 +490,14 @@
update_icon()
/obj/structure/bloodsucker/candelabrum/process()
- if(lit)
- for(var/mob/living/carbon/human/H in viewers(7, src))
- var/datum/antagonist/vassal/T = H.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
- if(AmBloodsucker(H) || T) //We dont want vassals or vampires affected by this
- return
- H.hallucination = 20
- SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "vampcandle", /datum/mood_event/vampcandle)
+ if(!lit)
+ return
+ for(var/mob/living/carbon/human/H in fov_viewers(7, src))
+ var/datum/antagonist/vassal/T = H.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
+ if(AmBloodsucker(H) || T) //We dont want vassals or vampires affected by this
+ return
+ H.hallucination = 20
+ SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "vampcandle", /datum/mood_event/vampcandle)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// OTHER THINGS TO USE: HUMAN BLOOD. /obj/effect/decal/cleanable/blood
diff --git a/code/modules/antagonists/bloodsucker/powers/cloak.dm b/code/modules/antagonists/bloodsucker/powers/cloak.dm
index 347700ca9a..5383e8c1ac 100644
--- a/code/modules/antagonists/bloodsucker/powers/cloak.dm
+++ b/code/modules/antagonists/bloodsucker/powers/cloak.dm
@@ -19,10 +19,9 @@
if(!.)
return
// must have nobody around to see the cloak
- for(var/mob/living/M in viewers(9, owner))
- if(M != owner)
- to_chat(owner, "You may only vanish into the shadows unseen.")
- return FALSE
+ for(var/mob/living/M in fov_viewers(9, owner) - owner)
+ to_chat(owner, "You may only vanish into the shadows unseen.")
+ return FALSE
return TRUE
/datum/action/bloodsucker/cloak/ActivatePower()
@@ -35,7 +34,7 @@
// Pay Blood Toll (if awake)
owner.alpha = max(35, owner.alpha - min(75, 10 + 5 * level_current))
bloodsuckerdatum.AddBloodVolume(-0.2)
-
+
runintent = (user.m_intent == MOVE_INTENT_RUN)
var/turf/T = get_turf(user)
lum = T.get_lumcount()
@@ -50,7 +49,7 @@
if(!runintent)
user.toggle_move_intent()
REMOVE_TRAIT(user, TRAIT_NORUNNING, "cloak of darkness")
-
+
sleep(5) // Check every few ticks
/datum/action/bloodsucker/cloak/ContinueActive(mob/living/user, mob/living/target)
diff --git a/code/modules/antagonists/bloodsucker/powers/feed.dm b/code/modules/antagonists/bloodsucker/powers/feed.dm
index 8ac4fcebc1..cbb9693fba 100644
--- a/code/modules/antagonists/bloodsucker/powers/feed.dm
+++ b/code/modules/antagonists/bloodsucker/powers/feed.dm
@@ -169,8 +169,8 @@
vision_distance = notice_range, ignored_mobs = target) // Only people who AREN'T the target will notice this action.
// 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.silicon_privileges && !M.eye_blind && !M.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
+ for(var/mob/living/M in fov_viewers(notice_range, owner) - owner - target)
+ if(M.client && !M.silicon_privileges && !M.eye_blind && !M.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
was_unnoticed = FALSE
break
if(was_unnoticed)
diff --git a/code/modules/antagonists/bloodsucker/powers/go_home.dm b/code/modules/antagonists/bloodsucker/powers/go_home.dm
index 4788d7639e..c46a7fce6c 100644
--- a/code/modules/antagonists/bloodsucker/powers/go_home.dm
+++ b/code/modules/antagonists/bloodsucker/powers/go_home.dm
@@ -64,8 +64,8 @@
var/turf/T = get_turf(user)
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.silicon_privileges && !M.eye_blind) // M.client <--- add this in after testing!
+ for(var/mob/living/M in fov_viewers(world.view, get_turf(owner)) - owner)
+ if(M.client && !M.silicon_privileges && !M.eye_blind)
am_seen = TRUE
if (!M.mind.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
drop_item = TRUE
diff --git a/code/modules/antagonists/bloodsucker/powers/mesmerize.dm b/code/modules/antagonists/bloodsucker/powers/mesmerize.dm
index 814ad2abd7..eea80d52f3 100644
--- a/code/modules/antagonists/bloodsucker/powers/mesmerize.dm
+++ b/code/modules/antagonists/bloodsucker/powers/mesmerize.dm
@@ -64,7 +64,7 @@
to_chat(owner, "Your victim's eyes are glazed over. They cannot perceive you.")
return FALSE
// Check: Target See Me? (behind wall)
- if(!(target in viewers(target_range, get_turf(owner))))
+ if(!(owner in target.fov_view()))
// Sub-Check: GET CLOSER
//if (!(owner in range(target_range, get_turf(target)))
// if (display_error)
@@ -117,8 +117,6 @@
RegisterSignal(target, COMSIG_MOVABLE_MOVED, .proc/ContinueTarget)
// 5 second windup
addtimer(CALLBACK(src, .proc/apply_effects, L, target, power_time), 6 SECONDS)
- ADD_TRAIT(target, TRAIT_COMBAT_MODE_LOCKED, src)
- ADD_TRAIT(L, TRAIT_COMBAT_MODE_LOCKED, src)
/datum/action/bloodsucker/targeted/mesmerize/proc/apply_effects(aggressor, victim, power_time)
var/mob/living/carbon/target = victim
@@ -127,7 +125,6 @@
return
PowerActivatedSuccessfully() // blood & cooldown only altered if power activated successfully - less "fuck you"-y
target.apply_status_effect(STATUS_EFFECT_MESMERIZE, power_time)
- REMOVE_TRAIT(L, TRAIT_COMBAT_MODE_LOCKED, src)
target.face_atom(L)
target.Stun(power_time)
to_chat(L, "[target] is fixed in place by your hypnotic gaze.")
@@ -136,8 +133,7 @@
spawn(power_time)
if(istype(target) && success)
target.notransform = FALSE
- REMOVE_TRAIT(target, TRAIT_COMBAT_MODE_LOCKED, src)
- if(istype(L) && target.stat == CONSCIOUS && (target in view(10, get_turf(L)))) // They Woke Up! (Notice if within view)
+ if(istype(L) && target.stat == CONSCIOUS && (target in L.fov_view(10))) // They Woke Up! (Notice if within view)
to_chat(L, "[target] has snapped out of their trance.")
diff --git a/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm b/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm
index 431416ede0..036ea37ada 100644
--- a/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm
+++ b/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm
@@ -5,6 +5,7 @@
clockwork_desc = "A sigil of some purpose."
icon_state = "sigil"
layer = LOW_OBJ_LAYER
+ plane = ABOVE_WALL_PLANE
alpha = 50
resistance_flags = NONE
var/affects_servants = FALSE
diff --git a/code/modules/antagonists/clockcult/clock_items/clock_weapons/ratvarian_shield.dm b/code/modules/antagonists/clockcult/clock_items/clock_weapons/ratvarian_shield.dm
new file mode 100644
index 0000000000..a681e9b38b
--- /dev/null
+++ b/code/modules/antagonists/clockcult/clock_items/clock_weapons/ratvarian_shield.dm
@@ -0,0 +1,82 @@
+//Subtype of (riot) shield because of already implemented shieldbash stuff aswell as integrity and simillar things
+//ratvarian shield: A shield that absorbs energy from attacks and uses it to empower its bashes with remendous force. It is also quite resistant to damage, though less so against lasers and energy weaponry.
+
+/obj/item/shield/riot/ratvarian
+ name = "ratvarian shield"
+ icon_state = "ratvarian_shield" //Its icons are in the same place the normal shields are in
+ item_state = "ratvarian_shield"
+ desc = "A resilient shield made out of brass.. It feels warm to the touch."
+ var/clockwork_desc = "A powerful shield of ratvarian making. It absorbs blocked attacks to charge devastating bashes."
+ armor = list("melee" = 80, "bullet" = 70, "laser" = -10, "energy" = -20, "bomb" = 60, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
+ shield_flags = SHIELD_FLAGS_DEFAULT
+ max_integrity = 300 //High integrity, extremely strong against melee / bullets, but still quite easy to destroy with lasers and energy
+ repair_material = /obj/item/stack/tile/brass
+ var/dam_absorbed = 0
+ var/bash_mult_steps = 30
+ var/max_bash_mult = 4
+
+/obj/item/shield/riot/ratvarian/examine(mob/user)
+ if((is_servant_of_ratvar(user) || isobserver(user)))
+ desc = clockwork_desc
+ desc +="\n The shield has absorbed [dam_absorbed] damage, multiplying the effectiveness of its bashes by [calc_bash_mult()]"
+ . = ..()
+ desc = initial(desc)
+
+obj/item/shield/riot/ratvarian/proc/calc_bash_mult()
+ var/bash_mult = 0
+ if(!dam_absorbed)
+ return 1
+ else
+ bash_mult += round(clamp(1 + (dam_absorbed / bash_mult_steps), 1, max_bash_mult), 0.1) //Multiplies the effect of bashes by up to [max_bash_mult], though never less than one
+ return bash_mult
+
+/obj/item/shield/riot/ratvarian/proc/calc_bash_absorb_use()
+ var/absorb_use = 0
+ absorb_use = max(0, round(dam_absorbed * (calc_bash_mult() / round(1 + (dam_absorbed / bash_mult_steps), 0.1)), 1)) //Calculates how much of the absorbed damage the bash would actually use, so its not wasted
+ return absorb_use
+
+/obj/item/shield/riot/ratvarian/on_shield_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return)
+ if(!damage)
+ return ..()
+
+ if(!is_servant_of_ratvar(owner))
+ owner.visible_message("As [owner] blocks the attack with [src], [owner.p_they()] suddenly drops it, whincing in pain! ", "As you block the attack with [src], it heats up tremendously, forcing you to drop it from the pain alone! ")
+ owner.emote("scream")
+ playsound(src, 'sound/machines/fryer/deep_fryer_emerge.ogg', 50)
+ if(iscarbon(owner)) //Type safety for if a drone somehow got a shield (ratvar protect us)
+ var/mob/living/carbon/C = owner
+ var/obj/item/bodypart/part = C.get_holding_bodypart_of_item(src)
+ C.apply_damage((iscultist(C) ? damage * 2 : damage), BURN, (istype(part, /obj/item/bodypart/l_arm) ? BODY_ZONE_L_ARM : BODY_ZONE_R_ARM)) //Deals the damage to the holder instead of absorbing it instead + forcedrops. Doubled if a cultist of Nar'Sie.
+ else
+ owner.adjustFireLoss(iscultist(owner) ? damage * 2 : damage)
+ addtimer(CALLBACK(owner, /mob/living.proc/dropItemToGround, src, TRUE), 1)
+ else if(!is_servant_of_ratvar(attacker)) //No exploiting my snowflake mechanics
+ dam_absorbed += damage
+ playsound(owner, 'sound/machines/clockcult/steam_whoosh.ogg', 30)
+
+ if(damage <= 10) //The shield itself is hard to break, this DOES NOT modify the actual blocking-mechanic
+ damage = 0
+ else
+ damage -= 5
+ return ..()
+
+/obj/item/shield/riot/ratvarian/shatter(mob/living/carbon/human/owner)
+ playsound(owner, 'sound/magic/clockwork/anima_fragment_death.ogg', 50)
+ new /obj/item/clockwork/alloy_shards/large(get_turf(src))
+
+/obj/item/shield/riot/ratvarian/user_shieldbash(mob/living/user, atom/target, harmful)
+ if(!harmful || !is_servant_of_ratvar(user)) // No fun for non-clockies, but you can keep the normal bashes. Until you try to block with it.
+ shieldbash_knockback = initial(shieldbash_knockback)
+ shieldbash_brutedamage = initial(shieldbash_brutedamage) //Prevention for funky stuff that might happen otherwise
+ shieldbash_stamdmg = initial(shieldbash_stamdmg)
+ return ..()
+ var/actual_bash_mult = calc_bash_mult()
+ shieldbash_knockback = round(initial(shieldbash_knockback) * actual_bash_mult, 1) //Modifying the strength of the bash, done with initial() to prevent magic-number issues if the original shieldbash values are changed
+ shieldbash_brutedamage = round(initial(shieldbash_brutedamage) * actual_bash_mult, 1) //Where I think of it, better round this stuff because we don't need even more things that deal like 3.25 damage
+ shieldbash_stamdmg = round(initial(shieldbash_stamdmg) * actual_bash_mult, 1) //Like 20 brute and 60 stam + a fuckton of knockback at the moment (at maximum charge), seems mostly fine? I think?
+ . = ..()
+ if(.) //If this bash actually hit someone
+ if(actual_bash_mult > 1)
+ playsound(user, 'sound/magic/fireball.ogg', 50, TRUE, frequency = 1.25)
+ dam_absorbed -= calc_bash_absorb_use()
+ return
diff --git a/code/modules/antagonists/clockcult/clock_mobs/clockwork_marauder.dm b/code/modules/antagonists/clockcult/clock_mobs/clockwork_marauder.dm
index f43f2814d8..dd37f3727c 100644
--- a/code/modules/antagonists/clockcult/clock_mobs/clockwork_marauder.dm
+++ b/code/modules/antagonists/clockcult/clock_mobs/clockwork_marauder.dm
@@ -17,7 +17,8 @@
obj_damage = 40
melee_damage_lower = 12
melee_damage_upper = 12
- attacktext = "slashes"
+ attack_verb_continuous = "slashes"
+ attack_verb_simple = "slash"
attack_sound = 'sound/weapons/bladeslice.ogg'
weather_immunities = list("lava")
movement_type = FLYING
@@ -68,14 +69,16 @@
maxHealth = 300
melee_damage_upper = 25
melee_damage_lower = 25
- attacktext = "devastates"
+ attack_verb_continuous = "devastates"
+ attack_verb_simple = "devastate"
speed = -1
obj_damage = 100
max_shield_health = INFINITY
else if(GLOB.ratvar_approaches) //Hefty health bonus and slight attack damage increase
melee_damage_upper = 15
melee_damage_lower = 15
- attacktext = "carves"
+ attack_verb_continuous = "carves"
+ attack_verb_simple = "carve"
obj_damage = 50
max_shield_health = 4
diff --git a/code/modules/antagonists/clockcult/clock_scriptures/scripture_scripts.dm b/code/modules/antagonists/clockcult/clock_scriptures/scripture_scripts.dm
index 130fc24583..c40168a986 100644
--- a/code/modules/antagonists/clockcult/clock_scriptures/scripture_scripts.dm
+++ b/code/modules/antagonists/clockcult/clock_scriptures/scripture_scripts.dm
@@ -100,6 +100,24 @@
quickbind = TRUE
quickbind_desc = "Creates a Judicial Visor, which can smite an area, applying Belligerent and briefly stunning."
+//Nezbere's shield: Creates a ratvarian shield which absorbs attacks, see ratvarian_shield.dm for details.
+/datum/clockwork_scripture/create_object/nezberes_shield
+ descname = "Shield with empowerable bashes"
+ name = "Nezbere's shield"
+ desc = "Creates a shield which generates charge from blocking damage, using it to empower its bashes tremendously. It is repaired with brass, and while very durable, extremely weak to lasers and, even more so, to energy weaponry."
+ invocations = list("Shield me...", "... from the coming dark!")
+ channel_time = 20
+ power_cost = 600 //Shouldn't be too spammable but not too hard to get either
+ whispered = TRUE
+ creator_message = "You form a ratvarian shield, which is capable of absorbing blocked attacks to empower its bashes."
+ object_path = /obj/item/shield/riot/ratvarian
+ usage_tip = "Bashes will only use charge when performed with intent to harm."
+ tier = SCRIPTURE_SCRIPT
+ space_allowed = TRUE
+ primary_component = VANGUARD_COGWHEEL
+ sort_priority = 5
+ quickbind = TRUE
+ quickbind_desc = "Creates a Ratvarian shield, which can absorb energy from attacks for use in powerful bashes."
//Clockwork Armaments: Grants the invoker the ability to call forth a Ratvarian spear and clockwork armor.
/datum/clockwork_scripture/clockwork_armaments
@@ -113,7 +131,7 @@
usage_tip = "Throwing the spear at a mob will do massive damage and knock them down, but break the spear. You will need to wait for 30 seconds before resummoning it."
tier = SCRIPTURE_SCRIPT
primary_component = VANGUARD_COGWHEEL
- sort_priority = 5
+ sort_priority = 6
important = TRUE
quickbind = TRUE
quickbind_desc = "Permanently binds clockwork armor and a Ratvarian spear to you."
@@ -213,7 +231,7 @@
usage_tip = "This gateway is strictly one-way and will only allow things through the invoker's portal."
tier = SCRIPTURE_SCRIPT
primary_component = GEIS_CAPACITOR
- sort_priority = 6
+ sort_priority = 7
quickbind = TRUE
quickbind_desc = "Allows you to create a one-way Spatial Gateway to a living Servant or Clockwork Obelisk."
diff --git a/code/modules/antagonists/clockcult/clockcult.dm b/code/modules/antagonists/clockcult/clockcult.dm
index a730b52cc1..b6ed7dfe65 100644
--- a/code/modules/antagonists/clockcult/clockcult.dm
+++ b/code/modules/antagonists/clockcult/clockcult.dm
@@ -5,6 +5,7 @@
antagpanel_category = "Clockcult"
job_rank = ROLE_SERVANT_OF_RATVAR
antag_moodlet = /datum/mood_event/cult
+ skill_modifiers = list(/datum/skill_modifier/job/level/wiring)
var/datum/action/innate/hierophant/hierophant_network = new
threat = 3
var/datum/team/clockcult/clock_team
diff --git a/code/modules/antagonists/devil/imp/imp.dm b/code/modules/antagonists/devil/imp/imp.dm
index 1539bc384b..7a6850bfa1 100644
--- a/code/modules/antagonists/devil/imp/imp.dm
+++ b/code/modules/antagonists/devil/imp/imp.dm
@@ -7,9 +7,12 @@
desc = "A large, menacing creature covered in armored black scales."
speak_emote = list("cackles")
emote_hear = list("cackles","screeches")
- response_help = "thinks better of touching"
- response_disarm = "flails at"
- response_harm = "punches"
+ response_help_continuous = "thinks better of touching"
+ response_help_simple = "think better of touching"
+ response_disarm_continuous = "flails at"
+ response_disarm_simple = "flail at"
+ response_harm_continuous = "punches"
+ response_harm_simple = "punch"
icon = 'icons/mob/mob.dmi'
icon_state = "imp"
icon_living = "imp"
@@ -23,7 +26,8 @@
minbodytemp = 250 //Weak to cold
maxbodytemp = INFINITY
faction = list("hell")
- attacktext = "wildly tears into"
+ attack_verb_continuous = "wildly tears into"
+ attack_verb_simple = "wildly tear into"
maxHealth = 200
health = 200
healable = 0
diff --git a/code/modules/antagonists/ert/ert.dm b/code/modules/antagonists/ert/ert.dm
index bda1fbabe6..1d773627c7 100644
--- a/code/modules/antagonists/ert/ert.dm
+++ b/code/modules/antagonists/ert/ert.dm
@@ -51,6 +51,7 @@
/datum/antagonist/ert/engineer
role = "Engineer"
outfit = /datum/outfit/ert/engineer
+ skill_modifiers = list(/datum/skill_modifier/job/level/wiring)
/datum/antagonist/ert/engineer/amber
outfit = /datum/outfit/ert/engineer/alert
@@ -61,6 +62,7 @@
/datum/antagonist/ert/medic
role = "Medical Officer"
outfit = /datum/outfit/ert/medic
+ skill_modifiers = list(/datum/skill_modifier/job/affinity/surgery)
/datum/antagonist/ert/medic/amber
outfit = /datum/outfit/ert/medic/alert
diff --git a/code/modules/antagonists/morph/morph.dm b/code/modules/antagonists/morph/morph.dm
index ad2eb4792a..e91feda006 100644
--- a/code/modules/antagonists/morph/morph.dm
+++ b/code/modules/antagonists/morph/morph.dm
@@ -28,7 +28,8 @@
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
vision_range = 1 // Only attack when target is close
wander = FALSE
- attacktext = "glomps"
+ attack_verb_continuous = "glomps"
+ attack_verb_simple = "glomp"
attack_sound = 'sound/effects/blobattack.ogg'
butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 2)
diff --git a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
index dc44a3b2d8..a11ecaa3df 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclearbomb.dm
@@ -451,9 +451,9 @@
return
qdel(src)
-/obj/machinery/nuclearbomb/tesla_act(power, tesla_flags)
+/obj/machinery/nuclearbomb/zap_act(power, zap_flags)
..()
- if(tesla_flags & TESLA_MACHINE_EXPLOSIVE)
+ if(zap_flags & ZAP_MACHINE_EXPLOSIVE)
qdel(src)//like the singulo, tesla deletes it. stops it from exploding over and over
#define NUKERANGE 127
@@ -606,6 +606,7 @@ This is here to make the tiles around the station mininuke change when it's arme
lefthand_file = 'icons/mob/inhands/equipment/idcards_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/idcards_righthand.dmi'
icon_state = "datadisk0"
+ w_volume = ITEM_VOLUME_DISK
/obj/item/disk/nuclear
name = "nuclear authentication disk"
diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm
index 7510ad6997..454cde6d72 100644
--- a/code/modules/antagonists/nukeop/nukeop.dm
+++ b/code/modules/antagonists/nukeop/nukeop.dm
@@ -5,6 +5,7 @@
job_rank = ROLE_OPERATIVE
antag_moodlet = /datum/mood_event/focused
threat = 10
+ skill_modifiers = list(/datum/skill_modifier/job/level/wiring)
var/datum/team/nuclear/nuke_team
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.
diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm
index 2638a6a8e3..e06e8691b3 100644
--- a/code/modules/antagonists/revenant/revenant.dm
+++ b/code/modules/antagonists/revenant/revenant.dm
@@ -27,19 +27,24 @@
sight = SEE_SELF
throwforce = 0
blood_volume = 0
+ has_field_of_vision = FALSE //we are a spoopy ghost
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
- response_help = "passes through"
- response_disarm = "swings through"
- response_harm = "punches through"
+ response_help_continuous = "passes through"
+ response_help_simple = "pass through"
+ response_disarm_continuous = "swings through"
+ response_disarm_simple = "swing through"
+ response_harm_continuous = "punches through"
+ response_harm_simple = "punch through"
unsuitable_atmos_damage = 0
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) //I don't know how you'd apply those, but revenants no-sell them anyway.
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 = INFINITY
harm_intent_damage = 0
- friendly = "touches"
+ friendly_verb_continuous = "touches"
+ friendly_verb_simple = "touch"
status_flags = 0
wander = FALSE
density = FALSE
diff --git a/code/modules/antagonists/slaughter/slaughter.dm b/code/modules/antagonists/slaughter/slaughter.dm
index 5d409a7fb9..6b063559dc 100644
--- a/code/modules/antagonists/slaughter/slaughter.dm
+++ b/code/modules/antagonists/slaughter/slaughter.dm
@@ -6,9 +6,12 @@
desc = "A large, menacing creature covered in armored black scales."
speak_emote = list("gurgles")
emote_hear = list("wails","screeches")
- response_help = "thinks better of touching"
- response_disarm = "flails at"
- response_harm = "punches"
+ response_help_continuous = "thinks better of touching"
+ response_help_simple = "think better of touching"
+ response_disarm_continuous = "flails at"
+ response_disarm_simple = "flail at"
+ response_harm_continuous = "punches"
+ response_harm_simple = "punch"
icon = 'icons/mob/mob.dmi'
icon_state = "daemon"
icon_living = "daemon"
@@ -24,7 +27,8 @@
minbodytemp = 0
maxbodytemp = INFINITY
faction = list("slaughter")
- attacktext = "wildly tears into"
+ attack_verb_continuous = "wildly tears into"
+ attack_verb_simple = "wildly tear into"
maxHealth = 200
health = 200
healable = 0
@@ -116,8 +120,10 @@
desc = "A large, adorable creature covered in armor with pink bows."
speak_emote = list("giggles","titters","chuckles")
emote_hear = list("guffaws","laughs")
- response_help = "hugs"
- attacktext = "wildly tickles"
+ response_help_continuous = "hugs"
+ response_help_simple = "hug"
+ attack_verb_continuous = "wildly tickles"
+ attack_verb_simple = "wildly tickle"
attack_sound = 'sound/items/bikehorn.ogg'
feast_sound = 'sound/spookoween/scary_horn2.ogg'
diff --git a/code/modules/antagonists/swarmer/swarmer.dm b/code/modules/antagonists/swarmer/swarmer.dm
index c1f493ac52..d0e36394ab 100644
--- a/code/modules/antagonists/swarmer/swarmer.dm
+++ b/code/modules/antagonists/swarmer/swarmer.dm
@@ -84,9 +84,11 @@
hud_possible = list(ANTAG_HUD, DIAG_STAT_HUD, DIAG_HUD)
obj_damage = 0
environment_smash = ENVIRONMENT_SMASH_NONE
- attacktext = "shocks"
+ attack_verb_continuous = "shocks"
+ attack_verb_simple = "shock"
attack_sound = 'sound/effects/empulse.ogg'
- friendly = "pinches"
+ friendly_verb_continuous = "pinches"
+ friendly_verb_simple = "pinch"
speed = 0
faction = list("swarmer")
AIStatus = AI_OFF
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index 1d30cdbf77..4422f2f174 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -4,6 +4,7 @@
antagpanel_category = "Traitor"
job_rank = ROLE_TRAITOR
antag_moodlet = /datum/mood_event/focused
+ skill_modifiers = list(/datum/skill_modifier/job/level/wiring/basic)
var/special_role = ROLE_TRAITOR
var/employer = "The Syndicate"
var/give_objectives = TRUE
diff --git a/code/modules/antagonists/wizard/equipment/spellbook.dm b/code/modules/antagonists/wizard/equipment/spellbook.dm
index d49a8f83c6..a9bc64a932 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook.dm
@@ -226,7 +226,11 @@
/datum/spellbook_entry/lightningbolt/Buy(mob/living/carbon/human/user,obj/item/spellbook/book) //return 1 on success
. = ..()
- user.flags_1 |= TESLA_IGNORE_1
+ ADD_TRAIT(user, TRAIT_TESLA_SHOCKIMMUNE, "lightning_bolt_spell")
+
+/datum/spellbook_entry/lightningbolt/Refund(mob/living/carbon/human/user, obj/item/spellbook/book)
+ . = ..()
+ REMOVE_TRAIT(user, TRAIT_TESLA_SHOCKIMMUNE, "lightning_bolt_spell")
/datum/spellbook_entry/infinite_guns
name = "Lesser Summon Guns"
diff --git a/code/modules/atmospherics/machinery/airalarm.dm b/code/modules/atmospherics/machinery/airalarm.dm
index 21eed2caf4..733c760855 100644
--- a/code/modules/atmospherics/machinery/airalarm.dm
+++ b/code/modules/atmospherics/machinery/airalarm.dm
@@ -67,6 +67,7 @@
desc = "A machine that monitors atmosphere levels. Goes off if the area is dangerous."
icon = 'icons/obj/monitors.dmi'
icon_state = "alarm0"
+ plane = ABOVE_WALL_PLANE
use_power = IDLE_POWER_USE
idle_power_usage = 4
active_power_usage = 8
diff --git a/code/modules/atmospherics/machinery/atmosmachinery.dm b/code/modules/atmospherics/machinery/atmosmachinery.dm
index 8f5ab7dd5e..97389848ba 100644
--- a/code/modules/atmospherics/machinery/atmosmachinery.dm
+++ b/code/modules/atmospherics/machinery/atmosmachinery.dm
@@ -17,6 +17,7 @@
active_power_usage = 0
power_channel = ENVIRON
layer = GAS_PIPE_HIDDEN_LAYER //under wires
+ plane = ABOVE_WALL_PLANE
resistance_flags = FIRE_PROOF
max_integrity = 200
obj_flags = CAN_BE_HIT | ON_BLUEPRINTS
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm b/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm
index c164dc5ae3..6b685d4bc1 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/circulator.dm
@@ -7,6 +7,7 @@
name = "circulator/heat exchanger"
desc = "A gas circulator pump and heat exchanger."
icon_state = "circ-off-0"
+ plane = GAME_PLANE
var/active = FALSE
diff --git a/code/modules/atmospherics/machinery/components/components_base.dm b/code/modules/atmospherics/machinery/components/components_base.dm
index a45728d51f..c229a4ba27 100644
--- a/code/modules/atmospherics/machinery/components/components_base.dm
+++ b/code/modules/atmospherics/machinery/components/components_base.dm
@@ -32,7 +32,7 @@
var/turf/T = loc
if(level == 2 || (istype(T) && !T.intact))
showpipe = TRUE
- plane = GAME_PLANE
+ plane = ABOVE_WALL_PLANE
else
showpipe = FALSE
plane = FLOOR_PLANE
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
index 9b7183092d..4f26a2f772 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm
@@ -6,6 +6,7 @@
max_integrity = 350
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 30, "acid" = 30)
layer = ABOVE_WINDOW_LAYER
+ plane = GAME_PLANE
state_open = FALSE
circuit = /obj/item/circuitboard/machine/cryo_tube
pipe_flags = PIPING_ONE_PER_TURF | PIPING_DEFAULT_LAYER_ONLY
@@ -15,8 +16,8 @@
var/volume = 100
var/efficiency = 1
- var/sleep_factor = 0.00125
- var/unconscious_factor = 0.001
+ var/base_knockout = 30 SECONDS
+ var/knockout_factor = 1
var/heat_capacity = 20000
var/conduction_coefficient = 0.3
@@ -53,10 +54,9 @@
var/C
for(var/obj/item/stock_parts/matter_bin/M in component_parts)
C += M.rating
-
+ // 2 bins total, so C ranges from 2 to 8.
efficiency = initial(efficiency) * C
- sleep_factor = initial(sleep_factor) * C
- unconscious_factor = initial(unconscious_factor) * C
+ knockout_factor = initial(knockout_factor) / max(1, (C * 0.33))
heat_capacity = initial(heat_capacity) / C
conduction_coefficient = initial(conduction_coefficient) * C
@@ -188,8 +188,10 @@
if(air1.gases.len)
if(mob_occupant.bodytemperature < T0C) // Sleepytime. Why? More cryo magic.
- mob_occupant.Sleeping((mob_occupant.bodytemperature * sleep_factor) * 2000)
- mob_occupant.Unconscious((mob_occupant.bodytemperature * unconscious_factor) * 2000)
+ // temperature factor goes from 1 to about 2.5
+ var/amount = max(1, (4 * log(T0C - mob_occupant.bodytemperature)) - 20) * knockout_factor * base_knockout
+ mob_occupant.Sleeping(amount)
+ mob_occupant.Unconscious(amount)
if(beaker)
if(reagent_transfer == 0) // Magically transfer reagents. Because cryo magic.
beaker.reagents.trans_to(occupant, 1, efficiency * 0.25) // Transfer reagents.
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/tank.dm b/code/modules/atmospherics/machinery/components/unary_devices/tank.dm
index c1bd59f49b..79ff24e8b7 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/tank.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/tank.dm
@@ -7,6 +7,7 @@
max_integrity = 800
density = TRUE
layer = ABOVE_WINDOW_LAYER
+ plane = GAME_PLANE
pipe_flags = PIPING_ONE_PER_TURF
var/volume = 10000 //in liters
var/gas_type = 0
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm
index dddfdf08c1..7af3e57bc7 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm
@@ -9,6 +9,7 @@
max_integrity = 300
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 30)
layer = OBJ_LAYER
+ plane = GAME_PLANE
circuit = /obj/item/circuitboard/machine/thermomachine
ui_x = 300
ui_y = 230
diff --git a/code/modules/cargo/bounties/medical.dm b/code/modules/cargo/bounties/medical.dm
index 7afc0d8786..c1f8d9c9d0 100644
--- a/code/modules/cargo/bounties/medical.dm
+++ b/code/modules/cargo/bounties/medical.dm
@@ -121,7 +121,7 @@
/datum/bounty/item/medical/defibrillator
name = "New defibillators"
- description = "After years of storge our defib units have become liabilities. Please send us some new ones."
+ description = "After years of storage our defibrillation units have worn out. Please send us some new ones."
reward = 2250
required_count = 5
wanted_types = list(/obj/item/defibrillator)
diff --git a/code/modules/cargo/console.dm b/code/modules/cargo/console.dm
index d1d895ca69..6968a5ccd8 100644
--- a/code/modules/cargo/console.dm
+++ b/code/modules/cargo/console.dm
@@ -119,6 +119,7 @@
var/list/data = list()
data["requestonly"] = requestonly
data["supplies"] = list()
+ data["emagged"] = obj_flags & EMAGGED
for(var/pack in SSshuttle.supply_packs)
var/datum/supply_pack/P = SSshuttle.supply_packs[pack]
if(!data["supplies"][P.group])
@@ -133,7 +134,8 @@
"cost" = P.cost,
"id" = pack,
"desc" = P.desc || P.name, // If there is a description, use it. Otherwise use the pack's name.
- "access" = P.access
+ "access" = P.access,
+ "can_private_buy" = P.can_private_buy
))
return data
@@ -195,9 +197,10 @@
rank = "Silicon"
var/datum/bank_account/account
- if(self_paid && ishuman(usr))
- var/mob/living/carbon/human/H = usr
- var/obj/item/card/id/id_card = H.get_idcard(TRUE)
+ if(self_paid)
+ if(!pack.can_private_buy && !(obj_flags & EMAGGED))
+ return
+ var/obj/item/card/id/id_card = usr.get_idcard(TRUE)
if(!istype(id_card))
say("No ID card detected.")
return
diff --git a/code/modules/cargo/exports.dm b/code/modules/cargo/exports.dm
index 0a1346ba01..671c22bde5 100644
--- a/code/modules/cargo/exports.dm
+++ b/code/modules/cargo/exports.dm
@@ -68,7 +68,7 @@ Credit dupes that require a lot of manual work shouldn't be removed, unless they
var/unit_name = "" // Unit name. Only used in "Received [total_amount] [name]s [message]." message
var/message = ""
var/cost = 100 // Cost of item, in cargo credits. Must not alow for infinite price dupes, see above.
- var/k_elasticity = 1/300 //coefficient used in marginal price calculation that roughly corresponds to the inverse of price elasticity, or "quantity elasticity" - CIT EDIT 1/30 - > 0
+ var/k_elasticity = 1/100 //coefficient used in marginal price calculation that roughly corresponds to the inverse of price elasticity, or "quantity elasticity" - CIT EDIT 1/30 - > 100
var/list/export_types = list() // Type of the exported object. If none, the export datum is considered base type.
var/include_subtypes = TRUE // Set to FALSE to make the datum apply only to a strict type.
var/list/exclude_types = list() // Types excluded from export
diff --git a/code/modules/cargo/exports/gear.dm b/code/modules/cargo/exports/gear.dm
index 8dba7f69da..646e1c6e47 100644
--- a/code/modules/cargo/exports/gear.dm
+++ b/code/modules/cargo/exports/gear.dm
@@ -1,5 +1,6 @@
/datum/export/gear
include_subtypes = FALSE
+ k_elasticity = 0 //We always want clothing/gear
//blanket
/datum/export/gear/hat
diff --git a/code/modules/cargo/exports/large_objects.dm b/code/modules/cargo/exports/large_objects.dm
index b7bdcb1f59..c7a5e59046 100644
--- a/code/modules/cargo/exports/large_objects.dm
+++ b/code/modules/cargo/exports/large_objects.dm
@@ -111,7 +111,7 @@
include_subtypes = FALSE
/datum/export/large/am_control_unit
- cost = 4000
+ cost = 2000
unit_name = "antimatter control unit"
export_types = list(/obj/machinery/power/am_control_unit)
diff --git a/code/modules/cargo/exports/organs_robotics.dm b/code/modules/cargo/exports/organs_robotics.dm
index 7a77568cc7..a6c5ee93ca 100644
--- a/code/modules/cargo/exports/organs_robotics.dm
+++ b/code/modules/cargo/exports/organs_robotics.dm
@@ -2,15 +2,14 @@
/datum/export/robotics
include_subtypes = FALSE
- k_elasticity = 0 //ALWAYS worth selling upgrades
+ k_elasticity = 1/50
/datum/export/implant
include_subtypes = FALSE
- k_elasticity = 0 //ALWAYS worth selling upgrades
+ k_elasticity = 1/50
/datum/export/organs
include_subtypes = TRUE
- k_elasticity = 0 //ALWAYS worth selling orgains
/datum/export/implant/autodoc
cost = 150
@@ -60,11 +59,6 @@
unit_name = "thrusters set implant"
export_types = list(/obj/item/organ/cyberimp/chest/thrusters)
-/datum/export/implant/thrusters
- cost = 150
- unit_name = "thrusters set implant"
- export_types = list(/obj/item/organ/cyberimp/chest/thrusters)
-
/datum/export/implant/arm
cost = 200
unit_name = "arm set implant"
diff --git a/code/modules/cargo/exports/parts.dm b/code/modules/cargo/exports/parts.dm
index 660d79cae4..da3c0cf31d 100644
--- a/code/modules/cargo/exports/parts.dm
+++ b/code/modules/cargo/exports/parts.dm
@@ -110,7 +110,7 @@
include_subtypes = FALSE
/datum/export/glasswork_lens
- cost = 1800
+ cost = 1600
unit_name = "small glass lens"
export_types = list(/obj/item/glasswork/glass_base/lens)
@@ -133,13 +133,13 @@
include_subtypes = FALSE
/datum/export/glasswork_teaplate
- cost = 1200
+ cost = 1000
unit_name = "tea gear"
export_types = list(/obj/item/tea_plate)
include_subtypes = FALSE
/datum/export/glasswork_teacup
- cost = 1800
+ cost = 1600
unit_name = "tea gear"
export_types = list(/obj/item/tea_cup)
include_subtypes = FALSE
diff --git a/code/modules/cargo/exports/sheets.dm b/code/modules/cargo/exports/sheets.dm
index be0d2d6bee..120bfbe5e4 100644
--- a/code/modules/cargo/exports/sheets.dm
+++ b/code/modules/cargo/exports/sheets.dm
@@ -120,12 +120,6 @@
message = "of cloth"
export_types = list(/obj/item/stack/sheet/cloth)
-/datum/export/stack/silk
- cost = 200 //The new plasma
- unit_name = "sheets"
- message = "of silk"
- export_types = list(/obj/item/stack/sheet/silk)
-
/datum/export/stack/duracloth
cost = 40
unit_name = "sheets"
diff --git a/code/modules/cargo/exports/tools.dm b/code/modules/cargo/exports/tools.dm
index a68d7238b5..a889f0ed13 100644
--- a/code/modules/cargo/exports/tools.dm
+++ b/code/modules/cargo/exports/tools.dm
@@ -1,4 +1,7 @@
-/datum/export/toolbox
+/datum/export/tool
+ k_elasticity = 1/500 //Tool selling almost allways fine a target
+
+/datum/export/tool/toolbox
cost = 6
unit_name = "toolbox"
export_types = list(/obj/item/storage/toolbox)
@@ -29,93 +32,93 @@
// Lights/Eletronic
-/datum/export/lights
+/datum/export/tool/lights
cost = 10
unit_name = "light fixer"
export_types = list(/obj/item/wallframe/light_fixture)
include_subtypes = TRUE
-/datum/export/apc_board
+/datum/export/tool/apc_board
cost = 5
unit_name = "apc electronics"
export_types = list(/obj/item/electronics/apc)
include_subtypes = TRUE
-/datum/export/apc_frame
+/datum/export/tool/apc_frame
cost = 3
unit_name = "apc frame"
export_types = list(/obj/item/wallframe/apc)
include_subtypes = TRUE
-/datum/export/floodlights
+/datum/export/tool/floodlights
cost = 15
unit_name = "floodlight fixer"
export_types = list(/obj/structure/floodlight_frame)
include_subtypes = TRUE
-/datum/export/bolbstubes
+/datum/export/tool/bolbstubes
cost = 1 //Time
unit_name = "light replacement"
export_types = list(/obj/item/light/tube, /obj/item/light/bulb)
-/datum/export/lightreplacer
+/datum/export/tool/lightreplacer
cost = 20
unit_name = "lightreplacer"
export_types = list(/obj/item/lightreplacer)
// Basic tools
-/datum/export/basicmining
+/datum/export/tool/basicmining
cost = 30
unit_name = "basic mining tool"
export_types = list(/obj/item/pickaxe, /obj/item/pickaxe/mini, /obj/item/shovel, /obj/item/resonator)
include_subtypes = FALSE
-/datum/export/upgradedmining
+/datum/export/tool/upgradedmining
cost = 80
unit_name = "mining tool"
export_types = list(/obj/item/pickaxe/silver, /obj/item/pickaxe/drill, /obj/item/gun/energy/plasmacutter, /obj/item/resonator/upgraded)
include_subtypes = FALSE
-/datum/export/advdmining
+/datum/export/tool/advdmining
cost = 150
unit_name = "advanced mining tool"
export_types = list(/obj/item/pickaxe/diamond, /obj/item/pickaxe/drill/diamonddrill, /obj/item/pickaxe/drill/jackhammer, /obj/item/gun/energy/plasmacutter/adv)
include_subtypes = FALSE
-/datum/export/screwdriver
+/datum/export/tool/screwdriver
cost = 2
unit_name = "screwdriver"
export_types = list(/obj/item/screwdriver)
include_subtypes = FALSE
-/datum/export/wrench
+/datum/export/tool/wrench
cost = 2
unit_name = "wrench"
export_types = list(/obj/item/wrench)
-/datum/export/crowbar
+/datum/export/tool/crowbar
cost = 2
unit_name = "crowbar"
export_types = list(/obj/item/crowbar)
-/datum/export/wirecutters
+/datum/export/tool/wirecutters
cost = 2
unit_name = "pair"
message = "of wirecutters"
export_types = list(/obj/item/wirecutters)
-/datum/export/weldingtool
+/datum/export/tool/weldingtool
cost = 5
unit_name = "welding tool"
export_types = list(/obj/item/weldingtool)
include_subtypes = FALSE
-/datum/export/weldingtool/emergency
+/datum/export/tool/weldingtool/emergency
cost = 2
unit_name = "emergency welding tool"
export_types = list(/obj/item/weldingtool/mini)
-/datum/export/weldingtool/industrial
+/datum/export/tool/weldingtool/industrial
cost = 10
unit_name = "industrial welding tool"
export_types = list(/obj/item/weldingtool/largetank, /obj/item/weldingtool/hugetank)
@@ -131,23 +134,23 @@
unit_name = "pocket fire extinguisher"
export_types = list(/obj/item/extinguisher/mini)
-/datum/export/flashlight
+/datum/export/tool/flashlight
cost = 3
unit_name = "flashlight"
export_types = list(/obj/item/flashlight)
include_subtypes = FALSE
-/datum/export/flashlight/flare
+/datum/export/tool/flashlight/flare
cost = 2
unit_name = "flare"
export_types = list(/obj/item/flashlight/flare)
-/datum/export/flashlight/seclite
+/datum/export/tool/flashlight/seclite
cost = 5
unit_name = "seclite"
export_types = list(/obj/item/flashlight/seclite)
-/datum/export/analyzer
+/datum/export/tool/analyzer
cost = 5
unit_name = "analyzer"
export_types = list(/obj/item/analyzer)
@@ -163,32 +166,32 @@
export_types = list(/obj/item/radio)
exclude_types = list(/obj/item/radio/mech)
-/datum/export/rcd
+/datum/export/tool/rcd
cost = 100
unit_name = "rapid construction device"
export_types = list(/obj/item/construction/rcd)
-/datum/export/rcd_ammo
+/datum/export/tool/rcd_ammo
cost = 60
unit_name = "compressed matter cardridge"
export_types = list(/obj/item/rcd_ammo)
-/datum/export/rpd
+/datum/export/tool/rpd
cost = 100
unit_name = "rapid piping device"
export_types = list(/obj/item/pipe_dispenser)
-/datum/export/rld
+/datum/export/tool/rld
cost = 150
unit_name = "rapid light device"
export_types = list(/obj/item/construction/rld)
-/datum/export/rped
+/datum/export/tool/rped
cost = 100
unit_name = "rapid part exchange device"
export_types = list(/obj/item/storage/part_replacer)
-/datum/export/bsrped
+/datum/export/tool/bsrped
cost = 200
unit_name = "blue space part exchange device"
export_types = list(/obj/item/storage/part_replacer/bluespace)
diff --git a/code/modules/cargo/gondolapod.dm b/code/modules/cargo/gondolapod.dm
index 1b4ebd6c1a..be2db06346 100644
--- a/code/modules/cargo/gondolapod.dm
+++ b/code/modules/cargo/gondolapod.dm
@@ -2,9 +2,12 @@
name = "gondola"
real_name = "gondola"
desc = "The silent walker. This one seems to be part of a delivery agency."
- response_help = "pets"
- response_disarm = "bops"
- response_harm = "kicks"
+ response_help_continuous = "pets"
+ response_help_simple = "pet"
+ response_disarm_continuous = "bops"
+ response_disarm_simple = "bop"
+ response_harm_continuous = "kicks"
+ response_harm_simple = "kick"
faction = list("gondola")
turns_per_move = 10
icon = 'icons/mob/gondolapod.dmi'
diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm
index 96557d58ef..f82e16ad5c 100644
--- a/code/modules/cargo/packs.dm
+++ b/code/modules/cargo/packs.dm
@@ -15,6 +15,7 @@
var/special_enabled = FALSE
var/DropPodOnly = FALSE //only usable by the Bluespace Drop Pod via the express cargo console
var/admin_spawned = FALSE //Can only an admin spawn this crate?
+ var/can_private_buy = TRUE //Can it be purchased privately by each crewmember?
/datum/supply_pack/proc/generate(atom/A, datum/bank_account/paying_account)
var/obj/structure/closet/crate/C
diff --git a/code/modules/cargo/packs/armory.dm b/code/modules/cargo/packs/armory.dm
index 87dfe243b3..3a3357cc42 100644
--- a/code/modules/cargo/packs/armory.dm
+++ b/code/modules/cargo/packs/armory.dm
@@ -10,6 +10,7 @@
group = "Armory"
access = ACCESS_ARMORY
crate_type = /obj/structure/closet/crate/secure/weapon
+ can_private_buy = FALSE
/datum/supply_pack/security/armory/bulletarmor
name = "Bulletproof Armor Crate"
@@ -214,7 +215,7 @@
/datum/supply_pack/security/armory/wt550ammo
name = "WT-550 Semi-Auto SMG Ammo Crate"
- desc = "Contains four 20-round magazines for the WT-550 Semi-Auto SMG. Each magazine is designed to facilitate rapid tactical reloads. Requires Armory access to open."
+ desc = "Contains four 32-round magazines for the WT-550 Semi-Auto SMG. Each magazine is designed to facilitate rapid tactical reloads. Requires Armory access to open."
cost = 1750
contains = list(/obj/item/ammo_box/magazine/wt550m9,
/obj/item/ammo_box/magazine/wt550m9,
@@ -224,7 +225,7 @@
/datum/supply_pack/security/armory/wt550ammo_nonlethal // Takes around 12 shots to stamcrit someone
name = "WT-550 Semi-Auto SMG Non-Lethal Ammo Crate"
- desc = "Contains four 20-round magazines for the WT-550 Semi-Auto SMG. Each magazine is designed to facilitate rapid tactical reloads. Requires Armory access to open."
+ desc = "Contains four 32-round magazines for the WT-550 Semi-Auto SMG. Each magazine is designed to facilitate rapid tactical reloads. Requires Armory access to open."
cost = 1000
contains = list(/obj/item/ammo_box/magazine/wt550m9/wtrubber,
/obj/item/ammo_box/magazine/wt550m9/wtrubber,
diff --git a/code/modules/cargo/packs/engine.dm b/code/modules/cargo/packs/engine.dm
index 89775c07ee..499881a110 100644
--- a/code/modules/cargo/packs/engine.dm
+++ b/code/modules/cargo/packs/engine.dm
@@ -148,7 +148,7 @@
crate_name = "supermatter shard crate"
crate_type = /obj/structure/closet/crate/secure/engineering
dangerous = TRUE
-
+
/datum/supply_pack/engine/tesla_coils
name = "Tesla Coil Crate"
desc = "Whether it's high-voltage executions, creating research points, or just plain old power generation: This pack of four Tesla coils can do it all!"
diff --git a/code/modules/cargo/packs/materials.dm b/code/modules/cargo/packs/materials.dm
index 1ce3a6b084..771f7ce222 100644
--- a/code/modules/cargo/packs/materials.dm
+++ b/code/modules/cargo/packs/materials.dm
@@ -81,32 +81,6 @@
contains = list(/obj/item/stack/sheet/mineral/wood/fifty)
crate_name = "wood planks crate"
-/datum/supply_pack/materials/rawcotton
- name = "Raw Cotton Crate"
- desc = "Plushies have been on the down in the market, and now due to a flood of raw cotton the price of it is so cheap, its a steal! Contains 40 raw cotton sheets."
- cost = 800 // 100 net cost, 20 x 20 = 400. 300 profit if turned into cloth sheets or more if turned to silk then 10 x 200 = 2000
- contains = list(/obj/item/stack/sheet/cotton/thirty,
- /obj/item/stack/sheet/cotton/ten
- )
- crate_name = "cotton crate"
- crate_type = /obj/structure/closet/crate/hydroponics
-
-/datum/supply_pack/materials/rawcottonbulk
- name = "Raw Cotton Crate (Bulk)"
- desc = "We have so much of this stuff we need to get rid of it in -bulk- now. This crate contains 240 raw cotton sheets."
- cost = 1300 // 600 net cost 20 x 120 = 2400 profit if turned into cloth sheets or if turned into silk 200 x 60 = 12000
- contains = list(/obj/item/stack/sheet/cotton/thirty,
- /obj/item/stack/sheet/cotton/thirty,
- /obj/item/stack/sheet/cotton/thirty,
- /obj/item/stack/sheet/cotton/thirty,
- /obj/item/stack/sheet/cotton/thirty,
- /obj/item/stack/sheet/cotton/thirty,
- /obj/item/stack/sheet/cotton/thirty,
- /obj/item/stack/sheet/cotton/thirty,
- )
- crate_name = "bulk cotton crate"
- crate_type = /obj/structure/closet/crate/hydroponics
-
/datum/supply_pack/materials/rcdammo
name = "Spare RCD ammo"
desc = "This crate contains sixteen RCD compressed matter packs, to help with any holes or projects people might be working on."
diff --git a/code/modules/cargo/packs/medical.dm b/code/modules/cargo/packs/medical.dm
index 5d02bbe60f..ab188f235b 100644
--- a/code/modules/cargo/packs/medical.dm
+++ b/code/modules/cargo/packs/medical.dm
@@ -252,6 +252,7 @@
crate_name = "virus crate"
crate_type = /obj/structure/closet/crate/secure/plasma
dangerous = TRUE
+ can_private_buy = FALSE
/datum/supply_pack/medical/anitvirus
name = "Virus Containment Crate"
@@ -271,4 +272,4 @@
/obj/item/storage/box/syringes,
/obj/item/storage/box/beakers)
crate_name = "virus containment unit crate"
- crate_type = /obj/structure/closet/crate/secure/plasma
\ No newline at end of file
+ crate_type = /obj/structure/closet/crate/secure/plasma
diff --git a/code/modules/cargo/packs/organic.dm b/code/modules/cargo/packs/organic.dm
index 22cb518926..249faae33d 100644
--- a/code/modules/cargo/packs/organic.dm
+++ b/code/modules/cargo/packs/organic.dm
@@ -370,22 +370,6 @@
/////////////////////////////////// Misc /////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
-/datum/supply_pack/organic/hunting
- name = "Hunting Gear"
- desc = "Even in space, we can find prey to hunt, this crate contains everthing a fine hunter needs to have a sporting time. This crate needs armory access to open. A true huntter only needs a fine bottle of cognac, a nice coat, some good o' cigars, and of cource a hunting shotgun. "
- cost = 3500
- contraband = TRUE
- contains = list(/obj/item/clothing/head/flatcap,
- /obj/item/clothing/suit/hooded/wintercoat/captain,
- /obj/item/reagent_containers/food/drinks/bottle/cognac,
- /obj/item/storage/fancy/cigarettes/cigars/havana,
- /obj/item/clothing/gloves/color/white,
- /obj/item/clothing/under/rank/civilian/curator,
- /obj/item/gun/ballistic/shotgun/lethal)
- access = ACCESS_ARMORY
- crate_name = "sporting crate"
- crate_type = /obj/structure/closet/crate/secure // Would have liked a wooden crate but access >:(
-
/datum/supply_pack/organic/party
name = "Party Equipment"
desc = "Celebrate both life and death on the station with Nanotrasen's Party Essentials(tm)! Contains seven colored glowsticks, four beers, two ales, a drinking shaker, and a bottle of patron & goldschlager!"
diff --git a/code/modules/cargo/packs/science.dm b/code/modules/cargo/packs/science.dm
index 51bb2b728f..fd6fee362d 100644
--- a/code/modules/cargo/packs/science.dm
+++ b/code/modules/cargo/packs/science.dm
@@ -17,6 +17,7 @@
cost = 2500
contains = list (/obj/item/reagent_containers/food/snacks/cube/ape)
crate_name = "ape cube crate"
+ can_private_buy = FALSE
/datum/supply_pack/science/beakers
name = "Chemistry Beakers Crate"
diff --git a/code/modules/cargo/packs/security.dm b/code/modules/cargo/packs/security.dm
index d134d7bab8..a48874e974 100644
--- a/code/modules/cargo/packs/security.dm
+++ b/code/modules/cargo/packs/security.dm
@@ -10,6 +10,7 @@
group = "Security"
access = ACCESS_SECURITY
crate_type = /obj/structure/closet/crate/secure/gear
+ can_private_buy = FALSE
/datum/supply_pack/security/ammo
name = "Ammo Crate - General Purpose"
@@ -57,6 +58,7 @@
/obj/item/toy/crayon/white,
/obj/item/clothing/head/fedora/det_hat)
crate_name = "forensics crate"
+ can_private_buy = TRUE
/datum/supply_pack/security/helmets
name = "Helmets Crate"
@@ -134,6 +136,7 @@
/obj/item/grenade/barrier)
cost = 2000
crate_name = "security barriers crate"
+ can_private_buy = TRUE
/datum/supply_pack/security/securityclothes
name = "Security Clothing Crate"
@@ -152,6 +155,7 @@
/obj/item/clothing/suit/armor/hos/navyblue,
/obj/item/clothing/head/beret/sec/navyhos)
crate_name = "security clothing crate"
+ can_private_buy = TRUE
/datum/supply_pack/security/supplies
name = "Security Supplies Crate"
@@ -179,6 +183,7 @@
contains = list(/obj/item/clothing/head/helmet/justice,
/obj/item/clothing/mask/gas/sechailer)
crate_name = "security clothing crate"
+ can_private_buy = TRUE
/datum/supply_pack/security/baton
name = "Stun Batons Crate"
@@ -207,3 +212,19 @@
/obj/item/storage/box/wall_flash,
/obj/item/storage/box/wall_flash)
crate_name = "wall-mounted flash crate"
+
+/datum/supply_pack/security/hunting
+ name = "Hunting Gear"
+ desc = "Even in space, we can find prey to hunt, this crate contains everthing a fine hunter needs to have a sporting time. This crate needs armory access to open. A true huntter only needs a fine bottle of cognac, a nice coat, some good o' cigars, and of cource a hunting shotgun. "
+ cost = 3500
+ contraband = TRUE
+ contains = list(/obj/item/clothing/head/flatcap,
+ /obj/item/clothing/suit/hooded/wintercoat/captain,
+ /obj/item/reagent_containers/food/drinks/bottle/cognac,
+ /obj/item/storage/fancy/cigarettes/cigars/havana,
+ /obj/item/clothing/gloves/color/white,
+ /obj/item/clothing/under/rank/civilian/curator,
+ /obj/item/gun/ballistic/shotgun/lethal)
+ access = ACCESS_ARMORY
+ crate_name = "sporting crate"
+ crate_type = /obj/structure/closet/crate/secure // Would have liked a wooden crate but access >:(
diff --git a/code/modules/cargo/packs/vending.dm b/code/modules/cargo/packs/vending.dm
index 344d19f6c9..810cfd8d6e 100644
--- a/code/modules/cargo/packs/vending.dm
+++ b/code/modules/cargo/packs/vending.dm
@@ -99,6 +99,7 @@
contains = list(/obj/machinery/vending/security)
crate_name = "SecTech supply crate"
crate_type = /obj/structure/closet/crate/secure/gear
+ can_private_buy = FALSE
/datum/supply_pack/vending/snack
name = "Snack Supply Crate"
diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm
index f865cfddbb..47255d9535 100644
--- a/code/modules/client/client_defines.dm
+++ b/code/modules/client/client_defines.dm
@@ -80,10 +80,24 @@
var/list/char_render_holders //Should only be a key-value list of north/south/east/west = obj/screen.
+ /// Keys currently held
+ var/list/keys_held = list()
+ /// These next two vars are to apply movement for keypresses and releases made while move delayed.
+ /// Because discarding that input makes the game less responsive.
+ /// On next move, add this dir to the move that would otherwise be done
+ var/next_move_dir_add
+ /// On next move, subtract this dir from the move that would otherwise be done
+ var/next_move_dir_sub
+ /// Amount of keydowns in the last keysend checking interval
var/client_keysend_amount = 0
+ /// World tick time where client_keysend_amount will reset
var/next_keysend_reset = 0
+ /// World tick time where keysend_tripped will reset back to false
var/next_keysend_trip_reset = 0
+ /// When set to true, user will be autokicked if they trip the keysends in a second limit again
var/keysend_tripped = FALSE
+ /// custom movement keys for this client
+ var/list/movement_keys = list()
/// Messages currently seen by this client
var/list/seen_messages
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 989ccaf450..a66d804b55 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -265,6 +265,9 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
else
prefs = new /datum/preferences(src)
GLOB.preferences_datums[ckey] = prefs
+ if(SSinput.initialized)
+ set_macros()
+ update_movement_keys(prefs)
prefs.last_ip = address //these are gonna be used for banning
prefs.last_id = computer_id //these are gonna be used for banning
@@ -328,9 +331,6 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
qdel(src)
return
- if(SSinput.initialized)
- set_macros()
-
chatOutput.start() // Starts the chat
if(alert_mob_dupe_login)
@@ -892,6 +892,23 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
y = clamp(y+change, min,max)
change_view("[x]x[y]")
+/client/proc/update_movement_keys(datum/preferences/direct_prefs)
+ var/datum/preferences/D = prefs || direct_prefs
+ if(!D?.key_bindings)
+ return
+ movement_keys = list()
+ for(var/key in D.key_bindings)
+ for(var/kb_name in D.key_bindings[key])
+ switch(kb_name)
+ if("North")
+ movement_keys[key] = NORTH
+ if("East")
+ movement_keys[key] = EAST
+ if("West")
+ movement_keys[key] = WEST
+ if("South")
+ movement_keys[key] = SOUTH
+
/client/proc/change_view(new_size)
if (isnull(new_size))
CRASH("change_view called without argument.")
@@ -902,23 +919,27 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
new_size = "15x15"
//END OF CIT CHANGES
+ var/list/old_view = getviewsize(view)
view = new_size
- apply_clickcatcher()
+ var/list/actualview = getviewsize(view)
+ apply_clickcatcher(actualview)
mob.reload_fullscreen()
if (isliving(mob))
var/mob/living/M = mob
M.update_damage_hud()
if (prefs.auto_fit_viewport)
fit_viewport()
+ SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_CHANGE_VIEW, src, old_view, actualview)
/client/proc/generate_clickcatcher()
if(!void)
void = new()
screen += void
-/client/proc/apply_clickcatcher()
+/client/proc/apply_clickcatcher(list/actualview)
generate_clickcatcher()
- var/list/actualview = getviewsize(view)
+ if(!actualview)
+ actualview = getviewsize(view)
void.UpdateGreed(actualview[1],actualview[2])
/client/proc/AnnouncePR(announcement)
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index bb105f2652..77bcbacfcf 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -1,11 +1,6 @@
- /* CAUTION! CAUTION! CAUTION! CAUTION! CAUTION! *\
- | THIS FILE CONTAINS HOOKS FOR FOR |
- | CHANGES SPECIFIC TO CITADEL. IF |
- | YOU'RE FIXING A MERGE CONFLICT |
- | HERE, PLEASE ASK FOR REVIEW FROM |
- | ANOTHER MAINTAINER TO ENSURE YOU |
- | DON'T INTRODUCE REGRESSIONS. |
- \* */
+#define DEFAULT_SLOT_AMT 2
+#define HANDS_SLOT_AMT 2
+#define BACKPACK_SLOT_AMT 4
GLOBAL_LIST_EMPTY(preferences_datums)
@@ -51,6 +46,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/chat_on_map = TRUE
var/max_chat_length = CHAT_MESSAGE_MAX_LENGTH
var/see_chat_non_mob = TRUE
+
+ /// Custom Keybindings
+ var/list/key_bindings = list()
+
+
var/tgui_fancy = TRUE
var/tgui_lock = TRUE
var/windowflashing = TRUE
@@ -189,9 +189,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/uplink_spawn_loc = UPLINK_PDA
- var/sprint_spacebar = FALSE
- var/sprint_toggle = FALSE
-
var/hud_toggle_flash = TRUE
var/hud_toggle_color = "#ffffff"
@@ -204,6 +201,10 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/vore_flags = 0
var/list/belly_prefs = list()
var/vore_taste = "nothing in particular"
+ var/toggleeatingnoise = TRUE
+ var/toggledigestionnoise = TRUE
+ var/hound_sleeper = TRUE
+ var/cit_toggles = TOGGLES_CITADEL
//backgrounds
var/mutable_appearance/character_background
@@ -214,6 +215,19 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/no_tetris_storage = FALSE
+ ///loadout stuff
+ var/gear_points = 10
+ var/list/gear_categories
+ var/list/chosen_gear = list()
+ var/gear_tab
+
+ var/screenshake = 100
+ var/damagescreenshake = 2
+ var/arousable = TRUE
+ var/widescreenpref = TRUE
+ var/autostand = TRUE
+ var/auto_ooc = FALSE
+
/datum/preferences/New(client/C)
parent = C
@@ -233,6 +247,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
return
//we couldn't load character data so just randomize the character appearance + name
random_character() //let's create a random character then - rather than a fat, bald and naked man.
+ key_bindings = deepCopyList(GLOB.hotkey_keybinding_list_by_key) // give them default keybinds and update their movement keys
+ C?.update_movement_keys(src)
real_name = pref_species.random_name(gender,1)
if(!loaded_preferences_successfully)
save_preferences()
@@ -254,6 +270,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "Loadout"
dat += "Game Preferences"
dat += "Content Preferences"
+ dat += "Keybindings"
if(!path)
dat += "
Please create an account to save your preferences
"
@@ -395,7 +412,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
mutant_colors = TRUE
if (CONFIG_GET(number/body_size_min) != CONFIG_GET(number/body_size_max))
- dat += "Sprite Size:[features["body_size"]]% "
+ dat += "Sprite Size:[features["body_size"]*100]% "
if((EYECOLOR in pref_species.species_traits) && !(NOEYES in pref_species.species_traits))
@@ -843,7 +860,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "See Runechat for non-mobs:[see_chat_non_mob ? "Enabled" : "Disabled"] "
dat += " "
dat += "Action Buttons:[(buttons_locked) ? "Locked In Place" : "Unlocked"] "
- dat += "Keybindings:[(hotkeys) ? "Hotkeys" : "Default"] "
dat += " "
dat += "PDA Color:Change "
dat += "PDA Style:[pda_style] "
@@ -939,8 +955,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += " "
dat += "Ambient Occlusion:[ambientocclusion ? "Enabled" : "Disabled"] "
dat += "Fit Viewport:[auto_fit_viewport ? "Auto" : "Manual"] "
- dat += "Sprint Key:[sprint_spacebar ? "Space" : "Shift"] "
- dat += "Toggle Sprint:[sprint_toggle ? "Enabled" : "Disabled"] "
dat += "HUD Button Flashes:[hud_toggle_flash ? "Enabled" : "Disabled"] "
dat += "HUD Button Flash Color:Change "
@@ -1063,6 +1077,56 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "Ass Slapping:[(cit_toggles & NO_ASS_SLAP) ? "Disallowed" : "Allowed"] "
dat += ""
dat += " "
+ if(5) // Custom keybindings
+ dat += "Keybindings:[(hotkeys) ? "Hotkeys" : "Input"] "
+ dat += "Keybindings mode controls how the game behaves with tab and map/input focus. If it is on Hotkeys, the game will always attempt to force you to map focus, meaning keypresses are sent \
+ directly to the map instead of the input. You will still be able to use the command bar, but you need to tab to do it every time you click on the game map. \
+ If it is on Input, the game will not force focus away from the input bar, and you can switch focus using TAB between these two modes: If the input bar is pink, that means that you are in non-hotkey mode, sending all keypresses of the normal \
+ alphanumeric characters, punctuation, spacebar, backspace, enter, etc, typing keys into the input bar. If the input bar is white, you are in hotkey mode, meaning all keypresses go into the game's keybind handling system unless you \
+ manually click on the input bar to shift focus there. \
+ Input mode is the closest thing to the old input system. \
+ IMPORTANT: While in input mode's non hotkey setting (tab toggled), Ctrl + KEY will send KEY to the keybind system as the key itself, not as Ctrl + KEY. This means Ctrl + T/W/A/S/D/all your familiar stuff still works, but you \
+ won't be able to access any regular Ctrl binds. "
+ // Create an inverted list of keybindings -> key
+ var/list/user_binds = list()
+ for (var/key in key_bindings)
+ for(var/kb_name in key_bindings[key])
+ user_binds[kb_name] += list(key)
+
+ var/list/kb_categories = list()
+ // Group keybinds by category
+ for (var/name in GLOB.keybindings_by_name)
+ var/datum/keybinding/kb = GLOB.keybindings_by_name[name]
+ kb_categories[kb.category] += list(kb)
+
+ dat += ""
+
+ for (var/category in kb_categories)
+ dat += "
[category]
"
+ for (var/i in kb_categories[category])
+ var/datum/keybinding/kb = i
+ if(!length(user_binds[kb.name]))
+ dat += " Unbound"
+ var/list/default_keys = hotkeys ? kb.hotkey_keys : kb.classic_keys
+ if(LAZYLEN(default_keys))
+ dat += "| Default: [default_keys.Join(", ")]"
+ dat += " "
+ else
+ var/bound_key = user_binds[kb.name][1]
+ dat += " [bound_key]"
+ for(var/bound_key_index in 2 to length(user_binds[kb.name]))
+ bound_key = user_binds[kb.name][bound_key_index]
+ dat += " | [bound_key]"
+ if(length(user_binds[kb.name]) < MAX_KEYS_PER_KEYBIND)
+ dat += "| Add Secondary"
+ var/list/default_keys = hotkeys ? kb.classic_keys : kb.hotkey_keys
+ if(LAZYLEN(default_keys))
+ dat += "| Default: [default_keys.Join(", ")]"
+ dat += " "
+
+ dat += "