diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm
index 68e9ee8ec0..82bfb49e38 100644
--- a/_maps/map_files/BoxStation/BoxStation.dmm
+++ b/_maps/map_files/BoxStation/BoxStation.dmm
@@ -53,6 +53,16 @@
/obj/structure/lattice/catwalk,
/turf/open/space,
/area/space/nearstation)
+"aah" = (
+/obj/structure/disposalpipe/segment{
+ dir = 4
+ },
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden,
+/turf/open/floor/plasteel,
+/area/security/main)
"aai" = (
/turf/closed/wall/r_wall,
/area/security/prison)
@@ -1541,6 +1551,18 @@
/obj/structure/lattice/catwalk,
/turf/open/space,
/area/solar/port/fore)
+"adB" = (
+/obj/structure/cable{
+ icon_state = "4-8"
+ },
+/obj/structure/disposalpipe/segment{
+ dir = 4
+ },
+/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{
+ dir = 1
+ },
+/turf/open/floor/plasteel,
+/area/security/main)
"adC" = (
/obj/structure/table,
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
@@ -2665,6 +2687,20 @@
/obj/effect/spawner/structure/window/reinforced,
/turf/open/floor/plating,
/area/security/brig)
+"afo" = (
+/obj/structure/noticeboard{
+ dir = 1;
+ pixel_y = -27
+ },
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/obj/effect/turf_decal/tile/red,
+/obj/effect/turf_decal/tile/red{
+ dir = 8
+ },
+/turf/open/floor/plasteel,
+/area/security/main)
"afp" = (
/obj/docking_port/stationary{
dir = 4;
@@ -3143,6 +3179,20 @@
},
/turf/open/floor/plasteel,
/area/security/prison)
+"agl" = (
+/obj/machinery/atmospherics/pipe/simple/supply/hidden{
+ dir = 4
+ },
+/obj/effect/turf_decal/tile/red,
+/obj/effect/turf_decal/tile/red{
+ dir = 8
+ },
+/obj/structure/chair/sofa/right{
+ dir = 1
+ },
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/turf/open/floor/plasteel,
+/area/security/main)
"agm" = (
/obj/machinery/light{
dir = 8
@@ -3321,6 +3371,15 @@
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/turf/open/floor/plasteel,
/area/security/main)
+"agD" = (
+/obj/effect/turf_decal/tile/red{
+ dir = 4
+ },
+/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
+ dir = 1
+ },
+/turf/open/floor/plasteel,
+/area/security/brig)
"agE" = (
/obj/structure/table,
/obj/item/storage/fancy/donut_box{
@@ -3332,6 +3391,13 @@
},
/turf/open/floor/plasteel,
/area/security/main)
+"agF" = (
+/obj/machinery/atmospherics/pipe/simple/supply/hidden,
+/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
+ dir = 4
+ },
+/turf/open/floor/plasteel,
+/area/security/brig)
"agG" = (
/obj/machinery/airalarm{
dir = 8;
@@ -3436,6 +3502,10 @@
},
/turf/open/floor/plasteel,
/area/security/brig)
+"agN" = (
+/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden,
+/turf/open/floor/plasteel,
+/area/security/courtroom)
"agO" = (
/obj/machinery/door/airlock/security/glass{
name = "Brig Infirmary";
@@ -3467,6 +3537,19 @@
},
/turf/open/floor/plasteel,
/area/security/brig)
+"agQ" = (
+/obj/machinery/light_switch{
+ pixel_y = 28
+ },
+/obj/structure/closet/secure_closet/courtroom,
+/obj/effect/decal/cleanable/cobweb,
+/obj/structure/sign/warning/securearea{
+ pixel_x = -32
+ },
+/obj/item/gavelhammer,
+/obj/machinery/atmospherics/pipe/simple/scrubbers,
+/turf/open/floor/plasteel,
+/area/security/courtroom)
"agR" = (
/obj/effect/turf_decal/loading_area{
dir = 4;
@@ -3888,16 +3971,6 @@
/obj/effect/turf_decal/bot,
/turf/open/floor/plasteel/dark,
/area/ai_monitored/security/armory)
-"ahC" = (
-/obj/structure/disposalpipe/segment{
- dir = 4
- },
-/obj/structure/cable{
- icon_state = "4-8"
- },
-/obj/machinery/atmospherics/pipe/manifold4w/scrubbers/hidden,
-/turf/open/floor/plasteel,
-/area/security/main)
"ahD" = (
/obj/structure/chair,
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
@@ -4202,21 +4275,6 @@
/obj/machinery/atmospherics/components/unary/vent_scrubber/on,
/turf/open/floor/plasteel/showroomfloor,
/area/security/brig)
-"aia" = (
-/obj/structure/noticeboard{
- dir = 1;
- pixel_y = -27
- },
-/obj/machinery/atmospherics/pipe/simple/supply/hidden{
- dir = 4
- },
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
-/obj/effect/turf_decal/tile/red,
-/obj/effect/turf_decal/tile/red{
- dir = 8
- },
-/turf/open/floor/plasteel,
-/area/security/main)
"aib" = (
/obj/structure/cable{
icon_state = "2-8"
@@ -4556,19 +4614,6 @@
},
/turf/open/floor/plating,
/area/maintenance/fore/secondary)
-"aiB" = (
-/obj/machinery/atmospherics/pipe/simple/supply/hidden{
- dir = 4
- },
-/obj/effect/turf_decal/tile/red,
-/obj/effect/turf_decal/tile/red{
- dir = 8
- },
-/obj/structure/chair/sofa/right{
- dir = 1
- },
-/turf/open/floor/plasteel,
-/area/security/main)
"aiC" = (
/obj/structure/cable{
icon_state = "1-4"
@@ -4729,10 +4774,6 @@
/obj/effect/turf_decal/box,
/turf/open/floor/plasteel/dark,
/area/security/brig)
-"aiP" = (
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
-/turf/closed/wall/r_wall,
-/area/security/main)
"aiQ" = (
/obj/effect/spawner/structure/window/reinforced,
/obj/structure/cable{
@@ -4847,18 +4888,6 @@
/obj/effect/turf_decal/bot,
/turf/open/floor/plasteel/dark,
/area/security/brig)
-"ajh" = (
-/obj/machinery/light_switch{
- pixel_y = 28
- },
-/obj/structure/closet/secure_closet/courtroom,
-/obj/effect/decal/cleanable/cobweb,
-/obj/structure/sign/warning/securearea{
- pixel_x = -32
- },
-/obj/item/gavelhammer,
-/turf/open/floor/plasteel,
-/area/security/courtroom)
"aji" = (
/obj/structure/chair{
name = "Judge"
@@ -5126,12 +5155,6 @@
/obj/effect/turf_decal/bot,
/turf/open/floor/plasteel/dark,
/area/security/brig)
-"ajM" = (
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
- dir = 4
- },
-/turf/open/floor/plasteel,
-/area/security/courtroom)
"ajN" = (
/obj/structure/cable{
icon_state = "4-8"
@@ -5286,16 +5309,6 @@
},
/turf/open/floor/plating,
/area/construction/mining/aux_base)
-"aka" = (
-/obj/effect/turf_decal/tile/red{
- dir = 4
- },
-/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
- dir = 1
- },
-/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
-/turf/open/floor/plasteel,
-/area/security/brig)
"akb" = (
/obj/machinery/light{
dir = 8
@@ -5609,11 +5622,6 @@
/obj/effect/turf_decal/tile/red,
/turf/open/floor/plasteel,
/area/security/brig)
-"akE" = (
-/obj/machinery/atmospherics/pipe/simple/supply/hidden,
-/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden,
-/turf/open/floor/plasteel,
-/area/security/brig)
"akF" = (
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/obj/effect/turf_decal/tile/red,
@@ -91862,11 +91870,11 @@ adl
aet
agy
aha
-ahC
-aia
-aiP
-aka
-akE
+aah
+afo
+adR
+agD
+agF
gyr
alq
amd
@@ -92633,11 +92641,11 @@ aff
aeU
ajA
akd
-ahF
-aiB
-abp
-ajh
-ajM
+adB
+agl
+uHp
+agQ
+agN
akw
alb
alG
diff --git a/code/__DEFINES/citadel_defines.dm b/code/__DEFINES/citadel_defines.dm
index 01355173d2..174b44c6fc 100644
--- a/code/__DEFINES/citadel_defines.dm
+++ b/code/__DEFINES/citadel_defines.dm
@@ -2,18 +2,6 @@
//Be sure to update the min/max of these if you do change them.
//Measurements are in imperial units. Inches, feet, yards, miles. Tsp, tbsp, cups, quarts, gallons, etc
-//HUD stuff
-#define ui_arousal "EAST-1:28,CENTER-4:8"//Below the health doll
-#define ui_stamina "EAST-1:28,CENTER:17" // replacing internals button
-#define ui_overridden_resist "EAST-3:24,SOUTH+1:7"
-#define ui_combat_toggle "EAST-4:22,SOUTH:5"
-
-//1:1 HUD layout stuff
-#define ui_boxcraft "EAST-4:22,SOUTH+1:6"
-#define ui_boxarea "EAST-4:6,SOUTH+1:6"
-#define ui_boxlang "EAST-5:22,SOUTH+1:6"
-#define ui_boxvore "EAST-5:22,SOUTH+1:6"
-
//Filters
#define CIT_FILTER_STAMINACRIT filter(type="drop_shadow", x=0, y=0, size=-3, color="#04080F")
@@ -35,6 +23,8 @@
#define MASTURBATE_LINKED_ORGAN (1<<6) //used to pass our mission to the linked organ
#define CAN_CLIMAX_WITH (1<<7)
#define GENITAL_CAN_AROUSE (1<<8)
+#define GENITAL_UNDIES_HIDDEN (1<<9)
+#define UPDATE_OWNER_APPEARANCE (1<<10)
#define DEF_VAGINA_SHAPE "Human"
@@ -71,6 +61,12 @@
#define MILK_RATE_MULT 1
#define MILK_EFFICIENCY 1
+//visibility toggles defines to avoid errors typos code errors.
+#define GEN_VISIBLE_ALWAYS "Always visible"
+#define GEN_VISIBLE_NO_CLOTHES "Hidden by clothes"
+#define GEN_VISIBLE_NO_UNDIES "Hidden by underwear"
+#define GEN_VISIBLE_NEVER "Always hidden"
+
//Individual logging define
#define INDIVIDUAL_LOOC_LOG "LOOC log"
diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index c750119701..be0a4d0f59 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -82,6 +82,7 @@
#define CANUNCONSCIOUS (1<<2)
#define CANPUSH (1<<3)
#define GODMODE (1<<4)
+#define CANSTAGGER (1<<5)
//Health Defines
#define HEALTH_THRESHOLD_CRIT 0
diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index 3d76390ce1..bd77e374aa 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -175,6 +175,7 @@
#define SPEECH_LANGUAGE 5
// #define SPEECH_IGNORE_SPAM 6
// #define SPEECH_FORCED 7
+#define COMSIG_MOB_ANTAG_ON_GAIN "mob_antag_on_gain" //from base of /datum/antagonist/on_gain(): (antag_datum)
// /mob/living signals
#define COMSIG_LIVING_REGENERATE_LIMBS "living_regenerate_limbs" //from base of /mob/living/regenerate_limbs(): (noheal, excluded_limbs)
@@ -185,6 +186,7 @@
#define COMSIG_LIVING_MINOR_SHOCK "living_minor_shock" //sent by stuff like stunbatons and tasers: ()
#define COMSIG_LIVING_REVIVE "living_revive" //from base of mob/living/revive() (full_heal, admin_revive)
#define COMSIG_MOB_CLIENT_LOGIN "comsig_mob_client_login" //sent when a mob/login() finishes: (client)
+#define COMSIG_MOB_CLIENT_MOVE "comsig_mob_client_move" //sent when client/Move() finishes with no early returns: (client, direction, n, oldloc)
#define COMSIG_LIVING_GUN_PROCESS_FIRE "living_gun_process_fire" //from base of /obj/item/gun/proc/process_fire(): (atom/target, params, zone_override)
#define COMSIG_LIVING_COMBAT_ENABLED "combatmode_enabled" //from base of mob/living/enable_combat_mode() (was_forced)
#define COMSIG_LIVING_COMBAT_DISABLED "combatmode_disabled" //from base of mob/living/disable_combat_mode() (was_forced)
diff --git a/code/__DEFINES/dynamic.dm b/code/__DEFINES/dynamic.dm
index 0d57961f48..ae5b031c8b 100644
--- a/code/__DEFINES/dynamic.dm
+++ b/code/__DEFINES/dynamic.dm
@@ -6,6 +6,7 @@
#define NO_ASSASSIN (1<<0)
#define WAROPS_ALWAYS_ALLOWED (1<<1)
#define USE_PREF_WEIGHTS (1<<2)
+#define FORCE_IF_WON (1<<3)
#define ONLY_RULESET (1<<0)
#define HIGHLANDER_RULESET (1<<1)
diff --git a/code/__DEFINES/maths.dm b/code/__DEFINES/maths.dm
index 6d263473b0..f37efd694c 100644
--- a/code/__DEFINES/maths.dm
+++ b/code/__DEFINES/maths.dm
@@ -30,7 +30,7 @@
#define FLOOR(x, y) ( round((x) / (y)) * (y) )
// Similar to clamp but the bottom rolls around to the top and vice versa. min is inclusive, max is exclusive
-#define WRAP(val, min, max) ( min == max ? min : (val) - (round(((val) - (min))/((max) - (min))) * ((max) - (min))) )
+#define WRAP(val, min, max) CLAMP(( min == max ? min : (val) - (round(((val) - (min))/((max) - (min))) * ((max) - (min))) ),min,max-1)
// Real modulus that handles decimals
#define MODULUS(x, y) ( (x) - (y) * round((x) / (y)) )
@@ -203,5 +203,7 @@
#define MANHATTAN_DISTANCE(a, b) (abs(a.x - b.x) + abs(a.y - b.y))
+#define LOGISTIC_FUNCTION(L,k,x,x_0) (L/(1+(NUM_E**(-k*(x-x_0)))))
+
/// Make sure something is a boolean TRUE/FALSE 1/0 value, since things like bitfield & bitflag doesn't always give 1s and 0s.
#define FORCE_BOOLEAN(x) ((x)? TRUE : FALSE)
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index 89cacc1c7b..9efffefce5 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -345,11 +345,10 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S
#define COLOUR_PRIORITY_AMOUNT 4 //how many priority levels there are.
//Endgame Results
-#define NUKE_NEAR_MISS 1
-#define NUKE_MISS_STATION 2
-#define NUKE_SYNDICATE_BASE 3
-#define STATION_DESTROYED_NUKE 4
-#define STATION_EVACUATED 5
+#define NUKE_MISS_STATION 1
+#define NUKE_SYNDICATE_BASE 2
+#define STATION_DESTROYED_NUKE 3
+#define STATION_EVACUATED 4
#define BLOB_WIN 8
#define BLOB_NUKE 9
#define BLOB_DESTROYED 10
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index 69e7ac99fc..b4735cd0d8 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -289,4 +289,5 @@
#define HUMAN_FIRE_STACK_ICON_NUM 3
#define PULL_PRONE_SLOWDOWN 0.6
-#define HUMAN_CARRY_SLOWDOWN 0
+#define FIREMAN_CARRY_SLOWDOWN 0
+#define PIGGYBACK_CARRY_SLOWDOWN 1
diff --git a/code/__DEFINES/vote.dm b/code/__DEFINES/vote.dm
index 12e802383e..175cce3dc4 100644
--- a/code/__DEFINES/vote.dm
+++ b/code/__DEFINES/vote.dm
@@ -5,6 +5,11 @@
#define MAJORITY_JUDGEMENT_VOTING 4
#define INSTANT_RUNOFF_VOTING 5
+#define SHOW_RESULTS (1<<0)
+#define SHOW_VOTES (1<<1)
+#define SHOW_WINNER (1<<2)
+#define SHOW_ABSTENTION (1<<3)
+
GLOBAL_LIST_INIT(vote_score_options,list("Bad","Poor","Acceptable","Good","Great"))
GLOBAL_LIST_INIT(vote_type_names,list(\
@@ -15,3 +20,10 @@ GLOBAL_LIST_INIT(vote_type_names,list(\
"Raw Score (returns results from 0 to 1, winner is 1)" = SCORE_VOTING,\
"Majority Judgement (single-winner score voting)" = MAJORITY_JUDGEMENT_VOTING,\
))
+
+GLOBAL_LIST_INIT(display_vote_settings, list(\
+"Results" = SHOW_RESULTS,
+"Ongoing Votes" = SHOW_VOTES,
+"Winner" = SHOW_WINNER,
+"Abstainers" = SHOW_ABSTENTION
+))
\ No newline at end of file
diff --git a/code/__HELPERS/_cit_helpers.dm b/code/__HELPERS/_cit_helpers.dm
index cc8ab0d250..9afae12c30 100644
--- a/code/__HELPERS/_cit_helpers.dm
+++ b/code/__HELPERS/_cit_helpers.dm
@@ -58,14 +58,13 @@ GLOBAL_LIST_EMPTY(ipc_antennas_list)
//Genitals and Arousal Lists
GLOBAL_LIST_EMPTY(genitals_list)
GLOBAL_LIST_EMPTY(cock_shapes_list)
-GLOBAL_LIST_EMPTY(gentlemans_organ_names)
GLOBAL_LIST_EMPTY(balls_shapes_list)
GLOBAL_LIST_EMPTY(breasts_shapes_list)
GLOBAL_LIST_EMPTY(vagina_shapes_list)
-GLOBAL_LIST_INIT(cum_into_containers_list, list(/obj/item/reagent_containers/food/snacks/pie)) //Yer fuggin snowflake name list jfc
-GLOBAL_LIST_INIT(dick_nouns, list("dick","cock","member","shaft"))
-GLOBAL_LIST_INIT(cum_id_list,"semen")
-GLOBAL_LIST_INIT(milk_id_list,"milk")
+//longcat memes.
+GLOBAL_LIST_INIT(dick_nouns, list("phallus", "willy", "dick", "prick", "member", "tool", "gentleman's organ", "cock", "wang", "knob", "dong", "joystick", "pecker", "johnson", "weenie", "tadger", "schlong", "thirsty ferret", "One eyed trouser trout", "Ding dong", "ankle spanker", "Pork sword", "engine cranker", "Harry hot dog", "Davy Crockett", "Kidney cracker", "Heat seeking moisture missile", "Giggle stick", "love whistle", "Tube steak", "Uncle Dick", "Purple helmet warrior"))
+
+GLOBAL_LIST_INIT(genitals_visibility_toggles, list(GEN_VISIBLE_ALWAYS, GEN_VISIBLE_NO_CLOTHES, GEN_VISIBLE_NO_UNDIES, GEN_VISIBLE_NEVER))
GLOBAL_LIST_INIT(dildo_shapes, list(
"Human" = "human",
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index ff2559b661..38e25b04c5 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -54,11 +54,6 @@
init_sprite_accessory_subtypes(/datum/sprite_accessory/vagina, GLOB.vagina_shapes_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/breasts, GLOB.breasts_shapes_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/testicles, GLOB.balls_shapes_list)
- GLOB.gentlemans_organ_names = list("phallus", "willy", "dick", "prick", "member", "tool", "gentleman's organ",
- "cock", "wang", "knob", "dong", "joystick", "pecker", "johnson", "weenie", "tadger", "schlong", "thirsty ferret",
- "baloney pony", "schlanger", "Mutton dagger", "old blind bob","Hanging Johnny", "fishing rod", "Tally whacker", "polly rocket",
- "One eyed trouser trout", "Ding dong", "ankle spanker", "Pork sword", "engine cranker", "Harry hot dog", "Davy Crockett",
- "Kidney cracker", "Heat seeking moisture missile", "Giggle stick", "love whistle", "Tube steak", "Uncle Dick", "Purple helmet warrior")
for(var/gpath in subtypesof(/obj/item/organ/genital))
var/obj/item/organ/genital/G = gpath
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index b8edf3b0f4..f7546193aa 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -185,7 +185,6 @@
"cock_diameter_ratio" = COCK_DIAMETER_RATIO_DEF,
"cock_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"),
"has_balls" = FALSE,
- "balls_internal" = FALSE,
"balls_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"),
"balls_size" = BALLS_SIZE_DEF,
"balls_shape" = DEF_BALLS_SHAPE,
@@ -200,13 +199,11 @@
"has_vag" = FALSE,
"vag_shape" = pick(GLOB.vagina_shapes_list),
"vag_color" = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F"),
- "vag_clits" = 1,
- "vag_clit_diam" = 0.25,
- "vag_clit_len" = 0.25,
"has_womb" = FALSE,
- "womb_cum_rate" = CUM_RATE,
- "womb_cum_mult" = CUM_RATE_MULT,
- "womb_efficiency" = CUM_EFFICIENCY,
+ "balls_visibility" = GEN_VISIBLE_NO_UNDIES,
+ "breasts_visibility"= GEN_VISIBLE_NO_UNDIES,
+ "cock_visibility" = GEN_VISIBLE_NO_UNDIES,
+ "vag_visibility" = GEN_VISIBLE_NO_UNDIES,
"ipc_screen" = snowflake_ipc_antenna_list ? pick(snowflake_ipc_antenna_list) : "None",
"ipc_antenna" = "None",
"flavor_text" = "",
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index e8a57bb257..46c2c919f4 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -152,10 +152,10 @@
else
if(ismob(A))
changeNext_move(CLICK_CD_MELEE)
- UnarmedAttack(A,1)
+ UnarmedAttack(A, 1)
else
if(W)
- W.afterattack(A,src,0,params)
+ W.ranged_attack_chain(src, A, params)
else
RangedAttack(A,params)
diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm
index 12f7986357..28fd7397b0 100644
--- a/code/_onclick/hud/_defines.dm
+++ b/code/_onclick/hud/_defines.dm
@@ -163,3 +163,14 @@
#define ui_ghost_reenter_corpse "SOUTH:6,CENTER:24"
#define ui_ghost_teleport "SOUTH:6,CENTER+1:24"
#define ui_ghost_pai "SOUTH: 6, CENTER+2:24"
+
+
+//UI position overrides for 1:1 screen layout. (default is 7:5)
+#define ui_stamina "EAST-1:28,CENTER:17" // replacing internals button
+#define ui_overridden_resist "EAST-3:24,SOUTH+1:7"
+#define ui_combat_toggle "EAST-4:22,SOUTH:5"
+
+#define ui_boxcraft "EAST-4:22,SOUTH+1:6"
+#define ui_boxarea "EAST-4:6,SOUTH+1:6"
+#define ui_boxlang "EAST-5:22,SOUTH+1:6"
+#define ui_boxvore "EAST-5:22,SOUTH+1:6"
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index baa1add7e1..2136ee96e4 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -8,12 +8,15 @@
*afterattack. The return value does not matter.
*/
/obj/item/proc/melee_attack_chain(mob/user, atom/target, params)
- if(item_flags & NO_ATTACK_CHAIN_SOFT_STAMCRIT)
- if(isliving(user))
- var/mob/living/L = user
+ if(isliving(user))
+ var/mob/living/L = user
+ if(item_flags & NO_ATTACK_CHAIN_SOFT_STAMCRIT)
if(IS_STAMCRIT(L))
to_chat(L, "You are too exhausted to swing [src]!")
return
+ if(!CHECK_MOBILITY(L, MOBILITY_USE))
+ to_chat(L, "You are unable to swing [src] right now!")
+ return
if(tool_behaviour && target.tool_act(user, src, tool_behaviour))
return
if(pre_attack(target, user, params))
@@ -24,6 +27,15 @@
return
afterattack(target, user, TRUE, params)
+/// Like melee_attack_chain but for ranged.
+/obj/item/proc/ranged_attack_chain(mob/user, atom/target, params)
+ if(isliving(user))
+ var/mob/living/L = user
+ if(!CHECK_MOBILITY(L, MOBILITY_USE))
+ to_chat(L, "You are unable to raise [src] right now!")
+ return
+ afterattack(target, user, FALSE, params)
+
// Called when the item is in the active hand, and clicked; alternately, there is an 'activate held object' verb or you can hit pagedown.
/obj/item/proc/attack_self(mob/user)
if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_SELF, user) & COMPONENT_NO_INTERACT)
@@ -50,7 +62,6 @@
user.changeNext_move(CLICK_CD_MELEE)
return I.attack(src, user)
-
/obj/item/proc/attack(mob/living/M, mob/living/user)
if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK, M, user) & COMPONENT_ITEM_NO_ATTACK)
return
@@ -117,6 +128,7 @@
if(user != src && check_shields(I, totitemdamage, "the [I.name]", MELEE_ATTACK, I.armour_penetration))
return FALSE
send_item_attack_message(I, user)
+ I.do_stagger_action(src, user)
if(I.force)
apply_damage(totitemdamage, I.damtype) //CIT CHANGE - replaces I.force with totitemdamage
if(I.damtype == BRUTE)
@@ -166,5 +178,33 @@
playsound(src, 'sound/weapons/dink.ogg', 30, 1)
return 1
+/// How much stamina this takes to swing this is not for realism purposes hecc off.
/obj/item/proc/getweight()
return total_mass || w_class * 1.25
+
+/// How long this staggers for. 0 and negatives supported.
+/obj/item/proc/melee_stagger_duration()
+ if(!isnull(stagger_force))
+ return stagger_force
+ /// totally not an untested, arbitrary equation.
+ return clamp((1.5 + (w_class/7.5)) * (force / 2), 0, 10 SECONDS)
+
+/obj/item/proc/do_stagger_action(mob/living/target, mob/living/user)
+ if(!CHECK_BITFIELD(target.status_flags, CANSTAGGER))
+ return FALSE
+ if(target.combat_flags & COMBAT_FLAG_SPRINT_ACTIVE)
+ target.do_staggered_animation()
+ var/duration = melee_stagger_duration()
+ if(!duration) //0
+ return FALSE
+ else if(duration > 0)
+ target.Stagger(duration)
+ else //negative
+ target.AdjustStaggered(duration)
+ return TRUE
+
+/mob/proc/do_staggered_animation()
+ set waitfor = FALSE
+ animate(src, pixel_x = -2, pixel_y = -2, time = 1, flags = ANIMATION_RELATIVE | ANIMATION_PARALLEL)
+ animate(pixel_x = 4, pixel_y = 4, time = 1, flags = ANIMATION_RELATIVE)
+ animate(pixel_x = -2, pixel_y = -2, time = 0.5, flags = ANIMATION_RELATIVE)
diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm
index bc290bedb9..8b08366c50 100644
--- a/code/controllers/configuration/entries/game_options.dm
+++ b/code/controllers/configuration/entries/game_options.dm
@@ -396,6 +396,10 @@
key_mode = KEY_MODE_TEXT
value_mode = VALUE_MODE_NUM
+/datum/config_entry/keyed_list/job_threat
+ key_mode = KEY_MODE_TEXT
+ value_mode = VALUE_MODE_NUM
+
/datum/config_entry/number/monkeycap
config_entry_value = 64
min_val = 0
@@ -436,3 +440,8 @@
/datum/config_entry/number/penis_max_inches_prefs
config_entry_value = 20
min_val = 0
+
+/datum/config_entry/keyed_list/safe_visibility_toggles
+ key_mode = KEY_MODE_TEXT
+ value_mode = VALUE_MODE_FLAG
+ config_entry_value = list(GEN_VISIBLE_NO_CLOTHES, GEN_VISIBLE_NO_UNDIES, GEN_VISIBLE_NEVER) //refer to cit_helpers for all toggles.
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index 31e436f206..91d0db85d1 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -66,6 +66,7 @@ SUBSYSTEM_DEF(job)
/datum/controller/subsystem/job/proc/GetJob(rank)
+ RETURN_TYPE(/datum/job)
if(!occupations.len)
SetupOccupations()
return name_occupations[rank]
@@ -738,4 +739,4 @@ SUBSYSTEM_DEF(job)
. |= player.mind
/datum/controller/subsystem/job/proc/JobDebug(message)
- log_job_debug(message)
\ No newline at end of file
+ log_job_debug(message)
diff --git a/code/controllers/subsystem/processing/quirks.dm b/code/controllers/subsystem/processing/quirks.dm
index 5aeaf43bab..c57bac5526 100644
--- a/code/controllers/subsystem/processing/quirks.dm
+++ b/code/controllers/subsystem/processing/quirks.dm
@@ -37,11 +37,11 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
if(job?.blacklisted_quirks)
cut = filter_quirks(my_quirks, job.blacklisted_quirks)
for(var/V in my_quirks)
- var/datum/quirk/Q = quirks[V]
- if(Q)
+ if(V in quirks)
+ var/datum/quirk/Q = quirks[V]
user.add_quirk(Q, spawn_effects)
else
- stack_trace("Invalid quirk \"[V]\" in client [cli.ckey] preferences")
+ log_admin("Invalid quirk \"[V]\" in client [cli.ckey] preferences")
cli.prefs.all_quirks -= V
badquirk = TRUE
if(badquirk)
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 2ef9608a96..a7be36abc2 100755
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -479,15 +479,15 @@ SUBSYSTEM_DEF(ticker)
var/vote_type = CONFIG_GET(string/map_vote_type)
switch(vote_type)
if("PLURALITY")
- SSvote.initiate_vote("map","server",hideresults=TRUE)
+ SSvote.initiate_vote("map","server", display = SHOW_RESULTS)
if("APPROVAL")
- SSvote.initiate_vote("map","server",hideresults=TRUE,votesystem = APPROVAL_VOTING)
+ SSvote.initiate_vote("map","server", display = SHOW_RESULTS, votesystem = APPROVAL_VOTING)
if("IRV")
- SSvote.initiate_vote("map","server",hideresults=TRUE,votesystem = INSTANT_RUNOFF_VOTING)
+ SSvote.initiate_vote("map","server", display = SHOW_RESULTS, votesystem = INSTANT_RUNOFF_VOTING)
if("SCORE")
- SSvote.initiate_vote("map","server",hideresults=TRUE,votesystem = MAJORITY_JUDGEMENT_VOTING)
+ SSvote.initiate_vote("map","server", display = SHOW_RESULTS, votesystem = MAJORITY_JUDGEMENT_VOTING)
else
- SSvote.initiate_vote("map","server",hideresults=TRUE)
+ SSvote.initiate_vote("map","server", display = SHOW_RESULTS)
// fallback
/datum/controller/subsystem/ticker/proc/HasRoundStarted()
@@ -503,9 +503,9 @@ SUBSYSTEM_DEF(ticker)
SSticker.modevoted = TRUE
var/dynamic = CONFIG_GET(flag/dynamic_voting)
if(dynamic)
- SSvote.initiate_vote("dynamic","server",hideresults=TRUE,votesystem=SCORE_VOTING,forced=TRUE,vote_time = 20 MINUTES)
+ SSvote.initiate_vote("dynamic", "server", display = NONE, votesystem = SCORE_VOTING, forced = TRUE,vote_time = 20 MINUTES)
else
- SSvote.initiate_vote("roundtype","server",hideresults=TRUE,votesystem=PLURALITY_VOTING,forced=TRUE, \
+ SSvote.initiate_vote("roundtype", "server", display = NONE, votesystem = PLURALITY_VOTING, forced=TRUE, \
vote_time = (CONFIG_GET(flag/modetier_voting) ? 1 MINUTES : 20 MINUTES))
/datum/controller/subsystem/ticker/Recover()
diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm
index e7e5754f6b..7d72951278 100644
--- a/code/controllers/subsystem/vote.dm
+++ b/code/controllers/subsystem/vote.dm
@@ -23,7 +23,7 @@ SUBSYSTEM_DEF(vote)
var/list/generated_actions = list()
var/next_pop = 0
- var/obfuscated = FALSE//CIT CHANGE - adds obfuscated/admin-only votes
+ var/display_votes = SHOW_RESULTS|SHOW_VOTES|SHOW_WINNER|SHOW_ABSTENTION //CIT CHANGE - adds obfuscated/admin-only votes
var/list/stored_gamemode_votes = list() //Basically the last voted gamemode is stored here for end-of-round use.
@@ -59,7 +59,7 @@ SUBSYSTEM_DEF(vote)
voted.Cut()
voting.Cut()
scores.Cut()
- obfuscated = FALSE //CIT CHANGE - obfuscated votes
+ display_votes = initial(display_votes) //CIT CHANGE - obfuscated votes
remove_action_buttons()
/datum/controller/subsystem/vote/proc/get_result()
@@ -250,7 +250,7 @@ SUBSYSTEM_DEF(vote)
if(winners.len > 0)
if(was_roundtype_vote)
stored_gamemode_votes = list()
- if(!obfuscated)
+ if(display_votes & SHOW_RESULTS)
if(vote_system == SCHULZE_VOTING)
text += "\nIt should be noted that this is not a raw tally of votes (impossible in ranked choice) but the score determined by the schulze method of voting, so the numbers will look weird!"
if(vote_system == MAJORITY_JUDGEMENT_VOTING)
@@ -261,15 +261,15 @@ SUBSYSTEM_DEF(vote)
votes = 0
if(was_roundtype_vote)
stored_gamemode_votes[choices[i]] = votes
- text += "\n[choices[i]]: [obfuscated ? "???" : votes]" //CIT CHANGE - adds obfuscated votes
+ text += "\n[choices[i]]: [display_votes & SHOW_RESULTS ? votes : "???"]" //CIT CHANGE - adds obfuscated votes
if(mode != "custom")
- if(winners.len > 1 && !obfuscated) //CIT CHANGE - adds obfuscated votes
+ if(winners.len > 1 && display_votes & SHOW_WINNER) //CIT CHANGE - adds obfuscated votes
text = "\nVote Tied Between:"
for(var/option in winners)
text += "\n\t[option]"
. = pick(winners)
- text += "\nVote Result: [obfuscated ? "???" : .]" //CIT CHANGE - adds obfuscated votes
- else
+ text += "\nVote Result: [display_votes & SHOW_WINNER ? . : "???"]" //CIT CHANGE - adds obfuscated votes
+ if(display_votes & SHOW_ABSTENTION)
text += "\nDid not vote: [GLOB.clients.len-voted.len]"
else if(vote_system == SCORE_VOTING)
for(var/score_name in scores)
@@ -278,7 +278,7 @@ SUBSYSTEM_DEF(vote)
score = 0
if(was_roundtype_vote)
stored_gamemode_votes[score_name] = score
- text = "\n[score_name]: [obfuscated ? "???" : score]"
+ text = "\n[score_name]: [display_votes & SHOW_RESULTS ? score : "???"]"
. = 1
else
text += "Vote Result: Inconclusive - No Votes!"
@@ -295,7 +295,7 @@ SUBSYSTEM_DEF(vote)
if(islist(myvote))
for(var/j=1,j<=myvote.len,j++)
SSblackbox.record_feedback("nested tally","voting",1,list(vote_title_text,"[j]\th",choices[myvote[j]]))
- if(obfuscated) //CIT CHANGE - adds obfuscated votes. this messages admins with the vote's true results
+ if(!(display_votes & SHOW_RESULTS)) //CIT CHANGE - adds obfuscated votes. this messages admins with the vote's true results
var/admintext = "Obfuscated results"
if(vote_system != SCORE_VOTING)
if(vote_system == SCHULZE_VOTING)
@@ -327,7 +327,7 @@ SUBSYSTEM_DEF(vote)
if(CONFIG_GET(flag/modetier_voting))
reset()
started_time = 0
- initiate_vote("mode tiers","server",hideresults=FALSE,votesystem=SCORE_VOTING,forced=TRUE, vote_time = 30 MINUTES)
+ initiate_vote("mode tiers","server", votesystem=SCORE_VOTING, forced=TRUE, vote_time = 30 MINUTES)
to_chat(world,"The vote will end right as the round starts.")
return .
if("restart")
@@ -354,11 +354,15 @@ SUBSYSTEM_DEF(vote)
return message_admins("A vote has tried to change the gamemode, but the game has already started. Aborting.")
GLOB.master_mode = "dynamic"
var/list/runnable_storytellers = config.get_runnable_storytellers()
+ var/datum/dynamic_storyteller/picked
for(var/T in runnable_storytellers)
var/datum/dynamic_storyteller/S = T
+ if(stored_gamemode_votes[initial(S.name)] == 1 && CHECK_BITFIELD(initial(S.flags), FORCE_IF_WON))
+ picked = S
runnable_storytellers[S] *= round(stored_gamemode_votes[initial(S.name)]*100000,1)
- var/datum/dynamic_storyteller/S = pickweightAllowZero(runnable_storytellers)
- GLOB.dynamic_storyteller_type = S
+ if(!picked)
+ picked = pickweightAllowZero(runnable_storytellers)
+ GLOB.dynamic_storyteller_type = picked
if("map")
var/datum/map_config/VM = config.maplist[.]
message_admins("The map has been voted for and will change to: [VM.map_name]")
@@ -432,7 +436,7 @@ SUBSYSTEM_DEF(vote)
saved -= usr.ckey
return 0
-/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, hideresults, votesystem = PLURALITY_VOTING, forced = FALSE,vote_time = -1)//CIT CHANGE - adds hideresults argument to votes to allow for obfuscated votes
+/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, display = display_votes, votesystem = PLURALITY_VOTING, forced = FALSE,vote_time = -1)//CIT CHANGE - adds display argument to votes to allow for obfuscated votes
vote_system = votesystem
if(!mode)
if(started_time)
@@ -452,7 +456,7 @@ SUBSYSTEM_DEF(vote)
SEND_SOUND(world, sound('sound/misc/notice2.ogg'))
reset()
- obfuscated = hideresults //CIT CHANGE - adds obfuscated votes
+ display_votes = display //CIT CHANGE - adds obfuscated votes
switch(vote_type)
if("restart")
choices.Add("Restart Round","Continue Playing")
@@ -503,6 +507,21 @@ SUBSYSTEM_DEF(vote)
if(!option || mode || !usr.client)
break
choices.Add(option)
+ var/keep_going = TRUE
+ var/toggles = SHOW_RESULTS|SHOW_VOTES|SHOW_WINNER
+ while(keep_going)
+ var/list/choices = list()
+ for(var/A in GLOB.display_vote_settings)
+ var/toggletext
+ var/bitflag = GLOB.display_vote_settings[A]
+ toggletext = "[toggles & bitflag ? "Show" : "Hide"] [A]"
+ choices[toggletext] = bitflag
+ var/chosen = input(usr, "Toggle vote display settings. Cancel to finalize.", toggles) as null|anything in choices
+ if(!chosen)
+ keep_going = FALSE
+ else
+ toggles ^= choices[chosen]
+ display_votes = toggles
else
return 0
mode = vote_type
@@ -573,7 +592,7 @@ SUBSYSTEM_DEF(vote)
ivotedforthis = ((C.ckey in voted) && (i in voted[C.ckey]))
if(!votes)
votes = 0
- . += "
[ivotedforthis ? "" : ""][choices[i]] ([obfuscated ? (admin ? "??? ([votes])" : "???") : votes] votes)[ivotedforthis ? "" : ""]" // CIT CHANGE - adds obfuscated votes
+ . += "[ivotedforthis ? "" : ""][choices[i]] ([display_votes & SHOW_VOTES ? votes : (admin ? "??? ([votes])" : "???")] votes)[ivotedforthis ? "" : ""]" // CIT CHANGE - adds obfuscated votes
if(choice_descs.len >= i)
. += "[choice_descs[i]]"
. += "
"
diff --git a/code/datums/beam.dm b/code/datums/beam.dm
index 2622b6ae39..6a9878e086 100644
--- a/code/datums/beam.dm
+++ b/code/datums/beam.dm
@@ -9,8 +9,8 @@
var/max_distance = 0
var/sleep_time = 3
var/finished = 0
- var/target_oldloc = null
- var/origin_oldloc = null
+ var/turf/target_oldloc
+ var/turf/origin_oldloc
var/static_beam = 0
var/beam_type = /obj/effect/ebeam //must be subtype
var/timing_id = null
@@ -23,13 +23,13 @@
target_oldloc = get_turf(target)
sleep_time = beam_sleep_time
if(origin_oldloc == origin && target_oldloc == target)
- static_beam = 1
+ static_beam = TRUE
max_distance = maxdistance
base_icon = new(beam_icon,beam_icon_state)
icon = beam_icon
icon_state = beam_icon_state
beam_type = btype
- if(time < INFINITY)
+ if(time < INFINITY)
addtimer(CALLBACK(src,.proc/End), time)
/datum/beam/proc/Start()
@@ -42,10 +42,14 @@
return
recalculating = TRUE
timing_id = null
- if(origin && target && get_dist(origin,target)[AM] pushes [user] off of [AM.p_them()]!")
/datum/component/riding/cyborg
diff --git a/code/datums/elements/flavor_text.dm b/code/datums/elements/flavor_text.dm
index 373647d6f5..ea8e361694 100644
--- a/code/datums/elements/flavor_text.dm
+++ b/code/datums/elements/flavor_text.dm
@@ -114,6 +114,9 @@ GLOBAL_LIST_EMPTY(mobs_with_editable_flavor_text) //et tu, hacky code
//subtypes with additional hooks for DNA and preferences.
/datum/element/flavor_text/carbon
+ //list of antagonists etcetera that should have nothing to do with people's snowflakes.
+ var/static/list/i_dont_even_know_who_you_are = typecacheof(list(/datum/antagonist/abductor, /datum/antagonist/ert,
+ /datum/antagonist/nukeop, /datum/antagonist/wizard))
/datum/element/flavor_text/carbon/Attach(datum/target, text = "", _name = "Flavor Text", _addendum, _max_len = MAX_FLAVOR_LEN, _always_show = FALSE, _edit = TRUE)
if(!iscarbon(target))
@@ -122,6 +125,7 @@ GLOBAL_LIST_EMPTY(mobs_with_editable_flavor_text) //et tu, hacky code
if(. == ELEMENT_INCOMPATIBLE)
return
RegisterSignal(target, COMSIG_CARBON_IDENTITY_TRANSFERRED_TO, .proc/update_dna_flavor_text)
+ RegisterSignal(target, COMSIG_MOB_ANTAG_ON_GAIN, .proc/on_antag_gain)
if(ishuman(target))
RegisterSignal(target, COMSIG_HUMAN_PREFS_COPIED_TO, .proc/update_prefs_flavor_text)
RegisterSignal(target, COMSIG_HUMAN_HARDSET_DNA, .proc/update_dna_flavor_text)
@@ -129,7 +133,7 @@ GLOBAL_LIST_EMPTY(mobs_with_editable_flavor_text) //et tu, hacky code
/datum/element/flavor_text/carbon/Detach(mob/living/carbon/C)
. = ..()
- UnregisterSignal(C, list(COMSIG_CARBON_IDENTITY_TRANSFERRED_TO, COMSIG_HUMAN_PREFS_COPIED_TO, COMSIG_HUMAN_HARDSET_DNA, COMSIG_HUMAN_ON_RANDOMIZE))
+ UnregisterSignal(C, list(COMSIG_CARBON_IDENTITY_TRANSFERRED_TO, COMSIG_MOB_ANTAG_ON_GAIN, COMSIG_HUMAN_PREFS_COPIED_TO, COMSIG_HUMAN_HARDSET_DNA, COMSIG_HUMAN_ON_RANDOMIZE))
/datum/element/flavor_text/carbon/proc/update_dna_flavor_text(mob/living/carbon/C)
texts_by_atom[C] = C.dna.features["flavor_text"]
@@ -144,3 +148,9 @@ GLOBAL_LIST_EMPTY(mobs_with_editable_flavor_text) //et tu, hacky code
/datum/element/flavor_text/carbon/proc/unset_flavor(mob/living/carbon/user)
texts_by_atom[user] = ""
+
+/datum/element/flavor_text/carbon/proc/on_antag_gain(mob/living/carbon/user, datum/antagonist/antag)
+ if(is_type_in_typecache(antag, i_dont_even_know_who_you_are))
+ texts_by_atom[user] = ""
+ if(user.dna)
+ user.dna.features["flavor_text"] = ""
\ No newline at end of file
diff --git a/code/datums/martial/boxing.dm b/code/datums/martial/boxing.dm
index ea883c5637..16d61c29f9 100644
--- a/code/datums/martial/boxing.dm
+++ b/code/datums/martial/boxing.dm
@@ -37,7 +37,7 @@
D.apply_damage(damage, STAMINA, affecting, armor_block)
log_combat(A, D, "punched (boxing) ")
- if(D.getStaminaLoss() > 100)
+ if(D.getStaminaLoss() > 100 && istype(D.mind?.martial_art, /datum/martial_art/boxing))
var/knockout_prob = (D.getStaminaLoss() + rand(-15,15))*0.75
if((D.stat != DEAD) && prob(knockout_prob))
D.visible_message("[A] has knocked [D] out with a haymaker!", \
diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm
index cf10244890..5a69ebe427 100644
--- a/code/datums/status_effects/debuffs.dm
+++ b/code/datums/status_effects/debuffs.dm
@@ -90,6 +90,7 @@
/datum/status_effect/staggered
id = "staggered"
blocks_sprint = TRUE
+ alert_type = null
/datum/status_effect/staggered/on_creation(mob/living/new_owner, set_duration)
if(isnum(set_duration))
diff --git a/code/datums/traits/neutral.dm b/code/datums/traits/neutral.dm
index ab2a69e7e9..d1b5240798 100644
--- a/code/datums/traits/neutral.dm
+++ b/code/datums/traits/neutral.dm
@@ -88,14 +88,6 @@
if(quirk_holder)
quirk_holder.remove_client_colour(/datum/client_colour/monochrome)
-/datum/quirk/libido
- name = "Nymphomania"
- desc = "You're always feeling a bit in heat. Also, you get aroused faster than usual."
- value = 0
- mob_trait = TRAIT_PERMABONER
- gain_text = "You are feeling extra wild."
- lose_text = "You don't feel that burning sensation anymore."
-
/datum/quirk/maso
name = "Masochism"
desc = "You are aroused by pain."
diff --git a/code/game/gamemodes/bloodsucker/bloodsucker.dm b/code/game/gamemodes/bloodsucker/bloodsucker.dm
index 3fe67fd431..01b6035034 100644
--- a/code/game/gamemodes/bloodsucker/bloodsucker.dm
+++ b/code/game/gamemodes/bloodsucker/bloodsucker.dm
@@ -23,7 +23,7 @@
antag_flag = ROLE_BLOODSUCKER
false_report_weight = 1
restricted_jobs = list("AI","Cyborg")
- protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
+ protected_jobs = list("Chaplain", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
required_players = 20
required_enemies = 2
recommended_enemies = 4
diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm
index 14fe960422..4db8f4129a 100644
--- a/code/game/gamemodes/changeling/changeling.dm
+++ b/code/game/gamemodes/changeling/changeling.dm
@@ -88,13 +88,13 @@ GLOBAL_VAR(changeling_team_objective_type) //If this is not null, we hand our th
user.underwear = chosen_prof.underwear
user.undie_color = chosen_prof.undie_color
user.undershirt = chosen_prof.undershirt
- user.shirt_color =chosen_prof.shirt_color
+ user.shirt_color = chosen_prof.shirt_color
user.socks = chosen_prof.socks
user.socks_color =chosen_prof.socks_color
chosen_dna.transfer_identity(user, 1)
user.updateappearance(mutcolor_update=1)
- user.update_body()
+ user.update_body(TRUE)
user.domutcheck()
//vars hackery. not pretty, but better than the alternative.
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index 4c3ef824b9..58dfd6d814 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -54,9 +54,9 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
// Current storyteller
var/datum/dynamic_storyteller/storyteller = null
// Threat logging vars
- /// The "threat cap", threat shouldn't normally go above this and is used in ruleset calculations
+ /// Target threat level right now. Events and antags will try to keep the round at this level.
var/threat_level = 0
- /// Set at the beginning of the round. Spent by the mode to "purchase" rules.
+ /// The current antag threat. Recalculated every time a ruletype starts or ends.
var/threat = 0
/// Starting threat level, for things that increase it but can bring it back down.
var/initial_threat_level = 0
@@ -261,11 +261,11 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
. += "Uncharted Space
"
. += "Congratulations and thank you for participating in the NT 'Frontier' space program! Your station is actively orbiting a high value system far from the nearest support stations. Little is known about your region of space, and the opportunity to encounter the unknown invites greater glory. You are encouraged to elevate security as necessary to protect Nanotrasen assets."
set_security_level(SEC_LEVEL_BLUE)
- if(80 to 99)
+ if(80 to 95)
. += "Black Orbit
"
. += "As part of a mandatory security protocol, we are required to inform you that as a result of your orbital pattern directly behind an astrological body (oriented from our nearest observatory), your station will be under decreased monitoring and support. It is anticipated that your extreme location and decreased surveillance could pose security risks. Avoid unnecessary risks and attempt to keep your station in one piece."
set_security_level(SEC_LEVEL_AMBER)
- if(100)
+ if(96 to 100)
. += "Impending Doom
"
. += "Your station is somehow in the middle of hostile territory, in clear view of any enemy of the corporation. Your likelihood to survive is low, and station destruction is expected and almost inevitable. Secure any sensitive material and neutralize any enemy you will come across. It is important that you at least try to maintain the station.
"
. += "Good luck."
@@ -330,7 +330,6 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
peaceful_percentage = round(LORENTZ_CUMULATIVE_DISTRIBUTION(relative_threat, GLOB.dynamic_curve_centre, GLOB.dynamic_curve_width), 0.01)*100
- threat = threat_level
SSblackbox.record_feedback("tally","dynamic_threat",threat_level,"Initial threat level")
SSblackbox.record_feedback("tally","dynamic_threat",GLOB.dynamic_curve_centre,"Curve centre")
SSblackbox.record_feedback("tally","dynamic_threat",GLOB.dynamic_curve_width,"Curve width")
@@ -463,7 +462,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
log_game("DYNAMIC: Additional ruleset picked successfully, now [executed_rules.len] picked. [extra_rulesets_amount] remaining.")
else
- if(threat >= 50)
+ if(threat_level >= 50)
message_admins("DYNAMIC: Picking first roundstart ruleset failed. You should report this.")
log_game("DYNAMIC: Picking first roundstart ruleset failed. drafted_rules.len = [drafted_rules.len] and threat = [threat]/[threat_level]")
return FALSE
@@ -509,9 +508,8 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
drafted_rules -= starting_rule
starting_rule.trim_candidates()
- var/added_threat = starting_rule.scale_up(extra_rulesets_amount, threat)
+ starting_rule.scale_up(extra_rulesets_amount, threat)
if (starting_rule.pre_execute())
- spend_threat(starting_rule.cost + added_threat)
log_threat("[starting_rule.ruletype] - [starting_rule.name] -[starting_rule.cost + starting_rule.scaled_times * starting_rule.scaling_cost] threat", verbose = TRUE)
if(starting_rule.flags & HIGHLANDER_RULESET)
highlander_executed = TRUE
@@ -534,8 +532,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
/datum/game_mode/dynamic/proc/execute_roundstart_rule(sent_rule)
var/datum/dynamic_ruleset/rule = sent_rule
if(rule.execute())
- if(rule.persistent)
- current_rules += rule
+ current_rules += rule
SSblackbox.record_feedback("associative","dynamic_rulesets",1,rule.get_blackbox_info())
return TRUE
rule.clean_up() // Refund threat, delete teams and so on.
@@ -607,7 +604,6 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
if ((forced || (new_rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && new_rule.cost <= threat)))
new_rule.trim_candidates()
if (new_rule.ready(forced))
- spend_threat(new_rule.cost)
log_threat("[new_rule.ruletype] - [new_rule.name] -[new_rule.cost] threat", verbose = TRUE)
if (new_rule.execute()) // This should never fail since ready() returned 1
if(new_rule.flags & HIGHLANDER_RULESET)
@@ -617,8 +613,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
log_game("DYNAMIC: Making a call to a specific ruleset...[new_rule.name]!")
SSblackbox.record_feedback("associative","dynamic_rulesets",1,new_rule.get_blackbox_info())
executed_rules += new_rule
- if (new_rule.persistent)
- current_rules += new_rule
+ current_rules += new_rule
return TRUE
else if (forced)
log_game("DYNAMIC: The ruleset [new_rule.name] couldn't be executed due to lack of eligible players.")
@@ -629,7 +624,6 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
var/datum/dynamic_ruleset/rule = sent_rule
if (rule.execute())
log_game("DYNAMIC: Injected a [rule.ruletype == "latejoin" ? "latejoin" : "midround"] ruleset [rule.name].")
- spend_threat(rule.cost)
log_threat("[rule.ruletype] [rule.name] spent [rule.cost]", verbose = TRUE)
if(rule.flags & HIGHLANDER_RULESET)
highlander_executed = TRUE
@@ -647,9 +641,9 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
return TRUE
stack_trace("The [rule.ruletype] rule \"[rule.name]\" failed to execute.")
return FALSE
-
+
/datum/game_mode/dynamic/process()
- if (pop_last_updated < world.time - (60 SECONDS))
+ if (pop_last_updated < world.time - (120 SECONDS))
pop_last_updated = world.time
update_playercounts()
@@ -658,7 +652,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
current_rules -= rule
SSblackbox.record_feedback("tally","dynamic",1,"Rulesets finished")
SSblackbox.record_feedback("associative","dynamic_rulesets_finished",1,rule.get_blackbox_info())
-
+
storyteller.do_process()
if (midround_injection_cooldown < world.time)
@@ -717,6 +711,7 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
current_players[CURRENT_OBSERVERS].Add(M)
continue
current_players[CURRENT_DEAD_PLAYERS].Add(M) // Players who actually died (and admins who ghosted, would be nice to avoid counting them somehow)
+ threat = storyteller.calculate_threat() + 50 // 50 is the centerpoint, so we want it to be around 50
/// Removes type from the list
/datum/game_mode/dynamic/proc/remove_from_list(list/type_list, type)
@@ -767,23 +762,21 @@ GLOBAL_VAR_INIT(dynamic_storyteller_type, /datum/dynamic_storyteller/classic)
SSblackbox.record_feedback("tally","dynamic",1,"Successful latejoin injections")
latejoin_injection_cooldown = storyteller.get_latejoin_cooldown() + world.time
-/// Refund threat, but no more than threat_level.
-/datum/game_mode/dynamic/proc/refund_threat(regain)
- threat = min(threat_level,threat+regain)
- SSblackbox.record_feedback("tally","dynamic_threat",regain,"Refunded threat")
- log_threat("[regain] refunded. Threat is now [threat].", verbose = TRUE)
-
-/// Generate threat and increase the threat_level if it goes beyond, capped at 100
+/// Increase the threat level.
/datum/game_mode/dynamic/proc/create_threat(gain)
- threat = min(100, threat+gain)
- if(threat > threat_level)
- threat_level = threat
+ threat_level += gain
SSblackbox.record_feedback("tally","dynamic_threat",gain,"Created threat")
- log_threat("[gain] created. Threat is now [threat] and threat level is now [threat_level].", verbose = TRUE)
+ log_threat("[gain] created. Threat level is now [threat_level].", verbose = TRUE)
-/// Expend threat, can't fall under 0.
+/// Decrease the threat level.
+/datum/game_mode/dynamic/proc/remove_threat(loss)
+ threat_level -= loss
+ SSblackbox.record_feedback("tally","dynamic_threat",loss,"Removed threat")
+ log_threat("[loss] removed. Threat level is now [threat_level].", verbose = TRUE)
+
+/// Fill up more of the threat level.
/datum/game_mode/dynamic/proc/spend_threat(cost)
- threat = max(threat-cost,0)
+ threat += cost
SSblackbox.record_feedback("tally","dynamic_threat",cost,"Threat spent")
log_threat("[cost] spent. Threat is now [threat].", verbose = TRUE)
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets.dm b/code/game/gamemodes/dynamic/dynamic_rulesets.dm
index 2f143f7233..eb7da2144f 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets.dm
@@ -174,8 +174,8 @@
/// This is called if persistent variable is true everytime SSTicker ticks.
/datum/dynamic_ruleset/proc/rule_process()
- return
-
+ return TRUE
+
/// Called on game mode pre_setup for roundstart rulesets.
/// Do everything you need to do before job is assigned here.
/// IMPORTANT: ASSIGN special_role HERE
@@ -201,8 +201,7 @@
/// Runs from gamemode process() if ruleset fails to start, like delayed rulesets not getting valid candidates.
/// This one only handles refunding the threat, override in ruleset to clean up the rest.
/datum/dynamic_ruleset/proc/clean_up()
- mode.refund_threat(cost + (scaled_times * scaling_cost))
- mode.log_threat("[ruletype] [name] refunded [cost + (scaled_times * scaling_cost)]",verbose=TRUE)
+ return
/// Gets weight of the ruleset
/// Note that this decreases weight if repeatable is TRUE and repeatable_weight_decrease is higher than 0
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
index 638d8b53b6..15c531b15d 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
@@ -315,7 +315,6 @@
/datum/dynamic_ruleset/midround/from_ghosts/wizard
name = "Wizard"
config_tag = "midround_wizard"
- persistent = TRUE
antag_datum = /datum/antagonist/wizard
antag_flag = ROLE_WIZARD
enemy_roles = list("Security Officer","Detective","Head of Security", "Captain")
@@ -344,7 +343,6 @@
/datum/dynamic_ruleset/midround/from_ghosts/wizard/rule_process() // i can literally copy this from are_special_antags_dead it's great
if(isliving(wizard.current) && wizard.current.stat!=DEAD)
return FALSE
-
for(var/obj/item/phylactery/P in GLOB.poi_list) //TODO : IsProperlyDead()
if(P.mind && P.mind.has_antag_datum(/datum/antagonist/wizard))
return FALSE
@@ -666,9 +664,6 @@
Mind.transfer_to(Ninja)
var/datum/antagonist/ninja/ninjadatum = new
ninjadatum.helping_station = pick(TRUE,FALSE)
- if(ninjadatum.helping_station)
- mode.refund_threat(cost+5)
- mode.log_threat("Ninja was helping station; [cost+5] cost refunded.")
Mind.add_antag_datum(ninjadatum)
if(Ninja.mind != Mind) //something has gone wrong!
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
index 2527c83077..a7fc9b86ce 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm
@@ -802,11 +802,10 @@
/datum/dynamic_ruleset/roundstart/bloodsucker
name = "Bloodsuckers"
config_tag = "bloodsucker"
- persistent = TRUE
antag_flag = ROLE_BLOODSUCKER
antag_datum = ANTAG_DATUM_BLOODSUCKER
minimum_required_age = 0
- protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
+ protected_roles = list("Chaplain", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
restricted_roles = list("Cyborg", "AI")
required_candidates = 1
weight = 2
diff --git a/code/game/gamemodes/dynamic/dynamic_storytellers.dm b/code/game/gamemodes/dynamic/dynamic_storytellers.dm
index d25416a4d1..63fd12f47a 100644
--- a/code/game/gamemodes/dynamic/dynamic_storytellers.dm
+++ b/code/game/gamemodes/dynamic/dynamic_storytellers.dm
@@ -5,17 +5,18 @@
var/list/property_weights = list() // See below.
var/curve_centre = 0 // As GLOB.dynamic_curve_centre.
var/curve_width = 1.8 // As GLOB.dynamic_curve_width.
- var/forced_threat_level = -1
+ var/forced_threat_level = -1 // As GLOB.dynamic_forced_threat_level
/*
NO_ASSASSIN: Will not have permanent assassination targets.
WAROPS_ALWAYS_ALLOWED: Can always do warops, regardless of threat level.
USE_PREF_WEIGHTS: Will use peoples' preferences to change the threat centre.
+ FORCE_IF_WON: If this mode won the vote, forces it
*/
var/flags = 0
+ var/dead_player_weight = 1 // How much dead players matter for threat calculation
var/weight = 3 // Weights for randomly picking storyteller. Multiplied by score after voting.
var/event_frequency_lower = 6 MINUTES // How rare events will be, at least.
var/event_frequency_upper = 20 MINUTES // How rare events will be, at most.
- var/pop_antag_ratio = 5 // How many non-antags there should be vs antags.
var/datum/game_mode/dynamic/mode = null // Cached as soon as it's made, by dynamic.
/**
@@ -39,6 +40,27 @@ Property weights are:
var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
mode.event_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time)
+/datum/dynamic_storyteller/proc/calculate_threat()
+ var/threat = 0
+ for(var/datum/antagonist/A in GLOB.antagonists)
+ if(A?.owner?.current && A.owner.current.stat != DEAD)
+ threat += A.threat()
+ for(var/r in SSevents.running)
+ var/datum/round_event/R = r
+ threat += R.threat()
+ for(var/mob/living/simple_animal/hostile/H in GLOB.mob_living_list)
+ var/turf/T = get_turf(H)
+ if(H.stat != DEAD && is_station_level(T.z) && !("Station" in H.faction))
+ threat += H.threat()
+ for (var/mob/M in mode.current_players[CURRENT_LIVING_PLAYERS])
+ if (M.stat != DEAD && M.mind && M.mind.assigned_role)
+ if(length(M.mind.antag_datums))
+ threat += SSjob.GetJob(M.mind.assigned_role).GetThreat()
+ else
+ threat -= SSjob.GetJob(M.mind.assigned_role).GetThreat()
+ threat += (mode.current_players[CURRENT_DEAD_PLAYERS].len)*dead_player_weight
+ return round(threat,0.1)
+
/datum/dynamic_storyteller/proc/do_process()
return
@@ -83,25 +105,9 @@ Property weights are:
if(mode.forced_injection)
mode.forced_injection = !dry_run
return 100
- var/chance = 0
- // If the high pop override is in effect, we reduce the impact of population on the antag injection chance
- var/high_pop_factor = (mode.current_players[CURRENT_LIVING_PLAYERS].len >= GLOB.dynamic_high_pop_limit)
- var/max_pop_per_antag = max(pop_antag_ratio,15 - round(mode.threat_level/10) - round(mode.current_players[CURRENT_LIVING_PLAYERS].len/(high_pop_factor ? 10 : 5)))
- if (!mode.current_players[CURRENT_LIVING_ANTAGS].len)
- chance += 80 // No antags at all? let's boost those odds!
- else
- var/current_pop_per_antag = mode.current_players[CURRENT_LIVING_PLAYERS].len / mode.current_players[CURRENT_LIVING_ANTAGS].len
- if (current_pop_per_antag > max_pop_per_antag)
- chance += min(50, 25+10*(current_pop_per_antag-max_pop_per_antag))
- else
- chance += 25-10*(max_pop_per_antag-current_pop_per_antag)
- if (mode.current_players[CURRENT_DEAD_PLAYERS].len > mode.current_players[CURRENT_LIVING_PLAYERS].len)
- chance -= 30 // More than half the crew died? ew, let's calm down on antags
- if (mode.threat > 70)
- chance += 15
- if (mode.threat < 30)
- chance -= 15
- return round(max(0,chance))
+ var/threat_perc = mode.threat/mode.threat_level
+
+ return round(max(0,100*(1-(threat_perc*threat_perc*threat_perc))))
/datum/dynamic_storyteller/proc/roundstart_draft()
var/list/drafted_rules = list()
@@ -114,32 +120,41 @@ Property weights are:
for(var/property in property_weights)
if(property in rule.property_weights) // just treat it as 0 if it's not in there
property_weight += rule.property_weights[property] * property_weights[property]
- drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
+ drafted_rules[rule] = (rule.get_weight() * property_weight)*rule.weight_mult
return drafted_rules
/datum/dynamic_storyteller/proc/midround_draft()
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/midround/rule in mode.midround_rules)
// if there are antags OR the rule is an antag rule, antag_acceptable will be true.
- if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost)
+ if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level))
// Classic secret : only autotraitor/minor roles
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
continue
rule.trim_candidates()
+ var/cost_difference = abs(rule.cost-(mode.threat_level-mode.threat))
+ /* Basically, the closer the cost is to the current threat-level-away-from-threat, the more likely it is to
+ pick this particular ruleset.
+ Let's use a toy example: there's 60 threat level and 10 threat spent.
+ We want to pick a ruleset that's close to that, so we run the below equation, on two rulesets.
+ Ruleset 1 has 30 cost, ruleset 2 has 5 cost.
+ When we do the math, ruleset 1's threat_weight is 0.538, and ruleset 2's is 0.238, meaning ruleset 1
+ is 2.26 times as likely to be picked, all other things considered.
+ Of course, we don't want it to GUARANTEE the closest, that's no fun, so it's just a weight.
+ */
+ var/threat_weight = 1-abs(1-LOGISTIC_FUNCTION(2,0.05,cost_difference,0))
if (rule.ready())
var/property_weight = 0
for(var/property in property_weights)
if(property in rule.property_weights)
property_weight += rule.property_weights[property] * property_weights[property]
- drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
- else if(mode.threat < rule.cost)
- SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
+ drafted_rules[rule] = round(((rule.get_weight() * property_weight)*rule.weight_mult*threat_weight)*1000,1)
return drafted_rules
/datum/dynamic_storyteller/proc/latejoin_draft(mob/living/carbon/human/newPlayer)
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/latejoin/rule in mode.latejoin_rules)
- if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost)
+ if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level - mode.threat))
// Classic secret : only autotraitor/minor roles
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
continue
@@ -150,56 +165,53 @@ Property weights are:
rule.candidates = list(newPlayer)
rule.trim_candidates()
+ var/cost_difference = abs(rule.cost-(mode.threat_level-mode.threat))
+ var/threat_weight = 1-abs(1-(LOGISTIC_FUNCTION(2,0.05,cost_difference,0)))
if (rule.ready())
var/property_weight = 0
for(var/property in property_weights)
if(property in rule.property_weights)
property_weight += rule.property_weights[property] * property_weights[property]
- drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
- else if(mode.threat < rule.cost)
- SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
+ drafted_rules[rule] = round(((rule.get_weight() * property_weight)*rule.weight_mult*threat_weight)*1000,1)
return drafted_rules
/datum/dynamic_storyteller/proc/event_draft()
var/list/drafted_rules = list()
for(var/datum/dynamic_ruleset/event/rule in mode.events)
- if(rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost)
+ if(rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && (mode.threat_level - mode.threat) >= rule.cost)
if(rule.ready())
var/property_weight = 0
for(var/property in property_weights)
if(property in rule.property_weights)
property_weight += rule.property_weights[property] * property_weights[property]
drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
- else if(mode.threat < rule.cost)
- SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
return drafted_rules
-/datum/dynamic_storyteller/cowabunga
+/datum/dynamic_storyteller/chaotic
name = "Chaotic"
config_tag = "chaotic"
curve_centre = 10
desc = "High chaos modes. Revs, wizard, clock cult. Multiple antags at once. Chaos is kept up all round."
- property_weights = list("extended" = -1, "chaos" = 10)
+ property_weights = list("extended" = -1, "chaos" = 2)
weight = 1
event_frequency_lower = 2 MINUTES
event_frequency_upper = 10 MINUTES
- flags = WAROPS_ALWAYS_ALLOWED
- pop_antag_ratio = 4
+ flags = WAROPS_ALWAYS_ALLOWED | FORCE_IF_WON
var/refund_cooldown = 0
-/datum/dynamic_storyteller/cowabunga/get_midround_cooldown()
+/datum/dynamic_storyteller/chaotic/do_process()
+ if(refund_cooldown < world.time)
+ mode.create_threat(20)
+ mode.log_threat("Chaotic storyteller ramped up the chaos. Threat level is now [mode.threat_level].")
+ refund_cooldown = world.time + 20 MINUTES
+
+/datum/dynamic_storyteller/chaotic/get_midround_cooldown()
return ..() / 4
-/datum/dynamic_storyteller/cowabunga/get_latejoin_cooldown()
+/datum/dynamic_storyteller/chaotic/get_latejoin_cooldown()
return ..() / 4
-/datum/dynamic_storyteller/cowabunga/do_process()
- if(refund_cooldown < world.time)
- mode.refund_threat(40)
- mode.log_threat("Chaotic storyteller refunded 40 threat. Threat is now [mode.threat].")
- refund_cooldown = world.time + 1200 SECONDS
-
/datum/dynamic_storyteller/team
name = "Teamwork"
config_tag = "teamwork"
@@ -223,23 +235,93 @@ Property weights are:
flags = WAROPS_ALWAYS_ALLOWED
property_weights = list("valid" = 1, "conversion" = 20)
-/datum/dynamic_storyteller/classic
+/datum/dynamic_storyteller/random
name = "Random"
config_tag = "random"
- desc = "No special weights attached. Anything goes."
- weight = 4
- curve_width = 4
- pop_antag_ratio = 7
- flags = USE_PREF_WEIGHTS
+ weight = 1
+ desc = "No weighting at all; every ruleset has the same chance of happening. Cooldowns vary wildly. As random as it gets."
+ forced_threat_level = 100
+
+/datum/dynamic_storyteller/random/get_midround_cooldown()
+ return rand(GLOB.dynamic_midround_delay_min/2, GLOB.dynamic_midround_delay_max*2)
+
+/datum/dynamic_storyteller/random/get_event_cooldown()
+ return rand(GLOB.dynamic_event_delay_min/2, GLOB.dynamic_event_delay_max*2)
+
+/datum/dynamic_storyteller/random/get_latejoin_cooldown()
+ return rand(GLOB.dynamic_latejoin_delay_min/2, GLOB.dynamic_latejoin_delay_max*2)
+
+/datum/dynamic_storyteller/random/get_injection_chance()
+ return 50 // i would do rand(0,100) but it's actually the same thing when you do the math
+
+/datum/dynamic_storyteller/random/calculate_threat()
+ return 0 // what IS threat
+
+/datum/dynamic_storyteller/random/roundstart_draft()
+ var/list/drafted_rules = list()
+ for (var/datum/dynamic_ruleset/roundstart/rule in mode.roundstart_rules)
+ if (rule.acceptable(mode.roundstart_pop_ready, mode.threat_level)) // If we got the population and threat required
+ rule.candidates = mode.candidates.Copy()
+ rule.trim_candidates()
+ if (rule.ready() && rule.candidates.len > 0)
+ drafted_rules[rule] = 1
+ return drafted_rules
+
+/datum/dynamic_storyteller/random/midround_draft()
+ var/list/drafted_rules = list()
+ for (var/datum/dynamic_ruleset/midround/rule in mode.midround_rules)
+ if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level))
+ // Classic secret : only autotraitor/minor roles
+ if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
+ continue
+ rule.trim_candidates()
+ if (rule.ready())
+ drafted_rules[rule] = 1
+ return drafted_rules
+
+/datum/dynamic_storyteller/random/latejoin_draft(mob/living/carbon/human/newPlayer)
+ var/list/drafted_rules = list()
+ for (var/datum/dynamic_ruleset/latejoin/rule in mode.latejoin_rules)
+ if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level))
+ // Classic secret : only autotraitor/minor roles
+ if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
+ continue
+ // No stacking : only one round-ender, unless threat level > stacking_limit.
+ if (mode.threat_level > GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking)
+ if(rule.flags & HIGHLANDER_RULESET && mode.highlander_executed)
+ continue
+ rule.candidates = list(newPlayer)
+ rule.trim_candidates()
+ if (rule.ready())
+ drafted_rules[rule] = 1
+ return drafted_rules
+
+/datum/dynamic_storyteller/random/event_draft()
+ var/list/drafted_rules = list()
+ for(var/datum/dynamic_ruleset/event/rule in mode.events)
+ if(rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level))
+ if(rule.ready())
+ drafted_rules[rule] = 1
+ return drafted_rules
/datum/dynamic_storyteller/story
name = "Story"
config_tag = "story"
- desc = "Antags with options for loadouts and gimmicks. Traitor, wizard, nukies."
+ desc = "Antags with options for loadouts and gimmicks. Traitor, wizard, nukies. Has a buildup-climax-falling action threat curve."
weight = 2
curve_width = 2
- pop_antag_ratio = 7
- property_weights = list("story_potential" = 10)
+ property_weights = list("story_potential" = 2)
+
+
+/datum/dynamic_storyteller/story/do_process()
+ var/current_time = (world.time / SSautotransfer.targettime)*180
+ mode.threat_level = round(mode.initial_threat_level*(sin(current_time)+0.5),0.1)
+
+/datum/dynamic_storyteller/classic
+ name = "Classic"
+ config_tag = "classic"
+ desc = "No special antagonist weights. Good variety, but not like random. Uses your chaos preference to weight."
+ flags = USE_PREF_WEIGHTS
/datum/dynamic_storyteller/suspicion
name = "Intrigue"
@@ -247,8 +329,8 @@ Property weights are:
desc = "Antags that instill distrust in the crew. Traitors, bloodsuckers."
weight = 2
curve_width = 2
- pop_antag_ratio = 7
- property_weights = list("trust" = -5)
+ dead_player_weight = 2
+ property_weights = list("trust" = -3)
/datum/dynamic_storyteller/liteextended
name = "Calm"
@@ -256,10 +338,10 @@ Property weights are:
desc = "Low-chaos round. Few antags. No conversion."
curve_centre = -3
curve_width = 0.5
- flags = NO_ASSASSIN
+ flags = NO_ASSASSIN | FORCE_IF_WON
weight = 1
- pop_antag_ratio = 10
- property_weights = list("extended" = 1, "chaos" = -1, "valid" = -1, "story_potential" = 1, "conversion" = -10)
+ dead_player_weight = 5
+ property_weights = list("extended" = 2, "chaos" = -1, "valid" = -1, "story_potential" = 1, "conversion" = -10)
/datum/dynamic_storyteller/no_antag
name = "Extended"
@@ -267,7 +349,7 @@ Property weights are:
desc = "No standard antags. Threatening events may still spawn."
curve_centre = -5
curve_width = 0.5
- flags = NO_ASSASSIN
+ flags = NO_ASSASSIN | FORCE_IF_WON
weight = 1
property_weights = list("extended" = 2)
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index b98462d4d4..5affeba6d6 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -323,7 +323,7 @@
var/free_tickets = CONFIG_GET(number/default_antag_tickets)
//Max extra tickets you can use
var/additional_tickets = CONFIG_GET(number/max_tickets_per_roll)
-
+
var/list/ckey_to_mind = list() //this is admittedly shitcode but I'm webediting
var/list/prev_tickets = SSpersistence.antag_rep //cache for hyper-speed in theory. how many tickets someone has stored
var/list/curr_tickets = list() //how many tickets someone has for *this* antag roll, so with the free tickets
@@ -337,7 +337,7 @@
continue
curr_tickets[mind_ckey] = amount
ckey_to_mind[mind_ckey] = M //make sure we can look them up after picking
-
+
if(!return_list) //return a single guy
var/ckey
if(length(curr_tickets))
@@ -584,7 +584,7 @@
//By default nuke just ends the round
/datum/game_mode/proc/OnNukeExplosion(off_station)
nuke_off_station = off_station
- if(off_station < 2)
+ if(!off_station)
station_was_nuked = TRUE //Will end the round on next check.
//Additional report section in roundend report
diff --git a/code/game/gamemodes/gangs/gang.dm b/code/game/gamemodes/gangs/gang.dm
index fa25701ac4..df94d68688 100644
--- a/code/game/gamemodes/gangs/gang.dm
+++ b/code/game/gamemodes/gangs/gang.dm
@@ -4,6 +4,7 @@
can_coexist_with_others = FALSE
job_rank = ROLE_GANG
antagpanel_category = "Gang"
+ threat = 2
var/hud_type = "gangster"
var/message_name = "Gangster"
var/datum/team/gang/gang
@@ -167,6 +168,7 @@
name = "Gang boss"
hud_type = "gang_boss"
message_name = "Leader"
+ threat = 10
/datum/antagonist/gang/boss/on_gain()
..()
@@ -474,4 +476,4 @@
#undef MAXIMUM_RECALLS
-#undef INFLUENCE_INTERVAL
\ No newline at end of file
+#undef INFLUENCE_INTERVAL
diff --git a/code/game/machinery/doors/shutters.dm b/code/game/machinery/doors/shutters.dm
index bd58649b97..9df7bffafd 100644
--- a/code/game/machinery/doors/shutters.dm
+++ b/code/game/machinery/doors/shutters.dm
@@ -1,13 +1,67 @@
/obj/machinery/door/poddoor/shutters
gender = PLURAL
name = "shutters"
- desc = "Heavy duty metal shutters that open mechanically."
+ desc = "Mechanical metal shutters operated by a button with a magnetic seal, keeping them airtight."
icon = 'icons/obj/doors/shutters.dmi'
layer = SHUTTER_LAYER
closingLayer = SHUTTER_LAYER
+ armor = list("melee" = 20, "bullet" = 20, "laser" = 20, "energy" = 75, "bomb" = 25, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70)
damage_deflection = 20
+ max_integrity = 100
/obj/machinery/door/poddoor/shutters/preopen
icon_state = "open"
density = FALSE
opacity = 0
+
+// The below shutters are the original ones from the TG codebase. They are remaining as "secure" shutters. If anyone wants to improve their animation, feel free.
+// The original shutters are now shutters_old.dmi; copy the naming format of the files into new a new .dmi to add new shutters that work with the poddoor code for animating the doors.
+// Originally, the shutters were reskins of blast doors. Eighty hits with the Cap's sabre to destroy one shutter is far too powerful considering shutters cannot be deconstructed (yet).
+// If you're a mapper and want super strong shutter, use the 'old' ones.
+
+/obj/machinery/door/poddoor/shutters/old
+ name = "strong shutters"
+ desc = "These shutters have an armoured frame; it looks like plasteel. These shutters look robust enough to survive explosions."
+ icon = 'icons/obj/doors/shutters_old.dmi'
+ icon_state = "closed"
+ armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 75, "bomb" = 30, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70)
+ max_integrity = 300
+
+/obj/machinery/door/poddoor/shutters/old/preopen
+ icon_state = "open"
+ density = FALSE
+ opacity = 0
+
+// End of old shutter stuff. Credit for the old shutter sprites to TG.
+
+/obj/machinery/door/poddoor/shutters/radiation
+ name = "radiation shutters"
+ desc = "Lead-lined shutters painted yellow with a radioactive hazard symbol on it. Blocks out most radiation"
+ icon = 'icons/obj/doors/shutters_radiation.dmi'
+ icon_state = "closed"
+ rad_insulation = 0.2
+
+/obj/machinery/door/poddoor/shutters/radiation/preopen
+ icon_state = "open"
+ density = FALSE
+ opacity = 0
+ rad_insulation = 1
+
+/obj/machinery/door/poddoor/shutters/radiation/do_animate(animation)
+ ..()
+ switch(animation)
+ if("opening")
+ rad_insulation = 1
+ if("closing")
+ rad_insulation = 0.2
+
+/obj/machinery/door/poddoor/shutters/window
+ name = "windowed shutters"
+ desc = "Mechanical shutters that have some form of plastic window in them, allowing you to see through the shutters at all times."
+ icon = 'icons/obj/doors/shutters_window.dmi'
+ icon_state = "closed"
+ opacity = 0
+
+/obj/machinery/door/poddoor/shutters/window/preopen
+ icon_state = "open"
+ density = FALSE
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 5a215f0396..e1d6101aa5 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -34,7 +34,12 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
var/usesound = null
var/throwhitsound = null
var/w_class = WEIGHT_CLASS_NORMAL
+
+ /// The amount of stamina it takes to swing an item in a normal melee attack do not lie to me and say it's for realism because it ain't. If null it will autocalculate from w_class.
var/total_mass //Total mass in arbitrary pound-like values. If there's no balance reasons for an item to have otherwise, this var should be the item's weight in pounds.
+ /// How long, in deciseconds, this staggers for, if null it will autocalculate from w_class and force. Unlike total mass this supports 0 and negatives.
+ var/stagger_force
+
var/slot_flags = 0 //This is used to determine on which slots an item can fit.
pass_flags = PASSTABLE
pressure_resistance = 4
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index bf023c2c07..29a31122bf 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -170,7 +170,7 @@
flags_1 = CONDUCT_1
brightness_on = 2
light_color = "#FFDDCC"
- flashlight_power = 0.3
+ flashlight_power = 0.5
var/holo_cooldown = 0
/obj/item/flashlight/pen/afterattack(atom/target, mob/user, proximity_flag)
@@ -227,15 +227,12 @@
custom_materials = null
on = TRUE
-
// green-shaded desk lamp
/obj/item/flashlight/lamp/green
desc = "A classic green-shaded desk lamp."
icon_state = "lampgreen"
item_state = "lampgreen"
-
-
/obj/item/flashlight/lamp/verb/toggle_light()
set name = "Toggle light"
set category = "Object"
@@ -258,12 +255,13 @@
desc = "A red Nanotrasen issued flare. There are instructions on the side, it reads 'pull cord, make light'."
w_class = WEIGHT_CLASS_SMALL
brightness_on = 7 // Pretty bright.
+ total_mass = 0.8
light_color = "#FA421A"
icon_state = "flare"
item_state = "flare"
actions_types = list()
var/fuel = 0
- var/on_damage = 7
+ var/on_damage = 9
var/produce_heat = 1500
heat = 1000
light_color = LIGHT_COLOR_FLARE
@@ -331,14 +329,15 @@
name = "torch"
desc = "A torch fashioned from some leaves and a log."
w_class = WEIGHT_CLASS_BULKY
- brightness_on = 4
+ brightness_on = 6 //When on were like a lantern
light_color = "#FAA44B"
icon_state = "torch"
item_state = "torch"
lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items_righthand.dmi'
light_color = LIGHT_COLOR_ORANGE
- on_damage = 10
+ total_mass = TOTAL_MASS_NORMAL_ITEM
+ on_damage = 12 //Its a log thats on fire
slot_flags = null
/obj/item/flashlight/lantern
@@ -348,10 +347,9 @@
lefthand_file = 'icons/mob/inhands/equipment/mining_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/mining_righthand.dmi'
desc = "A mining lantern."
- brightness_on = 6 // luminosity when on
+ brightness_on = 6 // luminosity when on
light_color = "#FFAA44"
- flashlight_power = 0.75
-
+ flashlight_power = 0.8
/obj/item/flashlight/slime
gender = PLURAL
@@ -372,7 +370,6 @@
var/emp_cur_charges = 4
var/charge_tick = 0
-
/obj/item/flashlight/emp/New()
..()
START_PROCESSING(SSobj, src)
@@ -435,7 +432,7 @@
var/fuel = 0
/obj/item/flashlight/glowstick/Initialize()
- fuel = rand(1600, 2000)
+ fuel = rand(1000, 1500)
light_color = color
. = ..()
@@ -540,7 +537,7 @@
/obj/item/flashlight/eyelight
name = "eyelight"
desc = "This shouldn't exist outside of someone's head, how are you seeing this?"
- brightness_on = 15
+ brightness_on = 10
flags_1 = CONDUCT_1
item_flags = DROPDEL
actions_types = list()
diff --git a/code/game/objects/items/miscellaneous.dm b/code/game/objects/items/miscellaneous.dm
index a2145e67ee..c6bb350929 100644
--- a/code/game/objects/items/miscellaneous.dm
+++ b/code/game/objects/items/miscellaneous.dm
@@ -58,6 +58,21 @@
new /obj/effect/abstract/DPtarget(get_turf(src), pod)
+/obj/item/choice_beacon/ingredients
+ name = "ingredient box delivery beacon"
+ desc = "Summon a box of ingredients from a wide selection!"
+ icon_state = "gangtool-red"
+
+/obj/item/choice_beacon/ingredients/generate_display_names()
+ var/static/list/ingredientboxes
+ if(!ingredientboxes)
+ ingredientboxes = list()
+ var/list/templist = typesof(/obj/item/storage/box/ingredients)
+ for(var/V in templist)
+ var/obj/item/storage/box/ingredients/A = V
+ ingredientboxes[initial(A.theme_name)] = A
+ return ingredientboxes
+
/obj/item/choice_beacon/hero
name = "heroic beacon"
desc = "To summon heroes from the past to protect the future."
@@ -72,7 +87,6 @@
hero_item_list[initial(A.name)] = A
return hero_item_list
-
/obj/item/storage/box/hero
name = "Courageous Tomb Raider - 1940's."
@@ -115,4 +129,5 @@
icon = 'icons/obj/supermatter.dmi'
icon_state = "supermatterspray"
w_class = WEIGHT_CLASS_SMALL
- var/usesleft = 2
\ No newline at end of file
+ var/usesleft = 2
+
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index a54d126abb..b596e40e36 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -120,6 +120,7 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \
)), \
null, \
new/datum/stack_recipe("iron door", /obj/structure/mineral_door/iron, 20, one_per_turf = TRUE, on_floor = TRUE), \
+ new/datum/stack_recipe("pestle", /obj/item/pestle, 1, time = 50), \
new/datum/stack_recipe("floodlight frame", /obj/structure/floodlight_frame, 5, one_per_turf = TRUE, on_floor = TRUE), \
))
@@ -255,6 +256,7 @@ GLOBAL_LIST_INIT(wood_recipes, list ( \
new/datum/stack_recipe("apiary", /obj/structure/beebox, 40, time = 50),\
null, \
new/datum/stack_recipe("picture frame", /obj/item/wallframe/picture, 1, time = 10),\
+ new/datum/stack_recipe("mortar", /obj/item/reagent_containers/glass/mortar, 3), \
new/datum/stack_recipe("honey frame", /obj/item/honey_frame, 5, time = 10),\
))
diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm
index 489704dbd5..a67757a535 100644
--- a/code/game/objects/items/storage/boxes.dm
+++ b/code/game/objects/items/storage/boxes.dm
@@ -946,12 +946,47 @@
/obj/item/reagent_containers/food/snacks/grown/apple,
/obj/item/reagent_containers/food/snacks/chocolatebar,
/obj/item/reagent_containers/food/snacks/grown/cherries,
+ /obj/item/reagent_containers/food/snacks/grown/berries,
/obj/item/reagent_containers/food/snacks/grown/banana,
/obj/item/reagent_containers/food/snacks/grown/cabbage,
/obj/item/reagent_containers/food/snacks/grown/soybeans,
/obj/item/reagent_containers/food/snacks/grown/corn,
/obj/item/reagent_containers/food/snacks/grown/mushroom/plumphelmet,
- /obj/item/reagent_containers/food/snacks/grown/mushroom/chanterelle)
+ /obj/item/reagent_containers/food/snacks/grown/mushroom/chanterelle,
+ /obj/item/reagent_containers/food/snacks/faggot,
+ /obj/item/reagent_containers/food/snacks/grown/citrus/orange,
+ /obj/item/reagent_containers/food/snacks/grown/citrus/lemon,
+ /obj/item/reagent_containers/food/snacks/grown/citrus/lime,
+ /obj/item/reagent_containers/food/snacks/grown/bluecherries,
+ /obj/item/reagent_containers/food/snacks/grown/cocoapod,
+ /obj/item/reagent_containers/food/snacks/grown/vanillapod,
+ /obj/item/reagent_containers/food/snacks/grown/grapes,
+ /obj/item/reagent_containers/food/snacks/grown/strawberry,
+ /obj/item/reagent_containers/food/snacks/grown/whitebeet,
+ /obj/item/reagent_containers/food/snacks/meat/slab/bear,
+ /obj/item/reagent_containers/food/snacks/meat/slab/spider,
+ /obj/item/reagent_containers/food/snacks/spidereggs,
+ /obj/item/reagent_containers/food/snacks/carpmeat,
+ /obj/item/reagent_containers/food/snacks/meat/slab/xeno,
+ /obj/item/reagent_containers/food/snacks/meat/slab/corgi,
+ /obj/item/reagent_containers/food/snacks/grown/oat,
+ /obj/item/reagent_containers/food/snacks/grown/wheat,
+ /obj/item/reagent_containers/honeycomb,
+ /obj/item/reagent_containers/food/snacks/grown/watermelon,
+ /obj/item/reagent_containers/food/snacks/grown/onion,
+ /obj/item/reagent_containers/food/snacks/grown/peach,
+ /obj/item/reagent_containers/food/snacks/grown/peanut,
+ /obj/item/reagent_containers/food/snacks/grown/pineapple,
+ /obj/item/reagent_containers/food/snacks/grown/pumpkin,
+ /obj/item/reagent_containers/food/snacks/meat/rawcrab,
+ /obj/item/reagent_containers/food/snacks/meat/slab/goliath,
+ /obj/item/reagent_containers/food/snacks/meat/slab/chicken,
+ /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/slime,
+ /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/golem,
+ /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/lizard,
+ /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/skeleton,
+ /obj/item/reagent_containers/food/snacks/egg,
+ /obj/item/reagent_containers/food/snacks/grown/eggplant)
new randomFood(src)
/obj/item/storage/box/ingredients/fiesta
@@ -1061,6 +1096,15 @@
new /obj/item/reagent_containers/food/snacks/grown/cabbage(src)
new /obj/item/reagent_containers/food/snacks/grown/chili(src)
+/obj/item/storage/box/ingredients/sushi
+ theme_name = "sushi"
+
+/obj/item/storage/box/ingredients/sushi/PopulateContents()
+ for(var/i in 1 to 3)
+ new /obj/item/reagent_containers/food/snacks/sea_weed(src)
+ new /obj/item/reagent_containers/food/snacks/carpmeat(src)
+ new /obj/item/reagent_containers/food/snacks/meat/rawcrab(src)
+
/obj/item/storage/box/emptysandbags
name = "box of empty sandbags"
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index c92bbdc996..4eaa0b4e0a 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -211,9 +211,6 @@
/obj/proc/check_uplink_validity()
return 1
-/obj/proc/intercept_user_move(dir, mob, newLoc, oldLoc)
- return
-
/obj/vv_get_dropdown()
. = ..()
.["Delete all of type"] = "?_src_=vars;[HrefToken()];delall=[REF(src)]"
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index e19feea582..a40314c7ad 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -121,7 +121,7 @@
return FALSE
var/turf/T = get_turf(src)
for(var/mob/living/L in T)
- if(L.anchored || horizontal && L.mob_size > MOB_SIZE_TINY && L.density)
+ if(L.move_resist >= MOVE_FORCE_VERY_STRONG || (horizontal && L.mob_size > MOB_SIZE_TINY && L.density))
if(user)
to_chat(user, "There's something large on top of [src], preventing it from opening." )
return FALSE
@@ -133,7 +133,7 @@
if(closet != src && !closet.wall_mounted)
return FALSE
for(var/mob/living/L in T)
- if(L.anchored || horizontal && L.mob_size > MOB_SIZE_TINY && L.density)
+ if(L.move_resist >= MOVE_FORCE_VERY_STRONG || horizontal && L.mob_size > MOB_SIZE_TINY && L.density)
if(user)
to_chat(user, "There's something too large in [src], preventing it from closing.")
return FALSE
@@ -216,7 +216,7 @@
if(!isliving(AM)) //let's not put ghosts or camera mobs inside closets...
return FALSE
var/mob/living/L = AM
- if(L.anchored || L.buckled || L.incorporeal_move || L.has_buckled_mobs())
+ if(L.move_resist >= MOVE_FORCE_VERY_STRONG || L.buckled || L.incorporeal_move || L.has_buckled_mobs())
return FALSE
if(L.mob_size > MOB_SIZE_TINY) // Tiny mobs are treated as items.
if(horizontal && L.density)
@@ -451,7 +451,7 @@
/obj/structure/closet/relaymove(mob/user)
if(user.stat || !isturf(loc) || !isliving(user))
return
- if(locked)
+ if(locked || welded)
if(message_cooldown <= world.time)
message_cooldown = world.time + 50
to_chat(user, "[src]'s door won't budge!")
diff --git a/code/game/objects/structures/dresser.dm b/code/game/objects/structures/dresser.dm
index ecacf49fc2..528153324d 100644
--- a/code/game/objects/structures/dresser.dm
+++ b/code/game/objects/structures/dresser.dm
@@ -73,7 +73,7 @@
H.socks_color = recolor_undergarment(H, "socks", H.socks_color)
add_fingerprint(H)
- H.update_body()
+ H.update_body(TRUE)
/obj/structure/dresser/proc/recolor_undergarment(mob/living/carbon/human/H, garment_type = "underwear", default_color)
var/n_color = input(H, "Choose your [garment_type]'\s color.", "Character Preference", default_color) as color|null
diff --git a/code/game/objects/structures/ghost_role_spawners.dm b/code/game/objects/structures/ghost_role_spawners.dm
index 7469bd2e72..65145c0eed 100644
--- a/code/game/objects/structures/ghost_role_spawners.dm
+++ b/code/game/objects/structures/ghost_role_spawners.dm
@@ -31,7 +31,7 @@
new_spawn.underwear = "Nude" //You're a plant, partner
new_spawn.undershirt = "Nude" //changing underwear/shirt/socks doesn't seem to function correctly right now because of some bug elsewhere?
new_spawn.socks = "Nude"
- new_spawn.update_body()
+ new_spawn.update_body(TRUE)
//Ash walker eggs: Spawns in ash walker dens in lavaland. Ghosts become unbreathing lizards that worship the Necropolis and are advised to retrieve corpses to create more ash walkers.
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 7be6269589..1df424815b 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -194,7 +194,7 @@
body += "Thunderdome Admin | "
body += "Thunderdome Observer | "
body += "Make mentor | "
- body += "Remove mentor"
+ body += "Remove mentor | "
body += "Allow reentering round"
body += "
"
body += "