mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-16 12:07:48 +00:00
* Prevents potentially infinite length books from being written and stored. I'm not sure if this is an actual issue, but I have a funny feeling it may become one someday * Moves the paper defines to their own file * It's become clear to me that I am stupid * git add --all * Makes book info into a datum to allow for easy passing around * Converts the library scanner to tgui, lays the groundwork for tgui visitor consoles * Makes the db request for book info sort Adds the frontend for the visitor's console Adds a hash to prevent duplicate db requests Adds a prams changed var to help facilitate a better search button Makes the page number code accept text as input * Makes the ui index at 1 even tho we index at 0 internally * Begins the conversion process for the library console. Changes the library console to override the visitor, to utalize for the archive access portion of the ui Makes scanner into a weakref, I'm coming for you handheld scanner Renames some vars to make things clearer * Converts the remaining refs of the old console typepath over, adds a circuit board for consoles because pain * Changes how bookshelves load in books Instead of loading them in lazyally, we load them during init This lets us track what books are stored in which areas Somewhat jutting off of this, adds map config for designating something as "part of the library" This will be useful later * Renames the random poster, adds a spritesheet for bibles. Both will be useful in a moment * Ok. This is a bit of a mess. Converts the library console to tgui. This comes with a few minor behavior changes: You can now select what type of poster you want to print, instead of just printing a random one It's now possible to heed the console's emag warning The console's inventory page will fill at roundstart with the books in your area/if you're in a library, any areas designated as "library like" in the map config You can see what type of bible the chaplin has selected? "Fixes": You can no longer just dump books into the scanner forever Implementation details: Any input that makes a db request will now A: freeze up any other db inputs until it's finished, and B: Start a 1 second timer before any new db requests can be made Of note, I'm handling html encoding in a very targeted way. All book_data datums need to have html encoded values. get_title/author/content exist so a defaulting and tgui appropriate version can be loaded in. This somewhat matches with the trusted var on set_title, it exists to prevent double html encoding. While we're here Input/DB (Book data should be html encoded) Inside book datum (Book data should be html encoded) Sending to tgui (Book data should be decoded during extraction with the get_() procs) Sending anywhere else (Book data should be html encoded, otherwise it's an xss vuln) Uhhhh tgui stuff? I'm using a custom theme for emag visuals, I'll get into that more later The visitor and book management console share the same data/act pipeline, which is why they're parented/subtyped They also share a page selection component, which is why the visitor's console imports it. Uhhhhh Oh right, fuck. Ok so the page selection component is kinda cursed, the left and right controls are fine But I'm trying to get a << < [page/max] > >> setup going, and that means resetting the center input past change so the default value can be used This ends up being slightly hacky. I'm sorry. Oh also, I implemented a custom tab setup for this ui. I have no idea why it was literally like 5 months ago. I think it looks pretty nice, but if you want me to nuke it I can. Sorry for any headache around this. More tgui stuff next * Scanner/visitor cleanup, some other odds and ends * Adds in a dark red and black theme for library computers to be triggered by an emag. Things of note: I'm overriding some lists that get passed into buttons and one other thing using set, since the list is alreadt generated by that step in the process? I think? I've added dimness control to the dimmer component, since well, it was dimming already dark uis. I also made and added a rather large background svg. I've got no experience with this sort of thing, and all the compression methods I found for this ended up being busts. I know this isn't acceptable as an end product, but I don't know how to get it there. Somewhat on that note, this ui might not be worth the size for the amount of use it gets. I'm fine with nuking it if that's the case, I bring this up because I have a very poor understanding of the logistics of something like this, so I have a feeling I've fucked up somewhere * Forgot these, just a scss file for library computers, barely used but I think it's worthwhile * Missed this eariler. As a part of the uploading tab, I'm displaying the contents of books. I'm loading in that context as raw html so paper -> book books look close to right. Means I need more html tags then our current sanitize provides. I don't think any of these will cause issues, and there's also a good chance I'm missing some. Will come up with a list later * Updates the rest of the maps to use the new management typepath * Fixes the default bible name being Default Bible Name, I am sorry * Turns out I had the scaling wrong for bible names, lead to weird stacking because the bible icon doesn't scale, so I lowered its sizing * Yeets unneeded exports (Thank you jlsnow) Haha wouldn't it be funny if I didn't know how components worked Co-authored-by: Jeremiah <42397676+jlsnow301@users.noreply.github.com> * Resets the maps to master * Fixes oversights from merge commit, changes maps * Removes needless Flex's from the scanner * Gives the library console the ability to parse markdown. Expands the list of acceptable html elements a bit * Adds audio cues for printing and inserting/removing from the scanner, makes the scanner nicer to use in general * Uses a compressed version of the background. It's still huge, but smaller at least * Adds the printing audio to the book binder * Cleans up tram * curse you tram * AHHHHHHHH * MY LIFE IS TRUE PAIN * Adds a path conversion statement to make people's lives easier * Apply's style's suggestions thx style Co-authored-by: Aleksej Komarov <stylemistake@gmail.com> * Compresses the background svg * Further js cleanup * We no longer render markdown in the ui, since any source of markdown is converted to html anyway * More ui changes Makes the tab/main screen logic use Flex rather then manuel offsets Makes modals better fit the size of their contents Readjusts the width of some inputs Properly uses the header prop for a table Makes the buttons in the upload panel look nicer Restructures the print tab a bit * Increase a modal's size * Fixes computers with no keyboard overlay showing their screen even when the power is out * Moves some data and logic onto the library subsystem. Kyler's review Fixes harddels held by the library scanner. Makes the scanner's buffer actually do something * Makes book icon randomization a proc rather then just copypasta'd code * Removes the kilo library edit, the soul was removed * Damn you san (Fixes mapconflicts) * Pain Co-authored-by: Jeremiah <42397676+jlsnow301@users.noreply.github.com> Co-authored-by: Aleksej Komarov <stylemistake@gmail.com>
485 lines
16 KiB
Plaintext
485 lines
16 KiB
Plaintext
#define BOOKCASE_UNANCHORED 0
|
|
#define BOOKCASE_ANCHORED 1
|
|
#define BOOKCASE_FINISHED 2
|
|
|
|
/* Library Items
|
|
*
|
|
* Contains:
|
|
* Bookcase
|
|
* Book
|
|
* Barcode Scanner
|
|
*/
|
|
|
|
/*
|
|
* Bookcase
|
|
*/
|
|
|
|
/obj/structure/bookcase
|
|
name = "bookcase"
|
|
icon = 'icons/obj/library.dmi'
|
|
icon_state = "bookempty"
|
|
desc = "A great place for storing knowledge."
|
|
anchored = FALSE
|
|
density = TRUE
|
|
opacity = FALSE
|
|
resistance_flags = FLAMMABLE
|
|
max_integrity = 200
|
|
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, FIRE = 50, ACID = 0)
|
|
var/state = BOOKCASE_UNANCHORED
|
|
/// When enabled, books_to_load number of random books will be generated for this bookcase
|
|
var/load_random_books = FALSE
|
|
/// The category of books to pick from when populating random books.
|
|
var/random_category = null
|
|
/// How many random books to generate.
|
|
var/books_to_load = 0
|
|
|
|
/obj/structure/bookcase/Initialize(mapload)
|
|
. = ..()
|
|
if(!mapload || QDELETED(src))
|
|
return
|
|
set_anchored(TRUE)
|
|
state = BOOKCASE_FINISHED
|
|
for(var/obj/item/I in loc)
|
|
if(!isbook(I))
|
|
continue
|
|
I.forceMove(src)
|
|
update_appearance()
|
|
SSlibrary.shelves_to_load += src
|
|
|
|
///Loads the shelf, both by allowing it to generate random items, and by adding its contents to a list used by library machines
|
|
/obj/structure/bookcase/proc/load_shelf()
|
|
//Loads a random selection of books in from the db, adds a copy of their info to a global list
|
|
//To send to library consoles as a starting inventory
|
|
if(load_random_books)
|
|
create_random_books(books_to_load, src, FALSE, random_category)
|
|
update_appearance() //Make sure you look proper
|
|
|
|
var/area/our_area = get_area(src)
|
|
var/area_type = our_area.type //Save me from the dark
|
|
|
|
if(!SSlibrary.books_by_area[area_type])
|
|
SSlibrary.books_by_area[area_type] = list()
|
|
|
|
//Time to populate that list
|
|
var/list/books_in_area = SSlibrary.books_by_area[area_type]
|
|
for(var/obj/item/book/book in contents)
|
|
var/datum/book_info/info = book.book_data
|
|
books_in_area += info.return_copy()
|
|
|
|
/obj/structure/bookcase/examine(mob/user)
|
|
. = ..()
|
|
if(!anchored)
|
|
. += span_notice("The <i>bolts</i> on the bottom are unsecured.")
|
|
else
|
|
. += span_notice("It's secured in place with <b>bolts</b>.")
|
|
switch(state)
|
|
if(BOOKCASE_UNANCHORED)
|
|
. += span_notice("There's a <b>small crack</b> visible on the back panel.")
|
|
if(BOOKCASE_ANCHORED)
|
|
. += span_notice("There's space inside for a <i>wooden</i> shelf.")
|
|
if(BOOKCASE_FINISHED)
|
|
. += span_notice("There's a <b>small crack</b> visible on the shelf.")
|
|
|
|
/obj/structure/bookcase/set_anchored(anchorvalue)
|
|
. = ..()
|
|
if(isnull(.))
|
|
return
|
|
state = anchorvalue
|
|
if(!anchorvalue) //in case we were vareditted or uprooted by a hostile mob, ensure we drop all our books instead of having them disappear till we're rebuild.
|
|
var/atom/Tsec = drop_location()
|
|
for(var/obj/I in contents)
|
|
if(!isbook(I))
|
|
continue
|
|
I.forceMove(Tsec)
|
|
update_appearance()
|
|
|
|
/obj/structure/bookcase/attackby(obj/item/I, mob/user, params)
|
|
switch(state)
|
|
if(BOOKCASE_UNANCHORED)
|
|
if(I.tool_behaviour == TOOL_WRENCH)
|
|
if(I.use_tool(src, user, 20, volume=50))
|
|
to_chat(user, span_notice("You wrench the frame into place."))
|
|
set_anchored(TRUE)
|
|
else if(I.tool_behaviour == TOOL_CROWBAR)
|
|
if(I.use_tool(src, user, 20, volume=50))
|
|
to_chat(user, span_notice("You pry the frame apart."))
|
|
deconstruct(TRUE)
|
|
|
|
if(BOOKCASE_ANCHORED)
|
|
if(istype(I, /obj/item/stack/sheet/mineral/wood))
|
|
var/obj/item/stack/sheet/mineral/wood/W = I
|
|
if(W.get_amount() >= 2)
|
|
W.use(2)
|
|
to_chat(user, span_notice("You add a shelf."))
|
|
state = BOOKCASE_FINISHED
|
|
update_appearance()
|
|
else if(I.tool_behaviour == TOOL_WRENCH)
|
|
I.play_tool_sound(src, 100)
|
|
to_chat(user, span_notice("You unwrench the frame."))
|
|
set_anchored(FALSE)
|
|
|
|
if(BOOKCASE_FINISHED)
|
|
var/datum/component/storage/STR = I.GetComponent(/datum/component/storage)
|
|
if(isbook(I))
|
|
if(!user.transferItemToLoc(I, src))
|
|
return
|
|
update_appearance()
|
|
else if(STR)
|
|
for(var/obj/item/T in I.contents)
|
|
if(istype(T, /obj/item/book) || istype(T, /obj/item/spellbook))
|
|
STR.remove_from_storage(T, src)
|
|
to_chat(user, span_notice("You empty \the [I] into \the [src]."))
|
|
update_appearance()
|
|
else if(istype(I, /obj/item/pen))
|
|
if(!user.is_literate())
|
|
to_chat(user, span_notice("You scribble illegibly on the side of [src]!"))
|
|
return
|
|
var/newname = tgui_input_text(user, "What would you like to title this bookshelf?", "Bookshelf Renaming", max_length = MAX_NAME_LEN)
|
|
if(!user.canUseTopic(src, BE_CLOSE))
|
|
return
|
|
if(!newname)
|
|
return
|
|
else
|
|
name = "bookcase ([sanitize(newname)])"
|
|
else if(I.tool_behaviour == TOOL_CROWBAR)
|
|
if(length(contents))
|
|
to_chat(user, span_warning("You need to remove the books first!"))
|
|
else
|
|
I.play_tool_sound(src, 100)
|
|
to_chat(user, span_notice("You pry the shelf out."))
|
|
new /obj/item/stack/sheet/mineral/wood(drop_location(), 2)
|
|
state = BOOKCASE_ANCHORED
|
|
update_appearance()
|
|
else
|
|
return ..()
|
|
|
|
/obj/structure/bookcase/attack_hand(mob/living/user, list/modifiers)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
if(!istype(user))
|
|
return
|
|
if(!length(contents))
|
|
return
|
|
var/obj/item/book/choice = tgui_input_list(user, "Book to remove from the shelf", "Remove Book", sort_names(contents.Copy()))
|
|
if(isnull(choice))
|
|
return
|
|
if(!(user.mobility_flags & MOBILITY_USE) || user.stat != CONSCIOUS || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED) || !in_range(loc, user))
|
|
return
|
|
if(ishuman(user))
|
|
if(!user.get_active_held_item())
|
|
user.put_in_hands(choice)
|
|
else
|
|
choice.forceMove(drop_location())
|
|
update_appearance()
|
|
|
|
/obj/structure/bookcase/deconstruct(disassembled = TRUE)
|
|
var/atom/Tsec = drop_location()
|
|
new /obj/item/stack/sheet/mineral/wood(Tsec, 4)
|
|
for(var/obj/item/I in contents)
|
|
if(!isbook(I)) //Wake me up inside
|
|
continue
|
|
I.forceMove(Tsec)
|
|
return ..()
|
|
|
|
/obj/structure/bookcase/update_icon_state()
|
|
if(state == BOOKCASE_UNANCHORED || state == BOOKCASE_ANCHORED)
|
|
icon_state = "bookempty"
|
|
return ..()
|
|
var/amount = length(contents)
|
|
icon_state = "book-[clamp(amount, 0, 5)]"
|
|
return ..()
|
|
|
|
/obj/structure/bookcase/manuals/engineering
|
|
name = "engineering manuals bookcase"
|
|
|
|
/obj/structure/bookcase/manuals/engineering/Initialize(mapload)
|
|
. = ..()
|
|
new /obj/item/book/manual/wiki/engineering_construction(src)
|
|
new /obj/item/book/manual/wiki/engineering_hacking(src)
|
|
new /obj/item/book/manual/wiki/engineering_guide(src)
|
|
new /obj/item/book/manual/wiki/robotics_cyborgs(src)
|
|
update_appearance()
|
|
|
|
|
|
/obj/structure/bookcase/manuals/research_and_development
|
|
name = "\improper R&D manuals bookcase"
|
|
|
|
/obj/structure/bookcase/manuals/research_and_development/Initialize(mapload)
|
|
. = ..()
|
|
new /obj/item/book/manual/wiki/research_and_development(src)
|
|
update_appearance()
|
|
|
|
|
|
/*
|
|
* Book
|
|
*/
|
|
//Some information about how html sanitization is handled
|
|
//All book info datums should store sanitized data. This cannot be worked around
|
|
//All inputs and outputs from the round (DB calls) need to use sanitized data
|
|
//All tgui menus should get unsanitized data, since jsx handles that on its own
|
|
//Everything else should use sanitized data. Yes including names, it's an xss vuln because of how chat works
|
|
///A datum which contains all the metadata of a book
|
|
/datum/book_info
|
|
///The title of the book
|
|
var/title
|
|
///The "author" of the book
|
|
var/author
|
|
///The info inside the book
|
|
var/content
|
|
|
|
/datum/book_info/New(_title, _author, _content)
|
|
title = _title
|
|
author = _author
|
|
content = _content
|
|
|
|
/datum/book_info/proc/set_title(_title, trusted = FALSE) //Trusted should only be used for books read from the db, or in cases that we can be sure the info has already been sanitized
|
|
if(trusted)
|
|
title = _title
|
|
return
|
|
title = reject_bad_text(trim(html_encode(_title), 30))
|
|
|
|
/datum/book_info/proc/get_title(default="N/A") //Loads in an html decoded version of the title. Only use this for tgui menus, absolutely nothing else.
|
|
return html_decode(title) || "N/A"
|
|
|
|
/datum/book_info/proc/set_author(_author, trusted = FALSE)
|
|
if(trusted)
|
|
author = _author
|
|
return
|
|
author = trim(html_encode(_author), MAX_NAME_LEN)
|
|
|
|
/datum/book_info/proc/get_author(default="N/A")
|
|
return html_decode(author) || "N/A"
|
|
|
|
/datum/book_info/proc/set_content(_content, trusted = FALSE)
|
|
if(trusted)
|
|
content = _content
|
|
return
|
|
content = trim(html_encode(_content), MAX_PAPER_LENGTH)
|
|
|
|
/datum/book_info/proc/get_content(default="N/A")
|
|
return html_decode(content) || "N/A"
|
|
|
|
///Returns a copy of the book_info datum
|
|
/datum/book_info/proc/return_copy()
|
|
var/datum/book_info/copycat = new(title, author, content)
|
|
return copycat
|
|
|
|
///Modify an existing book_info datum to match your data
|
|
/datum/book_info/proc/copy_into(datum/book_info/copycat)
|
|
copycat.set_title(title, trusted = TRUE)
|
|
copycat.set_author(author, trusted = TRUE)
|
|
copycat.set_content(content, trusted = TRUE)
|
|
return copycat
|
|
|
|
/datum/book_info/proc/compare(datum/book_info/cmp_with)
|
|
if(author != cmp_with.author)
|
|
return FALSE
|
|
if(title != cmp_with.title)
|
|
return FALSE
|
|
if(content != cmp_with.content)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/obj/item/book
|
|
name = "book"
|
|
icon = 'icons/obj/library.dmi'
|
|
icon_state ="book"
|
|
worn_icon_state = "book"
|
|
desc = "Crack it open, inhale the musk of its pages, and learn something new."
|
|
throw_speed = 1
|
|
throw_range = 5
|
|
w_class = WEIGHT_CLASS_NORMAL //upped to three because books are, y'know, pretty big. (and you could hide them inside eachother recursively forever)
|
|
attack_verb_continuous = list("bashes", "whacks", "educates")
|
|
attack_verb_simple = list("bash", "whack", "educate")
|
|
resistance_flags = FLAMMABLE
|
|
drop_sound = 'sound/items/handling/book_drop.ogg'
|
|
pickup_sound = 'sound/items/handling/book_pickup.ogg'
|
|
///Game time in 1/10th seconds
|
|
var/due_date = 0
|
|
///false - Normal book, true - Should not be treated as normal book, unable to be copied, unable to be modified
|
|
var/unique = FALSE
|
|
/// Specific window size for the book, i.e: "1920x1080", Size x Width
|
|
var/window_size = null
|
|
///The initial title, for use in var editing and such
|
|
var/starting_title
|
|
///The initial author, for use in var editing and such
|
|
var/starting_author
|
|
///The initial bit of content, for use in var editing and such
|
|
var/starting_content
|
|
///The packet of information that describes this book
|
|
var/datum/book_info/book_data
|
|
///Maximum icon state number
|
|
var/maximum_book_state = 8
|
|
|
|
/obj/item/book/Initialize()
|
|
. = ..()
|
|
book_data = new(starting_title, starting_author, starting_content)
|
|
|
|
/obj/item/book/proc/on_read(mob/user)
|
|
if(book_data?.content)
|
|
user << browse("<meta charset=UTF-8><TT><I>Penned by [book_data.author].</I></TT> <BR>" + "[book_data.content]", "window=book[window_size != null ? ";size=[window_size]" : ""]")
|
|
onclose(user, "book")
|
|
else
|
|
to_chat(user, span_notice("This book is completely blank!"))
|
|
|
|
/// Generates a random icon state for the book
|
|
/obj/item/book/proc/gen_random_icon_state()
|
|
icon_state = "book[rand(1, maximum_book_state)]"
|
|
|
|
/obj/item/book/attack_self(mob/user)
|
|
if(!user.can_read(src))
|
|
return
|
|
user.visible_message(span_notice("[user] opens a book titled \"[book_data.title]\" and begins reading intently."))
|
|
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "book_nerd", /datum/mood_event/book_nerd)
|
|
on_read(user)
|
|
|
|
/obj/item/book/attackby(obj/item/I, mob/user, params)
|
|
if(istype(I, /obj/item/pen))
|
|
if(user.is_blind())
|
|
to_chat(user, span_warning("As you are trying to write on the book, you suddenly feel very stupid!"))
|
|
return
|
|
if(unique)
|
|
to_chat(user, span_warning("These pages don't seem to take the ink well! Looks like you can't modify it."))
|
|
return
|
|
var/literate = user.is_literate()
|
|
if(!literate)
|
|
to_chat(user, span_notice("You scribble illegibly on the cover of [src]!"))
|
|
return
|
|
var/choice = tgui_input_list(usr, "What would you like to change?", "Book Alteration", list("Title", "Contents", "Author", "Cancel"))
|
|
if(isnull(choice))
|
|
return
|
|
if(!user.canUseTopic(src, BE_CLOSE, literate))
|
|
return
|
|
switch(choice)
|
|
if("Title")
|
|
var/newtitle = reject_bad_text(tgui_input_text(user, "Write a new title", "Book Title", max_length = 30))
|
|
if(!user.canUseTopic(src, BE_CLOSE, literate))
|
|
return
|
|
if (length_char(newtitle) > 30)
|
|
to_chat(user, span_warning("That title won't fit on the cover!"))
|
|
return
|
|
if(!newtitle)
|
|
to_chat(user, span_warning("That title is invalid."))
|
|
return
|
|
name = newtitle
|
|
book_data.set_title(html_decode(newtitle)) //Don't want to double encode here
|
|
if("Contents")
|
|
var/content = tgui_input_text(user, "Write your book's contents (HTML NOT allowed)", "Book Contents", multiline = TRUE)
|
|
if(!user.canUseTopic(src, BE_CLOSE, literate))
|
|
return
|
|
if(!content)
|
|
to_chat(user, span_warning("The content is invalid."))
|
|
return
|
|
book_data.set_content(html_decode(content))
|
|
if("Author")
|
|
var/author = tgui_input_text(user, "Write the author's name", "Author Name")
|
|
if(!user.canUseTopic(src, BE_CLOSE, literate))
|
|
return
|
|
if(!author)
|
|
to_chat(user, span_warning("The name is invalid."))
|
|
return
|
|
book_data.set_author(html_decode(author)) //Setting this encodes, don't want to double up
|
|
else
|
|
return
|
|
|
|
else if(istype(I, /obj/item/barcodescanner))
|
|
var/obj/item/barcodescanner/scanner = I
|
|
var/obj/machinery/computer/libraryconsole/bookmanagement/computer = scanner.computer_ref?.resolve()
|
|
if(!computer)
|
|
to_chat(user, span_alert("[scanner]'s screen flashes: 'No associated computer found!'"))
|
|
return ..()
|
|
|
|
scanner.book_data = book_data.return_copy()
|
|
switch(scanner.mode)
|
|
if(0)
|
|
to_chat(user, span_notice("[scanner]'s screen flashes: 'Book stored in buffer.'"))
|
|
if(1)
|
|
computer.buffer_book = book_data.return_copy()
|
|
to_chat(user, span_notice("[scanner]'s screen flashes: 'Book stored in buffer. Book title stored in associated computer buffer.'"))
|
|
if(2)
|
|
var/list/checkouts = computer.checkouts
|
|
for(var/checkout_ref in checkouts)
|
|
var/datum/borrowbook/maybe_ours = checkouts[checkout_ref]
|
|
if(!book_data.compare(maybe_ours.book_data))
|
|
continue
|
|
checkouts -= checkout_ref
|
|
computer.checkout_update()
|
|
to_chat(user, span_notice("[scanner]'s screen flashes: 'Book stored in buffer. Book has been checked in.'"))
|
|
return
|
|
|
|
to_chat(user, span_notice("[scanner]'s screen flashes: 'Book stored in buffer. No active check-out record found for current title.'"))
|
|
if(3)
|
|
var/datum/book_info/our_copy = book_data.return_copy()
|
|
computer.inventory[ref(our_copy)] = our_copy
|
|
computer.inventory_update()
|
|
to_chat(user, span_notice("[scanner]'s screen flashes: 'Book stored in buffer. Title added to general inventory.'"))
|
|
|
|
else if((istype(I, /obj/item/knife) || I.tool_behaviour == TOOL_WIRECUTTER) && !(flags_1 & HOLOGRAM_1))
|
|
to_chat(user, span_notice("You begin to carve out [book_data.title]..."))
|
|
if(do_after(user, 30, target = src))
|
|
to_chat(user, span_notice("You carve out the pages from [book_data.title]! You didn't want to read it anyway."))
|
|
var/obj/item/storage/book/carved_out = new
|
|
carved_out.name = src.name
|
|
carved_out.title = book_data.title
|
|
carved_out.icon_state = src.icon_state
|
|
if(user.is_holding(src))
|
|
qdel(src)
|
|
user.put_in_hands(carved_out)
|
|
return
|
|
else
|
|
carved_out.forceMove(drop_location())
|
|
qdel(src)
|
|
return
|
|
return
|
|
else
|
|
..()
|
|
|
|
|
|
/*
|
|
* Barcode Scanner
|
|
*/
|
|
/obj/item/barcodescanner
|
|
name = "barcode scanner"
|
|
icon = 'icons/obj/library.dmi'
|
|
icon_state ="scanner"
|
|
desc = "A fabulous tool if you need to scan a barcode."
|
|
throw_speed = 3
|
|
throw_range = 5
|
|
w_class = WEIGHT_CLASS_TINY
|
|
/// A weakref to our associated computer - Modes 1 to 3 use this
|
|
var/datum/weakref/computer_ref
|
|
/// Currently scanned book
|
|
var/datum/book_info/book_data
|
|
/// 0 - Scan only, 1 - Scan and Set Buffer, 2 - Scan and Attempt to Check In, 3 - Scan and Attempt to Add to Inventory
|
|
var/mode = 0
|
|
|
|
/obj/item/barcodescanner/attack_self(mob/user)
|
|
mode += 1
|
|
if(mode > 3)
|
|
mode = 0
|
|
to_chat(user, "[src] Status Display:")
|
|
var/modedesc
|
|
switch(mode)
|
|
if(0)
|
|
modedesc = "Scan book to local buffer."
|
|
if(1)
|
|
modedesc = "Scan book to local buffer and set associated computer buffer to match."
|
|
if(2)
|
|
modedesc = "Scan book to local buffer, attempt to check in scanned book."
|
|
if(3)
|
|
modedesc = "Scan book to local buffer, attempt to add book to general inventory."
|
|
else
|
|
modedesc = "ERROR"
|
|
to_chat(user, " - Mode [mode] : [modedesc]")
|
|
if(computer_ref?.resolve())
|
|
to_chat(user, "<font color=green>Computer has been associated with this unit.</font>")
|
|
else
|
|
to_chat(user, "<font color=red>No associated computer found. Only local scans will function properly.</font>")
|
|
to_chat(user, "\n")
|
|
|
|
|
|
#undef BOOKCASE_UNANCHORED
|
|
#undef BOOKCASE_ANCHORED
|
|
#undef BOOKCASE_FINISHED
|