//******************************* // // Library SQL Configuration // //******************************* // Requires Dantom.DB library ( http://www.byond.com/developer/Dantom/DB ) /* The Library ------------ A place for the crew to go, relax, and enjoy a good book. Aspiring authors can even self publish and, if they're lucky convince the on-staff Librarian to submit it to the Archives to be chronicled in history forever - some say even persisting through alternate dimensions. Written by TLE for /tg/station 13 Feel free to use this as you like. Some credit would be cool. Check us out at http://nanotrasen.com/ if you're so inclined. */ // CONTAINS: // Objects: // - bookcase // - book // - barcode scanner // Machinery: // - library computer // - visitor's computer // - book binder // - book scanner // Datum: // - borrowbook // Ideas for the future // --------------------- // - Visitor's computer should be able to search the current in-round library inventory (that the Librarian has stocked and checked in) // -- Give computer other features like an Instant Messenger application, or the ability to edit, save, and print documents. // - Admin interface directly tied to the Archive DB. Right now there's no way to delete uploaded books in-game. // -- If this gets implemented, allow Librarians to "tag" or "suggest" books to be deleted. The DB ID of the tagged books gets saved to a text file (or another table in the DB maybe?). // The admin interface would automatically take these IDs and SELECT them all from the DB to be displayed along with a Delete link to drop the row from the table. // - When the game sets up and the round begins, have it automatically pick random books from the DB to populate the library with. Even if the Librarian is a useless fuck there are at least a few books around. // - Allow books to be "hollowed out" like the Chaplain's Bible, allowing you to store one pocket-sized item inside. // - Make books/book cases burn when exposed to flame. // - Make book binder hackable. // - Books shouldn't print straight from the library computer. Make it synch with a machine like the book binder to print instead. This should consume some sort of resource. // Run all strings to be used in an SQL query through this proc first to properly escape out injection attempts. /proc/sanitizeSQL(var/t as text) var/sanitized_text = dd_replacetext(t, "'", "\\'") sanitized_text = dd_replacetext(sanitized_text, "\"", "\\\"") return sanitized_text /obj/structure/bookcase name = "bookcase" icon = 'library.dmi' icon_state = "bookcase" anchored = 1 density = 1 opacity = 1 var/category New() spawn(2) // allow library comp to exist var/list/books = book_mgr.getall() var/list/catbooks = new() // see if we have a library computer var/obj/machinery/librarycomp/comp if(istype(loc.loc, /area)) comp = locate() in loc.loc // get books in category for(var/datum/archived_book/B in books) if(!category || (category != B.category)) continue; catbooks += B if(catbooks.len <= 3) // if 3 or less books, fill shelf with that for(var/datum/archived_book/AB in catbooks) var/obj/item/weapon/book/B = new(src) B.name = "[AB.title]" B.title = AB.title B.author = AB.author B.dat = AB.dat B.gen_pages() B.icon_state = "book[rand(1,7)]" B.ssbn = AB.id B.author_real = AB.author_real B.author_key = AB.author_key B.photos = AB.photos // add to inventory if(comp) comp.inventory += B else // otherwise, pick 3 random books for(var/i = 1 to 3) var/datum/archived_book/AB = pick(catbooks) var/obj/item/weapon/book/B = new(src) B.name = "[AB.title]" B.title = AB.title B.author = AB.author B.dat = AB.dat B.gen_pages() B.icon_state = "book[rand(1,7)]" B.ssbn = AB.id B.author_real = AB.author_real B.author_key = AB.author_key B.photos = AB.photos // add to inventory if(comp) comp.inventory += B attackby(obj/O as obj, mob/user as mob) if(istype(O, /obj/item/weapon/book)) user.drop_item() O.loc = src else if(istype(O, /obj/item/weapon/pen)) var/newname = copytext(sanitize(input("What would you like to title this bookshelf?") as text|null),1,MAX_MESSAGE_LEN) if(!newname) return else setname(sanitize(newname)) else ..() attack_hand(var/mob/user as mob) var/list/books = list() for(var/obj/item/weapon/book/b in src.contents) books.Add(b) if(books.len) var/obj/item/weapon/book/choice = input("Which book would you like to remove from the shelf?") as null|anything in books if(choice) choice.loc = src.loc else return else user << "None of these books pique your interest in the slightest." proc setname(var/t as text) if(t) src.name = "bookcase ([t])" ex_act(severity) switch(severity) if(1.0) for(var/obj/item/weapon/book/b in src.contents) del(b) del(src) return if(2.0) for(var/obj/item/weapon/book/b in src.contents) if (prob(50)) b.loc = (get_turf(src)) else del(b) del(src) return if(3.0) if (prob(50)) for(var/obj/item/weapon/book/b in src.contents) b.loc = (get_turf(src)) del(src) return else return /obj/structure/bookcase/manuals/medical name = "Medical Manuals bookcase" New() ..() new /obj/item/weapon/book/manual/medical_cloning(src) /obj/structure/bookcase/manuals/engineering name = "Engineering Manuals bookcase" New() ..() new /obj/item/weapon/book/manual/engineering_construction(src) new /obj/item/weapon/book/manual/engineering_particle_accelerator(src) new /obj/item/weapon/book/manual/engineering_hacking(src) new /obj/item/weapon/book/manual/engineering_guide(src) new /obj/item/weapon/book/manual/engineering_singularity_safety(src) new /obj/item/weapon/book/manual/robotics_cyborgs(src) /obj/structure/bookcase/manuals/research_and_development name = "R&D Manuals bookcase" New() ..() new /obj/item/weapon/book/manual/research_and_development(src) /obj/item/weapon/book name = "book" icon = 'library.dmi' icon_state ="book" throw_speed = 1 throw_range = 5 w_class = 1.0 flags = FPRINT | TABLEPASS var dat // Actual page content due_date = 0 // Game time in 1/10th seconds author // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned unique = 0 // 0 - Normal book, 1 - Should not be treated as normal book, unable to be copied, unable to be modified title // The real name of the book. author_real // author's real_name author_key // author's byond key ssbn // the ssbn, if a downloaded book list/pages = new() // individual pages as a list of text cur_page = 1 // current page being read list/icon/photos // in-game photos used proc/navbar() return "
" \ + "
"+(cur_page > 1 \ ? "< " \ + "<" \ : "") \ + "
"+(cur_page < pages.len \ ? "> " \ + ">" \ : "") \ + "
[cur_page]/[pages.len]
" // should be called if dat is changed proc/gen_pages() // split into pages cur_page = 1 pages = dd_text2list(dat, "") var/PN = 1 for(var/page in pages) // look for photos and process var/i = 1 while(i <= lentext(page)) i = findtext(page, "", i+7) if(e_c == 0) e_c = INFINITY var/e = min(e_s, e_c) // find the closest of the two if(e == INFINITY) break var/i_num = text2num(copytext(page, i+7, e)) page = copytext(page, 1, i) + "[title]" \ + "" \ + "
[title]
[author]
" \ + "
[pages[cur_page]]
" \ + (pages.len > 1 ? navbar() : "") \ + "", "window=book;size=600x500") if(opening) if(title) user.visible_message("[user] opens a book titled \"[src.title]\" and begins reading intently.") else user.visible_message("[user] opens a book titled \"[src.name]\" and begins reading intently.") onclose(user, "book") else user << "This book is completely blank!" attackby(obj/item/weapon/W as obj, mob/user as mob) if(istype(W, /obj/item/weapon/pen)) if(unique) user << "These pages don't seem to take the ink well. Looks like you can't modify it." return var/choice = input("What would you like to change?") in list("Title", "Contents", "Author", "Cancel") switch(choice) if("Title") var/ntitle = copytext(sanitize(input("Write a new title:") as text|null),1,MAX_MESSAGE_LEN) if(!ntitle) return if("Contents") var/t = "[src.dat]" do t = input(user, "What text do you wish to add?", src.name, t) as message if ((!in_range(src, usr) && src.loc != user && src.loc.loc != user && user.equipped() != W)) return if(lentext(t) >= MAX_BOOK_MESSAGE_LEN) var/cont = input(user, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no") if(cont == "no") break while(lentext(t) > MAX_BOOK_MESSAGE_LEN) if ((!in_range(src, usr) && src.loc != user && src.loc.loc != user && user.equipped() != W)) return // check for exploits for(var/tag in paper_blacklist) if(findtext(t,"<"+tag)) user << "\blue You think to yourself, \"Hm.. this is only paper...\"" return src.dat = t gen_pages() if("Author") var/nauthor = copytext(sanitize(input("Write the author's name:") as text|null),1,MAX_NAME_LEN) if(!nauthor) return else src.author = sanitize(nauthor) else return else if(istype(W, /obj/item/weapon/barcodescanner)) var/obj/item/weapon/barcodescanner/scanner = W if(!scanner.computer) user << "[W]'s screen flashes: 'No associated computer found!'" else switch(scanner.mode) if(0) scanner.book = src user << "[W]'s screen flashes: 'Book stored in buffer.'" if(1) scanner.book = src scanner.computer.buffer_book = src.name user << "[W]'s screen flashes: 'Book stored in buffer. Book title stored in associated computer buffer.'" if(2) scanner.book = src for(var/datum/borrowbook/b in scanner.computer.checkouts) if(b.bookname == src.name) scanner.computer.checkouts.Remove(b) user << "[W]'s screen flashes: 'Book stored in buffer. Book has been checked in.'" return user << "[W]'s screen flashes: 'Book stored in buffer. No active check-out record found for current title.'" if(3) scanner.book = src for(var/obj/item/weapon/book in scanner.computer.inventory) if(book == src) user << "[W]'s screen flashes: 'Book stored in buffer. Title already present in inventory, aborting to avoid duplicate entry.'" return scanner.computer.inventory.Add(src) user << "[W]'s screen flashes: 'Book stored in buffer. Title added to general inventory.'" else if(istype(W, /obj/item/weapon/wirecutters) || istype(W, /obj/item/weapon/scalpel)) if(unique) user << "These pages seem to be made of a very robust paper. Looks like you can't carve it." return if(istype(src.loc, /mob) && usr.l_hand != src && usr.r_hand != src) user << "\red You either need hold the book or put it down on something first." return var/obj/item/weapon/storage/book/B = new(loc) B.icon_state = icon_state B.name = name B.desc = desc user.remove_from_mob(src) if(user.get_inactive_hand() == null) user.put_in_inactive_hand(B) else B.loc = user.loc user << "You carve out the inside of the book. Sneaky!" del(src) else ..() /obj/item/weapon/barcodescanner name = "barcode scanner" icon = 'library.dmi' icon_state ="scanner" throw_speed = 1 throw_range = 5 w_class = 1.0 flags = FPRINT | TABLEPASS var obj/machinery/librarycomp/computer // Associated computer - Modes 1 to 3 use this obj/item/weapon/book/book // Currently scanned book mode = 0 // 0 - Scan only, 1 - Scan and Set Buffer, 2 - Scan and Attempt to Check In, 3 - Scan and Attempt to Add to Inventory attack_self(mob/user as mob) mode += 1 if(mode > 3) mode = 0 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" user << " - Mode [mode] : [modedesc]" if(src.computer) user << "Computer has been associated with this unit." else user << "No associated computer found. Only local scans will function properly." user << "\n" datum/borrowbook // Datum used to keep track of who has borrowed what when and for how long. var bookname mobname getdate duedate /obj/machinery/librarypubliccomp name = "visitor computer" icon = 'library.dmi' icon_state = "computer" anchored = 1 density = 1 var screenstate = 0 title category = "Any" author SQLquery /obj/machinery/librarypubliccomp/attack_hand(var/mob/user as mob) usr.machine = src var/dat = "Library Visitor\n" // switch(screenstate) if(0) dat += "

Search Settings


" dat += "Filter by Title: [title]
" dat += "Filter by Category: [category]
" dat += "Filter by Author: [author]
" dat += "\[Start Search\]
" if(1) if(BOOKS_USE_SQL && config.sql_enabled) var/DBConnection/dbcon = new() dbcon.Connect("dbi:mysql:[sqldb]:[sqladdress]:[sqlport]","[sqllogin]","[sqlpass]") if(!dbcon.IsConnected()) dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance.
" else if(!SQLquery) dat += "ERROR: Malformed search request. Please contact your system administrator for assistance.
" else dat += "" dat += "" var/DBQuery/query = dbcon.NewQuery(SQLquery) query.Execute() while(query.NextRow()) var/author = query.item[1] var/title = query.item[2] var/category = query.item[3] var/id = query.item[4] dat += "" dat += "
AUTHORTITLECATEGORYSS13BN
[author][title][category][id]

" dbcon.Disconnect() else dat += "" dat += "" var/list/books = book_mgr.getall() for(var/datum/archived_book/book in books) // search queries if(author && !findtext(author, book.author)) continue; if(title && !findtext(title, book.title)) continue; if(!category || (category != "Any" && category != book.category)) continue; dat += "" dat += "
AUTHORTITLECATEGORYSS13BN
[book.author][book.title][book.category][book.id]

" dat += "\[Go Back\]
" user << browse(dat, "window=publiclibrary") onclose(user, "publiclibrary") /obj/machinery/librarypubliccomp/Topic(href, href_list) if(href_list["settitle"]) var/newtitle = input("Enter a title to search for:") as text|null if(newtitle) title = sanitize(newtitle) else title = null title = dd_replacetext(title, "'", "''") if(href_list["setcategory"]) var/newcategory = input("Choose a category to search for:") in list("Any", "Fiction", "Non-Fiction", "Adult", "Reference", "Religion") if(newcategory) category = newcategory else category = "Any" if(href_list["setauthor"]) var/newauthor = input("Enter an author to search for:") as text|null if(newauthor) author = sanitize(newauthor) else author = null author = dd_replacetext(author, "'", "''") if(href_list["search"]) if(BOOKS_USE_SQL && config.sql_enabled) SQLquery = "SELECT author, title, category, id FROM library WHERE " if(category == "Any") SQLquery += "author LIKE '%[author]%' AND title LIKE '%[title]%'" else SQLquery += "author LIKE '%[author]%' AND title LIKE '%[title]%' AND category='[category]'" screenstate = 1 if(href_list["back"]) screenstate = 0 src.add_fingerprint(usr) src.updateUsrDialog() return // TODO: Make this an actual /obj/machinery/computer that can be crafted from circuit boards and such /obj/machinery/librarycomp name = "Check-In/Out Computer" icon = 'library.dmi' icon_state = "computer" anchored = 1 density = 1 var arcanecheckout = 0 screenstate = 0 // 0 - Main Menu, 1 - Inventory, 2 - Checked Out, 3 - Check Out a Book buffer_book buffer_mob upload_category = "Fiction" list/checkouts = list() list/inventory = list() checkoutperiod = 5 // In minutes obj/machinery/libraryscanner/scanner // Book scanner that will be used when uploading books to the Archive bibledelay = 0 // LOL NO SPAM (1 minute delay) -- Doohl /obj/machinery/librarycomp/attack_hand(var/mob/user as mob) usr.machine = src var/dat = "Book Inventory Management\n" // switch(screenstate) if(0) // Main Menu dat += "1. View General Inventory
" dat += "2. View Checked Out Inventory
" dat += "3. Check out a Book
" dat += "4. Connect to External Archive
" dat += "5. Upload New Title to Archive
" dat += "6. Print a Bible
" if(src.emagged) dat += "7. Access the Forbidden Lore Vault
" if(src.arcanecheckout) new /obj/item/weapon/tome(src.loc) user << "Your sanity barely endures the seconds spent in the vault's browsing window. The only thing to remind you of this when you stop browsing is a dusty old tome sitting on the desk. You don't really remember printing it." for (var/mob/V in hearers(src)) V.show_message("[usr] stares at the blank screen for a few moments, his expression frozen in fear. When he finally awakens from it, he looks a lot older.", 2) src.arcanecheckout = 0 if(1) // Inventory dat += "

Inventory


" for(var/obj/item/weapon/book/b in inventory) dat += "[b.name] (Delete)
" dat += "(Return to main menu)
" if(2) // Checked Out dat += "

Checked Out Books


" for(var/datum/borrowbook/b in checkouts) var/timetaken = world.time - b.getdate //timetaken *= 10 timetaken /= 600 timetaken = round(timetaken) var/timedue = b.duedate - world.time //timedue *= 10 timedue /= 600 if(timedue <= 0) timedue = "(OVERDUE) [timedue]" else timedue = round(timedue) dat += "\"[b.bookname]\", Checked out to: [b.mobname]
--- Taken: [timetaken] minutes ago, Due: in [timedue] minutes
" dat += "(Check In)

" dat += "(Return to main menu)
" if(3) // Check Out a Book dat += "

Check Out a Book


" dat += "Book: [src.buffer_book] " dat += "\[Edit\]
" dat += "Recipient: [src.buffer_mob] " dat += "\[Edit\]
" dat += "Checkout Date : [world.time/600]
" dat += "Due Date: [(world.time + checkoutperiod)/600]
" dat += "(Checkout Period: [checkoutperiod] minutes) (+/-)" dat += "(Commit Entry)
" dat += "(Return to main menu)
" if(4) dat += "

External Archive

" if(BOOKS_USE_SQL && config.sql_enabled) var/DBConnection/dbcon = new() dbcon.Connect("dbi:mysql:[sqldb]:[sqladdress]:[sqlport]","[sqllogin]","[sqlpass]") if(!dbcon.IsConnected()) dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance." else dat += "(Order book by SS13BN)

" dat += "" dat += "" var/DBQuery/query = dbcon.NewQuery("SELECT id, author, title, category FROM library") query.Execute() while(query.NextRow()) var/id = query.item[1] var/author = query.item[2] var/title = query.item[3] var/category = query.item[4] dat += "" dat += "
AUTHORTITLECATEGORY
[author][title][category]\[Order\]

" dat += "(Return to main menu)
" dbcon.Disconnect() else dat += "(Order book by SS13BN)

" dat += "" dat += "" var/list/books = book_mgr.getall() for(var/datum/archived_book/book in books) dat += "" dat += "
AUTHORTITLECATEGORY
[book.author][book.title][book.category][book.id]

" dat += "(Return to main menu)
" if(5) dat += "

Upload a New Title

" if(!scanner) for(var/obj/machinery/libraryscanner/S in range(9)) scanner = S break if(!scanner) dat += "No scanner found within wireless network range.
" else if(!scanner.cache) dat += "No data found in scanner memory.
" else if(scanner.cache.ssbn && (scanner.cache.author_real != user.real_name && scanner.cache.author_key != user.client.ckey)) dat += "This book contains copy protection preventing you from re-uploading this to the database.
" else dat += "Data marked for [scanner.cache.ssbn ? "re-" : ""]upload...
" dat += "Title: [scanner.cache.title]
" if(!scanner.cache.author) scanner.cache.author = "Anonymous" dat += "Author: [scanner.cache.author]
" dat += "Category: [upload_category]
" dat += "\[Upload\]
" dat += "(Return to main menu)
" if(7) dat += "

Accessing Forbidden Lore Vault v 1.3

" dat += "Are you absolutely sure you want to proceed? EldritchTomes Inc. takes no responsibilities for loss of sanity resulting from this action.

" dat += "Yes.
" dat += "No.
" //dat += "Close

" user << browse(dat, "window=library") onclose(user, "library") /obj/machinery/librarycomp/attackby(obj/item/weapon/W as obj, mob/user as mob) if (src.density && istype(W, /obj/item/weapon/card/emag)) var/obj/item/weapon/card/emag/E = W if(E.uses) E.uses-- else return src.emagged = 1 if(istype(W, /obj/item/weapon/barcodescanner)) var/obj/item/weapon/barcodescanner/scanner = W scanner.computer = src user << "[scanner]'s associated machine has been set to [src]." for (var/mob/V in hearers(src)) V.show_message("[src] lets out a low, short blip.", 2) else ..() /obj/machinery/librarycomp/Topic(href, href_list) if(href_list["switchscreen"]) switch(href_list["switchscreen"]) if("0") screenstate = 0 if("1") screenstate = 1 if("2") screenstate = 2 if("3") screenstate = 3 if("4") screenstate = 4 if("5") screenstate = 5 if("6") if(!bibledelay) var/obj/item/weapon/storage/bible/B = new /obj/item/weapon/storage/bible(src.loc) if(ticker && ( ticker.Bible_icon_state && ticker.Bible_item_state) ) B.icon_state = ticker.Bible_icon_state B.item_state = ticker.Bible_item_state B.name = ticker.Bible_name B.deity_name = ticker.Bible_deity_name bibledelay = 1 spawn(60) bibledelay = 0 else for (var/mob/V in hearers(src)) V.show_message("[src]'s monitor flashes, \"Bible printer currently unavailable, please wait a moment.\"") if("7") screenstate = 7 if(href_list["arccheckout"]) if(src.emagged) src.arcanecheckout = 1 src.screenstate = 0 if(href_list["increasetime"]) checkoutperiod += 1 if(href_list["decreasetime"]) checkoutperiod -= 1 if(checkoutperiod < 1) checkoutperiod = 1 if(href_list["editbook"]) buffer_book = copytext(sanitize(input("Enter the book's title:") as text|null),1,MAX_MESSAGE_LEN) if(href_list["editmob"]) buffer_mob = copytext(sanitize(input("Enter the recipient's name:") as text|null),1,MAX_NAME_LEN) if(href_list["checkout"]) var/datum/borrowbook/b = new /datum/borrowbook b.bookname = sanitize(buffer_book) b.mobname = sanitize(buffer_mob) b.getdate = world.time b.duedate = world.time + (checkoutperiod * 600) checkouts.Add(b) if(href_list["checkin"]) var/datum/borrowbook/b = locate(href_list["checkin"]) checkouts.Remove(b) if(href_list["delbook"]) var/obj/item/weapon/book/b = locate(href_list["delbook"]) inventory.Remove(b) if(href_list["settitle"]) var/newtitle = input("Enter the book name: ", "Book Upload", scanner.cache.title) as text|null if(newtitle) scanner.cache.title = sanitize(newtitle) scanner.cache.name = "Book: [scanner.cache.title]" if(href_list["setauthor"]) var/newauthor = copytext(sanitize(input("Enter the author's name: ", "Book Upload", scanner.cache.author) as text|null),1,MAX_MESSAGE_LEN) if(newauthor) scanner.cache.author = newauthor if(href_list["setcategory"]) var/newcategory = input("Choose a category: ") in list("Fiction", "Non-Fiction", "Adult", "Reference", "Religion") if(newcategory) upload_category = newcategory if(href_list["upload"]) if(scanner) if(scanner.cache) var/choice = input("Are you certain you wish to upload this title to the Archive?") in list("Confirm", "Abort") if(choice == "Confirm") if(BOOKS_USE_SQL && config.sql_enabled) var/DBConnection/dbcon = new() dbcon.Connect("dbi:mysql:[sqldb]:[sqladdress]:[sqlport]","[sqllogin]","[sqlpass]") if(!dbcon.IsConnected()) alert("Connection to Archive has been severed. Aborting.") else /* var/sqltitle = dbcon.Quote(scanner.cache.name) var/sqlauthor = dbcon.Quote(scanner.cache.author) var/sqlcontent = dbcon.Quote(scanner.cache.dat) var/sqlcategory = dbcon.Quote(upload_category) */ var/sqltitle = dd_replacetext(scanner.cache.name, "'", "''") var/sqlauthor = dd_replacetext(scanner.cache.author, "'", "''") var/sqlcontent = dd_replacetext(scanner.cache.dat, "'", "''") var/sqlcategory = upload_category ///proc/dd_replacetext(text, search_string, replacement_string) var/DBQuery/query = dbcon.NewQuery("INSERT INTO library (author, title, content, category) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory]')") if(!query.Execute()) usr << query.ErrorMsg() else log_game("[usr.name]/[usr.key] has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] signs") alert("Upload Complete.") dbcon.Disconnect() else var/datum/archived_book/B = new() if(scanner.cache.title) B.title = scanner.cache.title else B.title = scanner.cache.name B.author = scanner.cache.author B.dat = scanner.cache.dat B.category = upload_category if(scanner.cache.ssbn) B.id = scanner.cache.ssbn else B.id = book_mgr.freeid() B.author_real = scanner.cache.author_real B.author_key = scanner.cache.author_key if(scanner.cache.photos.len >= 8) scanner.cache.photos.len = 8 B.photos = scanner.cache.photos B.save() log_game("[usr.name]/[usr.key] has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] signs") alert("Upload Complete.") if(href_list["targetid"]) if(BOOKS_USE_SQL && config.sql_enabled) var/sqlid = href_list["targetid"] var/DBConnection/dbcon = new() dbcon.Connect("dbi:mysql:[sqldb]:[sqladdress]:[sqlport]","[sqllogin]","[sqlpass]") if(!dbcon.IsConnected()) alert("Connection to Archive has been severed. Aborting.") if(bibledelay) for (var/mob/V in hearers(src)) V.show_message("[src]'s monitor flashes, \"Printer unavailable. Please allow a short time before attempting to print.\"") else bibledelay = 1 spawn(60) bibledelay = 0 var/DBQuery/query = dbcon.NewQuery("SELECT * FROM library WHERE id=[sqlid]") query.Execute() while(query.NextRow()) var/author = query.item[2] var/title = query.item[3] var/content = query.item[4] var/obj/item/weapon/book/B = new(src.loc) if(title) B.name = "[title]" else B.name = "Untitled book" B.title = title B.author = author B.dat = content B.gen_pages() B.icon_state = "book[rand(1,7)]" src.visible_message("[src]'s printer hums as it produces a completely bound book. How did it do that?") break dbcon.Disconnect() else var/datum/archived_book/AB = new(book_mgr.path(text2num(href_list["targetid"]))) if(!AB) alert("Book not found!") else var/obj/item/weapon/book/B = new(src.loc) B.name = "Book: [AB.title]" B.title = AB.title B.author = AB.author B.dat = AB.dat B.gen_pages() B.icon_state = "book[rand(1,7)]" B.author_real = AB.author_real B.author_key = AB.author_key B.photos = AB.photos src.visible_message("[src]'s printer hums as it produces a completely bound book. How did it do that?") if(href_list["orderbyid"]) var/orderid = input("Enter your order:") as num|null if(orderid) var/nhref = "src=\ref[src];targetid=[orderid]" spawn() src.Topic(nhref, params2list(nhref), src) src.add_fingerprint(usr) src.updateUsrDialog() return /obj/machinery/libraryscanner name = "scanner" icon = 'library.dmi' icon_state = "bigscanner" anchored = 1 density = 1 var obj/item/weapon/book/cache // Last scanned book /obj/machinery/libraryscanner/attackby(var/obj/O as obj, var/mob/user as mob) if(istype(O, /obj/item/weapon/book)) user.drop_item() O.loc = src /obj/machinery/libraryscanner/attack_hand(var/mob/user as mob) usr.machine = src var/dat = "Scanner Control Interface\n" // if(cache) dat += "Data stored in memory.
" else dat += "No data stored in memory.
" dat += "\[Scan\]" if(cache) dat += " \[Clear Memory\]

