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:

    + +

    kevinz000 updated:

    + +

    kevinz000 (port from VOREStation) updated:

    + +

    14 June 2020

    Bhijn updated: