diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm
index 57108bdf3b..9e393c1598 100644
--- a/code/__DEFINES/admin.dm
+++ b/code/__DEFINES/admin.dm
@@ -48,6 +48,7 @@
#define ADMIN_CENTCOM_REPLY(user) "(RPLY)"
#define ADMIN_SYNDICATE_REPLY(user) "(RPLY)"
#define ADMIN_SC(user) "(SC)"
+#define ADMIN_SMITE(user) "(SMITE)"
#define ADMIN_LOOKUP(user) "[key_name_admin(user)][ADMIN_QUE(user)]"
#define ADMIN_LOOKUPFLW(user) "[key_name_admin(user)][ADMIN_QUE(user)] [ADMIN_FLW(user)]"
#define ADMIN_SET_SD_CODE "(SETCODE)"
@@ -55,3 +56,7 @@
#define ADMIN_JMP(src) "(JMP)"
#define COORD(src) "[src ? "([src.x],[src.y],[src.z])" : "nonexistent location"]"
#define ADMIN_COORDJMP(src) "[src ? "[COORD(src)] [ADMIN_JMP(src)]" : "nonexistent location"]"
+
+#define ADMIN_PUNISHMENT_LIGHTNING "Lightning bolt"
+#define ADMIN_PUNISHMENT_BRAINDAMAGE "Brain damage"
+#define ADMIN_PUNISHMENT_GIB "Gib"
\ No newline at end of file
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index 46d2625967..8cbc3eebf2 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -43,6 +43,31 @@
areas |= T.loc
return areas
+/proc/get_adjacent_areas(atom/center)
+ . = list(get_area(get_ranged_target_turf(center, NORTH, 1)),
+ get_area(get_ranged_target_turf(center, SOUTH, 1)),
+ get_area(get_ranged_target_turf(center, EAST, 1)),
+ get_area(get_ranged_target_turf(center, WEST, 1)))
+ listclearnulls(.)
+
+/proc/get_open_turf_in_dir(atom/center, dir)
+ var/turf/open/T = get_ranged_target_turf(center, dir, 1)
+ if(istype(T))
+ return T
+
+/proc/get_adjacent_open_turfs(atom/center)
+ . = list(get_open_turf_in_dir(center, NORTH),
+ get_open_turf_in_dir(center, SOUTH),
+ get_open_turf_in_dir(center, EAST),
+ get_open_turf_in_dir(center, WEST))
+ listclearnulls(.)
+
+/proc/get_adjacent_open_areas(atom/center)
+ . = list()
+ var/list/adjacent_turfs = get_adjacent_open_turfs(center)
+ for(var/I in adjacent_turfs)
+ . |= get_area(I)
+
// Like view but bypasses luminosity check
/proc/get_hear(range, atom/source)
diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm
index 4d1653f0f5..ae1f2b26ec 100644
--- a/code/__HELPERS/lists.dm
+++ b/code/__HELPERS/lists.dm
@@ -67,6 +67,24 @@
return 0
return L[A.type]
+//Checks for a string in a list
+/proc/is_string_in_list(string, list/L)
+ if(!L || !L.len || !string)
+ return
+ for(var/V in L)
+ if(string == V)
+ return 1
+ return
+
+//Removes a string from a list
+/proc/remove_strings_from_list(string, list/L)
+ if(!L || !L.len || !string)
+ return
+ for(var/V in L)
+ if(V == string)
+ L -= V //No return here so that it removes all strings of that type
+ return
+
//returns a new list with only atoms that are in typecache L
/proc/typecache_filter_list(list/atoms, list/typecache)
. = list()
diff --git a/code/__HELPERS/matrices.dm b/code/__HELPERS/matrices.dm
index c77b8aa7f4..2e34624df5 100644
--- a/code/__HELPERS/matrices.dm
+++ b/code/__HELPERS/matrices.dm
@@ -61,7 +61,25 @@
/matrix/proc/get_y_shift()
. = f
-// Color matrices:
+/matrix/proc/get_x_skew()
+ . = b
+
+/matrix/proc/get_y_skew()
+ . = d
+
+//Skews a matrix in a particular direction
+//Missing arguments are treated as no skew in that direction
+
+//As Rotation is defined as a scale+skew, these procs will break any existing rotation
+//Unless the result is multiplied against the current matrix
+/matrix/proc/set_skew(x = 0, y = 0)
+ b = x
+ d = y
+
+
+/////////////////////
+// COLOUR MATRICES //
+/////////////////////
/* Documenting a couple of potentially useful color matrices here to inspire ideas
// Greyscale - indentical to saturation @ 0
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index 37a6b59522..b57d5b2548 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -195,7 +195,7 @@ Proc for attack log creation, because really why not
log_attack("[user ? "[user.name][(is_mob_user && user.ckey) ? "([user.ckey])" : ""]" : "NON-EXISTANT SUBJECT"] [what_done] [target ? "[target.name][(is_mob_target && target.ckey)? "([target.ckey])" : ""]" : "NON-EXISTANT SUBJECT"][object ? " with [object]" : " "][addition][(living_target) ? " (NEWHP: [living_target.health])" : ""][(attack_location) ? "([attack_location.x],[attack_location.y],[attack_location.z])" : ""]")
-/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1)
+/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks = null)
if(!user || !target)
return 0
var/user_loc = user.loc
@@ -228,14 +228,14 @@ Proc for attack log creation, because really why not
drifting = 0
user_loc = user.loc
- if((!drifting && user.loc != user_loc) || target.loc != target_loc || user.get_active_held_item() != holding || user.incapacitated() || user.lying )
+ if((!drifting && user.loc != user_loc) || target.loc != target_loc || user.get_active_held_item() != holding || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke()))
. = 0
break
if (progress)
qdel(progbar)
-/proc/do_after(mob/user, delay, needhand = 1, atom/target = null, progress = 1)
+/proc/do_after(mob/user, delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null)
if(!user)
return 0
var/atom/Tloc = null
@@ -270,7 +270,7 @@ Proc for attack log creation, because really why not
drifting = 0
Uloc = user.loc
- if(!user || user.stat || user.weakened || user.stunned || (!drifting && user.loc != Uloc))
+ if(!user || user.stat || user.weakened || user.stunned || (!drifting && user.loc != Uloc) || (extra_checks && !extra_checks.Invoke()))
. = 0
break
@@ -292,7 +292,7 @@ Proc for attack log creation, because really why not
if (progress)
qdel(progbar)
-/proc/do_after_mob(mob/user, var/list/targets, time = 30, uninterruptible = 0, progress = 1)
+/proc/do_after_mob(mob/user, var/list/targets, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks)
if(!user || !targets)
return 0
if(!islist(targets))
@@ -331,7 +331,7 @@ Proc for attack log creation, because really why not
user_loc = user.loc
for(var/atom/target in targets)
- if((!drifting && user_loc != user.loc) || originalloc[target] != target.loc || user.get_active_held_item() != holding || user.incapacitated() || user.lying )
+ if((!drifting && user_loc != user.loc) || originalloc[target] != target.loc || user.get_active_held_item() != holding || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke()))
. = 0
break mainloop
if(progbar)
diff --git a/strings/soapstone_prefixes.txt b/strings/soapstone_prefixes.txt
new file mode 100644
index 0000000000..728e073983
--- /dev/null
+++ b/strings/soapstone_prefixes.txt
@@ -0,0 +1,14 @@
+****
+****?
+****!
+try ****
+**** ahead
+be wary of ****
+need ****
+imminent ****
+weakness: ****
+huh, it's a ****...
+visions of ****....
+could this be a ****?
+time for ****
+praise the ****!
\ No newline at end of file
diff --git a/strings/soapstone_suffixes.json b/strings/soapstone_suffixes.json
new file mode 100644
index 0000000000..2b2ab81a48
--- /dev/null
+++ b/strings/soapstone_suffixes.json
@@ -0,0 +1,234 @@
+{
+ "Characters": [
+ "human",
+ "lizard",
+ "monkey",
+ "xeno",
+ "slimeperson",
+ "snowflake",
+ "enemy",
+ "tough enemy",
+ "Nanotrasen",
+ "Syndicate",
+ "skeleton",
+ "flier",
+ "statue",
+ "monster",
+ "strange creature",
+ "demon",
+ "boss",
+ "dark spirit",
+ "saint",
+ "wretch",
+ "charmer",
+ "miscreant",
+ "liar",
+ "fatty",
+ "beanpole",
+ "merchant",
+ "prisoner"
+ ],
+
+ "Careers": [
+ "assistant",
+ "captain",
+ "head of personnel",
+ "head of security",
+ "warden",
+ "security officer",
+ "detective",
+ "chief engineer",
+ "engineer",
+ "atmos tech",
+ "research director",
+ "scientist",
+ "robotics",
+ "chief medical officer",
+ "doctor",
+ "chemist",
+ "geneticist",
+ "virologist",
+ "janitor",
+ "bartender",
+ "cook",
+ "clown",
+ "mime",
+ "chaplain",
+ "librarian",
+ "quartermaster",
+ "cargo tech",
+ "miner",
+ "lawyer",
+ "AI",
+ "cyborg",
+ "drone",
+ "pAI"
+ ],
+
+ "Antagonists": [
+ "traitor",
+ "changeling",
+ "nuke op",
+ "wizard",
+ "alien",
+ "ghost",
+ "blob",
+ "cultist",
+ "devil"
+ ],
+
+ "Objects": [
+ "airlock",
+ "table",
+ "rack",
+ "weapon",
+ "trap",
+ "false wall",
+ "window",
+ "item",
+ "loot",
+ "teleporter",
+ "blueprints",
+ "boots",
+ "clothes",
+ "clothing",
+ "drugs",
+ "medicine",
+ "crate",
+ "locker",
+ "handcuffs",
+ "plant",
+ "food",
+ "drink",
+ "tool",
+ "amazing item",
+ "amazing loot",
+ "amazing clothing",
+ "amazing drugs",
+ "amazing rack"
+ ],
+
+ "Techniques": [
+ "close-ranged battle",
+ "ranged battle",
+ "eliminating one at a time",
+ "luring it out",
+ "beating to a pulp",
+ "lying in ambush",
+ "stealth",
+ "fleeing",
+ "charging",
+ "stabbing in the back"
+ ],
+
+ "Actions": [
+ "attacking",
+ "hacking",
+ "sneaking",
+ "slipping",
+ "running",
+ "walking",
+ "sprinting",
+ "running away",
+ "holding with both hands",
+ "jumping",
+ "restraining",
+ "crying",
+ "cheering"
+ ],
+
+ "Geography": [
+ "hallway",
+ "room",
+ "corridor",
+ "airlock",
+ "space",
+ "hidden path",
+ "gorgeous view",
+ "open area",
+ "tight spot",
+ "tunnel",
+ "dark area",
+ "bright area",
+ "shuttle",
+ "pod",
+ "security",
+ "arrivals",
+ "escape",
+ "engineering",
+ "atmospherics",
+ "medbay",
+ "science",
+ "brig",
+ "prison",
+ "asteroid",
+ "cargo",
+ "mining",
+ "cave",
+ "maintenance"
+ ],
+
+ "Orientation": [
+ "front",
+ "back",
+ "left",
+ "right",
+ "up",
+ "down",
+ "north",
+ "south",
+ "east",
+ "west",
+ "northeast",
+ "northwest",
+ "southeast",
+ "southwest",
+ "that way"
+ ],
+
+ "Body parts": [
+ "head",
+ "neck",
+ "stomach",
+ "back",
+ "arm",
+ "leg",
+ "heel",
+ "rear",
+ "tail",
+ "wings",
+ "anywhere",
+ "everywhere"
+ ],
+
+ "Concepts": [
+ "chance",
+ "hint",
+ "secret",
+ "happiness",
+ "sorrow",
+ "life",
+ "death",
+ "elation",
+ "grief",
+ "hope",
+ "despair",
+ "light",
+ "dark",
+ "bravery",
+ "resignation",
+ "comfort",
+ "tears"
+ ],
+
+ "Musings": [
+ "I can't take this...",
+ "it'll happen to you too",
+ "you don't deserve this",
+ "you bastard",
+ "you can do it",
+ "don't give up",
+ "don't you dare",
+ "do it"
+ ]
+}
\ No newline at end of file