\[Remove Book\]" else dat += "
" user << browse(dat, "window=scanner") onclose(user, "scanner") /obj/machinery/libraryscanner/Topic(href, href_list) if(href_list["scan"]) for(var/obj/item/weapon/book/B in contents) cache = B break if(href_list["clear"]) cache = null if(href_list["eject"]) for(var/obj/item/weapon/book/B in contents) B.loc = src.loc src.add_fingerprint(usr) src.updateUsrDialog() return /obj/machinery/bookbinder name = "Book Binder" icon = 'library.dmi' icon_state = "binder" anchored = 1 density = 1 var/obj/item/weapon/book/template var/list/icon_names var/i_preview = 1 var/printing = 0 proc/newGenericBook() template = new(src) template.title = "Print Job #" + "[rand(100, 999)]" template.author = "Anonymous" template.name = "Book: [template.title]" template.icon_state = "book[rand(1,7)]" template.dat = "" template.gen_pages() template.photos = new() icon_names = new() i_preview = 1 attackby(var/obj/O as obj, var/mob/user as mob) if(istype(O, /obj/item/weapon/paper)) user.drop_item() O.loc = src user.visible_message("[user] loads some paper into [src].", "You load some paper into [src].") if(template) template.dat += "[O:info]" template.gen_pages() else template = new(src) template.title = O.name == initial(O.name) ? "Print Job #" + "[rand(100, 999)]" : O.name template.name = "Book: [template.title]" template.icon_state = "book[rand(1,7)]" template.dat = O:info template.gen_pages() template.photos = new() icon_names = new() i_preview = 1 del(O) updateUsrDialog() else if(istype(O, /obj/item/weapon/photo)) user.drop_item() O.loc = src user.visible_message("[user] loads a photo into [src].", "You load a photo into [src].") if(!template) newGenericBook() if(template.photos.len >= 8) user << "\red The photo tray is already full!" O.loc = src.loc return var/icon/imported = new(O.icon) imported.Crop(6,8,27,27) imported.Scale(32,32) template.photos += imported icon_names += O.name del(O) updateUsrDialog() else if(istype(O, /obj/item/weapon/book)) if(template) user << "\red The binder already has a book in the buffer!" return user.drop_item() O.loc = src user.visible_message("[user] loads a book into [src] for editing.", "You load a book into [src] for editing.") var/obj/machinery/librarycomp/C = locate() in loc.loc if(C) if(C.inventory.Find(O)) C.inventory -= O template = O icon_names = new() for(var/i = 1 to template.photos.len) icon_names += "Photo [i]" updateUsrDialog() else ..() /obj/machinery/bookbinder/attack_paw(user as mob) return src.attack_hand(user) /obj/machinery/bookbinder/attack_ai(user as mob) return src.attack_hand(user) /obj/machinery/bookbinder/attack_hand(user as mob) if(..()) return if(src.stat) user << "[name] does not seem to be responding to your button mashing." return var/dat = "Book BinderNT Publishing House


