From e9bf432a0df148be149157b5af65f65becb92495 Mon Sep 17 00:00:00 2001 From: Ghommie <42542238+Ghommie@users.noreply.github.com> Date: Wed, 22 Apr 2020 03:30:40 +0200 Subject: [PATCH 1/4] Scavenging element. --- code/__DEFINES/dcs/signals.dm | 2 +- code/__DEFINES/misc.dm | 7 + code/__HELPERS/_lists.dm | 26 +-- code/__HELPERS/mobs.dm | 12 +- code/controllers/subsystem/vote.dm | 2 +- code/datums/ai_laws.dm | 2 +- code/datums/components/archaeology.dm | 95 ---------- code/datums/elements/scavenging.dm | 164 ++++++++++++++++++ code/game/gamemodes/game_mode.dm | 2 +- code/game/objects/effects/landmarks.dm | 2 +- code/game/objects/structures/loot_pile.dm | 66 +++++++ .../antagonists/traitor/datum_traitor.dm | 2 +- .../living/simple_animal/friendly/mouse.dm | 6 + icons/obj/loot_piles.dmi | Bin 0 -> 12091 bytes tgstation.dme | 2 + 15 files changed, 261 insertions(+), 129 deletions(-) delete mode 100644 code/datums/components/archaeology.dm create mode 100644 code/datums/elements/scavenging.dm create mode 100644 code/game/objects/structures/loot_pile.dm create mode 100644 icons/obj/loot_piles.dmi 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 0000000000000000000000000000000000000000..3d83d6612ec49b238a0934962192abac1d90d6f9 GIT binary patch literal 12091 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 B=b!)p literal 0 HcmV?d00001 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" From 122067c5f4e0fe81a722c68b961df7a0a3d1eac1 Mon Sep 17 00:00:00 2001 From: Ghommie <42542238+Ghommie@users.noreply.github.com> Date: Wed, 22 Apr 2020 03:52:45 +0200 Subject: [PATCH 2/4] wip --- code/datums/elements/scavenging.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/datums/elements/scavenging.dm b/code/datums/elements/scavenging.dm index b2ca7adcc3..c34fd8f430 100644 --- a/code/datums/elements/scavenging.dm +++ b/code/datums/elements/scavenging.dm @@ -67,8 +67,8 @@ 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! +/datum/element/scavenging/proc/scavenge_tool(atom/source, obj/item/I, mob/user, params) + if(user.a_intent == INTENT_HARM) //Robust trash disposal techniques! return var/speed_multi = tool_types[I.tool_behaviour] if(!speed_multi) From c38c1755cd84f75e245ea24665f0a585b6944f80 Mon Sep 17 00:00:00 2001 From: Ghommie <42542238+Ghommie@users.noreply.github.com> Date: Sat, 2 May 2020 19:05:41 +0200 Subject: [PATCH 3/4] Done, tested, too many options. --- code/__DEFINES/misc.dm | 7 ++ code/datums/elements/scavenging.dm | 101 ++++++++++++++++------ code/game/objects/structures/loot_pile.dm | 74 ++++++++-------- 3 files changed, 120 insertions(+), 62 deletions(-) diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 9febca663d..4cc269cefe 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -530,3 +530,10 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S #define SCAVENGING_SPAWN_MOUSE "spawn_mouse" #define SCAVENGING_SPAWN_MICE "spawn_mice" #define SCAVENGING_SPAWN_TOM "spawn_tom_the_mouse" + +//Scavenging element defines for ckey/mind restrictions. +#define NO_LOOT_RESTRICTION 0 +#define LOOT_RESTRICTION_MIND 1 +#define LOOT_RESTRICTION_CKEY 2 +#define LOOT_RESTRICTION_MIND_PILE 3 //limited to the current pile. +#define LOOT_RESTRICTION_CKEY_PILE 4 //Idem diff --git a/code/datums/elements/scavenging.dm b/code/datums/elements/scavenging.dm index c34fd8f430..1993005be7 100644 --- a/code/datums/elements/scavenging.dm +++ b/code/datums/elements/scavenging.dm @@ -16,13 +16,17 @@ 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/list/search_texts = list("starts searching through", "start searching through", "You hear rummaging...") + var/loot_restriction = NO_LOOT_RESTRICTION + var/maximum_loot_per_player = 1 //only relevant if there is a restriction. + var/list/scavenger_restriction_list //used for restrictions. 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) +/datum/element/scavenging/Attach(atom/target, amount = 5, list/loot, list/unique, time = 12 SECONDS, hands = TRUE, list/tools, list/texts, \ + del_deplete = FALSE, restriction = NO_LOOT_RESTRICTION, max_per_player = 1) . = ..() if(. == ELEMENT_INCOMPATIBLE || !length(loot) || !amount || !istype(target) || isarea(target)) return ELEMENT_INCOMPATIBLE @@ -40,6 +44,8 @@ if(texts) search_texts = texts del_atom_on_depletion = del_deplete + loot_restriction = restriction + maximum_loot_per_player = max_per_player if(can_use_hands) RegisterSignal(target, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW), .proc/scavenge_barehanded) if(tool_types) @@ -50,25 +56,25 @@ . = ..() loot_left_per_atom -= target progress_per_atom -= target + if(maximum_loot_per_player == LOOT_RESTRICTION_MIND_PILE || maximum_loot_per_player == LOOT_RESTRICTION_CKEY_PILE) + maximum_loot_per_player -= 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 + var/methods = tool_types.Copy() 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]" + methods += list("bare handed") + if(!length(methods)) + return + var/text = english_list(methods, "", " or ") + examine_list += "Looks like [source.p_they()] can be scavenged [length(tool_types) ? "with" : ""][length(methods == 1) ? "" : "either "][length(tool_types) ? "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/user, params) - if(user.a_intent == INTENT_HARM) //Robust trash disposal techniques! + if(user.a_intent == INTENT_HARM || !I.tool_behaviour) //Robust trash disposal techniques! return var/speed_multi = tool_types[I.tool_behaviour] if(!speed_multi) @@ -76,7 +82,10 @@ scavenge(source, user, speed_multi) return COMPONENT_NO_AFTERATTACK +/// This proc has to be asynced (cough cough, do_after) in order to return the comsig values in time to stop the attack chain. /datum/element/scavenging/proc/scavenge(atom/source, mob/user, speed_multi = 1) + set waitfor = FALSE + if(players_busy_scavenging[user]) return players_busy_scavenging[user] = TRUE @@ -84,37 +93,58 @@ 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]." + msg_first_person = "You [progress_done ? ", resume a ditched task and " : ""][search_texts[2]] [source]." 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)) + user.visible_message("[user] [search_texts[1]] [source].", msg_first_person, msg_blind) + if(do_after(user, scavenge_time * speed_multi, TRUE, source, TRUE, CALLBACK(src, .proc/set_progress, source, world.time), resume_time = progress_done * speed_multi)) 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 + return TRUE /datum/element/scavenging/proc/spawn_loot(atom/source, mob/user) + progress_per_atom -= source + 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.") + to_chat(user, "Looks likes there is nothing worth of interest left in [source], what a shame...") return + + var/num_times = 0 + switch(loot_restriction) + if(LOOT_RESTRICTION_MIND) + num_times = LAZYACCESS(scavenger_restriction_list, user.mind) + if(LOOT_RESTRICTION_CKEY) + num_times = LAZYACCESS(scavenger_restriction_list, user.ckey) + if(LOOT_RESTRICTION_MIND_PILE) + var/list/L = LAZYACCESS(scavenger_restriction_list, source) + if(L) + num_times = LAZYACCESS(L, user.mind) + if(LOOT_RESTRICTION_CKEY_PILE) + var/list/L = LAZYACCESS(scavenger_restriction_list, source) + if(L) + num_times = LAZYACCESS(L, user.ckey) + if(num_times >= maximum_loot_per_player) + to_chat(user, "You can't find anything else vaguely useful in [source]. Another set of eyes might, however.") + 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].") + to_chat(user, "You found something in [source]... 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.") + "You found something in [source]... 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) @@ -122,7 +152,7 @@ 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?") + to_chat(user, "You found something in [source]... 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 @@ -145,7 +175,7 @@ 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]") + to_chat(user, "You found something in [source]... it's \a [A][rarity_append]") if(unique_loot[loot]) var/loot_left = --unique_loot[loot] @@ -157,8 +187,29 @@ 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) + if(free) + return + + --loot_left_per_atom[source] + if(del_atom_on_depletion && !loot_left_per_atom[source]) + source.visible_message("[source] has been looted clean.") + qdel(source) + return + + if(!loot_restriction) + return + + LAZYINITLIST(scavenger_restriction_list) + switch(loot_restriction) + if(LOOT_RESTRICTION_MIND) + scavenger_restriction_list[user.mind]++ + if(LOOT_RESTRICTION_CKEY) + scavenger_restriction_list[user.ckey]++ + if(LOOT_RESTRICTION_MIND_PILE) + LAZYINITLIST(scavenger_restriction_list[source]) + var/list/L = scavenger_restriction_list[source] + L[user.mind]++ + if(LOOT_RESTRICTION_CKEY_PILE) + LAZYINITLIST(scavenger_restriction_list[source]) + var/list/L = scavenger_restriction_list[source] + L[user.ckey]++ diff --git a/code/game/objects/structures/loot_pile.dm b/code/game/objects/structures/loot_pile.dm index 0886c1c8b7..bbcfacd9c1 100644 --- a/code/game/objects/structures/loot_pile.dm +++ b/code/game/objects/structures/loot_pile.dm @@ -6,6 +6,7 @@ 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' + icon_state = "randompile" density = FALSE anchored = TRUE var/loot_amount = 5 @@ -14,43 +15,20 @@ 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) + var/list/loot /* * 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) + var/list/unique_loot + + /* + * used for restrictions such as "one per mind", "one per ckey". Depending on the setting, these can be either limited to + * the current pile or shared throughout all atoms attached to this element. + */ + var/loot_restriction = NO_LOOT_RESTRICTION + var/maximum_loot_per_player = 1 /obj/structure/loot_pile/Initialize() . = ..() @@ -58,9 +36,31 @@ /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) + if(loot) + AddElement(/datum/element/scavenging, loot_amount, loot, unique_loot, scavenge_time, can_use_hands, allowed_tools, null, delete_on_depletion, loot_restriction, maximum_loot_per_player) -/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 +//uses the maintenance_loot global list, mostly boring stuff and mices. +/obj/structure/loot_pile/maint + name = "trash pile" + desc = "A heap of garbage, but maybe there's something interesting inside?" + density = TRUE + layer = TABLE_LAYER + climbable = TRUE + pass_flags = LETPASSTHROW + loot = list( + SCAVENGING_FOUND_NOTHING = 50, + SCAVENGING_SPAWN_MOUSE = 10, + SCAVENGING_SPAWN_MICE = 5, + SCAVENGING_SPAWN_TOM = 1, + /obj/item/clothing/gloves/color/yellow = 0.5) + unique_loot = list(/obj/item/clothing/gloves/color/yellow = 5, SCAVENGING_SPAWN_TOM = 1) + +/obj/structure/loot_pile/maint/ComponentInitialize() + var/static/safe_maint_items + if(!safe_maint_items) + safe_maint_items = list() + for(var/A in GLOB.maintenance_loot) + if(ispath(A, /obj/item)) + safe_maint_items[A] = GLOB.maintenance_loot[A] + loot += safe_maint_items + return ..() From 7ae2615f519ad3ce2fffa8264d6351e955ee94f9 Mon Sep 17 00:00:00 2001 From: Ghommie <42542238+Ghommie@users.noreply.github.com> Date: Sat, 2 May 2020 19:13:21 +0200 Subject: [PATCH 4/4] Whops. --- code/game/objects/structures/loot_pile.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/objects/structures/loot_pile.dm b/code/game/objects/structures/loot_pile.dm index bbcfacd9c1..b6249ec1e4 100644 --- a/code/game/objects/structures/loot_pile.dm +++ b/code/game/objects/structures/loot_pile.dm @@ -60,7 +60,7 @@ if(!safe_maint_items) safe_maint_items = list() for(var/A in GLOB.maintenance_loot) - if(ispath(A, /obj/item)) - safe_maint_items[A] = GLOB.maintenance_loot[A] + if(ispath(A, /obj/item)) + safe_maint_items[A] = GLOB.maintenance_loot[A] loot += safe_maint_items return ..()