mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-11 17:35:05 +00:00
## About The Pull Request Apparently wall construction code is snowflaked and indented as fuck (and the same goes for door assemblies). I'm not bothering refactoring everything with them, only to reduce the indentation, changing a couple vars and overall making it easier to work with them later. This includes wall construction not being hardcoded to sheets but include the possibility to use other kind of stacks as well (if you don't count the snowflake interaction with iron rods). In layman's terms, this means you can make walls made out of sand (distinct from sandstone) again. Also I've done some small changes to the materials storage, so that it can eject ores too if the material doesn't have a sheet type. Also, I've been told there may be issues with broken, uninteractable (probably not properly initialized) glass sheets beside the ORM. I'm not 100% sure about the deets, but it may have something to do with spawning the glass on the same turf the ORM is listening to, when smelting sand, causing some race conditions, so let's spawn it in nullspace ## Why It's Good For The Game While I'm sure there may be more elegant solutions (just take a look at the wall and door construction code, they both use text2path oh god!), I'm just here to make things a lil' cleaner and be done with issues with the fact that sand is made of sand. ## Changelog 🆑 fix: You can once again make sand walls. fix: Deconstructing an autolathe with sand in it should now drop sand. /🆑
628 lines
24 KiB
Plaintext
628 lines
24 KiB
Plaintext
// Always announce this action
|
|
#define ALWAYS_ANNOUNCE (ALL)
|
|
// Announced when someone tries to ban someone without QM access
|
|
#define BAN_ATTEMPT_FAILURE_NO_ACCESS (1<<1)
|
|
// Announced when someone tries to ban someone with QM access without being the Captain
|
|
#define BAN_ATTEMPT_FAILURE_CHALLENGING_DA_CHIEF (1<<2)
|
|
// Announced when a silicon tries to ban someone
|
|
#define BAN_ATTEMPT_FAILURE_SOULLESS_MACHINE (1<<3)
|
|
// Announced when a user is banned from the ore silo
|
|
#define BAN_CONFIRMATION (1<<4)
|
|
// Announced when a user is unbanned from the ore silo
|
|
#define UNBAN_CONFIRMATION (1<<5)
|
|
// Announced when a suspicious(chameleon ID worn by user) log is Among the ore silo logs and someone tries to ban them
|
|
#define FAILED_OPERATION_SUSPICIOUS (1<<6)
|
|
// Announced when a user tries to ban someone without a bank account ID
|
|
#define FAILED_OPERATION_NO_BANK_ID (1<<7)
|
|
// Announced when a user tries to unrestrict the ore silo without QM access
|
|
#define UNRESTRICT_FAILURE_NO_ACCESS (1<<8)
|
|
// Announced when a silicon tries to unrestrict the ore silo
|
|
#define UNRESTRICT_FAILURE_SOULLESS_MACHINE (1<<9)
|
|
// Announced when a user removes the worn ID(with valid bank account) requirement from the ore silo
|
|
#define UNRESTRICT_CONFIRMATION (1<<10)
|
|
// Announced when a user restricts the ore silo to require a valid ID with bank account
|
|
#define RESTRICT_CONFIRMATION (1<<11)
|
|
|
|
/obj/machinery/ore_silo
|
|
name = "ore silo"
|
|
desc = "An all-in-one bluespace storage and transmission system for the station's mineral distribution needs."
|
|
icon = 'icons/obj/machines/ore_silo.dmi'
|
|
icon_state = "silo"
|
|
density = TRUE
|
|
circuit = /obj/item/circuitboard/machine/ore_silo
|
|
interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN|INTERACT_MACHINE_ALLOW_SILICON|INTERACT_MACHINE_OPEN_SILICON
|
|
processing_flags = NONE
|
|
|
|
/// Only the station loaded ore silo starts out with ID restrictions, else its optional
|
|
var/ID_required = FALSE
|
|
/// List of all connected components that are on hold from accessing materials.
|
|
var/list/holds = list()
|
|
/// List of all components that are sharing ores with this silo.
|
|
var/list/datum/component/remote_materials/ore_connected_machines = list()
|
|
/// Material Container
|
|
var/datum/component/material_container/materials
|
|
/// A list of names of bank account IDs that are banned from using this ore silo.
|
|
var/list/banned_users = list()
|
|
///The machine's internal radio, used to broadcast alerts.
|
|
var/obj/item/radio/radio
|
|
///The channels we announce over
|
|
var/list/radio_channels = list(
|
|
RADIO_CHANNEL_COMMON = NONE,
|
|
RADIO_CHANNEL_COMMAND = NONE,
|
|
RADIO_CHANNEL_SUPPLY = NONE,
|
|
RADIO_CHANNEL_SECURITY = NONE,
|
|
)
|
|
///List of announcement messages for silo restrictions
|
|
var/static/alist/announcement_messages = alist(
|
|
BAN_ATTEMPT_FAILURE_NO_ACCESS = "ACCESS ENFORCEMENT FAILURE: $SILO_USER_NAME lacks supply command authority.",
|
|
BAN_ATTEMPT_FAILURE_CHALLENGING_DA_CHIEF = "ACCESS ENFORCEMENT FAILURE: $SILO_USER_NAME attempting subversion of supply command authority.",
|
|
BAN_ATTEMPT_FAILURE_SOULLESS_MACHINE = "$SILO_USER_NAME INTERFACE_EXCEPTION -> BANNED_USERS+=\[$TARGET_NAME\] => NO_OP",
|
|
BAN_CONFIRMATION = "ACCESS ENFORCEMENT CONFIRMATION\[$SILO_USER_NAME\]: $TARGET_NAME banned from ore silo access.",
|
|
UNBAN_CONFIRMATION = "ACCESS ENFORCEMENT CONFIRMATION\[$SILO_USER_NAME\]: $TARGET_NAME unbanned from ore silo access.",
|
|
FAILED_OPERATION_SUSPICIOUS = "NULL_ACCOUNT_RESOLVE_PTR_#?",
|
|
FAILED_OPERATION_NO_BANK_ID = "ACCESS ENFORCEMENT FAILURE: No account ID found. Please contact a banker.",
|
|
UNRESTRICT_FAILURE_NO_ACCESS = "ID ACCESS REQUIREMENT ENFORCED: $SILO_USER_NAME lacks supply command authority; ID ACCESS REQUIREMENT REMOVAL FAILED.",
|
|
UNRESTRICT_FAILURE_SOULLESS_MACHINE = "$SILO_USER_NAME INTERFACE_EXCEPTION -> ID_ACCESS_REQUIREMENT = !ID_ACCESS_REQUIREMENT => NO_OP",
|
|
RESTRICT_CONFIRMATION = "ID ACCESS REQUIREMENT ROUTINE STARTED: $SILO_USER_NAME has enforced ID read requirement for this ore silo.",
|
|
UNRESTRICT_CONFIRMATION = "ID ACCESS REQUIREMENT ROUTINE SUSPENDED: $SILO_USER_NAME has removed ID read requirement for this ore silo.",
|
|
RESTRICT_FAILURE = "ID ACCESS REQUIREMENT ROUTINE FAILED TO START: $SILO_USER_NAME()"
|
|
)
|
|
|
|
/obj/machinery/ore_silo/Initialize(mapload)
|
|
. = ..()
|
|
|
|
materials = AddComponent( \
|
|
/datum/component/material_container, \
|
|
SSmaterials.materials_by_category[MAT_CATEGORY_SILO], \
|
|
INFINITY, \
|
|
MATCONTAINER_EXAMINE, \
|
|
container_signals = list( \
|
|
COMSIG_MATCONTAINER_ITEM_CONSUMED = TYPE_PROC_REF(/obj/machinery/ore_silo, on_item_consumed), \
|
|
COMSIG_MATCONTAINER_STACK_RETRIEVED = TYPE_PROC_REF(/obj/machinery/ore_silo, log_sheets_ejected), \
|
|
), \
|
|
allowed_items = /obj/item/stack \
|
|
)
|
|
|
|
if (!GLOB.ore_silo_default && mapload && is_station_level(z))
|
|
GLOB.ore_silo_default = src
|
|
ID_required = TRUE
|
|
|
|
register_context()
|
|
setup_radio()
|
|
configure_default_announcements_policy()
|
|
|
|
/obj/machinery/ore_silo/Destroy()
|
|
if (GLOB.ore_silo_default == src)
|
|
GLOB.ore_silo_default = null
|
|
|
|
for(var/datum/component/remote_materials/mats as anything in ore_connected_machines)
|
|
mats.disconnect()
|
|
|
|
ore_connected_machines = null
|
|
materials = null
|
|
QDEL_NULL(radio)
|
|
|
|
return ..()
|
|
|
|
/obj/machinery/ore_silo/emag_act(mob/living/user)
|
|
if(obj_flags & EMAGGED)
|
|
return FALSE
|
|
obj_flags |= EMAGGED
|
|
return TRUE
|
|
|
|
/obj/machinery/ore_silo/proc/setup_radio()
|
|
radio = new(src)
|
|
radio.subspace_transmission = TRUE
|
|
radio.canhear_range = 0
|
|
radio.set_listening(FALSE)
|
|
radio.keyslot = new
|
|
radio.keyslot.channels[RADIO_CHANNEL_COMMON] = TRUE
|
|
radio.keyslot.channels[RADIO_CHANNEL_COMMAND] = TRUE
|
|
radio.keyslot.channels[RADIO_CHANNEL_SUPPLY] = TRUE
|
|
radio.keyslot.channels[RADIO_CHANNEL_SECURITY] = TRUE
|
|
radio.recalculateChannels()
|
|
|
|
/obj/machinery/ore_silo/examine(mob/user)
|
|
. = ..()
|
|
. += span_notice("It can be linked to techfabs, circuit printers and protolathes with a multitool.")
|
|
. += span_notice("Its maintainence panel can be [EXAMINE_HINT("screwed")] [panel_open ? "closed" : "open"].")
|
|
if(panel_open)
|
|
. += span_notice("The whole machine can be [EXAMINE_HINT("pried")] apart.")
|
|
|
|
/obj/machinery/ore_silo/add_context(atom/source, list/context, obj/item/held_item, mob/user)
|
|
. = NONE
|
|
if(isnull(held_item))
|
|
return
|
|
|
|
if(held_item.tool_behaviour == TOOL_SCREWDRIVER)
|
|
context[SCREENTIP_CONTEXT_LMB] = "[panel_open ? "Close" : "Open"] Panel"
|
|
return CONTEXTUAL_SCREENTIP_SET
|
|
|
|
if(held_item.tool_behaviour == TOOL_MULTITOOL)
|
|
context[SCREENTIP_CONTEXT_LMB] = "Log Silo"
|
|
return CONTEXTUAL_SCREENTIP_SET
|
|
|
|
if(panel_open && held_item.tool_behaviour == TOOL_CROWBAR)
|
|
context[SCREENTIP_CONTEXT_LMB] = "Deconstruct"
|
|
return CONTEXTUAL_SCREENTIP_SET
|
|
|
|
/obj/machinery/ore_silo/proc/on_item_consumed(datum/component/material_container/container, obj/item/item_inserted, last_inserted_id, mats_consumed, amount_inserted, atom/context, alist/user_data)
|
|
SIGNAL_HANDLER
|
|
|
|
silo_log(context, "DEPOSITED", amount_inserted, item_inserted.name, mats_consumed, user_data)
|
|
|
|
SEND_SIGNAL(context, COMSIG_SILO_ITEM_CONSUMED, container, item_inserted, last_inserted_id, mats_consumed, amount_inserted)
|
|
|
|
/obj/machinery/ore_silo/proc/log_sheets_ejected(datum/component/material_container/container, obj/item/stack/sheet/sheets, atom/context, alist/user_data)
|
|
SIGNAL_HANDLER
|
|
|
|
silo_log(context, "WITHDRAWN", -sheets.amount * SHEET_MATERIAL_AMOUNT, "[sheets.name]", sheets.custom_materials, user_data)
|
|
|
|
/obj/machinery/ore_silo/screwdriver_act(mob/living/user, obj/item/tool)
|
|
. = ITEM_INTERACT_BLOCKING
|
|
if(default_deconstruction_screwdriver(user, icon_state, icon_state, tool))
|
|
return ITEM_INTERACT_SUCCESS
|
|
|
|
/obj/machinery/ore_silo/crowbar_act(mob/living/user, obj/item/tool)
|
|
. = ITEM_INTERACT_BLOCKING
|
|
if(default_deconstruction_crowbar(tool))
|
|
return ITEM_INTERACT_SUCCESS
|
|
|
|
/obj/machinery/ore_silo/multitool_act(mob/living/user, obj/item/multitool/I)
|
|
I.set_buffer(src)
|
|
balloon_alert(user, "saved to multitool buffer")
|
|
return ITEM_INTERACT_SUCCESS
|
|
|
|
|
|
/**
|
|
* The logic for disconnecting a remote receptacle (RCD, fabricator, etc.) is collected here for sanity's sake
|
|
* rather than being on specific types. Serves to agnosticize the remote_materials component somewhat rather than
|
|
* snowflaking code for silos into the component.
|
|
* * receptacle - The datum/component/remote_materials component that is getting connected.
|
|
* * physical_receptacle - the actual object in the game world that was connected to our material supply. Typed as atom/movable for
|
|
* future-proofing against anything that may conceivably one day have remote silo access, such as a cyborg, an implant, structures, vehicles,
|
|
* and so-on.
|
|
*/
|
|
/obj/machinery/ore_silo/proc/connect_receptacle(datum/component/remote_materials/receptacle, atom/movable/physical_receptacle)
|
|
ore_connected_machines += receptacle
|
|
receptacle.mat_container = src.materials
|
|
receptacle.silo = src
|
|
RegisterSignal(physical_receptacle, COMSIG_ORE_SILO_PERMISSION_CHECKED, PROC_REF(check_permitted))
|
|
|
|
/**
|
|
* The logic for disconnecting a remote receptacle (RCD, fabricator, etc.) is collected here for sanity's sake
|
|
* rather than being on specific types. Cleans up references to us and to the receptacle.
|
|
* * receptacle - The datum/component/remote_materials component that is getting destroyed.
|
|
* * physical_receptacle - the actual object in the game world that was connected to our material supply. Typed as atom/movable for
|
|
* future-proofing against anything that may conceivably one day have remote silo access, such as a cyborg, an implant, structures, vehicles,
|
|
* and so-on.
|
|
*/
|
|
/obj/machinery/ore_silo/proc/disconnect_receptacle(datum/component/remote_materials/receptacle, atom/movable/physical_receptacle)
|
|
ore_connected_machines -= receptacle
|
|
receptacle.mat_container = null
|
|
receptacle.silo = null
|
|
holds -= receptacle
|
|
UnregisterSignal(physical_receptacle, COMSIG_ORE_SILO_PERMISSION_CHECKED)
|
|
|
|
/obj/machinery/ore_silo/proc/check_permitted(datum/source, alist/user_data, atom/movable/physical_receptacle)
|
|
SIGNAL_HANDLER
|
|
|
|
if(!ID_required)
|
|
return COMPONENT_ORE_SILO_ALLOW
|
|
if(!islist(user_data))
|
|
// Just allow to salvage the situation
|
|
. = COMPONENT_ORE_SILO_ALLOW
|
|
user_data = ID_DATA(null)
|
|
CRASH("Invalid data passed to check_permitted")
|
|
if(user_data[SILICON_OVERRIDE] || user_data[CHAMELEON_OVERRIDE] || astype(user_data["accesses"], /list)?.Find(ACCESS_QM))
|
|
return COMPONENT_ORE_SILO_ALLOW
|
|
if(user_data[ID_READ_FAILURE])
|
|
physical_receptacle.say("SILO ERR: ID interface failure. Please contact the Head of Personnel.")
|
|
return COMPONENT_ORE_SILO_DENY
|
|
if(!user_data["account_id"] || !isnum(user_data["account_id"]))
|
|
if(prob(5))
|
|
physical_receptacle.say("SILO ERR: Bank account ID not found. Initiating anti-communist silo-access policy.")
|
|
physical_receptacle.say("SILO ERR: No account ID found. Please contact Head of Personnel.")
|
|
return COMPONENT_ORE_SILO_DENY
|
|
if(banned_users.Find(user_data["account_id"]))
|
|
physical_receptacle.say("SILO ERR: You are banned from using this ore silo.")
|
|
return COMPONENT_ORE_SILO_DENY
|
|
return COMPONENT_ORE_SILO_ALLOW
|
|
|
|
/obj/machinery/ore_silo/ui_assets(mob/user)
|
|
return list(
|
|
get_asset_datum(/datum/asset/spritesheet_batched/sheetmaterials)
|
|
)
|
|
|
|
/obj/machinery/ore_silo/ui_interact(mob/user, datum/tgui/ui)
|
|
ui = SStgui.try_update_ui(user, src, ui)
|
|
if(!ui)
|
|
ui = new(user, src, "OreSilo", "Ore Silo Control")
|
|
ui.open()
|
|
|
|
/obj/machinery/ore_silo/ui_static_data(mob/user)
|
|
return materials.ui_static_data()
|
|
|
|
/obj/machinery/ore_silo/ui_data(mob/user)
|
|
var/list/data = list()
|
|
|
|
data["materials"] = materials.ui_data()
|
|
|
|
data["machines"] = list()
|
|
for(var/datum/component/remote_materials/remote as anything in ore_connected_machines)
|
|
var/atom/parent = remote.parent
|
|
data["machines"] += list(
|
|
list(
|
|
"icon" = icon2base64(icon(initial(parent.icon), initial(parent.icon_state), frame = 1)),
|
|
"name" = parent.name,
|
|
"on_hold" = !!holds[remote],
|
|
"location" = get_area_name(parent, TRUE),
|
|
)
|
|
)
|
|
|
|
data["logs"] = list()
|
|
for(var/datum/ore_silo_log/entry as anything in GLOB.silo_access_logs[REF(src)])
|
|
data["logs"] += list(
|
|
list(
|
|
"raw_materials" = entry.get_raw_materials(""),
|
|
"machine_name" = entry.machine_name,
|
|
"area_name" = entry.area_name,
|
|
"action" = entry.action,
|
|
"amount" = entry.amount,
|
|
"time" = entry.timestamp,
|
|
"noun" = entry.noun,
|
|
"user_data" = entry.user_data,
|
|
)
|
|
)
|
|
data["banned_users"] = banned_users
|
|
data["ID_required"] = ID_required
|
|
|
|
return data
|
|
|
|
/obj/machinery/ore_silo/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
|
|
switch(action)
|
|
if("remove")
|
|
var/index = params["id"]
|
|
if(isnull(index))
|
|
return
|
|
|
|
index = text2num(index)
|
|
if(isnull(index))
|
|
return
|
|
|
|
var/datum/component/remote_materials/remote = ore_connected_machines[index]
|
|
if(isnull(remote))
|
|
return
|
|
|
|
remote.disconnect()
|
|
return TRUE
|
|
|
|
if("hold")
|
|
var/index = params["id"]
|
|
if(isnull(index))
|
|
return
|
|
|
|
index = text2num(index)
|
|
if(isnull(index))
|
|
return
|
|
|
|
var/datum/component/remote_materials/remote = ore_connected_machines[index]
|
|
if(isnull(remote))
|
|
return
|
|
|
|
remote.toggle_holding()
|
|
return TRUE
|
|
|
|
if("eject")
|
|
var/datum/material/ejecting = locate(params["ref"])
|
|
if(!istype(ejecting))
|
|
return
|
|
|
|
var/amount = params["amount"]
|
|
if(isnull(amount))
|
|
return
|
|
|
|
amount = text2num(amount)
|
|
if(isnull(amount))
|
|
return
|
|
|
|
materials.retrieve_stack(amount, ejecting, drop_location(), user_data = ID_DATA(ui.user))
|
|
return TRUE
|
|
|
|
if("toggle_ban")
|
|
var/list/banned_user_data = params["user_data"]
|
|
attempt_ban_toggle(ui.user, banned_user_data)
|
|
return TRUE
|
|
|
|
if("toggle_restrict")
|
|
attempt_toggle_restrict(ui.user)
|
|
return TRUE
|
|
/**
|
|
* Called from the ore silo's UI, when someone attempts to (un)ban a user from using the ore silo.
|
|
* The person doing the banning should have at least QM access. Unless this is emagged. Not modifiable by silicons unless emagged.
|
|
* Anyone but the Captain attempting to ban someone with QM access from the ore silo gets what is essentially a glorified version
|
|
* of the permission denied result.
|
|
* * user - The person who clicked the ban button in the UI.
|
|
* * target_user_data - Data in the form rendered from ID_DATA(target), passed into the ore silo logs by whatever the target did such
|
|
* as removing/adding sheets, printing items, etc
|
|
*/
|
|
/obj/machinery/ore_silo/proc/attempt_ban_toggle(mob/living/user, list/target_user_data)
|
|
if(!istype(user) || !istype(target_user_data))
|
|
CRASH("Bad arguments passed to [callee]")
|
|
var/emagged = obj_flags & EMAGGED
|
|
if((isAI(user) || iscyborg(user) || isdrone(user)) && !emagged)
|
|
to_chat(user, span_danger("A scroll of red text occludes your vision: ACCESS ENFORCEMENT _disabled_ for SILICON INTERFACE."))
|
|
user.flash_act(intensity = 1, affect_silicon = TRUE)
|
|
handle_access_action_feedback(
|
|
BAN_ATTEMPT_FAILURE_SOULLESS_MACHINE,
|
|
ID_DATA(user),
|
|
target_user_data
|
|
)
|
|
return
|
|
|
|
var/target_bank_id = target_user_data["account_id"]
|
|
var/target_is_banned = banned_users.Find(target_bank_id)
|
|
// Agent card bypasses bans but we want to specially handle them anyway
|
|
// so a bunch of random account IDs don't fill the list and do something
|
|
// like ban people who haven't joined the round yet
|
|
var/haxxor_card_ban_immunity = !isnull(target_user_data[CHAMELEON_OVERRIDE])
|
|
|
|
to_chat(user, span_warning("You press the button to [target_is_banned ? "un" : ""]ban [target_user_data["name"]]'s account..."))
|
|
// No feedback if emagged
|
|
if(emagged)
|
|
if(!haxxor_card_ban_immunity && isnum(target_bank_id))
|
|
target_is_banned ? banned_users.Remove(target_bank_id) : banned_users.Add(target_bank_id)
|
|
return
|
|
|
|
var/alist/silo_user_data = ID_DATA(user)
|
|
var/list/silo_user_accesses = astype(silo_user_data["accesses"], /list)
|
|
var/list/target_user_accesses = astype(target_user_data["accesses"], /list)
|
|
|
|
|
|
// Even though QM bypasses the access check (or rather always pases)
|
|
// perhaps the Captain would pre-emptively ban them right before a demotion
|
|
if(target_user_accesses?.Find(ACCESS_QM) && !silo_user_accesses?.Find(ACCESS_CAPTAIN))
|
|
handle_access_action_feedback(
|
|
BAN_ATTEMPT_FAILURE_CHALLENGING_DA_CHIEF,
|
|
silo_user_data,
|
|
target_user_data
|
|
)
|
|
return
|
|
if(!silo_user_accesses?.Find(ACCESS_QM))
|
|
handle_access_action_feedback(
|
|
BAN_ATTEMPT_FAILURE_NO_ACCESS,
|
|
silo_user_data,
|
|
target_user_data
|
|
)
|
|
return
|
|
if(haxxor_card_ban_immunity)
|
|
handle_access_action_feedback(
|
|
FAILED_OPERATION_SUSPICIOUS,
|
|
silo_user_data,
|
|
target_user_data
|
|
)
|
|
return
|
|
if(!target_bank_id || !isnum(target_bank_id))
|
|
handle_access_action_feedback(
|
|
FAILED_OPERATION_NO_BANK_ID,
|
|
silo_user_data,
|
|
target_user_data
|
|
)
|
|
return
|
|
if(target_is_banned)
|
|
banned_users.Remove(target_bank_id)
|
|
handle_access_action_feedback(
|
|
UNBAN_CONFIRMATION,
|
|
silo_user_data,
|
|
target_user_data
|
|
)
|
|
return
|
|
// If we got here, we are banning the user
|
|
banned_users.Add(target_bank_id)
|
|
handle_access_action_feedback(
|
|
BAN_CONFIRMATION,
|
|
silo_user_data,
|
|
target_user_data
|
|
)
|
|
/**
|
|
* Called from the ore silo tgui interface, for when someone attempts to restrict or unrestrict the ore silo from requiring
|
|
* an ID with an attached bank account (or, a chameleon ID, or, being a silicon)
|
|
* user - the person who tried to toggle the ore silo's access restriction. Needs to be someone with QM access, unless the
|
|
* silo is emagged. Shouldn't allow silicons to toggle this unless the silo is emagged.
|
|
*
|
|
*/
|
|
/obj/machinery/ore_silo/proc/attempt_toggle_restrict(mob/living/user)
|
|
if(!istype(user))
|
|
CRASH("No user to check toggle attempt restrictions. .ID_required is unchanged.")
|
|
var/emagged = obj_flags & EMAGGED
|
|
if(emagged)
|
|
ID_required = !ID_required
|
|
return
|
|
var/alist/silo_user_data = ID_DATA(user)
|
|
var/is_a_robot = silo_user_data[SILICON_OVERRIDE]
|
|
if(is_a_robot)
|
|
handle_access_action_feedback(
|
|
UNRESTRICT_FAILURE_SOULLESS_MACHINE,
|
|
silo_user_data,
|
|
null
|
|
)
|
|
return
|
|
var/list/user_accesses = astype(silo_user_data["accesses"], /list)
|
|
if(!user_accesses?.Find(ACCESS_QM))
|
|
handle_access_action_feedback(
|
|
UNRESTRICT_FAILURE_NO_ACCESS,
|
|
silo_user_data,
|
|
null
|
|
)
|
|
return
|
|
ID_required = !ID_required
|
|
handle_access_action_feedback(
|
|
ID_required ? RESTRICT_CONFIRMATION : UNRESTRICT_CONFIRMATION,
|
|
silo_user_data,
|
|
null)
|
|
|
|
// I must sacrifice the line diff to the gods of readable code
|
|
/// Set up the default announcement policy for actions
|
|
/// radio_channels[channel_name_key] = policy_bitmask
|
|
/// where channel_name_key is one of RADIO_CHANNEL_(COMMON|COMMAND|SECURITY|SUPPLY)
|
|
/// and policy_bitmask is a bitmask of actions that will be announced on that channel
|
|
/// by default
|
|
/obj/machinery/ore_silo/proc/configure_default_announcements_policy()
|
|
|
|
radio_channels[RADIO_CHANNEL_COMMON] = BAN_ATTEMPT_FAILURE_CHALLENGING_DA_CHIEF
|
|
radio_channels[RADIO_CHANNEL_COMMON] |= RESTRICT_CONFIRMATION
|
|
radio_channels[RADIO_CHANNEL_COMMON] |= UNRESTRICT_CONFIRMATION
|
|
|
|
// start off with the common channel bitmask policy as a base
|
|
radio_channels[RADIO_CHANNEL_COMMAND] = radio_channels[RADIO_CHANNEL_COMMON]
|
|
radio_channels[RADIO_CHANNEL_COMMAND] |= BAN_ATTEMPT_FAILURE_NO_ACCESS
|
|
radio_channels[RADIO_CHANNEL_COMMAND] |= BAN_ATTEMPT_FAILURE_SOULLESS_MACHINE
|
|
radio_channels[RADIO_CHANNEL_COMMAND] |= UNRESTRICT_FAILURE_NO_ACCESS
|
|
radio_channels[RADIO_CHANNEL_COMMAND] |= UNRESTRICT_FAILURE_SOULLESS_MACHINE
|
|
radio_channels[RADIO_CHANNEL_COMMAND] |= FAILED_OPERATION_NO_BANK_ID
|
|
radio_channels[RADIO_CHANNEL_COMMAND] |= BAN_CONFIRMATION
|
|
radio_channels[RADIO_CHANNEL_COMMAND] |= UNBAN_CONFIRMATION
|
|
|
|
// Security channel is used for security-related announcements
|
|
// but gets less information than command to avoid over-informing them without
|
|
// QM involvement
|
|
radio_channels[RADIO_CHANNEL_SECURITY] = radio_channels[RADIO_CHANNEL_COMMON]
|
|
radio_channels[RADIO_CHANNEL_SECURITY] |= BAN_ATTEMPT_FAILURE_NO_ACCESS
|
|
radio_channels[RADIO_CHANNEL_SECURITY] |= UNRESTRICT_FAILURE_NO_ACCESS
|
|
radio_channels[RADIO_CHANNEL_SECURITY] |= BAN_CONFIRMATION
|
|
radio_channels[RADIO_CHANNEL_SECURITY] |= UNBAN_CONFIRMATION
|
|
|
|
// Supply channel has the same policy by default as the command channel
|
|
// due to their usual purview of the ore silo
|
|
radio_channels[RADIO_CHANNEL_SUPPLY] = radio_channels[RADIO_CHANNEL_COMMAND]
|
|
|
|
/obj/machinery/ore_silo/proc/handle_access_action_feedback(action, alist/silo_user_data, list/target_user_data = null)
|
|
var/message = announcement_messages[action]
|
|
message = replacetext(message, "$TARGET_NAME", target_user_data?["name"])
|
|
message = replacetext(message, "$SILO_USER_NAME", silo_user_data["name"])
|
|
say(message)
|
|
for(var/channel in radio_channels)
|
|
// Key is the channel name, value is the bitmask of announced actions
|
|
if(action & radio_channels[channel])
|
|
var/say_cooldown_adherence_timer = 1 SECONDS * radio_channels.Find(channel) // * 1, * 2, * 3, etc.
|
|
addtimer(CALLBACK(radio, TYPE_PROC_REF(/obj/item, talk_into), src, message, channel), say_cooldown_adherence_timer)
|
|
|
|
#undef ALWAYS_ANNOUNCE
|
|
#undef BAN_ATTEMPT_FAILURE_NO_ACCESS
|
|
#undef BAN_ATTEMPT_FAILURE_CHALLENGING_DA_CHIEF
|
|
#undef BAN_ATTEMPT_FAILURE_SOULLESS_MACHINE
|
|
#undef BAN_CONFIRMATION
|
|
#undef UNBAN_CONFIRMATION
|
|
#undef FAILED_OPERATION_SUSPICIOUS
|
|
#undef FAILED_OPERATION_NO_BANK_ID
|
|
#undef UNRESTRICT_FAILURE_NO_ACCESS
|
|
#undef UNRESTRICT_FAILURE_SOULLESS_MACHINE
|
|
#undef UNRESTRICT_CONFIRMATION
|
|
#undef RESTRICT_CONFIRMATION
|
|
|
|
/**
|
|
* Creates a log entry for depositing/withdrawing from the silo both ingame and in text based log
|
|
*
|
|
* Arguments:
|
|
* - [M][/obj/machinery]: The machine performing the action.
|
|
* - action: Text that visually describes the action (smelted/deposited/resupplied...)
|
|
* - amount: The amount of sheets/objects deposited/withdrawn by this action. Positive for depositing, negative for withdrawing.
|
|
* - noun: Name of the object the action was performed with (sheet, units, ore...)
|
|
* - [mats][list]: Assoc list in format (material datum = amount of raw materials). Wants the actual amount of raw (iron, glass...) materials involved in this action. If you have 10 metal sheets each worth 100 iron you would pass a list with the iron material datum = 1000
|
|
* - user_data - ID_DATA(user), includes details (not currently) rendered to the player, such as bank account #, see the proc on SSid_access
|
|
*/
|
|
/obj/machinery/ore_silo/proc/silo_log(obj/machinery/M, action, amount, noun, list/mats, alist/user_data)
|
|
if (!length(mats))
|
|
return
|
|
|
|
var/datum/ore_silo_log/entry = new(M, action, amount, noun, mats, user_data)
|
|
var/list/datum/ore_silo_log/logs = GLOB.silo_access_logs[REF(src)]
|
|
if(!LAZYLEN(logs))
|
|
GLOB.silo_access_logs[REF(src)] = logs = list(entry)
|
|
else if(!logs[1].merge(entry))
|
|
logs.Insert(1, entry)
|
|
|
|
flick("silo_active", src)
|
|
|
|
///The log entry for an ore silo action
|
|
/datum/ore_silo_log
|
|
///The time of action
|
|
var/timestamp
|
|
///The name of the machine that remotely acted on the ore silo
|
|
var/machine_name
|
|
///The area of the machine that remotely acted on the ore silo
|
|
var/area_name
|
|
///The actual action performed by the machine
|
|
var/action
|
|
///An short verb describing the action
|
|
var/noun
|
|
///The amount of items affected by this action e.g. print quantity, sheets ejected etc.
|
|
var/amount
|
|
///List of individual materials used in the action
|
|
var/list/materials
|
|
///User data of the player doing material operations
|
|
var/alist/user_data
|
|
|
|
/datum/ore_silo_log/New(obj/machinery/M, _action, _amount, _noun, list/mats=list(), alist/user_data)
|
|
timestamp = station_time_timestamp()
|
|
machine_name = M.name
|
|
area_name = get_area_name(M, TRUE)
|
|
action = _action
|
|
amount = _amount
|
|
noun = _noun
|
|
materials = mats.Copy()
|
|
src.user_data = user_data
|
|
var/list/data = list(
|
|
"machine_name" = machine_name,
|
|
"area_name" = AREACOORD(M),
|
|
"action" = action,
|
|
"amount" = abs(amount),
|
|
"noun" = noun,
|
|
"raw_materials" = get_raw_materials(""),
|
|
"direction" = amount < 0 ? "withdrawn" : "deposited",
|
|
"user_data" = user_data,
|
|
)
|
|
logger.Log(
|
|
LOG_CATEGORY_SILO,
|
|
"[machine_name] in \[[AREACOORD(M)]\] [action] [abs(amount)]x [noun] | [get_raw_materials("")] | [user_data["name"]]",
|
|
data,
|
|
)
|
|
|
|
/**
|
|
* Merges a silo log entry with this one
|
|
* Arguments
|
|
*
|
|
* * datum/ore_silo_log/other - the other silo entry we are trying to merge with this one
|
|
*/
|
|
/datum/ore_silo_log/proc/merge(datum/ore_silo_log/other)
|
|
if (other == src || action != other.action || noun != other.noun)
|
|
return FALSE
|
|
if (machine_name != other.machine_name || area_name != other.area_name)
|
|
return FALSE
|
|
|
|
timestamp = other.timestamp
|
|
amount += other.amount
|
|
for(var/each in other.materials)
|
|
materials[each] += other.materials[each]
|
|
return TRUE
|
|
|
|
/**
|
|
* Returns list/materials but with each entry joined by an seperator to create 1 string
|
|
* Arguments
|
|
*
|
|
* * separator - the string used to concatenate all entries in list/materials
|
|
*/
|
|
/datum/ore_silo_log/proc/get_raw_materials(separator)
|
|
var/list/msg = list()
|
|
for(var/key in materials)
|
|
var/datum/material/M = key
|
|
var/val = round(materials[key]) / SHEET_MATERIAL_AMOUNT
|
|
msg += separator
|
|
separator = ", "
|
|
msg += "[amount < 0 ? "-" : "+"][val] [M.name]"
|
|
return msg.Join()
|