[MIRROR] Collector event machine (#10962)

Co-authored-by: SatinIsle <98125273+SatinIsle@users.noreply.github.com>
Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
CHOMPStation2StaffMirrorBot
2025-05-29 04:21:24 -07:00
committed by GitHub
parent 2d8c2baf79
commit baaced85c4
11 changed files with 562 additions and 1 deletions

View File

@@ -573,6 +573,8 @@
#define REAGENT_ID_NUKIEMEGASHRINK "nukie_mega_shrink"
#define REAGENT_NUKIEMEGAGROWTH "Nukie Mega Growth"
#define REAGENT_ID_NUKIEMEGAGROWTH "nukie_mega_growth"
#define REAGENT_NUKIEONE "Nukie One"
#define REAGENT_ID_NUKIEONE "nukie_one"
#define REAGENT_COATING "coating"
#define REAGENT_ID_COATING "coating"
#define REAGENT_BATTER "batter mix"

View File

@@ -582,6 +582,8 @@ var/list/admin_verbs_event_manager = list(
/client/proc/add_hidden_area,
/client/proc/remove_hidden_area,
/client/proc/hide_motion_tracker_feedback,
/client/proc/modify_event_collector,
/client/proc/induce_malfunction,
/datum/admins/proc/quick_nif, //CHOMPStation Add,
/datum/admins/proc/quick_authentic_nif, //CHOMPStation add
/client/proc/reload_jobwhitelist, //ChompADD

View File

@@ -0,0 +1,73 @@
/*
Event Collector Admin Commands
These need to be added to admin_verb_lists_vr.dm
*/
/client/proc/modify_event_collector(var/obj/structure/event_collector/target in GLOB.event_collectors)
set category = "Fun.Event Kit"
set desc="Configure Event Collector"
set name="Configure Collector"
if(!check_rights(R_ADMIN))
return
var/msg = "---------------\n"
if(target?.active_recipe?.len > 0)
msg += " [target] has [target.active_recipe.len] left in its current recipe\n"
for(var/i in target.active_recipe)
msg += "* [i] \n"
else
msg += "[target] has no more required items! \n"
if(target.calls_remaining > 0)
msg += "[target] has [target.calls_remaining] progress to go! - unless stopped or slowed, this is about [(target.calls_remaining / 10 ) * 2] seconds! \n"
var/blockers = target.get_blockers()
if(blockers > 0)
msg += "[target] has [blockers] things blocking/slowing it down! Anything more than 10 means it's stopped!"
to_chat(usr,msg)
var/list/options = list(
"Cancel",
"Start New Recipe",
"Clear Current Recipe",
"Force Clear Blockers"
)
var/option = tgui_input_list(usr, "What Would You Like To Do?", "Event Collector",options,"Cancel")
switch(option)
if("Cancel")
return
if("Start New Recipe")
target.pick_new_recipe()
if("Clear Current Recipe")
target.active_recipe = list()
target.awaiting_next_recipe = TRUE
target.calls_remaining = 0
if("Force Clear Blockers")
if(islist(GLOB.event_collector_blockers[target.blocker_channel]))
for(var/obj/structure/event_collector_blocker/tofix in GLOB.event_collector_blockers[target.blocker_channel])
tofix.fix()
if("Empty Stored Items")
target.empty_items()
/client/proc/induce_malfunction(var/obj/structure/event_collector_blocker/target in GLOB.event_collector_blockers)
set category = "Fun.Event Kit"
set desc="Configure Collector Blocker"
set name="Toggle Malfunction State"
if(!check_rights(R_ADMIN))
return
if(target.block_amount)
target.fix()
else
target.induce_failure()

View File

@@ -0,0 +1,131 @@
/obj/structure/event_collector_blocker
var/blocker_channel = "collector"
var/base_icon = "blocker"
icon = 'icons/obj/general_collector.dmi'
icon_state = "blocker_on"
desc = "blocker? I barely know er"
var/tools_to_fix = FALSE
//how much we're currently blocking
var/block_amount = 0
//how much we'll block when broken
var/default_block_amount = 100
//tool to what's fucked up
var/list/problem_descs = list(
TOOL_CROWBAR = "a panel is dislodged.",
TOOL_MULTITOOL = "a light next to a dataport is flashing some errors.",
TOOL_SCREWDRIVER = "there's some loose screws inside.",
TOOL_WIRECUTTER = "some loose wires are shorting things out.",
TOOL_WRENCH = "One of the supporting bolts are concerningly loose.",
TOOL_CABLE_COIL = "There's some wires that need replacement.",
TOOL_WELDER = "One of the brackets has a cracked weld."
)
//tool to how we unfuck it
var/list/fix_descs = list(
TOOL_CROWBAR = "you lodge the panel back in place.",
TOOL_MULTITOOL = "you reset the panel with the multitool.",
TOOL_SCREWDRIVER = "you tighten the loose screws.",
TOOL_WIRECUTTER = "you remove the excess wiring.",
TOOL_WRENCH = "you tighten up the supporting bolts.",
TOOL_CABLE_COIL = "you replace the worn wires.",
TOOL_WELDER = "you fix up the worn weld."
)
//what tools we need
var/list/active_repair_steps = list()
/obj/structure/event_collector_blocker/Initialize(mapload)
. = ..()
GLOB.event_collector_blockers |= src
if(GLOB.event_collector_associations == null)
GLOB.event_collector_associations = list()
if(GLOB.event_collector_associations[blocker_channel] == null)
GLOB.event_collector_associations[blocker_channel] = list()
GLOB.event_collector_associations[blocker_channel] |= src
/obj/structure/event_collector_blocker/Destroy()
GLOB.event_collector_blockers -= src
if(GLOB.event_collector_associations[blocker_channel])
GLOB.event_collector_associations[blocker_channel] -= src
. = ..()
/obj/structure/event_collector_blocker/update_icon()
. = ..()
icon_state = "[base_icon]_[block_amount ? "off" : "on"]"
/obj/structure/event_collector_blocker/proc/induce_failure(var/intensity = -1) //progress to remove from the machine
if(intensity == -1)
intensity = default_block_amount
block_amount = intensity
if(tools_to_fix)
active_repair_steps = list()
for(var/i in 1 to 4)
active_repair_steps += pick(list(TOOL_CROWBAR,TOOL_MULTITOOL,TOOL_SCREWDRIVER,TOOL_WRENCH,TOOL_CABLE_COIL,TOOL_WELDER)) //todo, make this a different list on the obj "Possible failures" or whatever.
update_icon();
/obj/structure/event_collector_blocker/proc/fix()
block_amount = 0
active_repair_steps = list()
update_icon();
/obj/structure/event_collector_blocker/examine(mob/user, infix, suffix)
. = ..()
if(block_amount)
if(tools_to_fix)
if(active_repair_steps.len >= 1)
. += span_warning(problem_descs[active_repair_steps[active_repair_steps.len]])
else
. += span_warning("It looks kinda messed up!")
else
. += span_warning("Looks like the breaker flipped!")
else
. += span_notice("Looks like it's functioning normally")
/obj/structure/event_collector_blocker/proc/get_repair_message(var/mob/user)
return "[user] repairs [src]"
/obj/structure/event_collector_blocker/attack_hand(mob/user)
if(!tools_to_fix && block_amount > 0)
user.visible_message("[user] fixes [src] up!") //swap this with a message var, or just change it to be suitable
fix()
. = ..()
/obj/structure/event_collector_blocker/attackby(obj/item/O, mob/user)
. = ..()
if(tools_to_fix)
if(active_repair_steps.len >= 1)
if(O.has_tool_quality(active_repair_steps[active_repair_steps.len]))
if(do_after(user, 2 SECONDS))
to_chat(usr,span_notice(fix_descs[active_repair_steps[active_repair_steps.len]]))
active_repair_steps.len = active_repair_steps.len - 1
if(active_repair_steps.len == 0)
fix()
else
to_chat(user,"WRONG TOOL!")
/obj/structure/event_collector_blocker/breaker //for these, if you change the base_icon you'll be good to go. ae, base_icon = breaker or circuit or whatever
name = "Breaker"
desc = "I barely know er!"
/obj/structure/event_collector_blocker/breaker/get_repair_message(var/mob/user)
return "[user] flips [src]!"
/obj/structure/event_collector_blocker/circuit_panel
name = "Circuit Panel"
desc = "Looks complicated!"
tools_to_fix = TRUE

View File

@@ -0,0 +1,262 @@
GLOBAL_LIST_INIT(event_collector_associations,list())
GLOBAL_LIST_INIT(event_collectors,list()) //for the verbs
GLOBAL_LIST_INIT(event_collector_blockers,list()) //ditto
/obj/structure/event_collector //set anchored, solid, etc to taste.
name = "event collector"
desc = "you really should set this up properly :("
icon = 'icons/obj/general_collector.dmi'
var/blocker_channel = "collector" //used for list management - blockers that have the same key will add themselves as a disabler to every collector with the same key
var/recipe_size = 3 //how many ingredients do we pick out from the ingredients list for a "recipe"?
var/blocker_insertion_impedement_threshold = -1 //if we have more blockers than this, we can't place item in :(
var/show_blocker_in_examine = TRUE
var/list/possible_ingredients = list(
/obj/item/trash,
/obj/item/toy/plushie/ipc,
/obj/item/toy/tennis
) //list of items that can make up a recipe.
var/no_dupes_in_recipe = FALSE //do we care about repeats? if so, set to true
var/need_recipe_in_order = FALSE //start from the first one! or ignore it and do whatever, man...
var/item_theft_mode = TRUE //if true, we store it in our contents for later use, otherwise, we just delete it.
var/completion_time = 60 //how long to "complete" a recipe before starting the next one - this is in processing calls, 2 seconds if no time dialation
var/step_insertion_time = 2 SECONDS //how long it takes to put an object into us!
var/list/step_insertion_verbs = list("inserts") //X inserts Item into [Src], picks from the list! Tosses, inserts, throws, etc
var/list/step_initiation_verbs = list("insert") //when we start it. X starts to insert Item into [src]. picks from list. put, insert, shove, etc
var/automatic_recipe_restart = TRUE //do we start a new recipe as soon as the active one's done? if not, admin only!
var/noisy_recipe_completion = TRUE //if true, alerts admins when a recipe is completed
var/noisy_step_completion = TRUE //if true, tells admins when a step is completed
var/step_in_examine = TRUE //if true, simply tells the user what type of item they need next. note that we use typeof here, so a gold screwdriver counts as a screwdriver, but it'll just ask for a screwdriver
var/animate_on_recipe_complete = TRUE //jiggle on complete?
var/animate_on_recipe_process = TRUE //jiggle with less intensity when working?
var/sound_for_recipe_complete
var/wait_between_items = 0 //How long must you wait before adding another item
var/next_item_added = 0 //world time when the next item can be added
var/type_to_spawn_on_complete
var/list/recipe_process_sounds = list('sound/effects/smoke.ogg', 'sound/effects/bubbles.ogg')
var/recipe_process_sound_chance = 50 //prob(50) per active process tick
//internal stuff, don't touch this with subtypes.
var/calls_remaining
var/awaiting_next_recipe = FALSE //are we waiting for the timer to get negatives?
var/list/disabling_sources //what things are disabling us?
var/list/active_recipe //volatile, when given an item it removes it
var/current_step = 0 //current step for icon states
/obj/structure/event_collector/Initialize(mapload)
. = ..()
GLOB.event_collectors |= src
/obj/strucutre/event_collector/Destroy()
GLOB.event_collectors -= src
. = ..()
/obj/structure/event_collector/proc/get_blockers()
. = 0
if(GLOB.event_collector_associations)
if(GLOB.event_collector_associations[blocker_channel])
for(var/obj/structure/event_collector_blocker/blocker in GLOB.event_collector_associations[blocker_channel])
. += blocker.block_amount
/obj/structure/event_collector/proc/jiggle_animation(var/intensity = 1)
var/matrix/secondary_effect = matrix()
var/matrix/effect = matrix()
effect.Turn(-5*intensity)
effect.Scale(1,1-intensity)
secondary_effect.Turn(5*intensity)
secondary_effect.Scale(1,1+intensity)
animate(src, transform = effect, time = 2)
animate(transform = secondary_effect, time = 2)
animate(transform = null, time = 2)
/obj/structure/event_collector/proc/recipe_completed()
if(animate_on_recipe_complete)
jiggle_animation(0.2)
if(automatic_recipe_restart)
pick_new_recipe()
if(noisy_recipe_completion)
message_admins("\[EVENT\]: Event Collection object [src] has completed a recipe!")
if(sound_for_recipe_complete)
playsound(src,sound_for_recipe_complete,75,1)
if(type_to_spawn_on_complete)
new type_to_spawn_on_complete(get_turf(loc))
current_step = 0
update_icon()
/obj/structure/event_collector/update_icon()
. = ..() //here more as a reminder than anything
/obj/structure/event_collector/proc/recipe_failed() //called when reset by an admin assuming they want it to be
return
/obj/structure/event_collector/proc/pick_new_recipe()
active_recipe = list() //clear it out
if(no_dupes_in_recipe)
var/list/destructive_clone = possible_ingredients.Copy()
for(var/i in 1 to recipe_size)
var/temp = pick(destructive_clone)
active_recipe += temp
destructive_clone -= temp
else
for(var/i in 1 to recipe_size)
active_recipe += pick(possible_ingredients)
if(noisy_step_completion)
var/next_item = "Nothing! The sequence is done!"
next_item = active_recipe[1]
message_admins("\[EVENT\] Event Collection object [src] has started a recipe! If it's in sequence, the next one is [next_item] ")
/obj/structure/event_collector/process()
var/blockers = get_blockers()
if(awaiting_next_recipe && blockers < 10)
if( recipe_process_sounds && prob(recipe_process_sound_chance) )
playsound(src,pick(recipe_process_sounds),25,TRUE)
calls_remaining -= max(0, 10-blockers) //10's a multiplier in case we want to scale it based on how many blockers
if(calls_remaining <= 0)
awaiting_next_recipe = FALSE
recipe_completed()
if(animate_on_recipe_process)
jiggle_animation(0.1)
/obj/structure/event_collector/Initialize(mapload)
. = ..()
START_PROCESSING(SSobj, src)
/obj/structure/event_collector/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
/obj/structure/event_collector/examine(mob/user)
. = ..()
if(!active_recipe) return
var/blocker_count = get_blockers()
if(step_in_examine == TRUE)
if(active_recipe.len == 0)
. += span_notice("It doesn't seem to need anything at the moment!")
else
if(need_recipe_in_order)
if(active_recipe.len > 0)
var/atom/firstitem = active_recipe[1]
. += span_notice("The next object in the sequence is a... [initial(firstitem.name)]")
else
. += span_notice("it doesn't seem to need anything at the moment!")
else
var/message = "It seems to need a "
for(var/i in 1 to active_recipe.len)
var/atom/x = active_recipe[i]
.+= span_notice(message + initial(x.name))
message = pick("and a ", "a ", "with a ")
if(show_blocker_in_examine)
if(blocker_count > 10)
. += span_danger("It's nonfunctional!")
else
if(blocker_count > 0)
. += span_warning("It's impeded!")
//following's for debug, comment out if ur happy with it
//. += "There are uhhhh this many things blocking: [blocker_count]."
/obj/structure/event_collector/attackby(obj/item/O, mob/user)
if(blocker_insertion_impedement_threshold > 0 && ( get_blockers() > blocker_insertion_impedement_threshold) )
to_chat(usr,"It's fucked! Fix it first!")
return
if(world.time < next_item_added)
to_chat(user,span_warning("It's not ready to take another item yet!"))
return
if(active_recipe.len > 0) //do we have something active at all
var/stored_index = -1 //shortcut
if(need_recipe_in_order) //can we put this in?
if(!(istype(O,active_recipe[1]))) //if we need the recipe in order, check the first thing in the list
to_chat(user,span_warning("That's not the next object in the recipe!"))
return
else
stored_index = 1
else
var/found = FALSE
for(var/ind in 1 to active_recipe.len) //isType != if(O.type in list) unfortunately
if(istype(O, active_recipe[ind]))
found = TRUE
stored_index = ind
break;
if(!found)
return;
//put it in
user.visible_message("[user] begins to [pick(step_initiation_verbs)] \The [O] into \The [src]")
if(do_after(user, step_insertion_time, src)) //wait a second or two
user.visible_message("[user] [pick(step_insertion_verbs)] \The [O] into \The [src]!")
if(ishuman(user)) //should always be?
var/mob/living/carbon/human/h = user
h.drop_item() //drop held item. this is also what plays the item sound via association
if(noisy_step_completion)
var/next_item = "Nothing! The sequence is done!"
if(active_recipe.len > 1)
next_item = active_recipe[2]
message_admins("\[EVENT\] Event Collection object [src] has completed a step in its recipe with [O]! if it's in sequence, the next one is [next_item] ")
active_recipe -= active_recipe[stored_index]
if(item_theft_mode)
O.forceMove(src) //note that this does NOT delete anything! ever! or release it manually! entirely so admins can manually collect or do stuff later via moving/ejecting.
else
qdel(O)
jiggle_animation(0.1)
if(active_recipe.len == 0)
start_recipe_process()
current_step += 1
update_icon()
post_recipe_complete(user)
next_item_added = (world.time + wait_between_items)
else
user.visible_message("[user] gives up!") //shitty, change later
/obj/structure/event_collector/proc/start_recipe_process()
awaiting_next_recipe = TRUE
calls_remaining = completion_time * 10
message_admins("\[EVENT\] Event Collection object [src] has started processing its current recipe! ETA: [(calls_remaining/10) / 2] ish seconds.")
/obj/structure/event_collector/proc/empty_items() //manual call only atm
for(var/atom/movable/to_move in contents)
to_move.forceMove(get_turf(src))
/obj/structure/event_collector/proc/post_object_insert(var/mob/user)
return
/obj/structure/event_collector/proc/post_recipe_complete()
return

View File

@@ -0,0 +1,47 @@
/obj/structure/event_collector/nukies
name = "Experimental Nukies Mixer"
desc = "A machine rigged together from various bits and pieces, designed to mix some reagents into new Nukies."
icon = 'icons/obj/cooking_event.dmi'
icon_state = "equipment_empty"
possible_ingredients = list(
/obj/item/collector_item/nukies_acid,
/obj/item/collector_item/nukies_formula,
/obj/item/collector_item/nukies_sludge
)
no_dupes_in_recipe = TRUE
automatic_recipe_restart = FALSE
step_in_examine = FALSE
wait_between_items = 1 MINUTE
need_recipe_in_order = TRUE
type_to_spawn_on_complete = /obj/item/reagent_containers/food/drinks/cans/nukie_one
/obj/structure/event_collector/update_icon()
. = ..()
if(!current_step)
icon_state = "equipment_empty"
else if(current_step <= 3)
icon_state = "equipment_[current_step]"
//simple obj defs here. overwrite or change as requested.
/obj/item/collector_item
icon = 'icons/obj/cooking_event.dmi'
icon_state = "acid"
name = "Mewriatic Acid"
desc = "Not a typo or label misprint. There's an entire dissolved cat in here. Says so right on the ingredients label."
/obj/item/collector_item/nukies_acid
/obj/item/collector_item/nukies_formula
icon_state = "formula"
name = "Nukies Secret Formula"
desc = "A disgustingly thick bottle of... something. It smells bad, in a good way."
/obj/item/collector_item/nukies_sludge
icon_state = "sludge"
name = "Sludge"
desc = "Eughghhgh. gross. Smells like gasoline and gamers."

View File

@@ -696,3 +696,17 @@
/obj/item/reagent_containers/food/drinks/cans/nukie_mega_grow/Initialize(mapload)
. = ..()
reagents.add_reagent(REAGENT_ID_NUKIEMEGAGROWTH, 60)
////////////////////////Event Only Nukie//////////////////////////////////
/obj/item/reagent_containers/food/drinks/cans/nukie_one
name = "\improper Nukies One"
desc = "The final. The ultimate nukeform. Power not meant to be supplied to mortal creatures. Legit."
icon_state = "nukie_one"
center_of_mass_x = 16
center_of_mass_y = 8
volume = 60
/obj/item/reagent_containers/food/drinks/cans/nukie_one/Initialize(mapload)
. = ..()
reagents.add_reagent(REAGENT_ID_NUKIEONE, 60)

View File

@@ -958,3 +958,29 @@
..()
var/new_size = clamp((M.size_multiplier + 0.01), RESIZE_MINIMUM_DORMS, RESIZE_MAXIMUM_DORMS)
M.resize(new_size, uncapped = M.has_large_resize_bounds(), aura_animation = FALSE)
/////////////////////////////Event only nukie//////////////////////////////////////
/datum/reagent/drink/coffee/nukie/mega/one //Basically macrocillin but for ingesting
name = REAGENT_NUKIEONE
id = REAGENT_ID_NUKIEONE
color = "#90ed87"
taste_description = "everything"
overdose = 10
adj_drowsy = -50
adj_sleepy = -100
/datum/reagent/drink/coffee/nukie/mega/one/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed)
..()
M.add_chemical_effect(CE_DARKSIGHT, 1)
M.add_chemical_effect(CE_SPEEDBOOST, 1)
M.heal_organ_damage(1.5 * removed, 1.5 * removed)
/datum/reagent/drink/coffee/nukie/mega/one/overdose(var/mob/living/carbon/M, var/alien, var/removed)
if(ishuman(M))
var/mob/living/carbon/human/H = M
H.eye_blurry += 20
H.adjustToxLoss(min(removed * overdose_mod * round(3 + 3 * volume / overdose), 1))
H.adjustFireLoss(min(removed * overdose_mod * round(3 + 3 * volume / overdose), 1))
H.adjustBruteLoss(min(removed * overdose_mod * round(3 + 3 * volume / overdose), 1))
H.add_modifier(/datum/modifier/berserk, 2 SECONDS, suppress_failure = TRUE)

BIN
icons/obj/cooking_event.dmi Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 827 B

View File

@@ -2622,6 +2622,10 @@
#include "code\modules\error_handler\error_viewer.dm"
#include "code\modules\error_handler\~defines.dm"
#include "code\modules\eventkit\event_machinery.dm"
#include "code\modules\eventkit\collector_event\admin_commands.dm"
#include "code\modules\eventkit\collector_event\blockers.dm"
#include "code\modules\eventkit\collector_event\event_collector.dm"
#include "code\modules\eventkit\collector_event\nukies_maker\nukies_collector.dm"
#include "code\modules\eventkit\generic_objects\generic_item.dm"
#include "code\modules\eventkit\generic_objects\generic_structure.dm"
#include "code\modules\eventkit\gm_interfaces\fake_pda_conversations.dm"
@@ -3531,6 +3535,7 @@
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\broodmother_spawn.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\carrier.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\electric.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\event_spiders.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\frost.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\giant_spider_vr.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\hunter.dm"
@@ -3543,7 +3548,6 @@
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\webslinger.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\hyena\hyena.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\passive\armadillo_ch.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\giant_spider\event_spiders.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\passive\cockroach.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\passive\crab.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\passive\fish.dm"