mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-29 16:18:01 +01:00
Implements data systems (#82816)
## About The Pull Request Subsystems currently come in two different flavors: 1. Systems that process at intervals with the master controller 2. Global data containers that do not fire And I think they should be split up... This moves 4 non firing, non init subsytems -> datasystem ## Why It's Good For The Game Clarity in code
This commit is contained in:
@@ -1,230 +0,0 @@
|
||||
/// Global list of areas which are considered to be inside the same department for our purposes
|
||||
GLOBAL_LIST_INIT(battle_royale_regions, list(
|
||||
"Medical Bay" = list(
|
||||
/area/station/command/heads_quarters/cmo,
|
||||
/area/station/medical,
|
||||
/area/station/security/checkpoint/medical,
|
||||
),
|
||||
"Research Division" = list(
|
||||
/area/station/command/heads_quarters/rd,
|
||||
/area/station/security/checkpoint/science,
|
||||
/area/station/science,
|
||||
),
|
||||
"Engineering Bay" = list(
|
||||
/area/station/command/heads_quarters/ce,
|
||||
/area/station/engineering,
|
||||
/area/station/maintenance/disposal/incinerator,
|
||||
/area/station/security/checkpoint/engineering,
|
||||
),
|
||||
"Cargo Bay" = list(
|
||||
/area/station/cargo,
|
||||
/area/station/command/heads_quarters/qm,
|
||||
/area/station/security/checkpoint/supply,
|
||||
),
|
||||
))
|
||||
|
||||
/// Basically just exists to hold references to datums so that they don't GC
|
||||
SUBSYSTEM_DEF(battle_royale)
|
||||
name = "Battle Royale"
|
||||
flags = SS_NO_INIT | SS_NO_FIRE
|
||||
/// List of battle royale datums currently running
|
||||
var/list/active_battles
|
||||
|
||||
/// Start a new battle royale using a passed list of implants
|
||||
/datum/controller/subsystem/battle_royale/proc/start_battle(list/competitors)
|
||||
var/datum/battle_royale_controller/controller = new()
|
||||
if (!controller.start(competitors))
|
||||
return FALSE
|
||||
LAZYADD(active_battles, controller)
|
||||
if (LAZYLEN(active_battles) == 1)
|
||||
start_broadcasting_network(BATTLE_ROYALE_CAMERA_NET)
|
||||
RegisterSignal(controller, COMSIG_QDELETING, PROC_REF(battle_ended))
|
||||
return TRUE
|
||||
|
||||
/// Drop reference when it kills itself
|
||||
/datum/controller/subsystem/battle_royale/proc/battle_ended(datum/source)
|
||||
SIGNAL_HANDLER
|
||||
LAZYREMOVE(active_battles, source)
|
||||
if (!LAZYLEN(active_battles))
|
||||
stop_broadcasting_network(BATTLE_ROYALE_CAMERA_NET)
|
||||
|
||||
|
||||
/// Datum which controls the conflict
|
||||
/datum/battle_royale_controller
|
||||
/// Where is our battle taking place?
|
||||
var/chosen_area
|
||||
/// Is the battle currently in progress?
|
||||
var/battle_running = TRUE
|
||||
/// Should we let everyone know that someone has died?
|
||||
var/announce_deaths = TRUE
|
||||
/// List of implants involved
|
||||
var/list/contestant_implants = list()
|
||||
/// Ways to describe that someone has died
|
||||
var/static/list/euphemisms = list(
|
||||
"cashed their last paycheque.",
|
||||
"didn't make it...",
|
||||
"didn't make the cut.",
|
||||
"had their head blown clean off!",
|
||||
"has been killed!",
|
||||
"has failed the challenge!",
|
||||
"has passed away.",
|
||||
"has died.",
|
||||
"is in a better place now.",
|
||||
"isn't going to be clocking in tomorrow!",
|
||||
"just flatlined.",
|
||||
"isn't today's winner.",
|
||||
"seems to have exploded!",
|
||||
"was just murdered on live tv!",
|
||||
"won't be making it to retirement.",
|
||||
"won't be getting back up after that one.",
|
||||
)
|
||||
/// Ways to tell people not to salt in deadchat, surely effective
|
||||
var/static/list/condolences = list(
|
||||
"Better luck next time!",
|
||||
"But stay tuned, there's still everything to play for!",
|
||||
"Did you catch who did it?",
|
||||
"It looked like that one really hurt...",
|
||||
"Let's get that one on action replay!",
|
||||
"Let's have a moment of silence, please.",
|
||||
"Let's hope the next one does better.",
|
||||
"Someone please notify their next of kin.",
|
||||
"They had a good run.",
|
||||
"Too bad!",
|
||||
"What a shame!",
|
||||
"What an upset!",
|
||||
"What's going to happen next?",
|
||||
"Who could have seen that coming?",
|
||||
"Who will be next?",
|
||||
)
|
||||
|
||||
/datum/battle_royale_controller/Destroy(force)
|
||||
contestant_implants = null
|
||||
return ..()
|
||||
|
||||
/// Start a battle royale with the list of provided implants
|
||||
/datum/battle_royale_controller/proc/start(list/implants, battle_time = 10 MINUTES)
|
||||
chosen_area = pick(GLOB.battle_royale_regions)
|
||||
for (var/obj/item/implant/explosive/battle_royale/contestant_implant in implants)
|
||||
contestant_implant.start_battle(chosen_area, GLOB.battle_royale_regions[chosen_area])
|
||||
if (isnull(contestant_implant))
|
||||
continue // Might have exploded if it was removed from a person
|
||||
RegisterSignal(contestant_implant, COMSIG_QDELETING, PROC_REF(implant_destroyed))
|
||||
contestant_implants |= contestant_implant
|
||||
|
||||
if (length(contestant_implants) <= 1)
|
||||
return FALSE // Well there's not much point is there
|
||||
|
||||
priority_announce(
|
||||
text = "Congratulations [station_name()], you have been chosen as the next site of the Rumble Royale! \n\
|
||||
Viewers across the sector will watch our [convert_integer_to_words(length(contestant_implants))] lucky contestants battle their way into your [chosen_area] and fight until only one is left standing! \n\
|
||||
If they don't make it in five minutes, they'll be disqualified. If you see one of our players struggling to get in, do lend them a hand... or don't, if you can live with the consequences! \n\
|
||||
As a gesture of gratitude, we will be providing our premium broadcast to your entertainment monitors at no cost so that you can watch the excitement. \n\
|
||||
Bystanders are advised not to intervene... but if you do, make it look good for the camera!",
|
||||
title = "Rumble Royale Beginning",
|
||||
sound = 'sound/machines/alarm.ogg',
|
||||
has_important_message = TRUE,
|
||||
sender_override = "Rumble Royale Pirate Broadcast Station",
|
||||
color_override = "red",
|
||||
)
|
||||
|
||||
for (var/obj/item/implant/explosive/battle_royale/contestant_implant as anything in contestant_implants)
|
||||
contestant_implant.announce()
|
||||
addtimer(CALLBACK(src, PROC_REF(limit_area)), battle_time / 2, TIMER_DELETE_ME)
|
||||
addtimer(CALLBACK(src, PROC_REF(finish)), battle_time, TIMER_DELETE_ME)
|
||||
return TRUE
|
||||
|
||||
/// An implant was destroyed, hopefully because it exploded. Count how many competitors remain.
|
||||
/datum/battle_royale_controller/proc/implant_destroyed(obj/item/implant/implant)
|
||||
SIGNAL_HANDLER
|
||||
contestant_implants -= implant
|
||||
if (!battle_running)
|
||||
return
|
||||
|
||||
if (length(contestant_implants) <= 1)
|
||||
announce_winner(implant)
|
||||
else if (announce_deaths)
|
||||
var/message = ""
|
||||
if (isnull(implant.imp_in))
|
||||
message = "Looks like someone removed and destroyed their implant, that's cheating!"
|
||||
else
|
||||
message = "[implant.imp_in.real_name] [pick(euphemisms)] [pick(condolences)]"
|
||||
priority_announce(
|
||||
text = message,
|
||||
title = "Rumble Royale Casualty Report",
|
||||
sound = 'sound/misc/notice1.ogg',
|
||||
has_important_message = TRUE,
|
||||
sender_override = "Rumble Royale Pirate Broadcast Station",
|
||||
color_override = "red",
|
||||
)
|
||||
|
||||
/// There's only one person left, we have a winner!
|
||||
/datum/battle_royale_controller/proc/announce_winner(obj/item/implant/losing_implant)
|
||||
battle_running = FALSE
|
||||
if (length(contestant_implants) > 1)
|
||||
return
|
||||
|
||||
var/message = ""
|
||||
var/mob/living/loser = losing_implant.imp_in
|
||||
var/obj/item/implant/winning_implant = pop(contestant_implants)
|
||||
var/mob/living/winner = winning_implant?.imp_in
|
||||
|
||||
if (isnull(winner) && isnull(loser))
|
||||
message = "Somehow, it seems like there's no winner tonight. What a disappointment!"
|
||||
else
|
||||
var/loser_text = isnull(loser) ? "With the disqualification of the other remaining contestant" : "With the death of [loser.real_name]"
|
||||
var/winner_text = isnull(winner) ? "we must sadly announce that the would-be winner has also been disqualified. Such bad showmanship!" : "only [winner.real_name] remains. Congratulations, we have a winner!"
|
||||
message = "[loser_text], [winner_text]"
|
||||
|
||||
if (!isnull(winner))
|
||||
podspawn(list(
|
||||
"target" = get_turf(winner),
|
||||
"style" = STYLE_SYNDICATE,
|
||||
"spawn" = /obj/item/food/roast_dinner,
|
||||
))
|
||||
|
||||
priority_announce(
|
||||
text = message,
|
||||
title = "Rumble Royale Winner",
|
||||
sound = 'sound/misc/notice1.ogg',
|
||||
has_important_message = TRUE,
|
||||
sender_override = "Rumble Royale Pirate Broadcast Station",
|
||||
color_override = "red",
|
||||
)
|
||||
|
||||
qdel(winning_implant) // You get to live!
|
||||
winner?.mind?.remove_antag_datum(/datum/antagonist/survivalist/battle_royale)
|
||||
qdel(src)
|
||||
|
||||
/// Called halfway through the battle, if you've not made it to the designated battle zone we kill you
|
||||
/datum/battle_royale_controller/proc/limit_area()
|
||||
priority_announce(
|
||||
text = "We're halfway done folks! And bad news to anyone who hasn't made it to the [chosen_area]... you're out!",
|
||||
title = "Rumble Royale Update",
|
||||
sound = 'sound/misc/notice1.ogg',
|
||||
has_important_message = TRUE,
|
||||
sender_override = "Rumble Royale Pirate Broadcast Station",
|
||||
color_override = "red",
|
||||
)
|
||||
|
||||
for (var/obj/item/implant/explosive/battle_royale/contestant_implant as anything in contestant_implants)
|
||||
contestant_implant.limit_areas()
|
||||
|
||||
/// Well you're out of time, bad luck
|
||||
/datum/battle_royale_controller/proc/finish()
|
||||
battle_running = FALSE
|
||||
|
||||
priority_announce(
|
||||
text = "Sorry remaining contestants, your time is up. \
|
||||
We're sorry to announce that this edition of Royal Rumble has no winner. \n\
|
||||
Better luck next time!",
|
||||
title = "Rumble Royale Concluded",
|
||||
sound = 'sound/misc/notice1.ogg',
|
||||
has_important_message = TRUE,
|
||||
sender_override = "Rumble Royale Pirate Broadcast Station",
|
||||
color_override = "red",
|
||||
)
|
||||
|
||||
for (var/obj/item/implant/explosive/battle_royale/contestant_implant as anything in contestant_implants)
|
||||
contestant_implant.explode()
|
||||
|
||||
qdel(src)
|
||||
@@ -1,64 +0,0 @@
|
||||
#define COMMUNICATION_COOLDOWN (30 SECONDS)
|
||||
#define COMMUNICATION_COOLDOWN_AI (30 SECONDS)
|
||||
#define COMMUNICATION_COOLDOWN_MEETING (5 MINUTES)
|
||||
|
||||
SUBSYSTEM_DEF(communications)
|
||||
name = "Communications"
|
||||
flags = SS_NO_INIT | SS_NO_FIRE
|
||||
|
||||
COOLDOWN_DECLARE(silicon_message_cooldown)
|
||||
COOLDOWN_DECLARE(nonsilicon_message_cooldown)
|
||||
|
||||
/// Are we trying to send a cross-station message that contains soft-filtered words? If so, flip to TRUE to extend the time admins have to cancel the message.
|
||||
var/soft_filtering = FALSE
|
||||
|
||||
/// A list of footnote datums, to be added to the bottom of the roundstart command report.
|
||||
var/list/command_report_footnotes = list()
|
||||
/// A counter of conditions that are blocking the command report from printing. Counter incremements up for every blocking condition, and de-incrememnts when it is complete.
|
||||
var/block_command_report = 0
|
||||
/// Has a special xenomorph egg been delivered?
|
||||
var/xenomorph_egg_delivered = FALSE
|
||||
/// The location where the special xenomorph egg was planted
|
||||
var/area/captivity_area
|
||||
|
||||
/datum/controller/subsystem/communications/proc/can_announce(mob/living/user, is_silicon)
|
||||
if(is_silicon && COOLDOWN_FINISHED(src, silicon_message_cooldown))
|
||||
return TRUE
|
||||
else if(!is_silicon && COOLDOWN_FINISHED(src, nonsilicon_message_cooldown))
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/datum/controller/subsystem/communications/proc/make_announcement(mob/living/user, is_silicon, input, syndicate, list/players)
|
||||
if(!can_announce(user, is_silicon))
|
||||
return FALSE
|
||||
if(is_silicon)
|
||||
minor_announce(html_decode(input),"[user.name] announces:", players = players)
|
||||
COOLDOWN_START(src, silicon_message_cooldown, COMMUNICATION_COOLDOWN_AI)
|
||||
else
|
||||
var/list/message_data = user.treat_message(input)
|
||||
if(syndicate)
|
||||
priority_announce(html_decode(message_data["message"]), null, 'sound/misc/announce_syndi.ogg', ANNOUNCEMENT_TYPE_SYNDICATE, has_important_message = TRUE, players = players, color_override = "red")
|
||||
else
|
||||
priority_announce(html_decode(message_data["message"]), null, 'sound/misc/announce.ogg', ANNOUNCEMENT_TYPE_CAPTAIN, has_important_message = TRUE, players = players)
|
||||
COOLDOWN_START(src, nonsilicon_message_cooldown, COMMUNICATION_COOLDOWN)
|
||||
user.log_talk(input, LOG_SAY, tag="priority announcement")
|
||||
message_admins("[ADMIN_LOOKUPFLW(user)] has made a priority announcement.")
|
||||
|
||||
/datum/controller/subsystem/communications/proc/send_message(datum/comm_message/sending,print = TRUE,unique = FALSE)
|
||||
for(var/obj/machinery/computer/communications/C in GLOB.shuttle_caller_list)
|
||||
if(!(C.machine_stat & (BROKEN|NOPOWER)) && is_station_level(C.z))
|
||||
if(unique)
|
||||
C.add_message(sending)
|
||||
else //We copy the message for each console, answers and deletions won't be shared
|
||||
var/datum/comm_message/M = new(sending.title,sending.content,sending.possible_answers.Copy())
|
||||
C.add_message(M)
|
||||
if(print)
|
||||
var/obj/item/paper/printed_paper = new /obj/item/paper(C.loc)
|
||||
printed_paper.name = "paper - '[sending.title]'"
|
||||
printed_paper.add_raw_text(sending.content)
|
||||
printed_paper.update_appearance()
|
||||
|
||||
#undef COMMUNICATION_COOLDOWN
|
||||
#undef COMMUNICATION_COOLDOWN_AI
|
||||
#undef COMMUNICATION_COOLDOWN_MEETING
|
||||
@@ -317,7 +317,7 @@ SUBSYSTEM_DEF(dynamic)
|
||||
SSticker.news_report = SSshuttle.emergency?.is_hijacked() ? SHUTTLE_HIJACK : STATION_EVACUATED
|
||||
|
||||
/datum/controller/subsystem/dynamic/proc/send_intercept()
|
||||
if(SScommunications.block_command_report) //If we don't want the report to be printed just yet, we put it off until it's ready
|
||||
if(DScommunications.block_command_report) //If we don't want the report to be printed just yet, we put it off until it's ready
|
||||
addtimer(CALLBACK(src, PROC_REF(send_intercept)), 10 SECONDS)
|
||||
return
|
||||
|
||||
@@ -349,10 +349,10 @@ SUBSYSTEM_DEF(dynamic)
|
||||
if(trait_list_strings.len > 0)
|
||||
. += "<hr><b>Identified shift divergencies:</b><BR>" + trait_list_strings.Join()
|
||||
|
||||
if(length(SScommunications.command_report_footnotes))
|
||||
if(length(DScommunications.command_report_footnotes))
|
||||
var/footnote_pile = ""
|
||||
|
||||
for(var/datum/command_footnote/footnote in SScommunications.command_report_footnotes)
|
||||
for(var/datum/command_footnote/footnote in DScommunications.command_report_footnotes)
|
||||
footnote_pile += "[footnote.message]<BR>"
|
||||
footnote_pile += "<i>[footnote.signature]</i><BR>"
|
||||
footnote_pile += "<BR>"
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
/*! How material datums work
|
||||
Materials are now instanced datums, with an associative list of them being kept in SSmaterials. We only instance the materials once and then re-use these instances for everything.
|
||||
|
||||
These materials call on_applied() on whatever item they are applied to, common effects are adding components, changing color and changing description. This allows us to differentiate items based on the material they are made out of.area
|
||||
|
||||
*/
|
||||
|
||||
SUBSYSTEM_DEF(materials)
|
||||
name = "Materials"
|
||||
flags = SS_NO_FIRE | SS_NO_INIT
|
||||
///Dictionary of material.id || material ref
|
||||
var/list/materials
|
||||
///Dictionary of type || list of material refs
|
||||
var/list/materials_by_type
|
||||
///Dictionary of type || list of material ids
|
||||
var/list/materialids_by_type
|
||||
///Dictionary of category || list of material refs
|
||||
var/list/materials_by_category
|
||||
///Dictionary of category || list of material ids, mostly used by rnd machines like autolathes.
|
||||
var/list/materialids_by_category
|
||||
///A cache of all material combinations that have been used
|
||||
var/list/list/material_combos
|
||||
///List of stackcrafting recipes for materials using base recipes
|
||||
var/list/base_stack_recipes = list(
|
||||
new /datum/stack_recipe("Chair", /obj/structure/chair/greyscale, one_per_turf = TRUE, on_solid_ground = TRUE, applies_mats = TRUE, category = CAT_FURNITURE),
|
||||
new /datum/stack_recipe("Toilet", /obj/structure/toilet/greyscale, one_per_turf = TRUE, on_solid_ground = TRUE, applies_mats = TRUE, category = CAT_FURNITURE),
|
||||
new /datum/stack_recipe("Sink Frame", /obj/structure/sinkframe, one_per_turf = TRUE, on_solid_ground = TRUE, applies_mats = TRUE, category = CAT_FURNITURE),
|
||||
new /datum/stack_recipe("Material floor tile", /obj/item/stack/tile/material, 1, 4, 20, applies_mats = TRUE, check_density = FALSE, category = CAT_TILES),
|
||||
new /datum/stack_recipe("Material airlock assembly", /obj/structure/door_assembly/door_assembly_material, 4, time = 5 SECONDS, one_per_turf = TRUE, on_solid_ground = TRUE, applies_mats = TRUE, category = CAT_DOORS),
|
||||
)
|
||||
///List of stackcrafting recipes for materials using rigid recipes
|
||||
var/list/rigid_stack_recipes = list(
|
||||
new /datum/stack_recipe("Carving block", /obj/structure/carving_block, 5, time = 3 SECONDS, one_per_turf = TRUE, on_solid_ground = TRUE, applies_mats = TRUE, category = CAT_STRUCTURE),
|
||||
)
|
||||
|
||||
///A list of dimensional themes used by the dimensional anomaly and other things, most of which require materials to function.
|
||||
var/list/datum/dimension_theme/dimensional_themes
|
||||
|
||||
///Ran on initialize, populated the materials and materials_by_category dictionaries with their appropiate vars (See these variables for more info)
|
||||
/datum/controller/subsystem/materials/proc/InitializeMaterials()
|
||||
materials = list()
|
||||
materials_by_type = list()
|
||||
materialids_by_type = list()
|
||||
materials_by_category = list()
|
||||
materialids_by_category = list()
|
||||
material_combos = list()
|
||||
for(var/type in subtypesof(/datum/material))
|
||||
var/datum/material/mat_type = type
|
||||
if(!(initial(mat_type.init_flags) & MATERIAL_INIT_MAPLOAD))
|
||||
continue // Do not initialize at mapload
|
||||
InitializeMaterial(list(mat_type))
|
||||
|
||||
dimensional_themes = init_subtypes_w_path_keys(/datum/dimension_theme)
|
||||
|
||||
/** Creates and caches a material datum.
|
||||
*
|
||||
* Arugments:
|
||||
* - [arguments][/list]: The arguments to use to create the material datum
|
||||
* - The first element is the type of material to initialize.
|
||||
*/
|
||||
/datum/controller/subsystem/materials/proc/InitializeMaterial(list/arguments)
|
||||
var/datum/material/mat_type = arguments[1]
|
||||
if(initial(mat_type.init_flags) & MATERIAL_INIT_BESPOKE)
|
||||
arguments[1] = GetIdFromArguments(arguments)
|
||||
|
||||
var/datum/material/mat_ref = new mat_type
|
||||
if(!mat_ref.Initialize(arglist(arguments)))
|
||||
return null
|
||||
|
||||
var/mat_id = mat_ref.id
|
||||
materials[mat_id] = mat_ref
|
||||
materials_by_type[mat_type] += list(mat_ref)
|
||||
materialids_by_type[mat_type] += list(mat_id)
|
||||
for(var/category in mat_ref.categories)
|
||||
materials_by_category[category] += list(mat_ref)
|
||||
materialids_by_category[category] += list(mat_id)
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_MATERIALS_INIT_MAT, mat_ref)
|
||||
return mat_ref
|
||||
|
||||
/** Fetches a cached material singleton when passed sufficient arguments.
|
||||
*
|
||||
* Arguments:
|
||||
* - [arguments][/list]: The list of arguments used to fetch the material ref.
|
||||
* - The first element is a material datum, text string, or material type.
|
||||
* - [Material datums][/datum/material] are assumed to be references to the cached datum and are returned
|
||||
* - Text is assumed to be the text ID of a material and the corresponding material is fetched from the cache
|
||||
* - A material type is checked for bespokeness:
|
||||
* - If the material type is not bespoke the type is assumed to be the id for a material and the corresponding material is loaded from the cache.
|
||||
* - If the material type is bespoke a text ID is generated from the arguments list and used to load a material datum from the cache.
|
||||
* - The following elements are used to generate bespoke IDs
|
||||
*/
|
||||
/datum/controller/subsystem/materials/proc/_GetMaterialRef(list/arguments)
|
||||
if(!materials)
|
||||
InitializeMaterials()
|
||||
|
||||
var/datum/material/key = arguments[1]
|
||||
if(istype(key))
|
||||
return key // We are assuming here that the only thing allowed to create material datums is [/datum/controller/subsystem/materials/proc/InitializeMaterial]
|
||||
|
||||
if(istext(key)) // Handle text id
|
||||
. = materials[key]
|
||||
if(!.)
|
||||
WARNING("Attempted to fetch material ref with invalid text id '[key]'")
|
||||
return
|
||||
|
||||
if(!ispath(key, /datum/material))
|
||||
CRASH("Attempted to fetch material ref with invalid key [key]")
|
||||
|
||||
if(!(initial(key.init_flags) & MATERIAL_INIT_BESPOKE))
|
||||
. = materials[key]
|
||||
if(!.)
|
||||
WARNING("Attempted to fetch reference to an abstract material with key [key]")
|
||||
return
|
||||
|
||||
key = GetIdFromArguments(arguments)
|
||||
return materials[key] || InitializeMaterial(arguments)
|
||||
|
||||
/** I'm not going to lie, this was swiped from [SSdcs][/datum/controller/subsystem/processing/dcs].
|
||||
* Credit does to ninjanomnom
|
||||
*
|
||||
* Generates an id for bespoke ~~elements~~ materials when given the argument list
|
||||
* Generating the id here is a bit complex because we need to support named arguments
|
||||
* Named arguments can appear in any order and we need them to appear after ordered arguments
|
||||
* We assume that no one will pass in a named argument with a value of null
|
||||
**/
|
||||
/datum/controller/subsystem/materials/proc/GetIdFromArguments(list/arguments)
|
||||
var/datum/material/mattype = arguments[1]
|
||||
var/list/fullid = list("[initial(mattype.id) || mattype]")
|
||||
var/list/named_arguments = list()
|
||||
for(var/i in 2 to length(arguments))
|
||||
var/key = arguments[i]
|
||||
var/value
|
||||
if(istext(key))
|
||||
value = arguments[key]
|
||||
if(!(istext(key) || isnum(key)))
|
||||
key = REF(key)
|
||||
key = "[key]" // Key is stringified so numbers dont break things
|
||||
if(!isnull(value))
|
||||
if(!(istext(value) || isnum(value)))
|
||||
value = REF(value)
|
||||
named_arguments["[key]"] = value
|
||||
else
|
||||
fullid += "[key]"
|
||||
|
||||
if(length(named_arguments))
|
||||
named_arguments = sort_list(named_arguments)
|
||||
fullid += named_arguments
|
||||
return list2params(fullid)
|
||||
|
||||
|
||||
/// Returns a list to be used as an object's custom_materials. Lists will be cached and re-used based on the parameters.
|
||||
/datum/controller/subsystem/materials/proc/FindOrCreateMaterialCombo(list/materials_declaration, multiplier)
|
||||
if(!LAZYLEN(materials_declaration))
|
||||
return null // If we get a null we pass it right back, we don't want to generate stack traces just because something is clearing out its materials list.
|
||||
|
||||
if(!material_combos)
|
||||
InitializeMaterials()
|
||||
var/list/combo_params = list()
|
||||
for(var/x in materials_declaration)
|
||||
var/datum/material/mat = x
|
||||
combo_params += "[istype(mat) ? mat.id : mat]=[OPTIMAL_COST(materials_declaration[mat] * multiplier)]"
|
||||
sortTim(combo_params, GLOBAL_PROC_REF(cmp_text_asc)) // We have to sort now in case the declaration was not in order
|
||||
var/combo_index = combo_params.Join("-")
|
||||
var/list/combo = material_combos[combo_index]
|
||||
if(!combo)
|
||||
combo = list()
|
||||
for(var/mat in materials_declaration)
|
||||
combo[GET_MATERIAL_REF(mat)] = OPTIMAL_COST(materials_declaration[mat] * multiplier)
|
||||
material_combos[combo_index] = combo
|
||||
return combo
|
||||
@@ -10,7 +10,7 @@ MOVEMENT_SUBSYSTEM_DEF(cliff_falling)
|
||||
|
||||
/datum/controller/subsystem/movement/cliff_falling/proc/start_falling(atom/movable/faller, turf/open/cliff/cliff)
|
||||
// Make them move
|
||||
var/mover = SSmove_manager.move(moving = faller, direction = cliff.fall_direction, delay = cliff.fall_speed, subsystem = src, priority = MOVEMENT_ABOVE_SPACE_PRIORITY, flags = MOVEMENT_LOOP_OUTSIDE_CONTROL | MOVEMENT_LOOP_NO_DIR_UPDATE)
|
||||
var/mover = DSmove_manager.move(moving = faller, direction = cliff.fall_direction, delay = cliff.fall_speed, subsystem = src, priority = MOVEMENT_ABOVE_SPACE_PRIORITY, flags = MOVEMENT_LOOP_OUTSIDE_CONTROL | MOVEMENT_LOOP_NO_DIR_UPDATE)
|
||||
|
||||
cliff_grinders[faller] = mover
|
||||
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
/**
|
||||
* Acts as a namespace for movement packet/type related procs
|
||||
*
|
||||
* Exists to provide an in code implementation of movement looping
|
||||
* Replaces things like walk() or walk_to(), among others
|
||||
*
|
||||
* Because we're doing things in engine, we have a lot more control over how different operations are performed
|
||||
* We also get more say in when things happen, so we can subject movements to the whims of the master controller
|
||||
* Rather then using a fuck ton of cpu just moving mobs or meteors
|
||||
*
|
||||
* The goal is to keep the loops themselves reasonably barebone, and implement more advanced behavior and control via the signals
|
||||
*
|
||||
* This may be bypassed in cases where snowflakes are nessesary, or where performance is important. S not a hard and fast thing
|
||||
*
|
||||
* Every atom can have a movement packet, which contains information and behavior about currently active loops, and queuing info
|
||||
* Loops control how movement actually happens. So there's a "move in this direction" loop, a "move randomly" loop
|
||||
*
|
||||
* You can find the logic for this control in this file
|
||||
*
|
||||
* Specifics of how different loops operate can be found in the movement_types.dm file, alongside the [add to loop][/datum/controller/subsystem/move_manager/proc/add_to_loop] helper procs that use them
|
||||
*
|
||||
**/
|
||||
SUBSYSTEM_DEF(move_manager)
|
||||
name = "Movement Handler"
|
||||
flags = SS_NO_INIT | SS_NO_FIRE
|
||||
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
|
||||
|
||||
///Adds a movable thing to a movement subsystem. Returns TRUE if it all worked, FALSE if it failed somehow
|
||||
/datum/controller/subsystem/move_manager/proc/add_to_loop(atom/movable/thing_to_add, datum/controller/subsystem/movement/subsystem = SSmovement, datum/move_loop/loop_type, priority = MOVEMENT_DEFAULT_PRIORITY, flags, datum/extra_info)
|
||||
var/datum/movement_packet/our_data = thing_to_add.move_packet
|
||||
if(!our_data)
|
||||
our_data = new(thing_to_add)
|
||||
|
||||
var/list/arguments = args.Copy(2) //Drop the atom, since the movement packet already knows about it
|
||||
return our_data.add_loop(arglist(arguments))
|
||||
|
||||
///Returns the subsystem's loop if we're processing on it, null otherwise
|
||||
/datum/controller/subsystem/move_manager/proc/processing_on(atom/movable/packet_owner, datum/controller/subsystem/movement/subsystem)
|
||||
var/datum/movement_packet/packet = packet_owner.move_packet
|
||||
if(!packet)
|
||||
return
|
||||
var/datum/move_loop/linked_loop = packet.existing_loops[subsystem]
|
||||
if(!linked_loop)
|
||||
return
|
||||
if(linked_loop.flags & MOVEMENT_LOOP_IGNORE_PRIORITY)
|
||||
return linked_loop
|
||||
if(linked_loop != packet.running_loop)
|
||||
return
|
||||
return linked_loop
|
||||
|
||||
///A packet of information that describes the current state of a moving object
|
||||
/datum/movement_packet
|
||||
///Our parent atom
|
||||
var/atom/movable/parent
|
||||
///The move loop that's currently running, excluding those that ignore priority.
|
||||
var/datum/move_loop/running_loop
|
||||
/**
|
||||
* Flags passed from the move loop before it calls move() and unset right after.
|
||||
* Allows for properties of a move loop to be easily checked by mechanics outside of it.
|
||||
* Having this a bitfield rather than a type var means we don't get screwed over
|
||||
* if the move loop gets deleted mid-move, FYI.
|
||||
*/
|
||||
var/processing_move_loop_flags = NONE
|
||||
///Assoc list of subsystems -> loop datum. Only one datum is allowed per subsystem
|
||||
var/list/existing_loops = list()
|
||||
|
||||
/datum/movement_packet/New(atom/movable/parent)
|
||||
src.parent = parent
|
||||
parent.move_packet = src
|
||||
|
||||
/datum/movement_packet/Destroy(force)
|
||||
parent.move_packet = null
|
||||
parent = null
|
||||
for(var/datum/controller/subsystem/processor as anything in existing_loops)
|
||||
var/datum/move_loop/loop = existing_loops[processor]
|
||||
if(QDELETED(loop))
|
||||
continue
|
||||
qdel(loop)
|
||||
existing_loops.Cut()
|
||||
existing_loops = null //Catch anyone modifying this post del
|
||||
return ..()
|
||||
|
||||
///Adds a loop to our parent. Returns the created loop if a success, null otherwise
|
||||
/datum/movement_packet/proc/add_loop(datum/controller/subsystem/movement/subsystem, datum/move_loop/loop_type, priority, flags, datum/extra_info)
|
||||
var/datum/move_loop/existing_loop = existing_loops[subsystem]
|
||||
|
||||
if(existing_loop && existing_loop.priority > priority)
|
||||
if(!(existing_loop.flags & MOVEMENT_LOOP_IGNORE_PRIORITY) && !(flags & MOVEMENT_LOOP_IGNORE_PRIORITY))
|
||||
return //Give up
|
||||
|
||||
if(existing_loop?.compare_loops(arglist(args.Copy(2))))
|
||||
return //it already exists stop trying to make the same moveloop
|
||||
|
||||
var/datum/move_loop/new_loop = new loop_type(src, subsystem, parent, priority, flags, extra_info) //Pass the mob to move and ourselves in via new
|
||||
var/list/arguments = args.Copy(6) //Just send the args we've not already dealt with
|
||||
|
||||
var/worked_out = new_loop.setup(arglist(arguments)) //Here goes the rest
|
||||
if(!worked_out)
|
||||
qdel(new_loop)
|
||||
return
|
||||
|
||||
existing_loops[subsystem] = new_loop
|
||||
if(existing_loop)
|
||||
qdel(existing_loop) //We need to do this here because otherwise the packet would think it was empty, and self destruct
|
||||
contest_running_loop(new_loop)
|
||||
return new_loop
|
||||
|
||||
///Attempts to contest the current running move loop. Returns TRUE if the loop is active, FALSE otherwise
|
||||
/datum/movement_packet/proc/contest_running_loop(datum/move_loop/contestant)
|
||||
var/datum/controller/subsystem/movement/contesting_subsystem = contestant.controller
|
||||
|
||||
if(contestant.flags & MOVEMENT_LOOP_IGNORE_PRIORITY)
|
||||
contesting_subsystem.add_loop(contestant)
|
||||
return TRUE
|
||||
if(!running_loop)
|
||||
running_loop = contestant
|
||||
contesting_subsystem.add_loop(running_loop)
|
||||
return TRUE
|
||||
if(running_loop.priority > contestant.priority)
|
||||
return FALSE
|
||||
|
||||
var/datum/controller/subsystem/movement/current_subsystem = running_loop.controller
|
||||
|
||||
var/current_running_loop = running_loop
|
||||
running_loop = contestant
|
||||
current_subsystem.remove_loop(current_running_loop)
|
||||
if(running_loop != contestant) // A signal registrant could have messed with things
|
||||
return FALSE
|
||||
contesting_subsystem.add_loop(contestant)
|
||||
return TRUE
|
||||
|
||||
///Tries to figure out the current favorite loop to run. More complex then just deciding between two different loops, assumes no running loop currently exists
|
||||
/datum/movement_packet/proc/decide_on_running_loop()
|
||||
if(running_loop)
|
||||
return
|
||||
if(!length(existing_loops)) //Die
|
||||
qdel(src)
|
||||
return
|
||||
var/datum/move_loop/favorite
|
||||
for(var/datum/controller/subsystem/movement/owner as anything in existing_loops)
|
||||
var/datum/move_loop/checking = existing_loops[owner]
|
||||
if(checking.flags & MOVEMENT_LOOP_IGNORE_PRIORITY)
|
||||
continue
|
||||
if(favorite && favorite.priority > checking.priority)
|
||||
continue
|
||||
favorite = checking
|
||||
|
||||
if(!favorite) //This isn't an error state, since some loops ignore the concept of a running loop
|
||||
return
|
||||
|
||||
var/datum/controller/subsystem/movement/favorite_subsystem = favorite.controller
|
||||
|
||||
running_loop = favorite
|
||||
favorite_subsystem.add_loop(running_loop)
|
||||
|
||||
/datum/movement_packet/proc/remove_loop(datum/controller/subsystem/movement/remove_from, datum/move_loop/loop_to_remove)
|
||||
if(loop_to_remove == running_loop)
|
||||
running_loop = null
|
||||
remove_from.remove_loop(loop_to_remove)
|
||||
if(loop_to_remove.flags & MOVEMENT_LOOP_IGNORE_PRIORITY)
|
||||
remove_from.remove_loop(loop_to_remove)
|
||||
if(QDELETED(src))
|
||||
return
|
||||
if(existing_loops[remove_from] == loop_to_remove)
|
||||
existing_loops -= remove_from
|
||||
decide_on_running_loop()
|
||||
return
|
||||
|
||||
/datum/movement_packet/proc/remove_subsystem(datum/controller/subsystem/movement/remove)
|
||||
var/datum/move_loop/our_loop = existing_loops[remove]
|
||||
if(!our_loop)
|
||||
return FALSE
|
||||
qdel(our_loop)
|
||||
return TRUE
|
||||
@@ -162,7 +162,7 @@
|
||||
status &= ~MOVELOOP_STATUS_PAUSED
|
||||
|
||||
///Removes the atom from some movement subsystem. Defaults to SSmovement
|
||||
/datum/controller/subsystem/move_manager/proc/stop_looping(atom/movable/moving, datum/controller/subsystem/movement/subsystem = SSmovement)
|
||||
/datum/system/move_manager/proc/stop_looping(atom/movable/moving, datum/controller/subsystem/movement/subsystem = SSmovement)
|
||||
var/datum/movement_packet/our_info = moving.move_packet
|
||||
if(!our_info)
|
||||
return FALSE
|
||||
@@ -183,7 +183,7 @@
|
||||
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
|
||||
*
|
||||
**/
|
||||
/datum/controller/subsystem/move_manager/proc/move(moving, direction, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
/datum/system/move_manager/proc/move(moving, direction, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
return add_to_loop(moving, subsystem, /datum/move_loop/move, priority, flags, extra_info, delay, timeout, direction)
|
||||
|
||||
///Replacement for walk()
|
||||
@@ -224,7 +224,7 @@
|
||||
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
|
||||
*
|
||||
**/
|
||||
/datum/controller/subsystem/move_manager/proc/force_move_dir(moving, direction, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
/datum/system/move_manager/proc/force_move_dir(moving, direction, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
return add_to_loop(moving, subsystem, /datum/move_loop/move/force, priority, flags, extra_info, delay, timeout, direction)
|
||||
|
||||
/datum/move_loop/move/force
|
||||
@@ -281,7 +281,7 @@
|
||||
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
|
||||
*
|
||||
**/
|
||||
/datum/controller/subsystem/move_manager/proc/force_move(moving, chasing, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
/datum/system/move_manager/proc/force_move(moving, chasing, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
return add_to_loop(moving, subsystem, /datum/move_loop/has_target/force_move, priority, flags, extra_info, delay, timeout, chasing)
|
||||
|
||||
///Used for force-move loops
|
||||
@@ -315,7 +315,7 @@
|
||||
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
|
||||
*
|
||||
**/
|
||||
/datum/controller/subsystem/move_manager/proc/jps_move(moving,
|
||||
/datum/system/move_manager/proc/jps_move(moving,
|
||||
chasing,
|
||||
delay,
|
||||
timeout,
|
||||
@@ -493,7 +493,7 @@
|
||||
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
|
||||
*
|
||||
**/
|
||||
/datum/controller/subsystem/move_manager/proc/move_to(moving, chasing, min_dist, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
/datum/system/move_manager/proc/move_to(moving, chasing, min_dist, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
return add_to_loop(moving, subsystem, /datum/move_loop/has_target/dist_bound/move_to, priority, flags, extra_info, delay, timeout, chasing, min_dist)
|
||||
|
||||
///Wrapper around walk_to()
|
||||
@@ -527,7 +527,7 @@
|
||||
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
|
||||
*
|
||||
**/
|
||||
/datum/controller/subsystem/move_manager/proc/move_away(moving, chasing, max_dist, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
/datum/system/move_manager/proc/move_away(moving, chasing, max_dist, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
return add_to_loop(moving, subsystem, /datum/move_loop/has_target/dist_bound/move_away, priority, flags, extra_info, delay, timeout, chasing, max_dist)
|
||||
|
||||
///Wrapper around walk_away()
|
||||
@@ -562,7 +562,7 @@
|
||||
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
|
||||
*
|
||||
**/
|
||||
/datum/controller/subsystem/move_manager/proc/move_towards(moving, chasing, delay, home, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
/datum/system/move_manager/proc/move_towards(moving, chasing, delay, home, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
return add_to_loop(moving, subsystem, /datum/move_loop/has_target/move_towards, priority, flags, extra_info, delay, timeout, chasing, home)
|
||||
|
||||
/**
|
||||
@@ -581,7 +581,7 @@
|
||||
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
|
||||
*
|
||||
**/
|
||||
/datum/controller/subsystem/move_manager/proc/home_onto(moving, chasing, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
/datum/system/move_manager/proc/home_onto(moving, chasing, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
return move_towards(moving, chasing, delay, TRUE, timeout, subsystem, priority, flags, extra_info)
|
||||
|
||||
///Used as a alternative to walk_towards
|
||||
@@ -717,7 +717,7 @@
|
||||
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
|
||||
*
|
||||
**/
|
||||
/datum/controller/subsystem/move_manager/proc/move_towards_legacy(moving, chasing, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
/datum/system/move_manager/proc/move_towards_legacy(moving, chasing, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
return add_to_loop(moving, subsystem, /datum/move_loop/has_target/move_towards_budget, priority, flags, extra_info, delay, timeout, chasing)
|
||||
|
||||
///The actual implementation of walk_towards()
|
||||
@@ -743,7 +743,7 @@
|
||||
* priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY
|
||||
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
|
||||
*/
|
||||
/datum/controller/subsystem/move_manager/proc/freeze(moving, halted_turf, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
/datum/system/move_manager/proc/freeze(moving, halted_turf, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
return add_to_loop(moving, subsystem, /datum/move_loop/freeze, priority, flags, extra_info, delay, timeout, halted_turf)
|
||||
|
||||
/// As close as you can get to a "do-nothing" move loop, the pure intention of this is to absolutely resist all and any automated movement until the move loop times out.
|
||||
@@ -767,7 +767,7 @@
|
||||
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
|
||||
*
|
||||
**/
|
||||
/datum/controller/subsystem/move_manager/proc/move_rand(moving, directions, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
/datum/system/move_manager/proc/move_rand(moving, directions, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
if(!directions)
|
||||
directions = GLOB.alldirs
|
||||
return add_to_loop(moving, subsystem, /datum/move_loop/move_rand, priority, flags, extra_info, delay, timeout, directions)
|
||||
@@ -819,7 +819,7 @@
|
||||
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
|
||||
*
|
||||
**/
|
||||
/datum/controller/subsystem/move_manager/proc/move_to_rand(moving, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
/datum/system/move_manager/proc/move_to_rand(moving, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
return add_to_loop(moving, subsystem, /datum/move_loop/move_to_rand, priority, flags, extra_info, delay, timeout)
|
||||
|
||||
///Wrapper around step_rand
|
||||
@@ -845,7 +845,7 @@
|
||||
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
|
||||
*
|
||||
**/
|
||||
/datum/controller/subsystem/move_manager/proc/move_disposals(moving, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
/datum/system/move_manager/proc/move_disposals(moving, delay, timeout, subsystem, priority, flags, datum/extra_info)
|
||||
return add_to_loop(moving, subsystem, /datum/move_loop/disposal_holder, priority, flags, extra_info, delay, timeout)
|
||||
|
||||
/// Disposal holders need to move through a chain of pipes
|
||||
|
||||
Reference in New Issue
Block a user