diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index 53348b3467..a09c96ad30 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -30,7 +30,7 @@
#define COMSIG_PARENT_ATTACKBY "atom_attackby" //from base of atom/attackby(): (/obj/item, /mob/living, params)
#define COMPONENT_NO_AFTERATTACK 1 //Return this in response if you don't want afterattack to be called
#define COMSIG_ATOM_HULK_ATTACK "hulk_attack" //from base of atom/attack_hulk(): (/mob/living/carbon/human)
-#define COMSIG_PARENT_EXAMINE "atom_examine" //from base of atom/examine(): (/mob)
+#define COMSIG_PARENT_EXAMINE "atom_examine" //from base of atom/examine(): (/mob, list/examine_list)
#define COMSIG_ATOM_GET_EXAMINE_NAME "atom_examine_name" //from base of atom/get_examine_name(): (/mob, list/overrides)
//Positions for overrides list
#define EXAMINE_POSITION_ARTICLE 1
diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm
index 3fb149d071..9febca663d 100644
--- a/code/__DEFINES/misc.dm
+++ b/code/__DEFINES/misc.dm
@@ -523,3 +523,10 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S
#define NIGHTSHIFT_AREA_NONE 4 //default/highest.
#define UNTIL(X) while(!(X)) stoplag()
+
+
+//Scavenging element defines for special loot "events".
+#define SCAVENGING_FOUND_NOTHING "found_nothing"
+#define SCAVENGING_SPAWN_MOUSE "spawn_mouse"
+#define SCAVENGING_SPAWN_MICE "spawn_mice"
+#define SCAVENGING_SPAWN_TOM "spawn_tom_the_mouse"
diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm
index a34b3ce67c..754b56a202 100644
--- a/code/__HELPERS/_lists.dm
+++ b/code/__HELPERS/_lists.dm
@@ -234,38 +234,20 @@
//2. Gets a number between 1 and that total
//3. For each element in the list, subtracts its weighting from that number
//4. If that makes the number 0 or less, return that element.
-/proc/pickweight(list/L)
+/proc/pickweight(list/L, base_weight = 1)
var/total = 0
var/item
for (item in L)
if (!L[item])
- L[item] = 1
+ L[item] = base_weight
total += L[item]
- total = rand(1, total)
+ total = rand() * total
for (item in L)
- total -=L [item]
+ total -= L[item]
if (total <= 0)
return item
- return null
-
-/proc/pickweightAllowZero(list/L) //The original pickweight proc will sometimes pick entries with zero weight. I'm not sure if changing the original will break anything, so I left it be.
- var/total = 0
- var/item
- for (item in L)
- if (!L[item])
- L[item] = 0
- total += L[item]
-
- total = rand(0, total)
- for (item in L)
- total -=L [item]
- if (total <= 0 && L[item])
- return item
-
- return null
-
//Pick a random element from the list and remove it from the list.
/proc/pick_n_take(list/L)
RETURN_TYPE(L[_].type)
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index 2827e40120..d3ad4dcaec 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -311,7 +311,7 @@ GLOBAL_LIST_EMPTY(species_list)
else
return "unknown"
-/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks = null, ignorehelditem = 0)
+/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks = null, ignorehelditem = FALSE, resume_time = 0 SECONDS)
if(!user || !target)
return 0
var/user_loc = user.loc
@@ -330,10 +330,10 @@ GLOBAL_LIST_EMPTY(species_list)
var/endtime = world.time+time
var/starttime = world.time
. = 1
- while (world.time < endtime)
+ while (world.time + resume_time < endtime)
stoplag(1)
if (progress)
- progbar.update(world.time - starttime)
+ progbar.update(world.time - starttime + resume_time)
if(QDELETED(user) || QDELETED(target))
. = 0
break
@@ -365,7 +365,7 @@ GLOBAL_LIST_EMPTY(species_list)
checked_health["health"] = health
return ..()
-/proc/do_after(mob/user, var/delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null, required_mobility_flags = (MOBILITY_USE|MOBILITY_MOVE))
+/proc/do_after(mob/user, var/delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null, required_mobility_flags = (MOBILITY_USE|MOBILITY_MOVE), resume_time = 0 SECONDS)
if(!user)
return 0
var/atom/Tloc = null
@@ -394,10 +394,10 @@ GLOBAL_LIST_EMPTY(species_list)
var/starttime = world.time
. = 1
var/mob/living/L = isliving(user) && user //evals to last thing eval'd
- while (world.time < endtime)
+ while (world.time + resume_time < endtime)
stoplag(1)
if (progress)
- progbar.update(world.time - starttime)
+ progbar.update(world.time - starttime + resume_time)
if(drifting && !user.inertia_dir)
drifting = 0
diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm
index a202afd905..df3f24f0dc 100644
--- a/code/controllers/subsystem/vote.dm
+++ b/code/controllers/subsystem/vote.dm
@@ -361,7 +361,7 @@ SUBSYSTEM_DEF(vote)
picked = S
runnable_storytellers[S] *= round(stored_gamemode_votes[initial(S.name)]*100000,1)
if(!picked)
- picked = pickweightAllowZero(runnable_storytellers)
+ picked = pickweight(runnable_storytellers, 0)
GLOB.dynamic_storyteller_type = picked
if("map")
var/datum/map_config/VM = config.maplist[.]
diff --git a/code/datums/ai_laws.dm b/code/datums/ai_laws.dm
index ebddc187ae..9c6bb2ebfd 100644
--- a/code/datums/ai_laws.dm
+++ b/code/datums/ai_laws.dm
@@ -253,7 +253,7 @@
var/datum/ai_laws/lawtype
var/list/law_weights = CONFIG_GET(keyed_list/law_weight)
while(!lawtype && law_weights.len)
- var/possible_id = pickweightAllowZero(law_weights)
+ var/possible_id = pickweight(law_weights, 0)
lawtype = lawid_to_type(possible_id)
if(!lawtype)
law_weights -= possible_id
diff --git a/code/datums/components/archaeology.dm b/code/datums/components/archaeology.dm
deleted file mode 100644
index b5740650e9..0000000000
--- a/code/datums/components/archaeology.dm
+++ /dev/null
@@ -1,95 +0,0 @@
-/datum/component/archaeology
- dupe_mode = COMPONENT_DUPE_UNIQUE
- var/list/archdrops = list(/obj/item/bikehorn = list(ARCH_PROB = 100, ARCH_MAXDROP = 1)) // honk~
- var/prob2drop
- var/dug
- var/datum/callback/callback
-
-/datum/component/archaeology/Initialize(list/_archdrops = list(), datum/callback/_callback)
- archdrops = _archdrops
- for(var/i in archdrops)
- if(isnull(archdrops[i][ARCH_MAXDROP]))
- archdrops[i][ARCH_MAXDROP] = 1
- stack_trace("ARCHAEOLOGY WARNING: [parent] contained a null max_drop value in [i].")
- if(isnull(archdrops[i][ARCH_PROB]))
- archdrops[i][ARCH_PROB] = 100
- stack_trace("ARCHAEOLOGY WARNING: [parent] contained a null probability value in [i].")
- callback = _callback
- RegisterSignal(parent, COMSIG_PARENT_ATTACKBY,.proc/Dig)
- RegisterSignal(parent, COMSIG_ATOM_EX_ACT, .proc/BombDig)
- RegisterSignal(parent, COMSIG_ATOM_SING_PULL, .proc/SingDig)
-
-/datum/component/archaeology/InheritComponent(datum/component/archaeology/A, i_am_original)
- var/list/other_archdrops = A.archdrops
- var/list/_archdrops = archdrops
- for(var/I in other_archdrops)
- _archdrops[I] += other_archdrops[I]
-
-/datum/component/archaeology/proc/Dig(datum/source, obj/item/I, mob/living/user)
- if(dug)
- to_chat(user, "Looks like someone has dug here already.")
- return
-
- if(!isturf(user.loc))
- return
-
- if(I.tool_behaviour == TOOL_SHOVEL || I.tool_behaviour == TOOL_MINING)
- to_chat(user, "You start digging...")
-
- if(I.use_tool(parent, user, 40, volume=50))
- to_chat(user, "You dig a hole.")
- gets_dug()
- dug = TRUE
- SSblackbox.record_feedback("tally", "pick_used_mining", 1, I.type)
- return COMPONENT_NO_AFTERATTACK
-
-/datum/component/archaeology/proc/gets_dug()
- if(dug)
- return
- else
- var/turf/open/OT = get_turf(parent)
- for(var/thing in archdrops)
- var/maxtodrop = archdrops[thing][ARCH_MAXDROP]
- for(var/i in 1 to maxtodrop)
- if(prob(archdrops[thing][ARCH_PROB])) // can't win them all!
- new thing(OT)
-
- if(isopenturf(OT))
- if(OT.postdig_icon_change)
- if(istype(OT, /turf/open/floor/plating/asteroid/) && !OT.postdig_icon)
- var/turf/open/floor/plating/asteroid/AOT = parent
- AOT.icon_plating = "[AOT.environment_type]_dug"
- AOT.icon_state = "[AOT.environment_type]_dug"
- else
- if(isplatingturf(OT))
- var/turf/open/floor/plating/POT = parent
- POT.icon_plating = "[POT.postdig_icon]"
- POT.icon_state = "[OT.postdig_icon]"
-
- if(OT.slowdown) //Things like snow slow you down until you dig them.
- OT.slowdown = 0
- dug = TRUE
- if(callback)
- callback.Invoke()
-
-/datum/component/archaeology/proc/SingDig(datum/source, S, current_size)
- switch(current_size)
- if(STAGE_THREE)
- if(prob(30))
- gets_dug()
- if(STAGE_FOUR)
- if(prob(50))
- gets_dug()
- else
- if(current_size >= STAGE_FIVE && prob(70))
- gets_dug()
-
-/datum/component/archaeology/proc/BombDig(datum/source, severity, target)
- switch(severity)
- if(3)
- return
- if(2)
- if(prob(20))
- gets_dug()
- if(1)
- gets_dug()
diff --git a/code/datums/elements/scavenging.dm b/code/datums/elements/scavenging.dm
new file mode 100644
index 0000000000..b2ca7adcc3
--- /dev/null
+++ b/code/datums/elements/scavenging.dm
@@ -0,0 +1,164 @@
+ /*
+ * Scavenging element. Its scope shouldn't elude your imagination.
+ * Basically loot piles that can be searched through for some items.
+ * In my opinion, these are more engaging than normal maintenance loot spawners.
+ * The loot doesn't have to be strictly made of items and objects, you could also use it to invoke some "events"
+ * such as mice, rats, an halloween spook, persistent relics, traps, etcetera, go wild.
+ */
+/datum/element/scavenging
+ element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH
+ id_arg_index = 3
+
+ var/list/loot_left_per_atom = list() //loot left per attached atom.
+ var/list/loot_table //pickweight list of available loot.
+ var/list/unique_loot //limited loot, once the associated value reaches zero, its key is removed from loot_table
+ var/scavenge_time = 12 SECONDS //how much time it takes
+ var/can_use_hands = TRUE //bare handed scavenge time multiplier. If set to zero, only tools are usable.
+ var/list/tool_types //which tool types the player can use instead of scavenging by hand, associated value is their speed.
+ var/del_atom_on_depletion = FALSE //Will the atom be deleted when there is no loot left?
+ var/list/search_texts = list("searches through ", "search through ", "You hear rummaging...")
+
+ var/mean_loot_weight = 0
+ var/list/progress_per_atom = list() //seconds of ditched progress per atom, used to resume the work instead of starting over.
+ var/static/list/players_busy_scavenging = list() //players already busy scavenging.
+
+/datum/element/scavenging/Attach(atom/target, amount = 5, list/loot, list/unique, time = 10 SECONDS, hands = TRUE, list/tools, list/texts, del_deplete = FALSE)
+ . = ..()
+ if(. == ELEMENT_INCOMPATIBLE || !length(loot) || !amount || !istype(target) || isarea(target))
+ return ELEMENT_INCOMPATIBLE
+ loot_left_per_atom[target] = amount
+ if(!loot_table)
+ loot_table = loot
+ for(var/A in loot_table) //tally the list weights
+ mean_loot_weight += loot_table[A]
+ mean_loot_weight /= length(loot_table)
+ if(!unique_loot)
+ unique_loot = unique || list()
+ scavenge_time = time
+ can_use_hands = hands
+ tool_types = tools
+ if(texts)
+ search_texts = texts
+ del_atom_on_depletion = del_deplete
+ if(can_use_hands)
+ RegisterSignal(target, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW), .proc/scavenge_barehanded)
+ if(tool_types)
+ RegisterSignal(target, COMSIG_PARENT_ATTACKBY, .proc/scavenge_tool)
+ RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/on_examine)
+
+/datum/element/scavenging/Detach(atom/target)
+ . = ..()
+ loot_left_per_atom -= target
+ progress_per_atom -= target
+ UnregisterSignal(target, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_PARENT_ATTACKBY, COMSIG_PARENT_EXAMINE))
+
+/datum/element/scavenging/proc/on_examine(atom/source, mob/user, list/examine_list)
+ var/text
+ var/methods = tool_types
+ if(can_use_hands)
+ if(!length(methods))
+ methods = list("bare handed")
+ else
+ methods += "simply bare handed"
+ text = english_list(methods, "", " or ")
+ . += "Looks like [source.p_they()] can be scavenged[length(tool_types) ? " with either a" : ""] [text]"
+
+/datum/element/scavenging/proc/scavenge_barehanded(atom/source, mob/user)
+ scavenge(source, user, 1)
+ return COMPONENT_NO_ATTACK_HAND
+
+/datum/element/scavenging/proc/scavenge_tool(atom/source, obj/item/I, mob/living/user, params)
+ if(mob.a_intent == INTENT_HARM) //Robust trash disposal techniques!
+ return
+ var/speed_multi = tool_types[I.tool_behaviour]
+ if(!speed_multi)
+ return
+ scavenge(source, user, speed_multi)
+ return COMPONENT_NO_AFTERATTACK
+
+/datum/element/scavenging/proc/scavenge(atom/source, mob/user, speed_multi = 1)
+ if(players_busy_scavenging[user])
+ return
+ players_busy_scavenging[user] = TRUE
+ var/progress_done = progress_per_atom[source]
+ var/len_messages = length(search_texts)
+ var/msg_first_person
+ if(len_messages >= 2)
+ msg_first_person = "You [progress_done ? "resume a ditched task and " : ""][search_texts[2]] [src]."
+ var/msg_blind
+ if(len_messages >= 3)
+ msg_blind = "[search_texts[3]]"
+ user.visible_message("[user] [search_texts[1]] [src].", msg_first_person, msg_blind)
+ if(do_after(user, scavenge_time, TRUE, source, TRUE, CALLBACK(src, .proc/set_progress, source, world.time), resume_time = progress_done))
+ spawn_loot(source, user)
+ players_busy_scavenging -= user
+ progress_per_atom -= source
+
+/datum/element/scavenging/proc/set_progress(atom/source, start_time)
+ progress_per_atom[source] = world.time - start_time
+
+/datum/element/scavenging/proc/spawn_loot(atom/source, mob/user)
+ var/loot = pickweight(loot_table)
+ var/special = TRUE
+ var/free = FALSE
+ if(!loot_left_per_atom[source])
+ to_chat(user, "Looks likes there is nothing worth of interest left in [src] anymore, further attempts would be futile.")
+ return
+ switch(loot) // TODO: datumize these out.
+ if(SCAVENGING_FOUND_NOTHING)
+ to_chat(user, "You found nothing, better luck next time.")
+ free = TRUE //doesn't consume the loot pile.
+ if(SCAVENGING_SPAWN_MOUSE)
+ var/nasty_rodent = pick("mouse", "rodent", "squeaky critter", "stupid pest", "annoying cable chewer", "nasty, ugly, evil, disease-ridden rodent")
+ to_chat(user, "You found something in [src]... no wait, that's just another [nasty_rodent].")
+ new /mob/living/simple_animal/mouse(source.loc)
+ if(SCAVENGING_SPAWN_MICE)
+ user.visible_message("A small gang of mice emerges from [source].", \
+ "You found something in [src]... no wait, that's just another- no wait, that's a lot of damn mice.")
+ for(var/i in 1 to rand(4, 6))
+ new /mob/living/simple_animal/mouse(source.loc)
+ if(SCAVENGING_SPAWN_TOM)
+ if(GLOB.tom_existed) //There can only be one.
+ to_chat(user, "You found nothing, better luck next time.")
+ free = TRUE
+ else
+ to_chat(user, "You found something in [src]... no wait, that's Tom, the mouse! What is he doing here?")
+ new /mob/living/simple_animal/mouse/brown/Tom(source.loc)
+ else
+ special = FALSE
+
+ if(!special) //generic loot. Nothing too strange like more loot spawners anyway.
+ var/atom/A = new loot(source.loc)
+ if(isitem(A) && !user.get_active_held_item())
+ user.put_in_hands(A)
+ var/rarity_append = "."
+ switch(loot_table[loot]/mean_loot_weight*100)
+ if(0 to 1)
+ rarity_append = "! AMAZING!"
+ if(1 to 2)
+ rarity_append = "! Woah!"
+ if(2 to 5)
+ rarity_append = ". Rare!"
+ if(5 to 10)
+ rarity_append = ". Great."
+ if(10 to 25)
+ rarity_append = ". Nice."
+ if(20 to 50)
+ rarity_append = ". Not bad."
+ to_chat(user, "You found something in [src]... it's \a [A][rarity_append]")
+
+ if(unique_loot[loot])
+ var/loot_left = --unique_loot[loot]
+ if(!loot_left)
+ loot_table -= loot
+ unique_loot -= loot
+ mean_loot_weight = 0
+ for(var/A in loot_table) //re-tally the list weights
+ mean_loot_weight += loot_table[A]
+ mean_loot_weight /= length(loot_table)
+
+ if(!free)
+ --loot_left_per_atom[source]
+ if(del_atom_on_depletion && !loot_left_per_atom[source])
+ source.visible_message("[src] has been looted clean.")
+ qdel(source)
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 5affeba6d6..57919863f2 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -272,7 +272,7 @@
reports += config.mode_reports[config_tag]
Count++
for(var/i in Count to rand(3,5)) //Between three and five wrong entries on the list.
- var/false_report_type = pickweightAllowZero(report_weights)
+ var/false_report_type = pickweight(report_weights, 0)
report_weights[false_report_type] = 0 //Make it so the same false report won't be selected twice
reports += config.mode_reports[false_report_type]
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index 04f14692e3..851a041aab 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -483,7 +483,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/start/new_player)
if(!SSmapping.station_room_templates[t])
log_world("Station room spawner placed at ([T.x], [T.y], [T.z]) has invalid ruin name of \"[t]\" in its list")
templates -= t
- template_name = pickweightAllowZero(templates)
+ template_name = pickweight(templates, 0)
if(!template_name)
GLOB.stationroom_landmarks -= src
qdel(src)
diff --git a/code/game/objects/structures/loot_pile.dm b/code/game/objects/structures/loot_pile.dm
new file mode 100644
index 0000000000..0886c1c8b7
--- /dev/null
+++ b/code/game/objects/structures/loot_pile.dm
@@ -0,0 +1,66 @@
+ /*
+ * Loot piles structures, somewhat inspired from Polaris 13 ones but without the one search per pile ckey/mind restriction
+ * because the actual code is located its own element and has enough variables already. the piles themselves merely cosmetical.
+ */
+/obj/structure/loot_pile
+ name = "pile of junk"
+ desc = "Lots of junk lying around. They say one man's trash is another man's treasure."
+ icon = 'icons/obj/loot_piles.dmi'
+ density = FALSE
+ anchored = TRUE
+ var/loot_amount = 5
+ var/delete_on_depletion = FALSE
+ var/can_use_hands = TRUE
+ var/scavenge_time = 12 SECONDS
+ var/allowed_tools = list(TOOL_SHOVEL = 0.6) //list of tool_behaviours with associated speed multipliers (lower is better)
+ var/icon_states_to_use = list("junk_pile1", "junk_pile2", "junk_pile3", "junk_pile4", "junk_pile5")
+ var/list/loot = list(
+ SCAVENGING_FOUND_NOTHING = 50,
+ SCAVENGING_SPAWN_MOUSE = 10,
+ SCAVENGING_SPAWN_MICE = 5,
+ SCAVENGING_SPAWN_TOM = 1,
+ /obj/item/flashlight/flare = 10,
+ /obj/item/flashlight/glowstick = 10,
+ /obj/item/flashlight/glowstick/blue = 10,
+ /obj/item/flashlight/glowstick/orange = 10,
+ /obj/item/flashlight/glowstick/red = 10,
+ /obj/item/flashlight/glowstick/yellow = 10,
+ /obj/item/clothing/mask/gas = 5,
+ /obj/item/clothing/mask/breath = 10,
+ /obj/item/storage/box = 10,
+ /obj/item/clothing/shoes/galoshes = 2,
+ /obj/item/clothing/shoes/sneakers/black = 10,
+ /obj/item/clothing/gloves/color/fyellow = 5,
+ /obj/item/clothing/gloves/color/yellow = 2,
+ /obj/item/clothing/glasses/sunglasses = 3,
+ /obj/item/clothing/glasses/meson = 3,
+ /obj/item/clothing/glasses/welding = 3,
+ /obj/item/clothing/head/hardhat = 10,
+ /obj/item/clothing/head/ushanka = 10,
+ /obj/item/clothing/head/welding = 5,
+ /obj/item/clothing/suit/hazardvest = 10,
+ /obj/item/clothing/under/syndicate/tacticool = 5,
+ /obj/item/clothing/under/pants/camo = 10,
+ /obj/item/stack/spacecash/c10 = 20,
+ /obj/item/stack/spacecash/c20 = 20,
+ /obj/item/stack/spacecash/c50 = 20,
+ /obj/item/radio/headset = 10)
+
+ /*
+ * Associated values in this list are not weights but numbers of times the kery can be rolled
+ * before being removed from ALL piles with same kind. This is why I wanted 'scavenging' to be an element and not a component.
+ */
+ var/list/unique_loot = list(/obj/item/clothing/gloves/color/yellow = 2, SCAVENGING_SPAWN_TOM = 1)
+
+/obj/structure/loot_pile/Initialize()
+ . = ..()
+ icon_state = pick(icon_states_to_use)
+
+/obj/structure/loot_pile/ComponentInitialize()
+ . = ..()
+ AddElement(/datum/element/scavenging, loot_amount, loot, unique_loot, scavenge_time, can_use_hands, allowed_tools, null, delete_on_depletion)
+
+/obj/structure/loot_pile/infinite
+ name = "endless pile of junk."
+ desc = "Lots of awful code lying around. They don't say one coder's trash is another coder's treasure though..."
+ loot_amount = INFINITY
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index cf10f87bf8..df5e6004ba 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -48,7 +48,7 @@
var/datum/traitor_class/class = GLOB.traitor_classes[C]
var/weight = LOGISTIC_FUNCTION(1.5*class.weight,chaos_weight,class.chaos,0)
weights[C] = weight * 1000
- var/choice = pickweightAllowZero(weights)
+ var/choice = pickweight(weights, 0)
if(!choice)
choice = TRAITOR_HUMAN // it's an "easter egg"
var/datum/traitor_class/actual_class = GLOB.traitor_classes[choice]
diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm
index 370442d4c4..7f83bb9f9b 100644
--- a/code/modules/mob/living/simple_animal/friendly/mouse.dm
+++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm
@@ -97,6 +97,8 @@
body_color = "brown"
icon_state = "mouse_brown"
+GLOBAL_VAR(tom_existed)
+
//TOM IS ALIVE! SQUEEEEEEEE~K :)
/mob/living/simple_animal/mouse/brown/Tom
name = "Tom"
@@ -106,6 +108,10 @@
response_harm = "splats"
gold_core_spawnable = NO_SPAWN
+/mob/living/simple_animal/mouse/brown/Tom/Initialize()
+ . = ..()
+ GLOB.tom_existed = TRUE
+
/obj/item/reagent_containers/food/snacks/deadmouse
name = "dead mouse"
desc = "It looks like somebody dropped the bass on it. A lizard's favorite meal."
diff --git a/icons/obj/loot_piles.dmi b/icons/obj/loot_piles.dmi
new file mode 100644
index 0000000000..3d83d6612e
Binary files /dev/null and b/icons/obj/loot_piles.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 3b3bb74df1..147c91c92e 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -516,6 +516,7 @@
#include "code\datums\elements\ghost_role_eligibility.dm"
#include "code\datums\elements\mob_holder.dm"
#include "code\datums\elements\polychromic.dm"
+#include "code\datums\elements\scavenging.dm"
#include "code\datums\elements\spellcasting.dm"
#include "code\datums\elements\swimming.dm"
#include "code\datums\elements\sword_point.dm"
@@ -1119,6 +1120,7 @@
#include "code\game\objects\structures\lattice.dm"
#include "code\game\objects\structures\life_candle.dm"
#include "code\game\objects\structures\loom.dm"
+#include "code\game\objects\structures\loot_pile.dm"
#include "code\game\objects\structures\manned_turret.dm"
#include "code\game\objects\structures\memorial.dm"
#include "code\game\objects\structures\mineral_doors.dm"