diff --git a/code/__DEFINES/_flags/_flags.dm b/code/__DEFINES/_flags/_flags.dm
index 8c8e03d865..dae3df6f0b 100644
--- a/code/__DEFINES/_flags/_flags.dm
+++ b/code/__DEFINES/_flags/_flags.dm
@@ -22,8 +22,8 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
//FLAGS BITMASK
///This flag is what recursive_hear_check() uses to determine wether to add an item to the hearer list or not.
#define HEAR_1 (1<<3)
-///Projectiels will check ricochet on things impacted that have this.
-#define CHECK_RICOCHET_1 (1<<4)
+///Projectiles will use default chance-based ricochet handling on things with this.
+#define DEFAULT_RICOCHET_1 (1<<4)
///Conducts electricity (metal etc.).
#define CONDUCT_1 (1<<5)
///For machines and structures that should not break into parts, eg, holodeck stuff.
diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index e7f79596b5..bf053635af 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -273,10 +273,3 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
* a "inefficiently" prefix will be added to the message.
*/
#define FEEBLE_ATTACK_MSG_THRESHOLD 0.5
-
-
-//bullet_act() return values
-#define BULLET_ACT_HIT "HIT" //It's a successful hit, whatever that means in the context of the thing it's hitting.
-#define BULLET_ACT_BLOCK "BLOCK" //It's a blocked hit, whatever that means in the context of the thing it's hitting.
-#define BULLET_ACT_FORCE_PIERCE "PIERCE" //It pierces through the object regardless of the bullet being piercing by default.
-#define BULLET_ACT_TURF "TURF" //It hit us but it should hit something on the same turf too. Usually used for turfs.
diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index 28ec3383ae..c55662e86f 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -1,3 +1,6 @@
+/// Checks if something is a BYOND object datatype rather than a primitive, or whatever's closest to one.
+#define is_object_datatype(object) (object && !ispath(object) && !istext(object) && !isnum(object))
+
// simple is_type and similar inline helpers
#define in_range(source, user) (get_dist(source, user) <= 1 && (get_step(source, 0)?:z) == (get_step(user, 0)?:z))
diff --git a/code/__DEFINES/projectiles.dm b/code/__DEFINES/projectiles.dm
new file mode 100644
index 0000000000..1bd67fbe02
--- /dev/null
+++ b/code/__DEFINES/projectiles.dm
@@ -0,0 +1,14 @@
+/// This atom should be ricocheted off of from its inherent properties using standard % chance handling.
+#define PROJECTILE_RICOCHET_YES 1
+/// This atom should not be ricocheted off of from its inherent properties.
+#define PROJECTILE_RICOCHET_NO 2
+/// This atom should prevent any kind of projectile ricochet from its inherent properties.
+#define PROJECTILE_RICOCHET_PREVENT 3
+/// This atom should force a projectile ricochet from its inherent properties.
+#define PROJECTILE_RICOCHET_FORCE 4
+
+//bullet_act() return values
+#define BULLET_ACT_HIT "HIT" //It's a successful hit, whatever that means in the context of the thing it's hitting.
+#define BULLET_ACT_BLOCK "BLOCK" //It's a blocked hit, whatever that means in the context of the thing it's hitting.
+#define BULLET_ACT_FORCE_PIERCE "PIERCE" //It pierces through the object regardless of the bullet being piercing by default.
+#define BULLET_ACT_TURF "TURF" //It hit us but it should hit something on the same turf too. Usually used for turfs.
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index ef3b2c54c3..f3f9f1e8b5 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -67,6 +67,8 @@
#define TRAIT_BLIND "blind"
#define TRAIT_MUTE "mute"
#define TRAIT_EMOTEMUTE "emotemute"
+#define TRAIT_LOOC_MUTE "looc_mute" //Just like unconsciousness, it disables LOOC salt.
+#define TRAIT_AOOC_MUTE "aooc_mute" //Same as above but for AOOC.
#define TRAIT_DEAF "deaf"
#define TRAIT_NEARSIGHT "nearsighted"
#define TRAIT_FAT "fat"
@@ -254,7 +256,7 @@
#define BOOK_TRAIT "granter (book)" // knowledge is power
// unique trait sources, still defines
-#define STATUE_MUTE "statue"
+#define STATUE_TRAIT "statue"
#define CLONING_POD_TRAIT "cloning-pod"
#define VIRTUAL_REALITY_TRAIT "vr_trait"
#define CHANGELING_DRAIN "drain"
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index 9419000e02..c3004a4501 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -125,7 +125,7 @@ GLOBAL_LIST_INIT(bitfields, list(
"UNUSED_RESERVATION_TURF_1" = UNUSED_RESERVATION_TURF_1,
"CAN_BE_DIRTY_1" = CAN_BE_DIRTY_1,
"HEAR_1" = HEAR_1,
- "CHECK_RICOCHET_1" = CHECK_RICOCHET_1,
+ "DEFAULT_RICOCHET_1" = DEFAULT_RICOCHET_1,
"CONDUCT_1" = CONDUCT_1,
"NO_LAVA_GEN_1" = NO_LAVA_GEN_1,
"NODECONSTRUCT_1" = NODECONSTRUCT_1,
diff --git a/code/datums/brain_damage/severe.dm b/code/datums/brain_damage/severe.dm
index bb37129b95..cd8612fd19 100644
--- a/code/datums/brain_damage/severe.dm
+++ b/code/datums/brain_damage/severe.dm
@@ -117,6 +117,11 @@
paralysis_type = "legs"
resilience = TRAUMA_RESILIENCE_ABSOLUTE
+/datum/brain_trauma/severe/paralysis/spinesnapped
+ random_gain = FALSE
+ paralysis_type = "legs"
+ resilience = TRAUMA_RESILIENCE_LOBOTOMY
+
/datum/brain_trauma/severe/narcolepsy
name = "Narcolepsy"
desc = "Patient may involuntarily fall asleep during normal activities."
diff --git a/code/datums/components/tackle.dm b/code/datums/components/tackle.dm
index 8b533c2ef0..9a98953985 100644
--- a/code/datums/components/tackle.dm
+++ b/code/datums/components/tackle.dm
@@ -354,7 +354,7 @@
playsound(user, 'sound/effects/blobattack.ogg', 60, TRUE)
playsound(user, 'sound/effects/splat.ogg', 70, TRUE)
user.emote("scream")
- user.gain_trauma(/datum/brain_trauma/severe/paralysis/paraplegic) // oopsie indeed!
+ user.gain_trauma(/datum/brain_trauma/severe/paralysis/spinesnapped) // oopsie indeed!
shake_camera(user, 7, 7)
user.overlay_fullscreen("flash", /obj/screen/fullscreen/flash)
user.clear_fullscreen("flash", 4.5)
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index a83eaed9d3..5875334ed6 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -141,8 +141,18 @@
return ..()
-/atom/proc/handle_ricochet(obj/item/projectile/P)
- return
+/**
+ * Checks if a projectile should ricochet off of us. Projectiles get final say.
+ * [__DEFINES/projectiles.dm] for return values.
+ */
+/atom/proc/check_projectile_ricochet(obj/item/projectile/P)
+ return (flags_1 & DEFAULT_RICOCHET_1)? PROJECTILE_RICOCHET_YES : PROJECTILE_RICOCHET_NO
+
+/**
+ * Handle a projectile ricochet. Return TRUE if we did something to the projectile like reflecting it/whatnot.
+ */
+/atom/proc/handle_projectile_ricochet(obj/item/projectile/P)
+ return FALSE
/atom/proc/CanPass(atom/movable/mover, turf/target)
return !density
diff --git a/code/game/objects/items/plushes.dm b/code/game/objects/items/plushes.dm
index bfbfdf9762..00a9ff22f4 100644
--- a/code/game/objects/items/plushes.dm
+++ b/code/game/objects/items/plushes.dm
@@ -140,7 +140,8 @@
if(jsonlist["icon_state"])
icon_state = jsonlist["icon_state"]
item_state = jsonlist["item_state"]
- icon = 'config/plushies/sprites.dmi'
+ var/static/config_sprites = file("config/plushies/sprites.dmi")
+ icon = config_sprites
if(jsonlist["attack_verb"])
attack_verb = jsonlist["attack_verb"]
if(jsonlist["squeak_override"])
diff --git a/code/game/objects/structures/petrified_statue.dm b/code/game/objects/structures/petrified_statue.dm
index a8a5a577c2..97eadff5f6 100644
--- a/code/game/objects/structures/petrified_statue.dm
+++ b/code/game/objects/structures/petrified_statue.dm
@@ -4,11 +4,13 @@
icon_state = "human_male"
density = TRUE
anchored = TRUE
+ flags_1 = PREVENT_CONTENTS_EXPLOSION_1
max_integrity = 200
- var/timer = 240 //eventually the person will be freed
+ var/timer = 8 MINUTES //eventually the person will be freed
var/mob/living/petrified_mob
-/obj/structure/statue/petrified/New(loc, mob/living/L, statue_timer)
+/obj/structure/statue/petrified/Initialize(mapload, mob/living/L, statue_timer)
+ . = ..()
if(statue_timer)
timer = statue_timer
if(L)
@@ -17,25 +19,18 @@
L.buckled.unbuckle_mob(L,force=1)
L.visible_message("[L]'s skin rapidly turns to marble!", "Your body freezes up! Can't... move... can't... think...")
L.forceMove(src)
- ADD_TRAIT(L, TRAIT_MUTE, STATUE_MUTE)
+ ADD_TRAIT(L, TRAIT_MUTE, STATUE_TRAIT)
+ ADD_TRAIT(L, TRAIT_EMOTEMUTE, STATUE_TRAIT)
+ ADD_TRAIT(L, TRAIT_LOOC_MUTE, STATUE_TRAIT)
+ ADD_TRAIT(L, TRAIT_AOOC_MUTE, STATUE_TRAIT)
+ ADD_TRAIT(L, TRAIT_MOBILITY_NOMOVE, STATUE_TRAIT)
+ ADD_TRAIT(L, TRAIT_MOBILITY_NOPICKUP, STATUE_TRAIT)
+ ADD_TRAIT(L, TRAIT_MOBILITY_NOUSE, STATUE_TRAIT)
L.faction += "mimic" //Stops mimics from instaqdeling people in statues
L.status_flags |= GODMODE
obj_integrity = L.health + 100 //stoning damaged mobs will result in easier to shatter statues
max_integrity = obj_integrity
- START_PROCESSING(SSobj, src)
- ..()
-
-/obj/structure/statue/petrified/process()
- if(!petrified_mob)
- STOP_PROCESSING(SSobj, src)
- timer--
- petrified_mob.Stun(40) //So they can't do anything while petrified
- if(timer <= 0)
- STOP_PROCESSING(SSobj, src)
- qdel(src)
-
-/obj/structure/statue/petrified/contents_explosion(severity, target)
- return
+ QDEL_IN(src, timer)
/obj/structure/statue/petrified/handle_atom_del(atom/A)
if(A == petrified_mob)
@@ -59,7 +54,13 @@
if(petrified_mob)
petrified_mob.status_flags &= ~GODMODE
petrified_mob.forceMove(loc)
- REMOVE_TRAIT(petrified_mob, TRAIT_MUTE, STATUE_MUTE)
+ REMOVE_TRAIT(petrified_mob, TRAIT_MUTE, STATUE_TRAIT)
+ REMOVE_TRAIT(petrified_mob, TRAIT_EMOTEMUTE, STATUE_TRAIT)
+ REMOVE_TRAIT(petrified_mob, TRAIT_LOOC_MUTE, STATUE_TRAIT)
+ REMOVE_TRAIT(petrified_mob, TRAIT_AOOC_MUTE, STATUE_TRAIT)
+ REMOVE_TRAIT(petrified_mob, TRAIT_MOBILITY_NOMOVE, STATUE_TRAIT)
+ REMOVE_TRAIT(petrified_mob, TRAIT_MOBILITY_NOPICKUP, STATUE_TRAIT)
+ REMOVE_TRAIT(petrified_mob, TRAIT_MOBILITY_NOUSE, STATUE_TRAIT)
petrified_mob.take_overall_damage((petrified_mob.health - obj_integrity + 100)) //any new damage the statue incurred is transfered to the mob
petrified_mob.faction -= "mimic"
petrified_mob = null
diff --git a/code/game/turfs/simulated/wall/mineral_walls.dm b/code/game/turfs/simulated/wall/mineral_walls.dm
index b04f4f0aa0..5c74eb4ecc 100644
--- a/code/game/turfs/simulated/wall/mineral_walls.dm
+++ b/code/game/turfs/simulated/wall/mineral_walls.dm
@@ -191,7 +191,7 @@
icon = 'icons/turf/walls/shuttle_wall.dmi'
icon_state = "map-shuttle"
explosion_block = 3
- flags_1 = CAN_BE_DIRTY_1 | CHECK_RICOCHET_1
+ flags_1 = CAN_BE_DIRTY_1 | DEFAULT_RICOCHET_1
sheet_type = /obj/item/stack/sheet/mineral/titanium
smooth = SMOOTH_MORE|SMOOTH_DIAGONAL
canSmoothWith = list(/turf/closed/wall/mineral/titanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/shuttle, /obj/structure/shuttle/engine/heater, /obj/structure/falsewall/titanium)
@@ -302,4 +302,4 @@
/turf/closed/wall/mineral/plastitanium/copyTurf(turf/T)
. = ..()
- T.transform = transform
\ No newline at end of file
+ T.transform = transform
diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm
index 5243341ac1..da5d3c643d 100644
--- a/code/game/turfs/simulated/walls.dm
+++ b/code/game/turfs/simulated/walls.dm
@@ -41,7 +41,7 @@
/turf/closed/wall/attack_tk()
return
-/turf/closed/wall/handle_ricochet(obj/item/projectile/P) //A huge pile of shitcode!
+/turf/closed/wall/handle_projectile_ricochet(obj/item/projectile/P) //A huge pile of shitcode!
var/turf/p_turf = get_turf(P)
var/face_direction = get_dir(src, p_turf)
var/face_angle = dir2angle(face_direction)
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
index df54df5c7d..bff431a6c7 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
@@ -346,7 +346,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
//print the key
if(islist(key))
recursive_list_print(output, key, datum_handler, atom_handler)
- else if(is_proper_datum(key) && (datum_handler || (isatom(key) && atom_handler)))
+ else if(is_object_datatype(key) && (datum_handler || (isatom(key) && atom_handler)))
if(isatom(key) && atom_handler)
output += atom_handler.Invoke(key)
else
@@ -360,7 +360,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
var/value = input[key]
if(islist(value))
recursive_list_print(output, value, datum_handler, atom_handler)
- else if(is_proper_datum(value) && (datum_handler || (isatom(value) && atom_handler)))
+ else if(is_object_datatype(value) && (datum_handler || (isatom(value) && atom_handler)))
if(isatom(value) && atom_handler)
output += atom_handler.Invoke(value)
else
@@ -498,7 +498,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
if(length(select_text))
var/text = islist(select_text)? select_text.Join() : select_text
var/static/result_offset = 0
- showmob << browse(text, "window=SDQL-result-[result_offset++]")
+ showmob << browse(text, "window=SDQL-result-[result_offset++];size=800x1200")
show_next_to_key = null
if(qdel_on_finish)
qdel(src)
@@ -646,7 +646,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
switch(query_tree[1])
if("call")
for(var/i in found)
- if(!is_proper_datum(i))
+ if(!is_object_datatype(i))
continue
world.SDQL_var(i, query_tree["call"][1], null, i, superuser, src)
obj_count_finished++
@@ -664,7 +664,10 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
var/list/text_list = list()
var/print_nulls = !(options & SDQL2_OPTION_SELECT_OUTPUT_SKIP_NULLS)
obj_count_finished = select_refs
+ var/n = 0
for(var/i in found)
+ if(++n == 20000)
+ text_list += "
TRUNCATED - 20000 OBJECT LIMIT HIT"
SDQL_print(i, text_list, print_nulls)
select_refs[REF(i)] = TRUE
SDQL2_TICK_CHECK
@@ -675,7 +678,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
if("set" in query_tree)
var/list/set_list = query_tree["set"]
for(var/d in found)
- if(!is_proper_datum(d))
+ if(!is_object_datatype(d))
continue
SDQL_internal_vv(d, set_list)
obj_count_finished++
@@ -685,47 +688,72 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
obj_count_finished = length(obj_count_finished)
state = SDQL2_STATE_SWITCHING
-/datum/SDQL2_query/proc/SDQL_print(object, list/text_list, print_nulls = TRUE)
- if(is_proper_datum(object))
- text_list += "[REF(object)] : [object]"
- if(istype(object, /atom))
- var/atom/A = object
- var/turf/T = A.loc
- var/area/a
- if(istype(T))
- text_list += " at [T] [ADMIN_COORDJMP(T)]"
- a = T.loc
- else
- var/turf/final = get_turf(T) //Recursive, hopefully?
- if(istype(final))
- text_list += " at [final] [ADMIN_COORDJMP(final)]"
- a = final.loc
+/**
+ * Recursively prints out an object to text list for SDQL2 output to admins, with VV links and all.
+ * Recursion limit: 50
+ * Limit imposed by callers should be around 10000 objects
+ * Seriously, if you hit those limits, you're doing something wrong.
+ */
+/datum/SDQL2_query/proc/SDQL_print(datum/object, list/text_list, print_nulls = TRUE, recursion = 1, linebreak = TRUE)
+ if(recursion > 50)
+ text_list += "
RECURSION LIMIT REACHED.
"
+ return
+ if(is_object_datatype(object))
+ if(!islist(object))
+ text_list += "[object.type] [REF(object)]: [object]"
+ if(istype(object, /atom))
+ if(istype(object, /turf))
+ var/turf/T = object
+ text_list += " [ADMIN_COORDJMP(T)] at [T.loc]"
else
- text_list += " at nonexistant location"
- if(a)
- text_list += " in area [a]"
- if(T.loc != a)
- text_list += " inside [T]"
- text_list += "
"
- else if(islist(object))
- var/list/L = object
- var/first = TRUE
- text_list += "\["
- for (var/x in L)
- if (!first)
- text_list += ", "
- first = FALSE
- SDQL_print(x, text_list)
- if (!isnull(x) && !isnum(x) && L[x] != null)
- text_list += " -> "
- SDQL_print(L[L[x]], text_list)
- text_list += "]
"
+ var/atom/A = object
+ var/atom/container = A.loc
+ if(isturf(container))
+ text_list += " in [container] [ADMIN_COORDJMP(container)] at [container.loc]"
+ else if(container)
+ var/turf/T = get_turf(container)
+ var/cref = REF(container)
+ text_list += " in [container]([cref])"
+ if(T)
+ text_list += " on [T] [ADMIN_COORDJMP(T)] at[T.loc]"
+ else
+ text_list += " in nullspace"
+ else // lists are snowflake and get special treatment.
+ text_list += "/list [REF(object)] \[
"
+ var/list/L = object
+ if(length(L))
+ for(var/key in object)
+ if(islist(key))
+ text_list += ""
+ SDQL_print(key, text_list, TRUE, recursion + 1, FALSE)
+ text_list += ""
+ else
+ SDQL_print(key, text_list, TRUE, recursion, FALSE)
+ if(IS_VALID_ASSOC_KEY(key) && !isnull(L[key]))
+ var/value = L[key]
+ text_list += " --> "
+ if(islist(value))
+ text_list += ""
+ SDQL_print(value, text_list, TRUE, recursion + 1, FALSE)
+ text_list += ""
+ else
+ SDQL_print(value, text_list, TRUE, recursion, FALSE)
+ text_list += "
"
+ text_list += "\]"
+ if(linebreak)
+ text_list += "
"
else
if(isnull(object))
if(print_nulls)
- text_list += "NULL
"
+ text_list += "NULL"
+ else if(istext(object))
+ text_list += "\"[object]\""
+ else if(isnum(object) || ispath(object))
+ text_list += "[object]"
else
- text_list += "[object]
"
+ text_list += "UNKNOWN: [object]"
+ if(linebreak)
+ text_list += "
"
/datum/SDQL2_query/CanProcCall()
if(!allow_admin_interact)
@@ -957,7 +985,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
var/static/list/exclude = list("usr", "src", "marked", "global")
var/long = start < expression.len
var/datum/D
- if(is_proper_datum(object))
+ if(is_object_datatype(object))
D = object
if (object == world && (!long || expression[start + 1] == ".") && !(expression[start] in exclude)) //3 == length("SS") + 1
@@ -1161,9 +1189,6 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
query_list += word
return query_list
-/proc/is_proper_datum(thing)
- return istype(thing, /datum) || istype(thing, /client)
-
/obj/effect/statclick/SDQL2_delete/Click()
var/datum/SDQL2_query/Q = target
Q.delete_click()
diff --git a/code/modules/admin/view_variables/debug_variables.dm b/code/modules/admin/view_variables/debug_variables.dm
index 5116cd5cd6..ab9f4a534c 100644
--- a/code/modules/admin/view_variables/debug_variables.dm
+++ b/code/modules/admin/view_variables/debug_variables.dm
@@ -16,11 +16,19 @@
header = "
"
var/item
+ var/name_part = VV_HTML_ENCODE(name)
+ if(level > 0 || islist(D)) //handling keys in assoc lists
+ if(istype(name,/datum))
+ name_part = "[VV_HTML_ENCODE(name)] [REF(name)]"
+ else if(islist(name))
+ var/list/L = name
+ name_part = " /list ([length(L)]) [REF(name)]"
+
if (isnull(value))
- item = "[VV_HTML_ENCODE(name)] = null"
+ item = "[name_part] = null"
else if (istext(value))
- item = "[VV_HTML_ENCODE(name)] = \"[VV_HTML_ENCODE(value)]\""
+ item = "[name_part] = \"[VV_HTML_ENCODE(value)]\""
else if (isicon(value))
#ifdef VARSICON
@@ -28,33 +36,31 @@
var/rnd = rand(1,10000)
var/rname = "tmp[REF(I)][rnd].png"
usr << browse_rsc(I, rname)
- item = "[VV_HTML_ENCODE(name)] = ([value])
"
+ item = "[name_part] = ([value])
"
#else
- item = "[VV_HTML_ENCODE(name)] = /icon ([value])"
+ item = "[name_part] = /icon ([value])"
#endif
else if (isfile(value))
- item = "[VV_HTML_ENCODE(name)] = '[value]'"
+ item = "[name_part] = '[value]'"
- else if(istype(value, /matrix)) // Needs to be before datum
+ else if(istype(value,/matrix)) // Needs to be before datum
var/matrix/M = value
- item = {"[VV_HTML_ENCODE(name)] =
-
- | |
-
- | [M.a] | [M.d] | 0 |
- | [M.b] | [M.e] | 0 |
- | [M.c] | [M.f] | 1 |
- |
- |
-
"} //TODO link to modify_transform wrapper for all matrices
-
+ item = {"[name_part] =
+ | |
+
+
+ | [M.a] | [M.d] | 0 |
+ | [M.b] | [M.e] | 0 |
+ | [M.c] | [M.f] | 1 |
+
+ | |
"} //TODO link to modify_transform wrapper for all matrices
else if (istype(value, /datum))
var/datum/DV = value
if ("[DV]" != "[DV.type]") //if the thing as a name var, lets use it.
- item = "[VV_HTML_ENCODE(name)] [REF(value)] = [DV] [DV.type]"
+ item = "[name_part] = [DV] [DV.type] [REF(value)]"
else
- item = "[VV_HTML_ENCODE(name)] [REF(value)] = [DV.type]"
+ item = "[name_part] = [DV.type] [REF(value)]"
else if (islist(value))
var/list/L = value
@@ -72,19 +78,19 @@
items += debug_variable(key, val, level + 1, sanitize = sanitize)
- item = "[VV_HTML_ENCODE(name)] = /list ([L.len])"
+ item = "[name_part] = /list ([L.len])"
else
- item = "[VV_HTML_ENCODE(name)] = /list ([L.len])"
+ item = "[name_part] = /list ([L.len])"
else if (name in GLOB.bitfields)
var/list/flags = list()
for (var/i in GLOB.bitfields[name])
if (value & GLOB.bitfields[name][i])
flags += i
- item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(jointext(flags, ", "))]"
+ item = "[name_part] = [VV_HTML_ENCODE(jointext(flags, ", "))]"
else
- item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(value)]"
+ item = "[name_part] = [VV_HTML_ENCODE(value)]"
return "[header][item]"
-#undef VV_HTML_ENCODE
+#undef VV_HTML_ENCODE
\ No newline at end of file
diff --git a/code/modules/antagonists/blob/blob/blobs/shield.dm b/code/modules/antagonists/blob/blob/blobs/shield.dm
index bc4e517ced..384df935f5 100644
--- a/code/modules/antagonists/blob/blob/blobs/shield.dm
+++ b/code/modules/antagonists/blob/blob/blobs/shield.dm
@@ -45,13 +45,15 @@
desc = "A solid wall of slightly twitching tendrils with a reflective glow."
damaged_desc = "A wall of twitching tendrils with a reflective glow."
icon_state = "blob_glow"
- flags_1 = CHECK_RICOCHET_1
point_return = 8
max_integrity = 100
brute_resist = 1
explosion_block = 2
-/obj/structure/blob/shield/reflective/handle_ricochet(obj/item/projectile/P)
+/obj/structure/blob/shield/reflective/check_projectile_ricochet(obj/item/projectile/P)
+ return PROJECTILE_RICOCHET_FORCE
+
+/obj/structure/blob/shield/reflective/handle_projectile_ricochet(obj/item/projectile/P)
var/turf/p_turf = get_turf(P)
var/face_direction = get_dir(src, p_turf)
var/face_angle = dir2angle(face_direction)
@@ -61,4 +63,4 @@
var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s)
P.setAngle(new_angle_s)
visible_message("[P] reflects off [src]!")
- return TRUE
\ No newline at end of file
+ return TRUE
diff --git a/code/modules/client/verbs/aooc.dm b/code/modules/client/verbs/aooc.dm
index bc32d3c222..1a019bba80 100644
--- a/code/modules/client/verbs/aooc.dm
+++ b/code/modules/client/verbs/aooc.dm
@@ -13,21 +13,38 @@ GLOBAL_VAR_INIT(normal_aooc_colour, "#ce254f")
if(!mob)
return
+ if(!(prefs.toggles & CHAT_OOC))
+ to_chat(src, " You have OOC muted.")
+ return
+ if(jobban_isbanned(mob, "OOC"))
+ to_chat(src, "You have been banned from OOC.")
+ return
+
if(!holder)
- if(mob.stat == DEAD)
- to_chat(usr, "You cannot use AOOC while dead.")
- return
- if(!is_special_character(mob))
- to_chat(usr, "You aren't an antagonist!")
- if(prefs.muted & MUTE_OOC)
- to_chat(src, "You cannot use AOOC (muted).")
- return
- if(jobban_isbanned(src.mob, "OOC"))
- to_chat(src, "You are banned from OOC.")
- return
if(!GLOB.aooc_allowed)
to_chat(src, "AOOC is currently muted.")
return
+ if(prefs.muted & MUTE_OOC)
+ to_chat(src, "You cannot use AOOC (muted).")
+ return
+ if(!is_special_character(mob))
+ to_chat(usr, "You aren't an antagonist!")
+ if(handle_spam_prevention(msg,MUTE_OOC))
+ return
+ if(findtext(msg, "byond://"))
+ to_chat(src, "Advertising other servers is not allowed.")
+ log_admin("[key_name(src)] has attempted to advertise in LOOC: [msg]")
+ return
+ if(mob.stat)
+ to_chat(usr, "You cannot use AOOC while unconscious or dead.")
+ return
+ if(isdead(mob))
+ to_chat(src, "You cannot use AOOC while ghosting.")
+ return
+ if(HAS_TRAIT(mob, TRAIT_AOOC_MUTE))
+ to_chat(src, "You cannot use AOOC right now.")
+ return
+
if(QDELETED(src))
return
diff --git a/code/modules/client/verbs/looc.dm b/code/modules/client/verbs/looc.dm
index 075cabcbbb..da39eabc12 100644
--- a/code/modules/client/verbs/looc.dm
+++ b/code/modules/client/verbs/looc.dm
@@ -38,11 +38,15 @@ GLOBAL_VAR_INIT(normal_looc_colour, "#6699CC")
log_admin("[key_name(src)] has attempted to advertise in LOOC: [msg]")
return
if(mob.stat)
- to_chat(src, "You cannot salt in LOOC while unconscious or dead.")
+ to_chat(src, "You cannot use LOOC while unconscious or dead.")
return
- if(istype(mob, /mob/dead))
+ if(isdead(mob))
to_chat(src, "You cannot use LOOC while ghosting.")
return
+ if(HAS_TRAIT(mob, TRAIT_LOOC_MUTE))
+ to_chat(src, "You cannot use LOOC right now.")
+ return
+
msg = emoji_parse(msg)
diff --git a/code/modules/food_and_drinks/kitchen_machinery/food_cart.dm b/code/modules/food_and_drinks/kitchen_machinery/food_cart.dm
index feba35da97..f09d3d6728 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/food_cart.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/food_cart.dm
@@ -1,5 +1,5 @@
#define STORAGE_CAPACITY 30
-#define LIQUID_CAPACIY 200
+#define LIQUID_CAPACITY 200
#define MIXER_CAPACITY 100
/obj/machinery/food_cart
@@ -19,7 +19,7 @@
/obj/machinery/food_cart/Initialize()
. = ..()
- create_reagents(LIQUID_CAPACIY, OPENCONTAINER | NO_REACT)
+ create_reagents(LIQUID_CAPACITY, OPENCONTAINER | NO_REACT)
mixer = new /obj/item/reagent_containers(src, MIXER_CAPACITY)
mixer.name = "Mixer"
@@ -60,6 +60,9 @@
return food_stored >= STORAGE_CAPACITY
/obj/machinery/food_cart/attackby(obj/item/O, mob/user, params)
+ if(O.tool_behaviour == TOOL_WRENCH)
+ default_unfasten_wrench(user, O, 0)
+ return TRUE
if(istype(O, /obj/item/reagent_containers/food/drinks/drinkingglass))
var/obj/item/reagent_containers/food/drinks/drinkingglass/DG = O
if(!DG.reagents.total_volume) //glass is empty
@@ -106,7 +109,7 @@
return
if(href_list["disposeI"])
- reagents.del_reagent(href_list["disposeI"])
+ reagents.del_reagent(text2path(href_list["disposeI"]))
if(href_list["dispense"])
if(stored_food[href_list["dispense"]]-- <= 0)
@@ -116,9 +119,13 @@
if(sanitize(O.name) == href_list["dispense"])
O.forceMove(drop_location())
break
+ log_combat(usr, src, "dispensed [O] from", null, "with [stored_food[href_list["dispense"]]] remaining")
if(href_list["portion"])
- portion = clamp(input("How much drink do you want to dispense per glass?") as num, 0, 50)
+ portion = clamp(input("How much drink do you want to dispense per glass?") as num|null, 0, 50)
+
+ if (isnull(portion))
+ return
if(href_list["pour"] || href_list["m_pour"])
if(glasses-- <= 0)
@@ -127,16 +134,16 @@
else
var/obj/item/reagent_containers/food/drinks/drinkingglass/DG = new(loc)
if(href_list["pour"])
- reagents.trans_id_to(DG, href_list["pour"], portion)
+ reagents.trans_id_to(DG, text2path(href_list["pour"]), portion)
if(href_list["m_pour"])
- mixer.reagents.trans_id_to(DG, href_list["m_pour"], portion)
+ mixer.reagents.trans_id_to(DG, text2path(href_list["m_pour"]), portion)
if(href_list["mix"])
- if(reagents.trans_id_to(mixer, href_list["mix"], portion) == 0)
+ if(reagents.trans_id_to(mixer, text2path(href_list["mix"]), portion) == 0)
to_chat(usr, "[mixer] is full!")
if(href_list["transfer"])
- if(mixer.reagents.trans_id_to(src, href_list["transfer"], portion) == 0)
+ if(mixer.reagents.trans_id_to(src, text2path(href_list["transfer"]), portion) == 0)
to_chat(usr, "[src] is full!")
updateDialog()
@@ -152,5 +159,5 @@
qdel(src)
#undef STORAGE_CAPACITY
-#undef LIQUID_CAPACIY
+#undef LIQUID_CAPACITY
#undef MIXER_CAPACITY
diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
index db6a1e9560..b780af6b8a 100644
--- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
+++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm
@@ -459,8 +459,18 @@
/datum/action/innate/slime_change/proc/change_form()
var/mob/living/carbon/human/H = owner
- var/select_alteration = input(owner, "Select what part of your form to alter", "Form Alteration", "cancel") in list("Hair Style", "Genitals", "Tail", "Snout", "Markings", "Ears", "Taur body", "Penis", "Vagina", "Penis Length", "Breast Size", "Breast Shape", "Cancel")
- if(select_alteration == "Hair Style")
+ var/select_alteration = input(owner, "Select what part of your form to alter", "Form Alteration", "cancel") in list("Body Color","Hair Style", "Genitals", "Tail", "Snout", "Markings", "Ears", "Taur body", "Penis", "Vagina", "Penis Length", "Breast Size", "Breast Shape", "Cancel")
+
+ if(select_alteration == "Body Color")
+ var/new_color = input(owner, "Choose your skin color:", "Race change","#"+H.dna.features["mcolor"]) as color|null
+ if(new_color)
+ var/temp_hsv = RGBtoHSV(new_color)
+ if(ReadHSV(temp_hsv)[3] >= ReadHSV("#7F7F7F")[3]) // mutantcolors must be bright
+ H.dna.features["mcolor"] = sanitize_hexcolor(new_color)
+ H.update_body()
+ else
+ to_chat(H, "Invalid color. Your color is not bright enough.")
+ else if(select_alteration == "Hair Style")
if(H.gender == MALE)
var/new_style = input(owner, "Select a facial hair style", "Hair Alterations") as null|anything in GLOB.facial_hair_styles_list
if(new_style)
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index 59dfcd3003..bf66556399 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -359,7 +359,8 @@
"Sleek" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "sleekmed"),
"Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinamed"),
"Eyebot" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "eyebotmed"),
- "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymed")
+ "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymed"),
+ "Zoomba" = image(icon = 'icons/mob/robots.dmi', icon_state = "zoomba_med")
)
var/list/L = list("Medihound" = "medihound", "Medihound Dark" = "medihounddark", "Vale" = "valemed")
for(var/a in L)
@@ -375,6 +376,8 @@
switch(med_borg_icon)
if("Default")
cyborg_base_icon = "medical"
+ if("Zoomba")
+ cyborg_base_icon = "zoomba_med"
if("Droid")
cyborg_base_icon = "medical"
cyborg_icon_override = 'modular_citadel/icons/mob/robots.dmi'
@@ -476,7 +479,8 @@
"Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "caneng"),
"Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinaeng"),
"Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "spidereng"),
- "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyeng")
+ "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyeng"),
+ "Zoomba" = image(icon = 'icons/mob/robots.dmi', icon_state = "zoomba_engi")
)
var/list/L = list("Pup Dozer" = "pupdozer", "Vale" = "valeeng")
for(var/a in L)
@@ -492,6 +496,8 @@
switch(engi_borg_icon)
if("Default")
cyborg_base_icon = "engineer"
+ if("Zoomba")
+ cyborg_base_icon = "zoomba_engi"
if("Default - Treads")
cyborg_base_icon = "engi-tread"
special_light_key = "engineer"
@@ -572,7 +578,8 @@
"Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "cansec"),
"Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinasec"),
"Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "spidersec"),
- "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavysec")
+ "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavysec"),
+ "Zoomba" = image(icon = 'icons/mob/robots.dmi', icon_state = "zoomba_sec")
)
var/list/L = list("K9" = "k9", "Vale" = "valesec", "K9 Dark" = "k9dark")
for(var/a in L)
@@ -588,6 +595,8 @@
switch(sec_borg_icon)
if("Default")
cyborg_base_icon = "sec"
+ if("Zoomba")
+ cyborg_base_icon = "zoomba_sec"
if("Default - Treads")
cyborg_base_icon = "sec-tread"
special_light_key = "sec"
@@ -827,6 +836,7 @@
"(Janitor) Sleek" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "sleekjan"),
"(Janitor) Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "canjan"),
"(Janitor) Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyjan"),
+ "Zoomba" = image(icon = 'icons/mob/robots.dmi', icon_state = "zoomba_jani")
)
var/list/L = list("(Service) DarkK9" = "k50", "(Service) Vale" = "valeserv", "(Service) ValeDark" = "valeservdark",
"(Janitor) Scrubpuppy" = "scrubpup")
@@ -841,6 +851,8 @@
service_icons = sortList(service_icons)
var/service_robot_icon = show_radial_menu(R, R , service_icons, custom_check = CALLBACK(src, .proc/check_menu, R), radius = 42, require_near = TRUE)
switch(service_robot_icon)
+ if("Zoomba")
+ cyborg_base_icon = "zoomba_jani"
if("(Service) Waitress")
cyborg_base_icon = "service_f"
special_light_key = "service"
@@ -944,6 +956,7 @@
"Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinamin"),
"Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "canmin"),
"Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymin"),
+ "Zoomba" = image(icon = 'icons/mob/robots.dmi', icon_state = "zoomba_miner")
)
var/list/L = list("Blade" = "blade", "Vale" = "valemine")
for(var/a in L)
@@ -987,6 +1000,8 @@
cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi'
sleeper_overlay = "valeminesleeper"
dogborg = TRUE
+ if("Zoomba")
+ cyborg_base_icon = "zoomba_miner"
else
return FALSE
return ..()
diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index 97f3dad3f0..39eccf9ad4 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -98,6 +98,10 @@
hud_possible = list(DIAG_STAT_HUD, DIAG_BOT_HUD, DIAG_HUD, DIAG_PATH_HUD = HUD_LIST_LIST) //Diagnostic HUD views
+ var/commissioned = FALSE // Will other (noncommissioned) bots salute this bot?
+ var/can_salute = TRUE
+ var/salute_delay = 60 SECONDS
+
/mob/living/simple_animal/bot/proc/get_mode()
if(client) //Player bots do not have modes, thus the override. Also an easy way for PDA users/AI to know when a bot is a player.
if(paicard)
@@ -251,6 +255,14 @@
if(!on || client)
return
+ if(!commissioned && can_salute)
+ for(var/mob/living/simple_animal/bot/B in get_hearers_in_view(5, get_turf(src)))
+ if(B.commissioned)
+ visible_message("[src] performs an elaborate salute for [B]!")
+ can_salute = FALSE
+ addtimer(VARSET_CALLBACK(src, can_salute, TRUE), salute_delay)
+ break
+
switch(mode) //High-priority overrides are processed first. Bots can do nothing else while under direct command.
if(BOT_RESPONDING) //Called by the AI.
call_mode()
diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm
index 73099d8d9f..f6aad5c03f 100644
--- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm
@@ -14,7 +14,7 @@
model = "Cleanbot"
bot_core_type = /obj/machinery/bot_core/cleanbot
window_id = "autoclean"
- window_name = "Automatic Station Cleaner v1.2"
+ window_name = "Automatic Station Cleaner v1.3"
pass_flags = PASSMOB
path_image_color = "#993299"
weather_immunities = list("lava","ash")
@@ -36,8 +36,62 @@
var/next_dest
var/next_dest_loc
+ var/obj/item/weapon
+ var/weapon_orig_force = 0
+ var/chosen_name
+
+ var/list/stolen_valor
+
+ var/static/list/officers = list("Captain", "Head of Personnel", "Head of Security")
+ var/static/list/command = list("Captain" = "Cpt.","Head of Personnel" = "Lt.")
+ var/static/list/security = list("Head of Security" = "Maj.", "Warden" = "Sgt.", "Detective" = "Det.", "Security Officer" = "Officer")
+ var/static/list/engineering = list("Chief Engineer" = "Chief Engineer", "Station Engineer" = "Engineer", "Atmospherics Technician" = "Technician")
+ var/static/list/medical = list("Chief Medical Officer" = "C.M.O.", "Medical Doctor" = "M.D.", "Chemist" = "Pharm.D.")
+ var/static/list/research = list("Research Director" = "Ph.D.", "Roboticist" = "M.S.", "Scientist" = "B.S.")
+ var/static/list/legal = list("Lawyer" = "Esq.")
+
+ var/list/prefixes
+ var/list/suffixes
+
+/mob/living/simple_animal/bot/cleanbot/proc/deputize(obj/item/W, mob/user)
+ if(in_range(src, user))
+ to_chat(user, "You attach \the [W] to \the [src].")
+ user.transferItemToLoc(W, src)
+ weapon = W
+ weapon_orig_force = weapon.force
+ if(!emagged)
+ weapon.force = weapon.force / 2
+ add_overlay(image(icon=weapon.lefthand_file,icon_state=weapon.item_state))
+
+/mob/living/simple_animal/bot/cleanbot/proc/update_titles()
+ var/working_title = ""
+
+ for(var/pref in prefixes)
+ for(var/title in pref)
+ if(title in stolen_valor)
+ working_title += pref[title] + " "
+ if(title in officers)
+ commissioned = TRUE
+ break
+
+ working_title += chosen_name
+
+ for(var/suf in suffixes)
+ for(var/title in suf)
+ if(title in stolen_valor)
+ working_title += " " + suf[title]
+ break
+
+ name = working_title
+
+/mob/living/simple_animal/bot/cleanbot/examine(mob/user)
+ . = ..()
+ if(weapon)
+ . += " Is that \a [weapon] taped to it...?"
+
/mob/living/simple_animal/bot/cleanbot/Initialize()
. = ..()
+ chosen_name = name
get_targets()
icon_state = "cleanbot[on]"
@@ -45,6 +99,18 @@
access_card.access += J.get_access()
prev_access = access_card.access
+ stolen_valor = list()
+
+ prefixes = list(command, security, engineering)
+ suffixes = list(research, medical, legal)
+
+/mob/living/simple_animal/bot/cleanbot/Destroy()
+ if(weapon)
+ var/atom/Tsec = drop_location()
+ weapon.force = weapon_orig_force
+ drop_part(weapon, Tsec)
+ return ..()
+
/mob/living/simple_animal/bot/cleanbot/turn_on()
..()
icon_state = "cleanbot[on]"
@@ -57,6 +123,8 @@
/mob/living/simple_animal/bot/cleanbot/bot_reset()
..()
+ if(weapon && (emagged == 2))
+ weapon.force = weapon_orig_force
ignore_list = list() //Allows the bot to clean targets it previously ignored due to being unreachable.
target = null
oldloc = null
@@ -66,6 +134,22 @@
text_dehack = "[name]'s software has been reset!"
text_dehack_fail = "[name] does not seem to respond to your repair code!"
+/mob/living/simple_animal/bot/cleanbot/Crossed(atom/movable/AM)
+ . = ..()
+
+ zone_selected = pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
+ if(weapon && has_gravity() && ismob(AM))
+ var/mob/living/carbon/C = AM
+ if(!istype(C))
+ return
+
+ if(!(C.job in stolen_valor))
+ stolen_valor += C.job
+ update_titles()
+
+ weapon.attack(C, src)
+ C.Knockdown(20)
+
/mob/living/simple_animal/bot/cleanbot/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/card/id)||istype(W, /obj/item/pda))
if(bot_core.allowed(user) && !open && !emagged)
@@ -79,6 +163,11 @@
else
to_chat(user, "The [src] doesn't seem to respect your authority.")
+ else if(istype(W, /obj/item/kitchen/knife) && user.a_intent != INTENT_HARM)
+ to_chat(user, "You start attaching \the [W] to \the [src]...")
+ if(do_after(user, 25, target = src))
+ deputize(W, user)
+
else if(istype(W, /obj/item/mop/advanced))
if(bot_core.allowed(user) && open && !CHECK_BITFIELD(upgrades,UPGRADE_CLEANER_ADVANCED_MOP))
to_chat(user, "You replace \the [src] old mop with a new better one!")
@@ -87,10 +176,10 @@
window_name = "Automatic Station Cleaner v2.1 BETA" //New!
qdel(W)
if(!open)
- to_chat(user, "The [src] access pannle is not open!")
+ to_chat(user, "The [src] access panel is not open!")
return
if(!bot_core.allowed(user))
- to_chat(user, "The [src] access pannel locked off to you!")
+ to_chat(user, "The [src] access panel locked off to you!")
return
else
to_chat(user, "The [src] already has this mop!")
@@ -116,6 +205,8 @@
/mob/living/simple_animal/bot/cleanbot/emag_act(mob/user)
. = ..()
if(emagged == 2)
+ if(weapon)
+ weapon.force = weapon_orig_force
if(user)
to_chat(user, "[src] buzzes and beeps.")
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 3705f7902c..d2643eeeb7 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -45,6 +45,8 @@
var/pixels_range_leftover = 0
/// "leftover" tick pixels and stuff yeah, so we don't round off things and introducing tracing inaccuracy.
var/pixels_tick_leftover = 0
+ /// Used to detect jumps in the middle of a pixel_move. Yes, this is ugly as sin code-wise but it works.
+ var/pixel_move_interrupted = FALSE
/// Pixels moved per second.
var/pixels_per_second = TILES_TO_PIXELS(12.5)
@@ -257,27 +259,54 @@
else
return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume
-/obj/item/projectile/proc/on_ricochet(atom/A)
- return
-
/obj/item/projectile/proc/store_hitscan_collision(datum/point/pcache)
beam_segments[beam_index] = pcache
beam_index = pcache
beam_segments[beam_index] = null
-/obj/item/projectile/Bump(atom/A)
- var/turf/T = get_turf(A)
- if(trajectory && check_ricochet(A) && check_ricochet_flag(A) && ricochets < ricochets_max)
- var/datum/point/pcache = trajectory.copy_to()
- ricochets++
- if(A.handle_ricochet(src))
- on_ricochet(A)
- ignore_source_check = TRUE
- decayedRange = max(0, decayedRange - reflect_range_decrease)
- range = decayedRange
- if(hitscan)
- store_hitscan_collision(pcache)
+/**
+ * Determines if we should ricochet off of something.
+ * By default, asks the thing if we should ricochet off it, but because we're called first, we get final say.
+ * Returns TRUE or FALSE.
+ */
+/obj/item/projectile/proc/check_ricochet(atom/A)
+ if(ricochets > ricochets_max) //safety thing, we don't care about what the other thing says about this.
+ return FALSE
+ var/them = A.check_projectile_ricochet(src)
+ switch(them)
+ if(PROJECTILE_RICOCHET_PREVENT)
+ return FALSE
+ if(PROJECTILE_RICOCHET_FORCE)
return TRUE
+ if(PROJECTILE_RICOCHET_NO)
+ return FALSE
+ if(PROJECTILE_RICOCHET_YES)
+ return prob(ricochet_chance)
+ else
+ CRASH("Invalid return value for projectile ricochet check from [A].")
+
+/**
+ * Handles ricocheting off of something.
+ * By default, also asks the thing to handle it, but because we're called first, we get final say.
+ */
+/obj/item/projectile/proc/handle_ricochet(atom/A)
+ ricochets++
+ ignore_source_check = TRUE
+ decayedRange = max(0, decayedRange - reflect_range_decrease)
+ pixel_move_interrupted = TRUE
+ range = decayedRange
+ return A.handle_projectile_ricochet(src)
+
+/obj/item/projectile/Bump(atom/A)
+ if(!trajectory)
+ return
+ var/turf/T = get_turf(A)
+ if(check_ricochet(A))
+ var/datum/point/pcache = trajectory.copy_to()
+ if(hitscan)
+ store_hitscan_collision(pcache)
+ handle_ricochet(A)
+ return TRUE
var/distance = get_dist(T, starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations.
if(def_zone && check_zone(def_zone) != BODY_ZONE_CHEST)
@@ -352,16 +381,6 @@
return T
//Returns null if nothing at all was found.
-/obj/item/projectile/proc/check_ricochet()
- if(prob(ricochet_chance))
- return TRUE
- return FALSE
-
-/obj/item/projectile/proc/check_ricochet_flag(atom/A)
- if(A.flags_1 & CHECK_RICOCHET_1)
- return TRUE
- return FALSE
-
/// one move is a tile.
/obj/item/projectile/proc/return_predicted_turf_after_moves(moves, forced_angle) //I say predicted because there's no telling that the projectile won't change direction/location in flight.
if(!trajectory && isnull(forced_angle) && isnull(Angle))
@@ -439,6 +458,7 @@
/obj/item/projectile/proc/setAngle(new_angle, hitscan_store_segment = TRUE) //wrapper for overrides.
Angle = new_angle
+ pixel_move_interrupted = TRUE
if(!nondirectional_sprite)
var/matrix/M = new
M.Turn(Angle)
@@ -465,6 +485,7 @@
trajectory.initialize_location(target.x, target.y, target.z, 0, 0)
if(hitscan)
record_hitscan_start(RETURN_PRECISE_POINT(src))
+ pixel_move_interrupted = TRUE
if(zc)
after_z_change(old, target)
@@ -513,7 +534,7 @@
* The proc to make the projectile go, using a simulated pixel movement line trace.
* Note: deciseconds_equivalent is currently only used for homing, times is the number of times to move pixel_increment_amount.
* Trajectory multiplier directly modifies the factor of pixel_increment_amount to go per time.
- * It's complicated, so probably just don'ot mess with this unless you know what you're doing.
+ * It's complicated, so probably just don't mess with this unless you know what you're doing.
*/
/obj/item/projectile/proc/pixel_move(times, hitscanning = FALSE, deciseconds_equivalent = world.tick_lag, trajectory_multiplier = 1, allow_animation = TRUE)
if(!loc || !trajectory)
@@ -523,6 +544,7 @@
M.Turn(Angle)
transform = M
var/forcemoved = FALSE
+ pixel_move_interrupted = FALSE // reset that
var/turf/oldloc = loc
var/old_px = pixel_x
var/old_py = pixel_y
@@ -558,7 +580,9 @@
if(!--safety)
CRASH("[type] took too long (allowed: [CEILING(pixel_increment_amount/world.icon_size,1)*2] moves) to get to its location.")
step_towards(src, T)
- if(QDELETED(src))
+ if(QDELETED(src) || pixel_move_interrupted) // this doesn't take into account with pixel_move_interrupted the portion of the move cut off by any forcemoves, but we're opting to ignore that for now
+ // the reason is the entire point of moving to pixel speed rather than tile speed is smoothness, which will be crucial when pixel movement is done in the future
+ // reverting back to tile is more or less the only way of fixing this issue.
return
pixels_range_leftover += pixel_increment_amount
if(pixels_range_leftover > world.icon_size)
diff --git a/code/modules/research/bepis.dm b/code/modules/research/bepis.dm
index c6ce45f160..20ca7987d5 100644
--- a/code/modules/research/bepis.dm
+++ b/code/modules/research/bepis.dm
@@ -33,8 +33,8 @@
var/inaccuracy_percentage = 1.5
var/positive_cash_offset = 0
var/negative_cash_offset = 0
- var/minor_rewards = list(/obj/item/stack/circuit_stack/full, //To add a new minor reward, add it here.
- /obj/item/airlock_painter/decal,
+ var/minor_rewards = list(/obj/item/stack/circuit_stack/full, //To add a new minor reward, add it here.
+ /obj/item/flashlight/flashdark,
/obj/item/pen/survival,
/obj/item/circuitboard/machine/sleeper/party,
/obj/item/toy/sprayoncan)
diff --git a/code/modules/surgery/advanced/lobotomy.dm b/code/modules/surgery/advanced/lobotomy.dm
index 2b2d11e54f..4a52e446bc 100644
--- a/code/modules/surgery/advanced/lobotomy.dm
+++ b/code/modules/surgery/advanced/lobotomy.dm
@@ -1,6 +1,6 @@
/datum/surgery/advanced/lobotomy
name = "Lobotomy"
- desc = "An invasive surgical procedure which guarantees removal of almost all brain traumas, but might cause another permanent trauma in return."
+ desc = "An invasive surgical procedure which guarantees removal of almost all brain traumas, at the cost of severe, albeit repairable, brain damage."
steps = list(
/datum/surgery_step/incise,
/datum/surgery_step/retract_skin,
@@ -43,11 +43,14 @@
target.mind.remove_antag_datum(/datum/antagonist/brainwashed)
switch(rand(1,6))//Now let's see what hopefully-not-important part of the brain we cut off
if(1)
- target.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_MAGIC)
+ target.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_SURGERY)
if(2)
- target.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_MAGIC)
+ target.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_SURGERY)
if(3)
- target.gain_trauma_type(BRAIN_TRAUMA_SPECIAL, TRAUMA_RESILIENCE_MAGIC)
+ target.gain_trauma_type(BRAIN_TRAUMA_SPECIAL, TRAUMA_RESILIENCE_SURGERY)
+ // you're cutting off a part of the brain.w
+ var/obj/item/organ/brain/B = target.getorganslot(ORGAN_SLOT_BRAIN)
+ B.applyOrganDamage(50, 100)
return TRUE
/datum/surgery_step/lobotomize/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery)
@@ -59,11 +62,11 @@
B.applyOrganDamage(80)
switch(rand(1,3))
if(1)
- target.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_MAGIC)
+ target.gain_trauma_type(BRAIN_TRAUMA_MILD, TRAUMA_RESILIENCE_LOBOTOMY)
if(2)
- target.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_MAGIC)
+ target.gain_trauma_type(BRAIN_TRAUMA_SEVERE, TRAUMA_RESILIENCE_LOBOTOMY)
if(3)
- target.gain_trauma_type(BRAIN_TRAUMA_SPECIAL, TRAUMA_RESILIENCE_MAGIC)
+ target.gain_trauma_type(BRAIN_TRAUMA_SPECIAL, TRAUMA_RESILIENCE_LOBOTOMY)
else
user.visible_message("[user] suddenly notices that the brain [user.p_they()] [user.p_were()] working on is not there anymore.", "You suddenly notice that the brain you were working on is not there anymore.")
return FALSE
diff --git a/code/modules/vending/assist.dm b/code/modules/vending/assist.dm
index ddd7736522..92e40bc3a8 100644
--- a/code/modules/vending/assist.dm
+++ b/code/modules/vending/assist.dm
@@ -14,7 +14,8 @@
/obj/item/stock_parts/cell/upgraded = 2)
premium = list(/obj/item/stock_parts/cell/upgraded/plus = 2,
/obj/item/flashlight/lantern = 2,
- /obj/item/beacon = 2)
+ /obj/item/beacon = 2,
+ /obj/item/airlock_painter/decal = 5)
product_ads = "Only the finest!;Have some tools.;The most robust equipment.;The finest gear in space!"
armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50)
refill_canister = /obj/item/vending_refill/assist
@@ -24,4 +25,4 @@
payment_department = NO_FREEBIES
/obj/item/vending_refill/assist
- icon_state = "refill_engi"
\ No newline at end of file
+ icon_state = "refill_engi"
diff --git a/code/modules/vending/engivend.dm b/code/modules/vending/engivend.dm
index a0248d2d12..50e0fb9cde 100644
--- a/code/modules/vending/engivend.dm
+++ b/code/modules/vending/engivend.dm
@@ -15,6 +15,7 @@
/obj/item/electronics/airalarm = 10,
/obj/item/electronics/firealarm = 10,
/obj/item/electronics/firelock = 10,
+ /obj/item/airlock_painter/decal = 5,
/obj/item/rcd_ammo = 3
)
contraband = list(/obj/item/stock_parts/cell/potato = 3,
@@ -35,4 +36,4 @@
cost_multiplier_per_dept = list(ACCOUNT_ENG = 0)
/obj/item/vending_refill/engivend
- icon_state = "refill_engi"
\ No newline at end of file
+ icon_state = "refill_engi"
diff --git a/code/modules/vending/youtool.dm b/code/modules/vending/youtool.dm
index c59020d3a4..c936d9c32c 100644
--- a/code/modules/vending/youtool.dm
+++ b/code/modules/vending/youtool.dm
@@ -18,7 +18,8 @@
/obj/item/clothing/gloves/color/fyellow = 4,
/obj/item/multitool = 2)
premium = list(/obj/item/clothing/gloves/color/yellow = 2,
- /obj/item/weldingtool/hugetank = 2)
+ /obj/item/weldingtool/hugetank = 2,
+ /obj/item/airlock_painter/decal = 3)
armor = list("melee" = 100, "bullet" = 100, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 70)
refill_canister = /obj/item/vending_refill/tool
resistance_flags = FIRE_PROOF
@@ -28,4 +29,4 @@
cost_multiplier_per_dept = list(ACCOUNT_ENG = 0)
/obj/item/vending_refill/tool
- icon_state = "refill_engi"
\ No newline at end of file
+ icon_state = "refill_engi"
diff --git a/config/plushies/defines.txt b/config/plushies/defines.txt
index e69de29bb2..e7a92f6ac2 100644
--- a/config/plushies/defines.txt
+++ b/config/plushies/defines.txt
@@ -0,0 +1,2 @@
+# EXAMPLE
+# SNOWFLAKE_PLUSHIES example {"name":"example","desc":"thanks, coders.","icon_state":"","attack_verb":["thumped","whomped","bumped"],"squeak_override":{"sound/weapons/magout.ogg":1}}
diff --git a/config/plushies/plushie_config.txt b/config/plushies/plushie_config.txt
deleted file mode 100644
index 7cd1d88f3e..0000000000
--- a/config/plushies/plushie_config.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-# EXAMPLE
-# SNOWFLAKE_PLUSHIES example {"name":"example","desc":"thanks, coders.","icon_state":"","attack_verb":["thumped","whomped","bumped"],"squeak_override":{"sound/weapons/magout.ogg":1}}
diff --git a/html/changelog.html b/html/changelog.html
index b963b11b47..ce00531001 100644
--- a/html/changelog.html
+++ b/html/changelog.html
@@ -50,6 +50,22 @@
-->
+
15 June 2020
+
Anturk, kevinz000 updated:
+
+ - VV now properly allows access to datums in associative lists
+ - SDQL2 printout has been upgraded for the 5th time.
+
+
kevinz000 updated:
+
+ - plushies now work. credits to timothytea.
+ - You can now tape knives to cleanbots
+
+
kevinz000 (port from VOREStation) updated:
+
+ - Ported zoomba skins for cyborgs.
+
+
14 June 2020
Bhijn updated:
diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml
index e3332985d5..a25fb6595c 100644
--- a/html/changelogs/.all_changelog.yml
+++ b/html/changelogs/.all_changelog.yml
@@ -25950,3 +25950,12 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py.
- rscadd: Parrying has been added, keybound to F. Timing-based counterattacks, effect
heavily dependent on the item, WIP.
- balance: Disks are now smaller.
+2020-06-15:
+ Anturk, kevinz000:
+ - bugfix: VV now properly allows access to datums in associative lists
+ - rscadd: SDQL2 printout has been upgraded for the 5th time.
+ kevinz000:
+ - bugfix: plushies now work. credits to timothytea.
+ - rscadd: You can now tape knives to cleanbots
+ kevinz000 (port from VOREStation):
+ - imageadd: Ported zoomba skins for cyborgs.
diff --git a/html/changelogs/AutoChangeLog-pr-12375.yml b/html/changelogs/AutoChangeLog-pr-12375.yml
new file mode 100644
index 0000000000..9d9b79a3a6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-12375.yml
@@ -0,0 +1,4 @@
+author: "Ghommie"
+delete-after: True
+changes:
+ - bugfix: "You can't blink nor use LOOC/AOOC as a petrified statue anymore."
diff --git a/html/changelogs/AutoChangeLog-pr-12393.yml b/html/changelogs/AutoChangeLog-pr-12393.yml
new file mode 100644
index 0000000000..28136bed22
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-12393.yml
@@ -0,0 +1,4 @@
+author: "Trilbyspaceclone"
+delete-after: True
+changes:
+ - tweak: "BEPIS decal painter has been moved to venders, replacing it being the flashdark"
diff --git a/html/changelogs/AutoChangeLog-pr-12509.yml b/html/changelogs/AutoChangeLog-pr-12509.yml
new file mode 100644
index 0000000000..a5e13bada0
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-12509.yml
@@ -0,0 +1,4 @@
+author: "timothyteakettle"
+delete-after: True
+changes:
+ - rscadd: "slimes can now change their color using alter form"
diff --git a/html/changelogs/AutoChangeLog-pr-12518.yml b/html/changelogs/AutoChangeLog-pr-12518.yml
new file mode 100644
index 0000000000..91580faf20
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-12518.yml
@@ -0,0 +1,5 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - refactor: "projectile ricochets now use a less hilariously terrible way of being handled and should be easier to w"
+ - bugfix: "projectile runtime/ricocheting"
diff --git a/html/changelogs/AutoChangeLog-pr-12520.yml b/html/changelogs/AutoChangeLog-pr-12520.yml
new file mode 100644
index 0000000000..22890dc4fd
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-12520.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - balance: "Lobotomy no longer has a 50% chance of giving you a nigh-unremovable trauma, but does 50 brain damage even on success. On failure, it will give you a lobotomy-class trauma."
diff --git a/html/changelogs/AutoChangeLog-pr-12521.yml b/html/changelogs/AutoChangeLog-pr-12521.yml
new file mode 100644
index 0000000000..1977538e26
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-12521.yml
@@ -0,0 +1,4 @@
+author: "kevinz000"
+delete-after: True
+changes:
+ - balance: "spinesnapping from tackling now only gives a lobotomy class trauma instead of magic."
diff --git a/html/changelogs/AutoChangeLog-pr-12523.yml b/html/changelogs/AutoChangeLog-pr-12523.yml
new file mode 100644
index 0000000000..58f1c7fe28
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-12523.yml
@@ -0,0 +1,4 @@
+author: "timothyteakettle"
+delete-after: True
+changes:
+ - bugfix: "Food carts now function as intended, allowing the pouring and mixing of drinks."
diff --git a/icons/mob/robots.dmi b/icons/mob/robots.dmi
index 082bfb3c3e..9bb41bf527 100644
Binary files a/icons/mob/robots.dmi and b/icons/mob/robots.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 609a5b08e3..6cf3b96855 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -83,6 +83,7 @@
#include "code\__DEFINES\preferences.dm"
#include "code\__DEFINES\procpath.dm"
#include "code\__DEFINES\profile.dm"
+#include "code\__DEFINES\projectiles.dm"
#include "code\__DEFINES\qdel.dm"
#include "code\__DEFINES\radiation.dm"
#include "code\__DEFINES\radio.dm"