" if(printing) dat += "[src] is currently printing." else // general settings dat += "New Book" if(template) template.cache_imgs(user) dat += " Delete Book
" dat += "Title: [template.title]
" dat += "Author: [template.author]
" // articles dat += "Pages:
    " var/ID = 1 for(var/P in template.pages) dat += "
  1. Page [ID]" dat += " Preview" dat += " Delete
  2. " ID++ dat += "
New Page
" // images dat += "Images:
    " ID = 1 for(var/icon/I in template.photos) dat += "
  1. [ID]: [icon_names[ID]]" dat += " Delete" ID++ dat += "
" if(i_preview >= 1 && i_preview <= template.photos.len) var/iconname = "book_preview.png" user << browse_rsc(template.photos[i_preview],iconname) dat += "[i_preview] ([icon_names[i_preview]]): " dat += "
Print" dat += "
" user << browse(dat, "window=bookbinder") onclose(user, "bookbinder") /obj/machinery/bookbinder/Topic(href, href_list) if(..()) return usr.machine = src src.add_fingerprint(usr) if(href_list["new"]) if(template) var/R = alert("There is already a book in the buffer. Do you want to delete it and start over?", "Book Binder", "Delete", "Cancel") if(R == "Cancel") return del(template) newGenericBook() updateUsrDialog() if(href_list["delete"]) if(template) del(template) updateUsrDialog() if(href_list["print"]) printing = 1 updateUsrDialog() src.visible_message("[src] begins to hum as it warms up its printing drums.") sleep(rand(50,100)) src.visible_message("[src] whirs as it prints and binds a new book.") if(!template.ssbn) template.author_real = usr.real_name if(usr.client) template.author_key = usr.client.ckey template.loc = src.loc template = null printing = 0 updateUsrDialog() if(href_list["p_new"]) template.dat += "" template.gen_pages() updateUsrDialog() if(href_list["title"]) var/n_name = input(usr, "What would you like to title your book?", "Book Binder", template.title) as text|null if(n_name) template.title = sanitize(n_name) template.name = "Book: [template.title]" updateUsrDialog() if(href_list["author"]) var/n_name = input(usr, "Who would you like your pen name to be?", "Book Binder", template.author) as text|null if(n_name) template.author = sanitize(n_name) updateUsrDialog() if(href_list["p_edit"]) var/PN = text2num(href_list["p_edit"]) var/list/pages = dd_text2list(template.dat, "") var/t = pages[PN] do t = input(usr, "What text do you wish to add?", "Book Binder P.[PN]", t) as message if(lentext(t) >= MAX_BOOK_MESSAGE_LEN) var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no") if(cont == "no") break while(lentext(t) > MAX_BOOK_MESSAGE_LEN) // check for exploits for(var/tag in paper_blacklist) if(findtext(t,"<"+tag)) usr << "\blue You think to yourself, \"Hm.. this is only paper...\"" return // the actual pages list is formatted and shouldn't be directly editted pages[PN] = t template.dat = dd_list2text(pages, "") template.gen_pages() updateUsrDialog() if(href_list["p_delete"]) var/i = text2num(href_list["p_delete"]) var/list/pages = dd_text2list(template.dat, "") pages.Cut(i, i+1) template.dat = dd_list2text(pages, "") template.gen_pages() updateUsrDialog() if(href_list["p_preview"]) var/i = text2num(href_list["p_preview"]) var/dat = template.pages[i] usr << browse(dat, "window=bookbinder_preview;size=600x500") onclose(usr, "bookbinder_preview") if(href_list["i_view"]) i_preview = text2num(href_list["i_view"]) updateUsrDialog() if(href_list["i_delete"]) var/i = text2num(href_list["i_delete"]) template.photos.Cut(i, i+1) icon_names.Cut(i, i+1) updateUsrDialog()