#define BARCODE_MODE_SCAN_SELECT 1
#define BARCODE_MODE_SCAN_INVENTORY 2
#define BARCODE_MODE_CHECKOUT 3
#define BARCODE_MODE_CHECKIN 4
/*
* Bookcase
*/
/obj/structure/bookcase
name = "bookcase"
icon = 'icons/obj/library.dmi'
icon_state = "bookshelf-0"
anchored = TRUE
density = TRUE
opacity = TRUE
resistance_flags = FLAMMABLE
max_integrity = 200
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 50, ACID = 0)
var/list/allowed_books = list(/obj/item/book, /obj/item/spellbook, /obj/item/storage/bible, /obj/item/tome) //Things allowed in the bookcase
/obj/structure/bookcase/Initialize(mapload)
. = ..()
if(mapload)
// same reasoning as closets
addtimer(CALLBACK(src, PROC_REF(take_contents)), 0)
/obj/structure/bookcase/proc/take_contents()
for(var/obj/item/I in get_turf(src))
if(is_type_in_list(I, allowed_books))
I.forceMove(src)
update_icon(UPDATE_ICON_STATE)
/obj/structure/bookcase/attackby(obj/item/O, mob/user)
if(is_type_in_list(O, allowed_books))
if(!user.drop_item())
return
O.forceMove(src)
update_icon(UPDATE_ICON_STATE)
return TRUE
if(istype(O, /obj/item/storage/bag/books))
var/obj/item/storage/bag/books/B = O
for(var/obj/item/T in B.contents)
if(is_type_in_list(T, allowed_books))
B.remove_from_storage(T, src)
to_chat(user, "You empty [O] into [src].")
update_icon(UPDATE_ICON_STATE)
return TRUE
if(is_pen(O))
rename_interactive(user, O)
return TRUE
return ..()
/obj/structure/bookcase/attack_hand(mob/user)
if(!length(contents))
return
var/obj/item/book/choice = tgui_input_list(user, "Which book would you like to remove from [src]?", "Bookcase", contents)
if(!choice)
return
if(user.incapacitated() || !Adjacent(user))
return
if(!user.get_active_hand())
user.put_in_hands(choice)
else
choice.forceMove(get_turf(src))
update_icon(UPDATE_ICON_STATE)
/obj/structure/bookcase/deconstruct(disassembled = TRUE)
new /obj/item/stack/sheet/wood(loc, 5)
for(var/obj/item/I in contents)
if(is_type_in_list(I, allowed_books))
I.forceMove(get_turf(src))
..()
/obj/structure/bookcase/update_icon_state()
icon_state = "bookshelf-[min(length(contents), 5)]"
/obj/structure/bookcase/screwdriver_act(mob/user, obj/item/I)
if(flags & NODECONSTRUCT)
return
. = TRUE
if(!I.tool_use_check(user, 0))
return
TOOL_ATTEMPT_DISMANTLE_MESSAGE
if(!I.use_tool(src, user, 20, volume = I.tool_volume))
return
TOOL_DISMANTLE_SUCCESS_MESSAGE
deconstruct(TRUE)
/obj/structure/bookcase/wrench_act(mob/user, obj/item/I)
. = TRUE
default_unfasten_wrench(user, I, 0)
/obj/structure/bookcase/manuals/medical
name = "Medical Manuals bookcase"
/obj/structure/bookcase/manuals/medical/Initialize()
. = ..()
new /obj/item/book/manual/medical_cloning(src)
update_icon(UPDATE_ICON_STATE)
/obj/structure/bookcase/manuals/engineering
name = "Engineering Manuals bookcase"
/obj/structure/bookcase/manuals/engineering/Initialize()
. = ..()
new /obj/item/book/manual/wiki/engineering_construction(src)
new /obj/item/book/manual/engineering_particle_accelerator(src)
new /obj/item/book/manual/wiki/hacking(src)
new /obj/item/book/manual/wiki/engineering_guide(src)
new /obj/item/book/manual/engineering_singularity_safety(src)
new /obj/item/book/manual/wiki/robotics_cyborgs(src)
update_icon(UPDATE_ICON_STATE)
/obj/structure/bookcase/manuals/research_and_development
name = "R&D Manuals bookcase"
/obj/structure/bookcase/manuals/research_and_development/Initialize()
. = ..()
new /obj/item/book/manual/research_and_development(src)
update_icon(UPDATE_ICON_STATE)
/obj/structure/bookcase/sop
name = "bookcase (Standard Operating Procedures)"
/obj/structure/bookcase/sop/Initialize()
. = ..()
new /obj/item/book/manual/wiki/sop_command(src)
new /obj/item/book/manual/wiki/sop_engineering(src)
new /obj/item/book/manual/wiki/sop_general(src)
new /obj/item/book/manual/wiki/sop_legal(src)
new /obj/item/book/manual/wiki/sop_medical(src)
new /obj/item/book/manual/wiki/sop_science(src)
new /obj/item/book/manual/wiki/sop_security(src)
new /obj/item/book/manual/wiki/sop_service(src)
new /obj/item/book/manual/wiki/sop_supply(src)
update_icon(UPDATE_ICON_STATE)
/obj/structure/bookcase/random
var/category = null
var/book_count = 5
icon_state = "random_bookcase"
anchored = TRUE
/obj/structure/bookcase/random/Initialize(mapload)
. = ..()
var/list/books = GLOB.library_catalog.get_random_book(book_count, doAsync = FALSE)
for(var/datum/cachedbook/book as anything in books)
new /obj/item/book(src, book, TRUE, FALSE)
update_icon(UPDATE_ICON_STATE)
/*
* Book binder
*/
/obj/machinery/bookbinder
name = "Book Binder"
icon = 'icons/obj/library.dmi'
icon_state = "binder"
anchored = TRUE
density = TRUE
var/datum/cachedbook/selected_content = new()
var/printing = FALSE
/obj/machinery/bookbinder/attack_ai(mob/user)
return attack_hand(user)
/obj/machinery/bookbinder/attack_hand(mob/user)
if(..())
return
ui_interact(user)
/obj/machinery/bookbinder/attack_ghost(mob/user)
ui_interact(user)
/obj/machinery/bookbinder/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/paper))
select_paper(I)
if(istype(I, /obj/item/paper_bundle))
select_paper_stack(I)
if(istype(I, /obj/item/book))
select_book(I)
if(default_unfasten_wrench(user, I, time = 60))
return
return ..()
/obj/machinery/bookbinder/proc/select_paper(obj/item/paper/P)
selected_content.title = P.name
selected_content.author = null
selected_content.content = list(P.info)
/obj/machinery/bookbinder/proc/select_paper_stack(obj/item/paper_bundle/P)
selected_content.title = P.name
selected_content.author = null
selected_content.content = list()
for(var/obj/item/paper/I in P.contents)
selected_content.content += I.info
/obj/machinery/bookbinder/proc/select_book(obj/item/book/B)
if(!B || B.protected || B.carved)
return
selected_content.title = B.title
selected_content.author = B.author
selected_content.author = B.summary
selected_content.content = B.pages
for(var/c in B.categories)
if(c)
selected_content.categories += c
/obj/machinery/bookbinder/ui_state(mob/user)
return GLOB.default_state
/obj/machinery/bookbinder/ui_interact(mob/user, datum/tgui/ui = null)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "BookBinder", name)
ui.open()
/obj/machinery/bookbinder/ui_data(mob/user)
var/list/data = list()
var/list/selected_book_data = list(
"title" = selected_content.title ? selected_content.title : "not specified",
"author" = selected_content.author ? selected_content.author : "not specified",
"summary" = selected_content.summary ? selected_content.summary : "no summary",
"copyright" = selected_content.copyright ? selected_content.copyright : FALSE,
"categories" = selected_content.categories ? selected_content.categories : list()
)
data["selectedbook"] = selected_book_data
data["modal"] = ui_modal_data(src)
return data
/obj/machinery/bookbinder/ui_static_data(mob/user)
var/list/static_data = list()
static_data["book_categories"] = list()
for(var/datum/library_category/category in GLOB.library_catalog.categories)
var/category_info = list(
"category_id" = category.category_id,
"description" = category.description,
)
static_data["book_categories"] += list(category_info)
return static_data
/obj/machinery/bookbinder/ui_act(action, list/params, datum/tgui/ui)
if(..())
return
if(ui_act_modal(action, params))
return
add_fingerprint(ui.user)
switch(action)
if("print_book")
if(!printing)
printing = TRUE
visible_message("[src] begins to hum as it warms up its printing drums.")
addtimer(CALLBACK(src, PROC_REF(print_book)), 5 SECONDS)
else
playsound(src, 'sound/machines/synth_no.ogg', 15, TRUE)
if("toggle_binder_category")
var/category_id = text2num(params["category_id"])
if(category_id in selected_content.categories)
selected_content.categories -= category_id
else
if(length(selected_content.categories) >= 3)
playsound(src, 'sound/machines/synth_no.ogg', 15, TRUE)
return
selected_content.categories += category_id
/obj/machinery/bookbinder/proc/ui_act_modal(action, list/params)
. = TRUE
var/id = params["id"] // The modal's ID
var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"]
switch(ui_modal_act(src, action, params))
if(UI_MODAL_OPEN)
switch(id)
if("edit_selected_title")
ui_modal_input(src, id, "Please input the new title:", null, arguments, selected_content.title)
if("edit_selected_author")
ui_modal_input(src, id, "Please input the new author:", null, arguments, selected_content.author)
if("edit_selected_summary")
ui_modal_input(src, id, "Please input the new summary:", null, arguments, selected_content.summary)
else
return FALSE
if(UI_MODAL_ANSWER)
var/answer = params["answer"]
switch(id)
if("edit_selected_title")
if(length(answer) >= MAX_NAME_LEN)
return
selected_content.title = strip_html(answer)
if("edit_selected_author")
if(length(answer) >= MAX_NAME_LEN)
return
selected_content.author = strip_html(answer)
if("edit_selected_summary")
if(length(answer) >= MAX_SUMMARY_LEN)
return
selected_content.summary = strip_html(answer)
else
return FALSE
else
return FALSE
/obj/machinery/bookbinder/proc/print_book()
visible_message("[src] whirs as it prints and binds a new book.")
new /obj/item/book(loc, selected_content, FALSE, FALSE)
printing = FALSE
/*
* Barcode Scanner
*/
/obj/item/barcodescanner
name = "barcode scanner"
desc = "A scanner used for managing library books, one can connect it the library system by tapping on a computer with it in hand."
icon = 'icons/obj/library.dmi'
icon_state ="scanner"
throw_speed = 1
throw_range = 5
w_class = WEIGHT_CLASS_TINY
var/list/modes = list(BARCODE_MODE_SCAN_SELECT, BARCODE_MODE_SCAN_INVENTORY, BARCODE_MODE_CHECKOUT, BARCODE_MODE_CHECKIN)
/// Associated Library Computer, needed to perform actions
var/obj/machinery/computer/library/computer
var/mode = BARCODE_MODE_SCAN_SELECT
/obj/item/barcodescanner/attack_self(mob/user)
if(!check_connection(user))
return
mode++
if(mode > length(modes))
mode = modes[1]
var/modedesc
switch(mode)
if(BARCODE_MODE_SCAN_SELECT)
modedesc = "Scan book to computer."
if(BARCODE_MODE_SCAN_INVENTORY)
modedesc = "Scan book into to general inventory."
if(BARCODE_MODE_CHECKOUT)
modedesc = "Checkout Book"
if(BARCODE_MODE_CHECKIN)
modedesc = "Checkin Book"
else
modedesc = "ERROR"
playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE)
to_chat(user, "[src] mode: [modedesc]")
/obj/item/barcodescanner/proc/connect(obj/machinery/computer/library/library_computer)
if(!istype(library_computer))
return FALSE
if(computer == library_computer)
return TRUE //we're succesfully connected already, let player know it was a "succesful connection"
disconnect() //clear references to old computer, we have to unregister signals
computer = library_computer
RegisterSignal(library_computer, COMSIG_PARENT_QDELETING, PROC_REF(disconnect))
return TRUE
/obj/item/barcodescanner/proc/disconnect()
if(!computer)
return //proc will runtime if computer is null
UnregisterSignal(computer, COMSIG_PARENT_QDELETING)
computer = null
/obj/item/barcodescanner/proc/scanID(obj/item/card/id/ID, mob/user)
if(!check_connection(user))
return
if(!ID.registered_name)
computer.user_data.patron_name = null
computer.user_data.patron_account = null //account number should reset every scan so we don't accidently have an account number but no name
playsound(src, 'sound/machines/synth_no.ogg', 15, TRUE)
to_chat(user, "[src]'s screen flashes: 'ERROR! No name associated with this ID Card'")
return //no point in continuing if the ID card has no associated name!
computer.user_data.patron_name = ID.registered_name
playsound(src, 'sound/items/scannerbeep.ogg', 15, TRUE)
if(!ID.associated_account_number)
computer.user_data.patron_account = null
to_chat(user, "[src]'s screen flashes: 'WARNING! Patron without associated account number Selected'")
return
computer.user_data.patron_account = ID.associated_account_number
to_chat(user, "[src]'s screen flashes: 'Patron Selected'")
/obj/item/barcodescanner/proc/scanBook(obj/item/book/B, mob/user as mob)
if(!check_connection(user))
return
switch(mode)
if(BARCODE_MODE_SCAN_SELECT)
computer.select_book(B)
playsound(src, 'sound/machines/terminal_select.ogg', 15, TRUE)
to_chat(user, "[src]'s screen flashes: 'Book selected in library computer.'")
if(BARCODE_MODE_SCAN_INVENTORY)
if(computer.inventoryAdd(B))
playsound(src, 'sound/items/scannerbeep.ogg', 15, TRUE)
to_chat(user, "[src]'s screen flashes: 'Title added to general inventory.'")
else
playsound(src, 'sound/machines/synth_no.ogg', 15, TRUE)
to_chat(user, "[src]'s screen flashes: 'Title already in general inventory.'")
if(BARCODE_MODE_CHECKOUT)
var/confirm
if(!computer.user_data.patron_account)
confirm = alert("Warning: patron does not have an associated account number! Are you sure you want to checkout [B] to [computer.user_data.patron_name]?", "Confirm Checkout", "Yes", "No")
else
confirm = alert("Are you sure you want to checkout [B] to [computer.user_data.patron_name]?", "Confirm Checkout", "Yes", "No")
if(confirm == "No")
return
if(computer.checkout(B))
playsound(src, 'sound/items/scannerbeep.ogg', 15, TRUE)
to_chat(user, "[src]'s screen flashes: 'Title checked out to [computer.user_data.patron_name].'")
else
playsound(src, 'sound/machines/synth_no.ogg', 15, TRUE)
to_chat(user, "[src]'s screen flashes: 'ERROR! Book Checkout Unsuccessful.'")
if(BARCODE_MODE_CHECKIN)
if(computer.checkin(B))
playsound(src, 'sound/items/scannerbeep.ogg', 15, TRUE)
to_chat(user, "[src]'s screen flashes: 'Title checked back into general inventory.'")
else
playsound(src, 'sound/machines/synth_no.ogg', 15, TRUE)
to_chat(user, "[src]'s screen flashes: 'ERROR! Book Checkout Unsuccessful.'")
/obj/item/barcodescanner/proc/check_connection(mob/user as mob) //fuck you null references!
if(computer)
return TRUE
else
playsound(src, 'sound/machines/synth_no.ogg', 15, TRUE)
to_chat(user, "Please reconnect [src] to a library computer.")
return FALSE