mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-13 08:03:43 +01:00
File standardisation (#13131)
* Adds the check components * Adds in trailing newlines * Converts all CRLF to LF * Post merge EOF * Post merge line endings * Final commit
This commit is contained in:
@@ -90,4 +90,4 @@
|
||||
return pagelist
|
||||
|
||||
/obj/machinery/computer/library/proc/getBookByID(var/id as text)
|
||||
return library_catalog.getBookByID(id)
|
||||
return library_catalog.getBookByID(id)
|
||||
|
||||
@@ -1,465 +1,465 @@
|
||||
/*
|
||||
* Library Computer
|
||||
*/
|
||||
/obj/machinery/computer/library/checkout
|
||||
name = "Check-In/Out Computer"
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state = "computer"
|
||||
anchored = 1
|
||||
density = 1
|
||||
var/arcanecheckout = 0
|
||||
//var/screenstate = 0 // 0 - Main Menu, 1 - Inventory, 2 - Checked Out, 3 - Check Out a Book
|
||||
var/buffer_book
|
||||
var/buffer_mob
|
||||
var/upload_category = "Fiction"
|
||||
var/list/checkouts = list()
|
||||
var/list/inventory = list()
|
||||
var/checkoutperiod = 5 // In minutes
|
||||
var/obj/machinery/libraryscanner/scanner // Book scanner that will be used when uploading books to the Archive
|
||||
|
||||
var/bibledelay = 0 // LOL NO SPAM (1 minute delay) -- Doohl
|
||||
var/booklist
|
||||
|
||||
/obj/machinery/computer/library/checkout/attack_hand(var/mob/user as mob)
|
||||
if(..())
|
||||
return
|
||||
interact(user)
|
||||
|
||||
/obj/machinery/computer/library/checkout/interact(var/mob/user)
|
||||
if(interact_check(user))
|
||||
return
|
||||
|
||||
var/dat = ""
|
||||
switch(screenstate)
|
||||
if(0)
|
||||
// Main Menu
|
||||
|
||||
dat += {"<ol>
|
||||
<li><A href='?src=[UID()];switchscreen=1'>View General Inventory</A></li>
|
||||
<li><A href='?src=[UID()];switchscreen=2'>View Checked Out Inventory</A></li>
|
||||
<li><A href='?src=[UID()];switchscreen=3'>Check out a Book</A></li>
|
||||
<li><A href='?src=[UID()];switchscreen=4'>Connect to External Archive</A></li>
|
||||
<li><A href='?src=[UID()];switchscreen=5'>Upload New Title to Archive</A></li>
|
||||
<li><A href='?src=[UID()];switchscreen=6'>Print a Bible</A></li>
|
||||
<li><A href='?src=[UID()];switchscreen=7'>Print a Manual</A></li>"}
|
||||
if(src.emagged)
|
||||
dat += "<li><A href='?src=[UID()];switchscreen=8'>Access the Forbidden Lore Vault</A></li>"
|
||||
dat += "</ol>"
|
||||
|
||||
if(src.arcanecheckout)
|
||||
new /obj/item/tome(src.loc)
|
||||
to_chat(user, "<span class='warning'>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.</span>")
|
||||
user.visible_message("[user] stares at the blank screen for a few moments, [user.p_their()] expression frozen in fear. When [user.p_they()] finally awaken[user.p_s()] from it, [user.p_they()] look[user.p_s()] a lot older.", 2)
|
||||
src.arcanecheckout = 0
|
||||
if(1)
|
||||
// Inventory
|
||||
dat += "<h3>Inventory</h3>"
|
||||
for(var/obj/item/book/b in inventory)
|
||||
dat += "[b.name] <A href='?src=[UID()];delbook=\ref[b]'>(Delete)</A><BR>"
|
||||
dat += "<A href='?src=[UID()];switchscreen=0'>(Return to main menu)</A><BR>"
|
||||
if(2)
|
||||
// Checked Out
|
||||
dat += "<h3>Checked Out Books</h3><BR>"
|
||||
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 = "<font color=red><b>(OVERDUE)</b> [timedue]</font>"
|
||||
else
|
||||
timedue = round(timedue)
|
||||
|
||||
dat += {"\"[b.bookname]\", Checked out to: [b.mobname]<BR>--- Taken: [timetaken] minutes ago, Due: in [timedue] minutes<BR>
|
||||
<A href='?src=[UID()];checkin=\ref[b]'>(Check In)</A><BR><BR>"}
|
||||
dat += "<A href='?src=[UID()];switchscreen=0'>(Return to main menu)</A><BR>"
|
||||
if(3)
|
||||
// Check Out a Book
|
||||
|
||||
dat += {"<h3>Check Out a Book</h3><BR>
|
||||
Book: [src.buffer_book]
|
||||
<A href='?src=[UID()];editbook=1'>\[Edit\]</A><BR>
|
||||
Recipient: [src.buffer_mob]
|
||||
<A href='?src=[UID()];editmob=1'>\[Edit\]</A><BR>
|
||||
Checkout Date : [world.time/600]<BR>
|
||||
Due Date: [(world.time + checkoutperiod)/600]<BR>
|
||||
(Checkout Period: [checkoutperiod] minutes) (<A href='?src=[UID()];increasetime=1'>+</A>/<A href='?src=[UID()];decreasetime=1'>-</A>)
|
||||
<A href='?src=[UID()];checkout=1'>(Commit Entry)</A><BR>
|
||||
<A href='?src=[UID()];switchscreen=0'>(Return to main menu)</A><BR>"}
|
||||
if(4)
|
||||
dat += "<h3>External Archive</h3>"
|
||||
if(!dbcon.IsConnected())
|
||||
dat += "<font color=red><b>ERROR</b>: Unable to contact External Archive. Please contact your system administrator for assistance.</font>"
|
||||
else
|
||||
num_results = src.get_num_results()
|
||||
num_pages = Ceiling(num_results/LIBRARY_BOOKS_PER_PAGE)
|
||||
dat += {"<ul>
|
||||
<li><A href='?src=[UID()];id=-1'>(Order book by SS<sup>13</sup>BN)</A></li>
|
||||
</ul>"}
|
||||
var/pagelist = get_pagelist()
|
||||
|
||||
dat += {"<h2>Search Settings</h2><br />
|
||||
<A href='?src=[UID()];settitle=1'>Filter by Title: [query.title]</A><br />
|
||||
<A href='?src=[UID()];setcategory=1'>Filter by Category: [query.category]</A><br />
|
||||
<A href='?src=[UID()];setauthor=1'>Filter by Author: [query.author]</A><br />
|
||||
<A href='?src=[UID()];search=1'>\[Start Search\]</A><br />"}
|
||||
dat += pagelist
|
||||
|
||||
dat += {"<form name='pagenum' action='?src=[UID()]' method='get'>
|
||||
<input type='hidden' name='src' value='[UID()]'>
|
||||
<input type='text' name='pagenum' value='[page_num]' maxlength="5" size="5">
|
||||
<input type='submit' value='Jump To Page'>
|
||||
</form>"}
|
||||
|
||||
dat += {"<table border=\"0\">
|
||||
<tr>
|
||||
<td>Author</td>
|
||||
<td>Title</td>
|
||||
<td>Category</td>
|
||||
<td>Controls</td>
|
||||
</tr>"}
|
||||
|
||||
for(var/datum/cachedbook/CB in get_page(page_num))
|
||||
var/author = CB.author
|
||||
var/controls = "<A href='?src=[UID()];id=[CB.id]'>\[Order\]</A>"
|
||||
controls += {" <A href="?src=[UID()];flag=[CB.id]">\[Flag[CB.flagged ? "ged" : ""]\]</A>"}
|
||||
if(check_rights(R_ADMIN, 0, user = user))
|
||||
controls += " <A style='color:red' href='?src=[UID()];del=[CB.id]'>\[Delete\]</A>"
|
||||
author += " (<A style='color:red' href='?src=[UID()];delbyckey=[ckey(CB.ckey)]'>[ckey(CB.ckey)])</A>)"
|
||||
dat += {"<tr>
|
||||
<td>[author]</td>
|
||||
<td>[CB.title]</td>
|
||||
<td>[CB.category]</td>
|
||||
<td>[controls]</td>
|
||||
</tr>"}
|
||||
|
||||
dat += "</table><br />[pagelist]"
|
||||
|
||||
dat += "<br /><A href='?src=[UID()];switchscreen=0'>(Return to main menu)</A><BR>"
|
||||
if(5)
|
||||
dat += "<h3>Upload a New Title</h3>"
|
||||
if(!scanner)
|
||||
for(var/obj/machinery/libraryscanner/S in range(9))
|
||||
scanner = S
|
||||
break
|
||||
if(!scanner)
|
||||
dat += "<FONT color=red>No scanner found within wireless network range.</FONT><BR>"
|
||||
else if(!scanner.cache)
|
||||
dat += "<FONT color=red>No data found in scanner memory.</FONT><BR>"
|
||||
else
|
||||
|
||||
dat += {"<TT>Data marked for upload...</TT><BR>
|
||||
<TT>Title: </TT>[scanner.cache.name]<BR>"}
|
||||
if(!scanner.cache.author)
|
||||
scanner.cache.author = "Anonymous"
|
||||
|
||||
dat += {"<TT>Author: </TT><A href='?src=[UID()];uploadauthor=1'>[scanner.cache.author]</A><BR>
|
||||
<TT>Category: </TT><A href='?src=[UID()];uploadcategory=1'>[upload_category]</A><BR>
|
||||
<A href='?src=[UID()];upload=1'>\[Upload\]</A><BR>"}
|
||||
dat += "<A href='?src=[UID()];switchscreen=0'>(Return to main menu)</A><BR>"
|
||||
if(7)
|
||||
dat += "<H3>Print a Manual</H3>"
|
||||
dat += "<table>"
|
||||
|
||||
var/list/forbidden = list(
|
||||
/obj/item/book/manual
|
||||
)
|
||||
|
||||
if(!emagged)
|
||||
forbidden |= /obj/item/book/manual/nuclear
|
||||
|
||||
var/manualcount = 1
|
||||
var/obj/item/book/manual/M = null
|
||||
|
||||
for(var/manual_type in (typesof(/obj/item/book/manual) - forbidden))
|
||||
M = new manual_type()
|
||||
dat += "<tr><td><A href='?src=[UID()];manual=[manualcount]'>[M.title]</A></td></tr>"
|
||||
manualcount++
|
||||
QDEL_NULL(M)
|
||||
dat += "</table>"
|
||||
dat += "<BR><A href='?src=[UID()];switchscreen=0'>(Return to main menu)</A><BR>"
|
||||
|
||||
if(8)
|
||||
|
||||
dat += {"<h3>Accessing Forbidden Lore Vault v 1.3</h3>
|
||||
Are you absolutely sure you want to proceed? EldritchTomes Inc. takes no responsibilities for loss of sanity resulting from this action.<p>
|
||||
<A href='?src=[UID()];arccheckout=1'>Yes.</A><BR>
|
||||
<A href='?src=[UID()];switchscreen=0'>No.</A><BR>"}
|
||||
|
||||
var/datum/browser/B = new /datum/browser(user, "library", "Book Inventory Management")
|
||||
B.set_content(dat)
|
||||
B.open()
|
||||
|
||||
/obj/machinery/computer/library/checkout/emag_act(mob/user)
|
||||
if(density && !emagged)
|
||||
emagged = 1
|
||||
to_chat(user, "<span class='notice'>You override the library computer's printing restrictions.</span>")
|
||||
|
||||
/obj/machinery/computer/library/checkout/attackby(obj/item/W as obj, mob/user as mob)
|
||||
if(default_unfasten_wrench(user, W))
|
||||
power_change()
|
||||
return
|
||||
if(istype(W, /obj/item/barcodescanner))
|
||||
var/obj/item/barcodescanner/scanner = W
|
||||
scanner.computer = src
|
||||
to_chat(user, "[scanner]'s associated machine has been set to [src].")
|
||||
audible_message("[src] lets out a low, short blip.", 2)
|
||||
return 1
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/machinery/computer/library/checkout/Topic(href, href_list)
|
||||
if(..())
|
||||
usr << browse(null, "window=library")
|
||||
onclose(usr, "library")
|
||||
return 1
|
||||
|
||||
if(href_list["pagenum"])
|
||||
if(!num_pages)
|
||||
page_num = 1
|
||||
else
|
||||
var/pn = text2num(href_list["pagenum"])
|
||||
if(!isnull(pn))
|
||||
page_num = Clamp(pn, 1, num_pages)
|
||||
|
||||
if(href_list["page"])
|
||||
if(num_pages == 0)
|
||||
page_num = 1
|
||||
else
|
||||
page_num = Clamp(text2num(href_list["page"]), 1, num_pages)
|
||||
if(href_list["settitle"])
|
||||
var/newtitle = input("Enter a title to search for:") as text|null
|
||||
if(newtitle)
|
||||
query.title = sanitize(newtitle)
|
||||
else
|
||||
query.title = null
|
||||
if(href_list["setcategory"])
|
||||
var/newcategory = input("Choose a category to search for:") in (list("Any") + library_section_names)
|
||||
if(newcategory == "Any")
|
||||
query.category = null
|
||||
else if(newcategory)
|
||||
query.category = sanitize(newcategory)
|
||||
if(href_list["setauthor"])
|
||||
var/newauthor = input("Enter an author to search for:") as text|null
|
||||
if(newauthor)
|
||||
query.author = sanitize(newauthor)
|
||||
else
|
||||
query.author = null
|
||||
|
||||
if(href_list["search"])
|
||||
num_results = src.get_num_results()
|
||||
num_pages = Ceiling(num_results/LIBRARY_BOOKS_PER_PAGE)
|
||||
page_num = 1
|
||||
|
||||
screenstate = 4
|
||||
if(href_list["del"])
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
var/datum/cachedbook/target = getBookByID(href_list["del"]) // Sanitized in getBookByID
|
||||
var/ans = alert(usr, "Are you sure you wish to delete \"[target.title]\", by [target.author]? This cannot be undone.", "Library System", "Yes", "No")
|
||||
if(ans=="Yes")
|
||||
var/DBQuery/query = dbcon.NewQuery("DELETE FROM [format_table_name("library")] WHERE id=[target.id]")
|
||||
var/response = query.Execute()
|
||||
if(!response)
|
||||
to_chat(usr, query.ErrorMsg())
|
||||
return
|
||||
log_admin("LIBRARY: [key_name(usr)] has deleted \"[target.title]\", by [target.author] ([target.ckey])!")
|
||||
message_admins("[key_name_admin(usr)] has deleted \"[target.title]\", by [target.author] ([target.ckey])!")
|
||||
src.updateUsrDialog()
|
||||
return
|
||||
|
||||
if(href_list["delbyckey"])
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
var/tckey = ckey(href_list["delbyckey"])
|
||||
var/ans = alert(usr,"Are you sure you wish to delete all books by [tckey]? This cannot be undone.", "Library System", "Yes", "No")
|
||||
if(ans=="Yes")
|
||||
var/DBQuery/query = dbcon.NewQuery("DELETE FROM [format_table_name("library")] WHERE ckey='[sanitizeSQL(tckey)]'")
|
||||
var/response = query.Execute()
|
||||
if(!response)
|
||||
to_chat(usr, query.ErrorMsg())
|
||||
return
|
||||
var/affected=query.RowsAffected()
|
||||
if(affected==0)
|
||||
to_chat(usr, "<span class='danger'>Unable to find any matching rows.</span>")
|
||||
return
|
||||
log_admin("LIBRARY: [key_name(usr)] has deleted [affected] books written by [tckey]!")
|
||||
message_admins("[key_name_admin(usr)] has deleted [affected] books written by [tckey]!")
|
||||
src.updateUsrDialog()
|
||||
return
|
||||
|
||||
if(href_list["flag"])
|
||||
if(!dbcon.IsConnected())
|
||||
alert("Connection to Archive has been severed. Aborting.")
|
||||
return
|
||||
var/id = href_list["flag"]
|
||||
if(id)
|
||||
var/datum/cachedbook/B = getBookByID(id)
|
||||
if(B)
|
||||
if((input(usr, "Are you sure you want to flag [B.title] as having inappropriate content?", "Flag Book #[B.id]") in list("Yes", "No")) == "Yes")
|
||||
library_catalog.flag_book_by_id(usr, id)
|
||||
|
||||
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/storage/bible/B = new /obj/item/storage/bible(src.loc)
|
||||
if(SSticker && ( SSticker.Bible_icon_state && SSticker.Bible_item_state) )
|
||||
B.icon_state = SSticker.Bible_icon_state
|
||||
B.item_state = SSticker.Bible_item_state
|
||||
B.name = SSticker.Bible_name
|
||||
B.deity_name = SSticker.Bible_deity_name
|
||||
|
||||
bibledelay = 1
|
||||
spawn(60)
|
||||
bibledelay = 0
|
||||
|
||||
else
|
||||
visible_message("<b>[src]</b>'s monitor flashes, \"Bible printer currently unavailable, please wait a moment.\"")
|
||||
|
||||
if("7")
|
||||
screenstate = 7
|
||||
if("8")
|
||||
screenstate = 8
|
||||
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/book/b = locate(href_list["delbook"])
|
||||
inventory.Remove(b)
|
||||
if(href_list["uploadauthor"])
|
||||
var/newauthor = copytext(sanitize(input("Enter the author's name: ") as text|null),1,MAX_MESSAGE_LEN)
|
||||
if(newauthor && scanner)
|
||||
scanner.cache.author = newauthor
|
||||
if(href_list["uploadcategory"])
|
||||
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")
|
||||
establish_db_connection()
|
||||
if(!dbcon.IsConnected())
|
||||
alert("Connection to Archive has been severed. Aborting.")
|
||||
else
|
||||
var/sqltitle = sanitizeSQL(scanner.cache.name)
|
||||
var/sqlauthor = sanitizeSQL(scanner.cache.author)
|
||||
var/sqlcontent = sanitizeSQL(scanner.cache.dat)
|
||||
var/sqlcategory = sanitizeSQL(upload_category)
|
||||
var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("library")] (author, title, content, category, ckey, flagged) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory]', '[ckey(usr.key)]', 0)")
|
||||
var/response = query.Execute()
|
||||
if(!response)
|
||||
to_chat(usr, query.ErrorMsg())
|
||||
else
|
||||
log_admin("[usr.name]/[usr.key] has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] characters in length")
|
||||
message_admins("[key_name_admin(usr)] has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] characters in length")
|
||||
|
||||
if(href_list["id"])
|
||||
if(href_list["id"]=="-1")
|
||||
href_list["id"] = input("Enter your order:") as null|num
|
||||
if(!href_list["id"])
|
||||
return
|
||||
|
||||
if(!dbcon.IsConnected())
|
||||
alert("Connection to Archive has been severed. Aborting.")
|
||||
return
|
||||
|
||||
var/datum/cachedbook/newbook = getBookByID(href_list["id"]) // Sanitized in getBookByID
|
||||
if(!newbook)
|
||||
alert("No book found")
|
||||
return
|
||||
if((newbook.forbidden == 2 && !emagged) || newbook.forbidden == 1)
|
||||
alert("This book is forbidden and cannot be printed.")
|
||||
return
|
||||
|
||||
if(bibledelay)
|
||||
audible_message("<b>[src]</b>'s monitor flashes, \"Printer unavailable. Please allow a short time before attempting to print.\"")
|
||||
else
|
||||
bibledelay = 1
|
||||
spawn(60)
|
||||
bibledelay = 0
|
||||
make_external_book(newbook)
|
||||
if(href_list["manual"])
|
||||
if(!href_list["manual"]) return
|
||||
var/bookid = href_list["manual"]
|
||||
|
||||
if(!dbcon.IsConnected())
|
||||
alert("Connection to Archive has been severed. Aborting.")
|
||||
return
|
||||
|
||||
var/datum/cachedbook/newbook = getBookByID("M[bookid]")
|
||||
if(!newbook)
|
||||
alert("No book found")
|
||||
return
|
||||
if((newbook.forbidden == 2 && !emagged) || newbook.forbidden == 1)
|
||||
alert("This book is forbidden and cannot be printed.")
|
||||
return
|
||||
|
||||
if(bibledelay)
|
||||
for(var/mob/V in hearers(src))
|
||||
V.show_message("<b>[src]</b>'s monitor flashes, \"Printer unavailable. Please allow a short time before attempting to print.\"")
|
||||
else
|
||||
bibledelay = 1
|
||||
spawn(60)
|
||||
bibledelay = 0
|
||||
make_external_book(newbook)
|
||||
|
||||
src.add_fingerprint(usr)
|
||||
src.updateUsrDialog()
|
||||
return
|
||||
|
||||
/*
|
||||
* Library Scanner
|
||||
*/
|
||||
|
||||
/obj/machinery/computer/library/checkout/proc/make_external_book(var/datum/cachedbook/newbook)
|
||||
if(!newbook || !newbook.id)
|
||||
return
|
||||
var/obj/item/book/B = new newbook.path(loc)
|
||||
|
||||
if(!newbook.programmatic)
|
||||
B.name = "Book: [newbook.title]"
|
||||
B.title = newbook.title
|
||||
B.author = newbook.author
|
||||
B.dat = newbook.content
|
||||
B.icon_state = "book[rand(1,16)]"
|
||||
visible_message("[src]'s printer hums as it produces a completely bound book. How did it do that?")
|
||||
/*
|
||||
* Library Computer
|
||||
*/
|
||||
/obj/machinery/computer/library/checkout
|
||||
name = "Check-In/Out Computer"
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state = "computer"
|
||||
anchored = 1
|
||||
density = 1
|
||||
var/arcanecheckout = 0
|
||||
//var/screenstate = 0 // 0 - Main Menu, 1 - Inventory, 2 - Checked Out, 3 - Check Out a Book
|
||||
var/buffer_book
|
||||
var/buffer_mob
|
||||
var/upload_category = "Fiction"
|
||||
var/list/checkouts = list()
|
||||
var/list/inventory = list()
|
||||
var/checkoutperiod = 5 // In minutes
|
||||
var/obj/machinery/libraryscanner/scanner // Book scanner that will be used when uploading books to the Archive
|
||||
|
||||
var/bibledelay = 0 // LOL NO SPAM (1 minute delay) -- Doohl
|
||||
var/booklist
|
||||
|
||||
/obj/machinery/computer/library/checkout/attack_hand(var/mob/user as mob)
|
||||
if(..())
|
||||
return
|
||||
interact(user)
|
||||
|
||||
/obj/machinery/computer/library/checkout/interact(var/mob/user)
|
||||
if(interact_check(user))
|
||||
return
|
||||
|
||||
var/dat = ""
|
||||
switch(screenstate)
|
||||
if(0)
|
||||
// Main Menu
|
||||
|
||||
dat += {"<ol>
|
||||
<li><A href='?src=[UID()];switchscreen=1'>View General Inventory</A></li>
|
||||
<li><A href='?src=[UID()];switchscreen=2'>View Checked Out Inventory</A></li>
|
||||
<li><A href='?src=[UID()];switchscreen=3'>Check out a Book</A></li>
|
||||
<li><A href='?src=[UID()];switchscreen=4'>Connect to External Archive</A></li>
|
||||
<li><A href='?src=[UID()];switchscreen=5'>Upload New Title to Archive</A></li>
|
||||
<li><A href='?src=[UID()];switchscreen=6'>Print a Bible</A></li>
|
||||
<li><A href='?src=[UID()];switchscreen=7'>Print a Manual</A></li>"}
|
||||
if(src.emagged)
|
||||
dat += "<li><A href='?src=[UID()];switchscreen=8'>Access the Forbidden Lore Vault</A></li>"
|
||||
dat += "</ol>"
|
||||
|
||||
if(src.arcanecheckout)
|
||||
new /obj/item/tome(src.loc)
|
||||
to_chat(user, "<span class='warning'>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.</span>")
|
||||
user.visible_message("[user] stares at the blank screen for a few moments, [user.p_their()] expression frozen in fear. When [user.p_they()] finally awaken[user.p_s()] from it, [user.p_they()] look[user.p_s()] a lot older.", 2)
|
||||
src.arcanecheckout = 0
|
||||
if(1)
|
||||
// Inventory
|
||||
dat += "<h3>Inventory</h3>"
|
||||
for(var/obj/item/book/b in inventory)
|
||||
dat += "[b.name] <A href='?src=[UID()];delbook=\ref[b]'>(Delete)</A><BR>"
|
||||
dat += "<A href='?src=[UID()];switchscreen=0'>(Return to main menu)</A><BR>"
|
||||
if(2)
|
||||
// Checked Out
|
||||
dat += "<h3>Checked Out Books</h3><BR>"
|
||||
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 = "<font color=red><b>(OVERDUE)</b> [timedue]</font>"
|
||||
else
|
||||
timedue = round(timedue)
|
||||
|
||||
dat += {"\"[b.bookname]\", Checked out to: [b.mobname]<BR>--- Taken: [timetaken] minutes ago, Due: in [timedue] minutes<BR>
|
||||
<A href='?src=[UID()];checkin=\ref[b]'>(Check In)</A><BR><BR>"}
|
||||
dat += "<A href='?src=[UID()];switchscreen=0'>(Return to main menu)</A><BR>"
|
||||
if(3)
|
||||
// Check Out a Book
|
||||
|
||||
dat += {"<h3>Check Out a Book</h3><BR>
|
||||
Book: [src.buffer_book]
|
||||
<A href='?src=[UID()];editbook=1'>\[Edit\]</A><BR>
|
||||
Recipient: [src.buffer_mob]
|
||||
<A href='?src=[UID()];editmob=1'>\[Edit\]</A><BR>
|
||||
Checkout Date : [world.time/600]<BR>
|
||||
Due Date: [(world.time + checkoutperiod)/600]<BR>
|
||||
(Checkout Period: [checkoutperiod] minutes) (<A href='?src=[UID()];increasetime=1'>+</A>/<A href='?src=[UID()];decreasetime=1'>-</A>)
|
||||
<A href='?src=[UID()];checkout=1'>(Commit Entry)</A><BR>
|
||||
<A href='?src=[UID()];switchscreen=0'>(Return to main menu)</A><BR>"}
|
||||
if(4)
|
||||
dat += "<h3>External Archive</h3>"
|
||||
if(!dbcon.IsConnected())
|
||||
dat += "<font color=red><b>ERROR</b>: Unable to contact External Archive. Please contact your system administrator for assistance.</font>"
|
||||
else
|
||||
num_results = src.get_num_results()
|
||||
num_pages = Ceiling(num_results/LIBRARY_BOOKS_PER_PAGE)
|
||||
dat += {"<ul>
|
||||
<li><A href='?src=[UID()];id=-1'>(Order book by SS<sup>13</sup>BN)</A></li>
|
||||
</ul>"}
|
||||
var/pagelist = get_pagelist()
|
||||
|
||||
dat += {"<h2>Search Settings</h2><br />
|
||||
<A href='?src=[UID()];settitle=1'>Filter by Title: [query.title]</A><br />
|
||||
<A href='?src=[UID()];setcategory=1'>Filter by Category: [query.category]</A><br />
|
||||
<A href='?src=[UID()];setauthor=1'>Filter by Author: [query.author]</A><br />
|
||||
<A href='?src=[UID()];search=1'>\[Start Search\]</A><br />"}
|
||||
dat += pagelist
|
||||
|
||||
dat += {"<form name='pagenum' action='?src=[UID()]' method='get'>
|
||||
<input type='hidden' name='src' value='[UID()]'>
|
||||
<input type='text' name='pagenum' value='[page_num]' maxlength="5" size="5">
|
||||
<input type='submit' value='Jump To Page'>
|
||||
</form>"}
|
||||
|
||||
dat += {"<table border=\"0\">
|
||||
<tr>
|
||||
<td>Author</td>
|
||||
<td>Title</td>
|
||||
<td>Category</td>
|
||||
<td>Controls</td>
|
||||
</tr>"}
|
||||
|
||||
for(var/datum/cachedbook/CB in get_page(page_num))
|
||||
var/author = CB.author
|
||||
var/controls = "<A href='?src=[UID()];id=[CB.id]'>\[Order\]</A>"
|
||||
controls += {" <A href="?src=[UID()];flag=[CB.id]">\[Flag[CB.flagged ? "ged" : ""]\]</A>"}
|
||||
if(check_rights(R_ADMIN, 0, user = user))
|
||||
controls += " <A style='color:red' href='?src=[UID()];del=[CB.id]'>\[Delete\]</A>"
|
||||
author += " (<A style='color:red' href='?src=[UID()];delbyckey=[ckey(CB.ckey)]'>[ckey(CB.ckey)])</A>)"
|
||||
dat += {"<tr>
|
||||
<td>[author]</td>
|
||||
<td>[CB.title]</td>
|
||||
<td>[CB.category]</td>
|
||||
<td>[controls]</td>
|
||||
</tr>"}
|
||||
|
||||
dat += "</table><br />[pagelist]"
|
||||
|
||||
dat += "<br /><A href='?src=[UID()];switchscreen=0'>(Return to main menu)</A><BR>"
|
||||
if(5)
|
||||
dat += "<h3>Upload a New Title</h3>"
|
||||
if(!scanner)
|
||||
for(var/obj/machinery/libraryscanner/S in range(9))
|
||||
scanner = S
|
||||
break
|
||||
if(!scanner)
|
||||
dat += "<FONT color=red>No scanner found within wireless network range.</FONT><BR>"
|
||||
else if(!scanner.cache)
|
||||
dat += "<FONT color=red>No data found in scanner memory.</FONT><BR>"
|
||||
else
|
||||
|
||||
dat += {"<TT>Data marked for upload...</TT><BR>
|
||||
<TT>Title: </TT>[scanner.cache.name]<BR>"}
|
||||
if(!scanner.cache.author)
|
||||
scanner.cache.author = "Anonymous"
|
||||
|
||||
dat += {"<TT>Author: </TT><A href='?src=[UID()];uploadauthor=1'>[scanner.cache.author]</A><BR>
|
||||
<TT>Category: </TT><A href='?src=[UID()];uploadcategory=1'>[upload_category]</A><BR>
|
||||
<A href='?src=[UID()];upload=1'>\[Upload\]</A><BR>"}
|
||||
dat += "<A href='?src=[UID()];switchscreen=0'>(Return to main menu)</A><BR>"
|
||||
if(7)
|
||||
dat += "<H3>Print a Manual</H3>"
|
||||
dat += "<table>"
|
||||
|
||||
var/list/forbidden = list(
|
||||
/obj/item/book/manual
|
||||
)
|
||||
|
||||
if(!emagged)
|
||||
forbidden |= /obj/item/book/manual/nuclear
|
||||
|
||||
var/manualcount = 1
|
||||
var/obj/item/book/manual/M = null
|
||||
|
||||
for(var/manual_type in (typesof(/obj/item/book/manual) - forbidden))
|
||||
M = new manual_type()
|
||||
dat += "<tr><td><A href='?src=[UID()];manual=[manualcount]'>[M.title]</A></td></tr>"
|
||||
manualcount++
|
||||
QDEL_NULL(M)
|
||||
dat += "</table>"
|
||||
dat += "<BR><A href='?src=[UID()];switchscreen=0'>(Return to main menu)</A><BR>"
|
||||
|
||||
if(8)
|
||||
|
||||
dat += {"<h3>Accessing Forbidden Lore Vault v 1.3</h3>
|
||||
Are you absolutely sure you want to proceed? EldritchTomes Inc. takes no responsibilities for loss of sanity resulting from this action.<p>
|
||||
<A href='?src=[UID()];arccheckout=1'>Yes.</A><BR>
|
||||
<A href='?src=[UID()];switchscreen=0'>No.</A><BR>"}
|
||||
|
||||
var/datum/browser/B = new /datum/browser(user, "library", "Book Inventory Management")
|
||||
B.set_content(dat)
|
||||
B.open()
|
||||
|
||||
/obj/machinery/computer/library/checkout/emag_act(mob/user)
|
||||
if(density && !emagged)
|
||||
emagged = 1
|
||||
to_chat(user, "<span class='notice'>You override the library computer's printing restrictions.</span>")
|
||||
|
||||
/obj/machinery/computer/library/checkout/attackby(obj/item/W as obj, mob/user as mob)
|
||||
if(default_unfasten_wrench(user, W))
|
||||
power_change()
|
||||
return
|
||||
if(istype(W, /obj/item/barcodescanner))
|
||||
var/obj/item/barcodescanner/scanner = W
|
||||
scanner.computer = src
|
||||
to_chat(user, "[scanner]'s associated machine has been set to [src].")
|
||||
audible_message("[src] lets out a low, short blip.", 2)
|
||||
return 1
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/machinery/computer/library/checkout/Topic(href, href_list)
|
||||
if(..())
|
||||
usr << browse(null, "window=library")
|
||||
onclose(usr, "library")
|
||||
return 1
|
||||
|
||||
if(href_list["pagenum"])
|
||||
if(!num_pages)
|
||||
page_num = 1
|
||||
else
|
||||
var/pn = text2num(href_list["pagenum"])
|
||||
if(!isnull(pn))
|
||||
page_num = Clamp(pn, 1, num_pages)
|
||||
|
||||
if(href_list["page"])
|
||||
if(num_pages == 0)
|
||||
page_num = 1
|
||||
else
|
||||
page_num = Clamp(text2num(href_list["page"]), 1, num_pages)
|
||||
if(href_list["settitle"])
|
||||
var/newtitle = input("Enter a title to search for:") as text|null
|
||||
if(newtitle)
|
||||
query.title = sanitize(newtitle)
|
||||
else
|
||||
query.title = null
|
||||
if(href_list["setcategory"])
|
||||
var/newcategory = input("Choose a category to search for:") in (list("Any") + library_section_names)
|
||||
if(newcategory == "Any")
|
||||
query.category = null
|
||||
else if(newcategory)
|
||||
query.category = sanitize(newcategory)
|
||||
if(href_list["setauthor"])
|
||||
var/newauthor = input("Enter an author to search for:") as text|null
|
||||
if(newauthor)
|
||||
query.author = sanitize(newauthor)
|
||||
else
|
||||
query.author = null
|
||||
|
||||
if(href_list["search"])
|
||||
num_results = src.get_num_results()
|
||||
num_pages = Ceiling(num_results/LIBRARY_BOOKS_PER_PAGE)
|
||||
page_num = 1
|
||||
|
||||
screenstate = 4
|
||||
if(href_list["del"])
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
var/datum/cachedbook/target = getBookByID(href_list["del"]) // Sanitized in getBookByID
|
||||
var/ans = alert(usr, "Are you sure you wish to delete \"[target.title]\", by [target.author]? This cannot be undone.", "Library System", "Yes", "No")
|
||||
if(ans=="Yes")
|
||||
var/DBQuery/query = dbcon.NewQuery("DELETE FROM [format_table_name("library")] WHERE id=[target.id]")
|
||||
var/response = query.Execute()
|
||||
if(!response)
|
||||
to_chat(usr, query.ErrorMsg())
|
||||
return
|
||||
log_admin("LIBRARY: [key_name(usr)] has deleted \"[target.title]\", by [target.author] ([target.ckey])!")
|
||||
message_admins("[key_name_admin(usr)] has deleted \"[target.title]\", by [target.author] ([target.ckey])!")
|
||||
src.updateUsrDialog()
|
||||
return
|
||||
|
||||
if(href_list["delbyckey"])
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
var/tckey = ckey(href_list["delbyckey"])
|
||||
var/ans = alert(usr,"Are you sure you wish to delete all books by [tckey]? This cannot be undone.", "Library System", "Yes", "No")
|
||||
if(ans=="Yes")
|
||||
var/DBQuery/query = dbcon.NewQuery("DELETE FROM [format_table_name("library")] WHERE ckey='[sanitizeSQL(tckey)]'")
|
||||
var/response = query.Execute()
|
||||
if(!response)
|
||||
to_chat(usr, query.ErrorMsg())
|
||||
return
|
||||
var/affected=query.RowsAffected()
|
||||
if(affected==0)
|
||||
to_chat(usr, "<span class='danger'>Unable to find any matching rows.</span>")
|
||||
return
|
||||
log_admin("LIBRARY: [key_name(usr)] has deleted [affected] books written by [tckey]!")
|
||||
message_admins("[key_name_admin(usr)] has deleted [affected] books written by [tckey]!")
|
||||
src.updateUsrDialog()
|
||||
return
|
||||
|
||||
if(href_list["flag"])
|
||||
if(!dbcon.IsConnected())
|
||||
alert("Connection to Archive has been severed. Aborting.")
|
||||
return
|
||||
var/id = href_list["flag"]
|
||||
if(id)
|
||||
var/datum/cachedbook/B = getBookByID(id)
|
||||
if(B)
|
||||
if((input(usr, "Are you sure you want to flag [B.title] as having inappropriate content?", "Flag Book #[B.id]") in list("Yes", "No")) == "Yes")
|
||||
library_catalog.flag_book_by_id(usr, id)
|
||||
|
||||
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/storage/bible/B = new /obj/item/storage/bible(src.loc)
|
||||
if(SSticker && ( SSticker.Bible_icon_state && SSticker.Bible_item_state) )
|
||||
B.icon_state = SSticker.Bible_icon_state
|
||||
B.item_state = SSticker.Bible_item_state
|
||||
B.name = SSticker.Bible_name
|
||||
B.deity_name = SSticker.Bible_deity_name
|
||||
|
||||
bibledelay = 1
|
||||
spawn(60)
|
||||
bibledelay = 0
|
||||
|
||||
else
|
||||
visible_message("<b>[src]</b>'s monitor flashes, \"Bible printer currently unavailable, please wait a moment.\"")
|
||||
|
||||
if("7")
|
||||
screenstate = 7
|
||||
if("8")
|
||||
screenstate = 8
|
||||
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/book/b = locate(href_list["delbook"])
|
||||
inventory.Remove(b)
|
||||
if(href_list["uploadauthor"])
|
||||
var/newauthor = copytext(sanitize(input("Enter the author's name: ") as text|null),1,MAX_MESSAGE_LEN)
|
||||
if(newauthor && scanner)
|
||||
scanner.cache.author = newauthor
|
||||
if(href_list["uploadcategory"])
|
||||
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")
|
||||
establish_db_connection()
|
||||
if(!dbcon.IsConnected())
|
||||
alert("Connection to Archive has been severed. Aborting.")
|
||||
else
|
||||
var/sqltitle = sanitizeSQL(scanner.cache.name)
|
||||
var/sqlauthor = sanitizeSQL(scanner.cache.author)
|
||||
var/sqlcontent = sanitizeSQL(scanner.cache.dat)
|
||||
var/sqlcategory = sanitizeSQL(upload_category)
|
||||
var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("library")] (author, title, content, category, ckey, flagged) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory]', '[ckey(usr.key)]', 0)")
|
||||
var/response = query.Execute()
|
||||
if(!response)
|
||||
to_chat(usr, query.ErrorMsg())
|
||||
else
|
||||
log_admin("[usr.name]/[usr.key] has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] characters in length")
|
||||
message_admins("[key_name_admin(usr)] has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] characters in length")
|
||||
|
||||
if(href_list["id"])
|
||||
if(href_list["id"]=="-1")
|
||||
href_list["id"] = input("Enter your order:") as null|num
|
||||
if(!href_list["id"])
|
||||
return
|
||||
|
||||
if(!dbcon.IsConnected())
|
||||
alert("Connection to Archive has been severed. Aborting.")
|
||||
return
|
||||
|
||||
var/datum/cachedbook/newbook = getBookByID(href_list["id"]) // Sanitized in getBookByID
|
||||
if(!newbook)
|
||||
alert("No book found")
|
||||
return
|
||||
if((newbook.forbidden == 2 && !emagged) || newbook.forbidden == 1)
|
||||
alert("This book is forbidden and cannot be printed.")
|
||||
return
|
||||
|
||||
if(bibledelay)
|
||||
audible_message("<b>[src]</b>'s monitor flashes, \"Printer unavailable. Please allow a short time before attempting to print.\"")
|
||||
else
|
||||
bibledelay = 1
|
||||
spawn(60)
|
||||
bibledelay = 0
|
||||
make_external_book(newbook)
|
||||
if(href_list["manual"])
|
||||
if(!href_list["manual"]) return
|
||||
var/bookid = href_list["manual"]
|
||||
|
||||
if(!dbcon.IsConnected())
|
||||
alert("Connection to Archive has been severed. Aborting.")
|
||||
return
|
||||
|
||||
var/datum/cachedbook/newbook = getBookByID("M[bookid]")
|
||||
if(!newbook)
|
||||
alert("No book found")
|
||||
return
|
||||
if((newbook.forbidden == 2 && !emagged) || newbook.forbidden == 1)
|
||||
alert("This book is forbidden and cannot be printed.")
|
||||
return
|
||||
|
||||
if(bibledelay)
|
||||
for(var/mob/V in hearers(src))
|
||||
V.show_message("<b>[src]</b>'s monitor flashes, \"Printer unavailable. Please allow a short time before attempting to print.\"")
|
||||
else
|
||||
bibledelay = 1
|
||||
spawn(60)
|
||||
bibledelay = 0
|
||||
make_external_book(newbook)
|
||||
|
||||
src.add_fingerprint(usr)
|
||||
src.updateUsrDialog()
|
||||
return
|
||||
|
||||
/*
|
||||
* Library Scanner
|
||||
*/
|
||||
|
||||
/obj/machinery/computer/library/checkout/proc/make_external_book(var/datum/cachedbook/newbook)
|
||||
if(!newbook || !newbook.id)
|
||||
return
|
||||
var/obj/item/book/B = new newbook.path(loc)
|
||||
|
||||
if(!newbook.programmatic)
|
||||
B.name = "Book: [newbook.title]"
|
||||
B.title = newbook.title
|
||||
B.author = newbook.author
|
||||
B.dat = newbook.content
|
||||
B.icon_state = "book[rand(1,16)]"
|
||||
visible_message("[src]'s printer hums as it produces a completely bound book. How did it do that?")
|
||||
|
||||
@@ -1,128 +1,128 @@
|
||||
/obj/machinery/computer/library/public
|
||||
name = "visitor computer"
|
||||
|
||||
/obj/machinery/computer/library/public/attack_hand(var/mob/user as mob)
|
||||
if(..())
|
||||
return
|
||||
interact(user)
|
||||
|
||||
/obj/machinery/computer/library/public/attackby(obj/item/W as obj, mob/user as mob)
|
||||
if(default_unfasten_wrench(user, W))
|
||||
power_change()
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/machinery/computer/library/public/interact(var/mob/user)
|
||||
if(interact_check(user))
|
||||
return
|
||||
|
||||
var/dat = ""
|
||||
switch(screenstate)
|
||||
if(0)
|
||||
|
||||
dat += {"<h2>Search Settings</h2><br />
|
||||
<A href='?src=[UID()];settitle=1'>Filter by Title: [query.title]</A><br />
|
||||
<A href='?src=[UID()];setcategory=1'>Filter by Category: [query.category]</A><br />
|
||||
<A href='?src=[UID()];setauthor=1'>Filter by Author: [query.author]</A><br />
|
||||
<A href='?src=[UID()];search=1'>\[Start Search\]</A><br />"}
|
||||
if(1)
|
||||
establish_db_connection()
|
||||
if(!dbcon.IsConnected())
|
||||
dat += "<font color=red><b>ERROR</b>: Unable to contact External Archive. Please contact your system administrator for assistance.</font><br />"
|
||||
else if(num_results == 0)
|
||||
dat += "<em>No results found.</em>"
|
||||
else
|
||||
var/pagelist = get_pagelist()
|
||||
|
||||
dat += pagelist
|
||||
dat += {"<form name='pagenum' action='?src=[UID()]' method='get'>
|
||||
<input type='hidden' name='src' value='[UID()]'>
|
||||
<input type='text' name='pagenum' value='[page_num]' maxlength="5" size="5">
|
||||
<input type='submit' value='Jump To Page'>
|
||||
</form>"}
|
||||
dat += {"<table border=\"0\">
|
||||
<tr>
|
||||
<td>Author</td>
|
||||
<td>Title</td>
|
||||
<td>Category</td>
|
||||
<td>SS<sup>13</sup>BN</td>
|
||||
<td>Controls</td>
|
||||
</tr>"}
|
||||
for(var/datum/cachedbook/CB in get_page(page_num))
|
||||
dat += {"<tr>
|
||||
<td>[CB.author]</td>
|
||||
<td>[CB.title]</td>
|
||||
<td>[CB.category]</td>
|
||||
<td>[CB.id]</td>
|
||||
<td><A href="?src=[UID()];flag=[CB.id]">\[Flag[CB.flagged ? "ged" : ""]\]</A></td>
|
||||
</tr>"}
|
||||
|
||||
dat += "</table><br />[pagelist]"
|
||||
dat += "<A href='?src=[UID()];back=1'>\[Go Back\]</A><br />"
|
||||
var/datum/browser/B = new /datum/browser(user, "library", "Library Visitor")
|
||||
B.set_content(dat)
|
||||
B.open()
|
||||
|
||||
/obj/machinery/computer/library/public/Topic(href, href_list)
|
||||
if(..())
|
||||
usr << browse(null, "window=publiclibrary")
|
||||
onclose(usr, "publiclibrary")
|
||||
return
|
||||
|
||||
if(href_list["pagenum"])
|
||||
if(!num_pages)
|
||||
page_num = 1
|
||||
else
|
||||
var/pn = text2num(href_list["pagenum"])
|
||||
if(!isnull(pn))
|
||||
page_num = Clamp(pn, 1, num_pages)
|
||||
|
||||
if(href_list["settitle"])
|
||||
var/newtitle = input("Enter a title to search for:") as text|null
|
||||
if(newtitle)
|
||||
query.title = sanitize(newtitle)
|
||||
else
|
||||
query.title = null
|
||||
if(href_list["setcategory"])
|
||||
var/newcategory = input("Choose a category to search for:") in (list("Any") + library_section_names)
|
||||
if(newcategory == "Any")
|
||||
query.category = null
|
||||
else if(newcategory)
|
||||
query.category = sanitize(newcategory)
|
||||
if(href_list["setauthor"])
|
||||
var/newauthor = input("Enter an author to search for:") as text|null
|
||||
if(newauthor)
|
||||
query.author = sanitize(newauthor)
|
||||
else
|
||||
query.author = null
|
||||
|
||||
if(href_list["page"])
|
||||
if(num_pages == 0)
|
||||
page_num = 1
|
||||
else
|
||||
page_num = Clamp(text2num(href_list["page"]), 1, num_pages)
|
||||
|
||||
if(href_list["search"])
|
||||
num_results = src.get_num_results()
|
||||
num_pages = Ceiling(num_results/LIBRARY_BOOKS_PER_PAGE)
|
||||
page_num = 1
|
||||
|
||||
screenstate = 1
|
||||
|
||||
if(href_list["back"])
|
||||
screenstate = 0
|
||||
|
||||
if(href_list["flag"])
|
||||
if(!dbcon.IsConnected())
|
||||
alert("Connection to Archive has been severed. Aborting.")
|
||||
return
|
||||
var/id = href_list["flag"]
|
||||
if(id)
|
||||
var/datum/cachedbook/B = getBookByID(id)
|
||||
if(B)
|
||||
if((input(usr, "Are you sure you want to flag [B.title] as having inappropriate content?", "Flag Book #[B.id]") in list("Yes", "No")) == "Yes")
|
||||
library_catalog.flag_book_by_id(usr, id)
|
||||
|
||||
add_fingerprint(usr)
|
||||
updateUsrDialog()
|
||||
return
|
||||
/obj/machinery/computer/library/public
|
||||
name = "visitor computer"
|
||||
|
||||
/obj/machinery/computer/library/public/attack_hand(var/mob/user as mob)
|
||||
if(..())
|
||||
return
|
||||
interact(user)
|
||||
|
||||
/obj/machinery/computer/library/public/attackby(obj/item/W as obj, mob/user as mob)
|
||||
if(default_unfasten_wrench(user, W))
|
||||
power_change()
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/machinery/computer/library/public/interact(var/mob/user)
|
||||
if(interact_check(user))
|
||||
return
|
||||
|
||||
var/dat = ""
|
||||
switch(screenstate)
|
||||
if(0)
|
||||
|
||||
dat += {"<h2>Search Settings</h2><br />
|
||||
<A href='?src=[UID()];settitle=1'>Filter by Title: [query.title]</A><br />
|
||||
<A href='?src=[UID()];setcategory=1'>Filter by Category: [query.category]</A><br />
|
||||
<A href='?src=[UID()];setauthor=1'>Filter by Author: [query.author]</A><br />
|
||||
<A href='?src=[UID()];search=1'>\[Start Search\]</A><br />"}
|
||||
if(1)
|
||||
establish_db_connection()
|
||||
if(!dbcon.IsConnected())
|
||||
dat += "<font color=red><b>ERROR</b>: Unable to contact External Archive. Please contact your system administrator for assistance.</font><br />"
|
||||
else if(num_results == 0)
|
||||
dat += "<em>No results found.</em>"
|
||||
else
|
||||
var/pagelist = get_pagelist()
|
||||
|
||||
dat += pagelist
|
||||
dat += {"<form name='pagenum' action='?src=[UID()]' method='get'>
|
||||
<input type='hidden' name='src' value='[UID()]'>
|
||||
<input type='text' name='pagenum' value='[page_num]' maxlength="5" size="5">
|
||||
<input type='submit' value='Jump To Page'>
|
||||
</form>"}
|
||||
dat += {"<table border=\"0\">
|
||||
<tr>
|
||||
<td>Author</td>
|
||||
<td>Title</td>
|
||||
<td>Category</td>
|
||||
<td>SS<sup>13</sup>BN</td>
|
||||
<td>Controls</td>
|
||||
</tr>"}
|
||||
for(var/datum/cachedbook/CB in get_page(page_num))
|
||||
dat += {"<tr>
|
||||
<td>[CB.author]</td>
|
||||
<td>[CB.title]</td>
|
||||
<td>[CB.category]</td>
|
||||
<td>[CB.id]</td>
|
||||
<td><A href="?src=[UID()];flag=[CB.id]">\[Flag[CB.flagged ? "ged" : ""]\]</A></td>
|
||||
</tr>"}
|
||||
|
||||
dat += "</table><br />[pagelist]"
|
||||
dat += "<A href='?src=[UID()];back=1'>\[Go Back\]</A><br />"
|
||||
var/datum/browser/B = new /datum/browser(user, "library", "Library Visitor")
|
||||
B.set_content(dat)
|
||||
B.open()
|
||||
|
||||
/obj/machinery/computer/library/public/Topic(href, href_list)
|
||||
if(..())
|
||||
usr << browse(null, "window=publiclibrary")
|
||||
onclose(usr, "publiclibrary")
|
||||
return
|
||||
|
||||
if(href_list["pagenum"])
|
||||
if(!num_pages)
|
||||
page_num = 1
|
||||
else
|
||||
var/pn = text2num(href_list["pagenum"])
|
||||
if(!isnull(pn))
|
||||
page_num = Clamp(pn, 1, num_pages)
|
||||
|
||||
if(href_list["settitle"])
|
||||
var/newtitle = input("Enter a title to search for:") as text|null
|
||||
if(newtitle)
|
||||
query.title = sanitize(newtitle)
|
||||
else
|
||||
query.title = null
|
||||
if(href_list["setcategory"])
|
||||
var/newcategory = input("Choose a category to search for:") in (list("Any") + library_section_names)
|
||||
if(newcategory == "Any")
|
||||
query.category = null
|
||||
else if(newcategory)
|
||||
query.category = sanitize(newcategory)
|
||||
if(href_list["setauthor"])
|
||||
var/newauthor = input("Enter an author to search for:") as text|null
|
||||
if(newauthor)
|
||||
query.author = sanitize(newauthor)
|
||||
else
|
||||
query.author = null
|
||||
|
||||
if(href_list["page"])
|
||||
if(num_pages == 0)
|
||||
page_num = 1
|
||||
else
|
||||
page_num = Clamp(text2num(href_list["page"]), 1, num_pages)
|
||||
|
||||
if(href_list["search"])
|
||||
num_results = src.get_num_results()
|
||||
num_pages = Ceiling(num_results/LIBRARY_BOOKS_PER_PAGE)
|
||||
page_num = 1
|
||||
|
||||
screenstate = 1
|
||||
|
||||
if(href_list["back"])
|
||||
screenstate = 0
|
||||
|
||||
if(href_list["flag"])
|
||||
if(!dbcon.IsConnected())
|
||||
alert("Connection to Archive has been severed. Aborting.")
|
||||
return
|
||||
var/id = href_list["flag"]
|
||||
if(id)
|
||||
var/datum/cachedbook/B = getBookByID(id)
|
||||
if(B)
|
||||
if((input(usr, "Are you sure you want to flag [B.title] as having inappropriate content?", "Flag Book #[B.id]") in list("Yes", "No")) == "Yes")
|
||||
library_catalog.flag_book_by_id(usr, id)
|
||||
|
||||
add_fingerprint(usr)
|
||||
updateUsrDialog()
|
||||
return
|
||||
|
||||
+311
-311
@@ -1,311 +1,311 @@
|
||||
/* Library Items
|
||||
*
|
||||
* Contains:
|
||||
* Bookcase
|
||||
* Book
|
||||
* Barcode Scanner
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Bookcase
|
||||
*/
|
||||
|
||||
/obj/structure/bookcase
|
||||
name = "bookcase"
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state = "book-0"
|
||||
anchored = 1
|
||||
density = 1
|
||||
opacity = 1
|
||||
resistance_flags = FLAMMABLE
|
||||
max_integrity = 200
|
||||
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 0)
|
||||
var/tmp/busy = 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()
|
||||
..()
|
||||
for(var/obj/item/I in loc)
|
||||
if(is_type_in_list(I, allowed_books))
|
||||
I.forceMove(src)
|
||||
update_icon()
|
||||
|
||||
/obj/structure/bookcase/attackby(obj/item/O as obj, mob/user as mob, params)
|
||||
if(busy) //So that you can't mess with it while deconstructing
|
||||
return 1
|
||||
if(is_type_in_list(O, allowed_books))
|
||||
if(!user.drop_item())
|
||||
return
|
||||
O.forceMove(src)
|
||||
update_icon()
|
||||
return 1
|
||||
else 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(istype(T, /obj/item/book) || istype(T, /obj/item/spellbook) || istype(T, /obj/item/tome) || istype(T, /obj/item/storage/bible))
|
||||
B.remove_from_storage(T, src)
|
||||
to_chat(user, "<span class='notice'>You empty [O] into [src].</span>")
|
||||
update_icon()
|
||||
return 1
|
||||
else if(istype(O, /obj/item/wrench))
|
||||
user.visible_message("<span class='warning'>[user] starts disassembling \the [src].</span>", \
|
||||
"<span class='notice'>You start disassembling \the [src].</span>")
|
||||
playsound(get_turf(src), O.usesound, 50, 1)
|
||||
busy = 1
|
||||
|
||||
if(do_after(user, 50 * O.toolspeed, target = src))
|
||||
playsound(get_turf(src), O.usesound, 75, 1)
|
||||
user.visible_message("<span class='warning'>[user] disassembles \the [src].</span>", \
|
||||
"<span class='notice'>You disassemble \the [src].</span>")
|
||||
busy = 0
|
||||
density = 0
|
||||
deconstruct(TRUE)
|
||||
else
|
||||
busy = 0
|
||||
return 1
|
||||
else if(istype(O, /obj/item/pen))
|
||||
var/newname = stripped_input(user, "What would you like to title this [name]?")
|
||||
if(newname)
|
||||
name = ("bookcase ([sanitize(newname)])")
|
||||
return 1
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/structure/bookcase/attack_hand(var/mob/user as mob)
|
||||
if(contents.len)
|
||||
var/obj/item/book/choice = input("Which book would you like to remove from [src]?") as null|anything in contents
|
||||
if(choice)
|
||||
if(user.incapacitated() || user.lying || !Adjacent(user))
|
||||
return
|
||||
if(!user.get_active_hand())
|
||||
user.put_in_hands(choice)
|
||||
else
|
||||
choice.forceMove(get_turf(src))
|
||||
update_icon()
|
||||
|
||||
/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))
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/bookcase/update_icon()
|
||||
if(contents.len < 5)
|
||||
icon_state = "book-[contents.len]"
|
||||
else
|
||||
icon_state = "book-5"
|
||||
|
||||
|
||||
/obj/structure/bookcase/manuals/medical
|
||||
name = "Medical Manuals bookcase"
|
||||
|
||||
New()
|
||||
..()
|
||||
new /obj/item/book/manual/medical_cloning(src)
|
||||
update_icon()
|
||||
|
||||
|
||||
/obj/structure/bookcase/manuals/engineering
|
||||
name = "Engineering Manuals bookcase"
|
||||
|
||||
New()
|
||||
..()
|
||||
new /obj/item/book/manual/engineering_construction(src)
|
||||
new /obj/item/book/manual/engineering_particle_accelerator(src)
|
||||
new /obj/item/book/manual/engineering_hacking(src)
|
||||
new /obj/item/book/manual/engineering_guide(src)
|
||||
new /obj/item/book/manual/engineering_singularity_safety(src)
|
||||
new /obj/item/book/manual/robotics_cyborgs(src)
|
||||
update_icon()
|
||||
|
||||
/obj/structure/bookcase/manuals/research_and_development
|
||||
name = "R&D Manuals bookcase"
|
||||
|
||||
New()
|
||||
..()
|
||||
new /obj/item/book/manual/research_and_development(src)
|
||||
update_icon()
|
||||
|
||||
|
||||
/*
|
||||
* Book
|
||||
*/
|
||||
/obj/item/book
|
||||
name = "book"
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state ="book"
|
||||
throw_speed = 1
|
||||
throw_range = 5
|
||||
force = 2
|
||||
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 = list("bashed", "whacked")
|
||||
resistance_flags = FLAMMABLE
|
||||
|
||||
var/dat // Actual page content
|
||||
var/due_date = 0 // Game time in 1/10th seconds
|
||||
var/author // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned
|
||||
var/unique = 0 // 0 - Normal book, 1 - Should not be treated as normal book, unable to be copied, unable to be modified
|
||||
var/title // The real name of the book.
|
||||
var/carved = 0 // Has the book been hollowed out for use as a secret storage item?
|
||||
var/forbidden = 0 // Prevent ordering of this book. (0=no, 1=yes, 2=emag only)
|
||||
var/obj/item/store // What's in the book?
|
||||
|
||||
/obj/item/book/attack_self(var/mob/user as mob)
|
||||
if(carved)
|
||||
if(store)
|
||||
to_chat(user, "<span class='notice'>[store] falls out of [title]!</span>")
|
||||
store.forceMove(get_turf(loc))
|
||||
store = null
|
||||
return
|
||||
else
|
||||
to_chat(user, "<span class='notice'>The pages of [title] have been cut out!</span>")
|
||||
return
|
||||
if(src.dat)
|
||||
user << browse("<TT><I>Penned by [author].</I></TT> <BR>" + "[dat]", "window=book")
|
||||
if(!isobserver(user))
|
||||
user.visible_message("[user] opens a book titled \"[title]\" and begins reading intently.")
|
||||
onclose(user, "book")
|
||||
else
|
||||
to_chat(user, "This book is completely blank!")
|
||||
|
||||
/obj/item/book/attackby(obj/item/W as obj, mob/user as mob, params)
|
||||
if(carved)
|
||||
if(!store)
|
||||
if(W.w_class < WEIGHT_CLASS_NORMAL)
|
||||
user.drop_item()
|
||||
W.forceMove(src)
|
||||
store = W
|
||||
to_chat(user, "<span class='notice'>You put [W] in [title].</span>")
|
||||
return 1
|
||||
else
|
||||
to_chat(user, "<span class='notice'>[W] won't fit in [title].</span>")
|
||||
return 1
|
||||
else
|
||||
to_chat(user, "<span class='notice'>There's already something in [title]!</span>")
|
||||
return 1
|
||||
if(istype(W, /obj/item/pen))
|
||||
if(unique)
|
||||
to_chat(user, "These pages don't seem to take the ink well. Looks like you can't modify it.")
|
||||
return 1
|
||||
var/choice = input("What would you like to change?") in list("Title", "Contents", "Author", "Cancel")
|
||||
switch(choice)
|
||||
if("Title")
|
||||
var/newtitle = reject_bad_text(stripped_input(usr, "Write a new title:"))
|
||||
if(!newtitle)
|
||||
to_chat(usr, "The title is invalid.")
|
||||
return 1
|
||||
else
|
||||
src.name = newtitle
|
||||
src.title = newtitle
|
||||
if("Contents")
|
||||
var/content = strip_html(input(usr, "Write your book's contents (HTML NOT allowed):") as message|null, MAX_BOOK_MESSAGE_LEN)
|
||||
if(!content)
|
||||
to_chat(usr, "The content is invalid.")
|
||||
return 1
|
||||
else
|
||||
src.dat += content
|
||||
if("Author")
|
||||
var/newauthor = stripped_input(usr, "Write the author's name:")
|
||||
if(!newauthor)
|
||||
to_chat(usr, "The name is invalid.")
|
||||
return 1
|
||||
else
|
||||
src.author = newauthor
|
||||
return 1
|
||||
else if(istype(W, /obj/item/barcodescanner))
|
||||
var/obj/item/barcodescanner/scanner = W
|
||||
if(!scanner.computer)
|
||||
to_chat(user, "[W]'s screen flashes: 'No associated computer found!'")
|
||||
else
|
||||
switch(scanner.mode)
|
||||
if(0)
|
||||
scanner.book = src
|
||||
to_chat(user, "[W]'s screen flashes: 'Book stored in buffer.'")
|
||||
if(1)
|
||||
scanner.book = src
|
||||
scanner.computer.buffer_book = src.name
|
||||
to_chat(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)
|
||||
to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Book has been checked in.'")
|
||||
return 1
|
||||
to_chat(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/book in scanner.computer.inventory)
|
||||
if(book == src)
|
||||
to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Title already present in inventory, aborting to avoid duplicate entry.'")
|
||||
return 1
|
||||
scanner.computer.inventory.Add(src)
|
||||
to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Title added to general inventory.'")
|
||||
return 1
|
||||
else if(istype(W, /obj/item/kitchen/knife) && !carved)
|
||||
carve_book(user, W)
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/book/wirecutter_act(mob/user, obj/item/I)
|
||||
return carve_book(user, I)
|
||||
|
||||
/obj/item/book/attack(mob/M, mob/living/user)
|
||||
if(user.a_intent == INTENT_HELP)
|
||||
force = 0
|
||||
attack_verb = list("educated")
|
||||
else
|
||||
force = initial(force)
|
||||
attack_verb = list("bashed", "whacked")
|
||||
..()
|
||||
|
||||
/obj/item/book/proc/carve_book(mob/user, obj/item/I)
|
||||
if(!I.sharp && I.tool_behaviour != TOOL_WIRECUTTER) //Only sharp and wirecutter things can carve books
|
||||
to_chat(user, "<span class='warning>You can't carve [title] using that!</span>")
|
||||
return
|
||||
if(carved)
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You begin to carve out [title].</span>")
|
||||
if(I.use_tool(src, user, 30, volume = I.tool_volume))
|
||||
to_chat(user, "<span class='notice'>You carve out the pages from [title]! You didn't want to read it anyway.</span>")
|
||||
carved = TRUE
|
||||
return TRUE
|
||||
/*
|
||||
* Barcode Scanner
|
||||
*/
|
||||
/obj/item/barcodescanner
|
||||
name = "barcode scanner"
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state ="scanner"
|
||||
throw_speed = 1
|
||||
throw_range = 5
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
var/obj/machinery/computer/library/checkout/computer // Associated computer - Modes 1 to 3 use this
|
||||
var/obj/item/book/book // Currently scanned book
|
||||
var/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
|
||||
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(src.computer)
|
||||
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")
|
||||
/* Library Items
|
||||
*
|
||||
* Contains:
|
||||
* Bookcase
|
||||
* Book
|
||||
* Barcode Scanner
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Bookcase
|
||||
*/
|
||||
|
||||
/obj/structure/bookcase
|
||||
name = "bookcase"
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state = "book-0"
|
||||
anchored = 1
|
||||
density = 1
|
||||
opacity = 1
|
||||
resistance_flags = FLAMMABLE
|
||||
max_integrity = 200
|
||||
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 0)
|
||||
var/tmp/busy = 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()
|
||||
..()
|
||||
for(var/obj/item/I in loc)
|
||||
if(is_type_in_list(I, allowed_books))
|
||||
I.forceMove(src)
|
||||
update_icon()
|
||||
|
||||
/obj/structure/bookcase/attackby(obj/item/O as obj, mob/user as mob, params)
|
||||
if(busy) //So that you can't mess with it while deconstructing
|
||||
return 1
|
||||
if(is_type_in_list(O, allowed_books))
|
||||
if(!user.drop_item())
|
||||
return
|
||||
O.forceMove(src)
|
||||
update_icon()
|
||||
return 1
|
||||
else 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(istype(T, /obj/item/book) || istype(T, /obj/item/spellbook) || istype(T, /obj/item/tome) || istype(T, /obj/item/storage/bible))
|
||||
B.remove_from_storage(T, src)
|
||||
to_chat(user, "<span class='notice'>You empty [O] into [src].</span>")
|
||||
update_icon()
|
||||
return 1
|
||||
else if(istype(O, /obj/item/wrench))
|
||||
user.visible_message("<span class='warning'>[user] starts disassembling \the [src].</span>", \
|
||||
"<span class='notice'>You start disassembling \the [src].</span>")
|
||||
playsound(get_turf(src), O.usesound, 50, 1)
|
||||
busy = 1
|
||||
|
||||
if(do_after(user, 50 * O.toolspeed, target = src))
|
||||
playsound(get_turf(src), O.usesound, 75, 1)
|
||||
user.visible_message("<span class='warning'>[user] disassembles \the [src].</span>", \
|
||||
"<span class='notice'>You disassemble \the [src].</span>")
|
||||
busy = 0
|
||||
density = 0
|
||||
deconstruct(TRUE)
|
||||
else
|
||||
busy = 0
|
||||
return 1
|
||||
else if(istype(O, /obj/item/pen))
|
||||
var/newname = stripped_input(user, "What would you like to title this [name]?")
|
||||
if(newname)
|
||||
name = ("bookcase ([sanitize(newname)])")
|
||||
return 1
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/structure/bookcase/attack_hand(var/mob/user as mob)
|
||||
if(contents.len)
|
||||
var/obj/item/book/choice = input("Which book would you like to remove from [src]?") as null|anything in contents
|
||||
if(choice)
|
||||
if(user.incapacitated() || user.lying || !Adjacent(user))
|
||||
return
|
||||
if(!user.get_active_hand())
|
||||
user.put_in_hands(choice)
|
||||
else
|
||||
choice.forceMove(get_turf(src))
|
||||
update_icon()
|
||||
|
||||
/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))
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/bookcase/update_icon()
|
||||
if(contents.len < 5)
|
||||
icon_state = "book-[contents.len]"
|
||||
else
|
||||
icon_state = "book-5"
|
||||
|
||||
|
||||
/obj/structure/bookcase/manuals/medical
|
||||
name = "Medical Manuals bookcase"
|
||||
|
||||
New()
|
||||
..()
|
||||
new /obj/item/book/manual/medical_cloning(src)
|
||||
update_icon()
|
||||
|
||||
|
||||
/obj/structure/bookcase/manuals/engineering
|
||||
name = "Engineering Manuals bookcase"
|
||||
|
||||
New()
|
||||
..()
|
||||
new /obj/item/book/manual/engineering_construction(src)
|
||||
new /obj/item/book/manual/engineering_particle_accelerator(src)
|
||||
new /obj/item/book/manual/engineering_hacking(src)
|
||||
new /obj/item/book/manual/engineering_guide(src)
|
||||
new /obj/item/book/manual/engineering_singularity_safety(src)
|
||||
new /obj/item/book/manual/robotics_cyborgs(src)
|
||||
update_icon()
|
||||
|
||||
/obj/structure/bookcase/manuals/research_and_development
|
||||
name = "R&D Manuals bookcase"
|
||||
|
||||
New()
|
||||
..()
|
||||
new /obj/item/book/manual/research_and_development(src)
|
||||
update_icon()
|
||||
|
||||
|
||||
/*
|
||||
* Book
|
||||
*/
|
||||
/obj/item/book
|
||||
name = "book"
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state ="book"
|
||||
throw_speed = 1
|
||||
throw_range = 5
|
||||
force = 2
|
||||
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 = list("bashed", "whacked")
|
||||
resistance_flags = FLAMMABLE
|
||||
|
||||
var/dat // Actual page content
|
||||
var/due_date = 0 // Game time in 1/10th seconds
|
||||
var/author // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned
|
||||
var/unique = 0 // 0 - Normal book, 1 - Should not be treated as normal book, unable to be copied, unable to be modified
|
||||
var/title // The real name of the book.
|
||||
var/carved = 0 // Has the book been hollowed out for use as a secret storage item?
|
||||
var/forbidden = 0 // Prevent ordering of this book. (0=no, 1=yes, 2=emag only)
|
||||
var/obj/item/store // What's in the book?
|
||||
|
||||
/obj/item/book/attack_self(var/mob/user as mob)
|
||||
if(carved)
|
||||
if(store)
|
||||
to_chat(user, "<span class='notice'>[store] falls out of [title]!</span>")
|
||||
store.forceMove(get_turf(loc))
|
||||
store = null
|
||||
return
|
||||
else
|
||||
to_chat(user, "<span class='notice'>The pages of [title] have been cut out!</span>")
|
||||
return
|
||||
if(src.dat)
|
||||
user << browse("<TT><I>Penned by [author].</I></TT> <BR>" + "[dat]", "window=book")
|
||||
if(!isobserver(user))
|
||||
user.visible_message("[user] opens a book titled \"[title]\" and begins reading intently.")
|
||||
onclose(user, "book")
|
||||
else
|
||||
to_chat(user, "This book is completely blank!")
|
||||
|
||||
/obj/item/book/attackby(obj/item/W as obj, mob/user as mob, params)
|
||||
if(carved)
|
||||
if(!store)
|
||||
if(W.w_class < WEIGHT_CLASS_NORMAL)
|
||||
user.drop_item()
|
||||
W.forceMove(src)
|
||||
store = W
|
||||
to_chat(user, "<span class='notice'>You put [W] in [title].</span>")
|
||||
return 1
|
||||
else
|
||||
to_chat(user, "<span class='notice'>[W] won't fit in [title].</span>")
|
||||
return 1
|
||||
else
|
||||
to_chat(user, "<span class='notice'>There's already something in [title]!</span>")
|
||||
return 1
|
||||
if(istype(W, /obj/item/pen))
|
||||
if(unique)
|
||||
to_chat(user, "These pages don't seem to take the ink well. Looks like you can't modify it.")
|
||||
return 1
|
||||
var/choice = input("What would you like to change?") in list("Title", "Contents", "Author", "Cancel")
|
||||
switch(choice)
|
||||
if("Title")
|
||||
var/newtitle = reject_bad_text(stripped_input(usr, "Write a new title:"))
|
||||
if(!newtitle)
|
||||
to_chat(usr, "The title is invalid.")
|
||||
return 1
|
||||
else
|
||||
src.name = newtitle
|
||||
src.title = newtitle
|
||||
if("Contents")
|
||||
var/content = strip_html(input(usr, "Write your book's contents (HTML NOT allowed):") as message|null, MAX_BOOK_MESSAGE_LEN)
|
||||
if(!content)
|
||||
to_chat(usr, "The content is invalid.")
|
||||
return 1
|
||||
else
|
||||
src.dat += content
|
||||
if("Author")
|
||||
var/newauthor = stripped_input(usr, "Write the author's name:")
|
||||
if(!newauthor)
|
||||
to_chat(usr, "The name is invalid.")
|
||||
return 1
|
||||
else
|
||||
src.author = newauthor
|
||||
return 1
|
||||
else if(istype(W, /obj/item/barcodescanner))
|
||||
var/obj/item/barcodescanner/scanner = W
|
||||
if(!scanner.computer)
|
||||
to_chat(user, "[W]'s screen flashes: 'No associated computer found!'")
|
||||
else
|
||||
switch(scanner.mode)
|
||||
if(0)
|
||||
scanner.book = src
|
||||
to_chat(user, "[W]'s screen flashes: 'Book stored in buffer.'")
|
||||
if(1)
|
||||
scanner.book = src
|
||||
scanner.computer.buffer_book = src.name
|
||||
to_chat(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)
|
||||
to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Book has been checked in.'")
|
||||
return 1
|
||||
to_chat(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/book in scanner.computer.inventory)
|
||||
if(book == src)
|
||||
to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Title already present in inventory, aborting to avoid duplicate entry.'")
|
||||
return 1
|
||||
scanner.computer.inventory.Add(src)
|
||||
to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Title added to general inventory.'")
|
||||
return 1
|
||||
else if(istype(W, /obj/item/kitchen/knife) && !carved)
|
||||
carve_book(user, W)
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/book/wirecutter_act(mob/user, obj/item/I)
|
||||
return carve_book(user, I)
|
||||
|
||||
/obj/item/book/attack(mob/M, mob/living/user)
|
||||
if(user.a_intent == INTENT_HELP)
|
||||
force = 0
|
||||
attack_verb = list("educated")
|
||||
else
|
||||
force = initial(force)
|
||||
attack_verb = list("bashed", "whacked")
|
||||
..()
|
||||
|
||||
/obj/item/book/proc/carve_book(mob/user, obj/item/I)
|
||||
if(!I.sharp && I.tool_behaviour != TOOL_WIRECUTTER) //Only sharp and wirecutter things can carve books
|
||||
to_chat(user, "<span class='warning>You can't carve [title] using that!</span>")
|
||||
return
|
||||
if(carved)
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You begin to carve out [title].</span>")
|
||||
if(I.use_tool(src, user, 30, volume = I.tool_volume))
|
||||
to_chat(user, "<span class='notice'>You carve out the pages from [title]! You didn't want to read it anyway.</span>")
|
||||
carved = TRUE
|
||||
return TRUE
|
||||
/*
|
||||
* Barcode Scanner
|
||||
*/
|
||||
/obj/item/barcodescanner
|
||||
name = "barcode scanner"
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state ="scanner"
|
||||
throw_speed = 1
|
||||
throw_range = 5
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
var/obj/machinery/computer/library/checkout/computer // Associated computer - Modes 1 to 3 use this
|
||||
var/obj/item/book/book // Currently scanned book
|
||||
var/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
|
||||
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(src.computer)
|
||||
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")
|
||||
|
||||
@@ -1,230 +1,230 @@
|
||||
#define LIBRARY_BOOKS_PER_PAGE 25
|
||||
|
||||
var/global/datum/library_catalog/library_catalog = new()
|
||||
var/global/list/library_section_names = list("Any", "Fiction", "Non-Fiction", "Adult", "Reference", "Religion")
|
||||
|
||||
|
||||
/hook/startup/proc/load_manuals()
|
||||
library_catalog.initialize()
|
||||
return 1
|
||||
|
||||
/*
|
||||
* Borrowbook datum
|
||||
*/
|
||||
/datum/borrowbook // Datum used to keep track of who has borrowed what when and for how long.
|
||||
var/bookname
|
||||
var/mobname
|
||||
var/getdate
|
||||
var/duedate
|
||||
|
||||
/*
|
||||
* Cachedbook datum
|
||||
*/
|
||||
/datum/cachedbook // Datum used to cache the SQL DB books locally in order to achieve a performance gain.
|
||||
var/id
|
||||
var/title
|
||||
var/author
|
||||
var/ckey // ADDED 24/2/2015 - N3X
|
||||
var/category
|
||||
var/content
|
||||
var/programmatic=0 // Is the book programmatically added to the catalog?
|
||||
var/forbidden=0
|
||||
var/path = /obj/item/book // Type path of the book to generate
|
||||
var/flagged = 0
|
||||
|
||||
/datum/cachedbook/proc/LoadFromRow(var/list/row)
|
||||
id = row["id"]
|
||||
author = row["author"]
|
||||
title = row["title"]
|
||||
category = row["category"]
|
||||
ckey = row["ckey"]
|
||||
flagged = row["flagged"]
|
||||
if("content" in row)
|
||||
content = row["content"]
|
||||
programmatic=0
|
||||
|
||||
// Builds a SQL statement
|
||||
/datum/library_query
|
||||
var/author
|
||||
var/category
|
||||
var/title
|
||||
|
||||
/datum/library_query/proc/toSQL()
|
||||
var/list/where = list()
|
||||
if(author || title || category)
|
||||
if(author)
|
||||
where.Add("author LIKE '%[sanitizeSQL(author)]%'")
|
||||
if(category)
|
||||
where.Add("category = '[sanitizeSQL(category)]'")
|
||||
if(title)
|
||||
where.Add("title LIKE '%[sanitizeSQL(title)]%'")
|
||||
return " WHERE " + jointext(where, " AND ")
|
||||
return ""
|
||||
|
||||
// So we can have catalogs of books that are programmatic, and ones that aren't.
|
||||
/datum/library_catalog
|
||||
var/list/cached_books = list()
|
||||
|
||||
/datum/library_catalog/proc/initialize()
|
||||
var/newid=1
|
||||
for(var/typepath in subtypesof(/obj/item/book/manual))
|
||||
var/obj/item/book/B = new typepath(null)
|
||||
var/datum/cachedbook/CB = new()
|
||||
CB.forbidden = B.forbidden
|
||||
CB.title = B.name
|
||||
CB.author = B.author
|
||||
CB.programmatic=1
|
||||
CB.path=typepath
|
||||
CB.id = "M[newid]"
|
||||
newid++
|
||||
cached_books["[CB.id]"]=CB
|
||||
|
||||
/datum/library_catalog/proc/flag_book_by_id(mob/user, id)
|
||||
var/global/books_flagged_this_round[0]
|
||||
|
||||
if("[id]" in cached_books)
|
||||
var/datum/cachedbook/CB = cached_books["[id]"]
|
||||
if(CB.programmatic)
|
||||
to_chat(user, "<span class='danger'>That book cannot be flagged in the system, as it does not actually exist in the database.</span>")
|
||||
return
|
||||
|
||||
if("[id]" in books_flagged_this_round)
|
||||
to_chat(user, "<span class='danger'>This book has already been flagged this shift.</span>")
|
||||
return
|
||||
|
||||
books_flagged_this_round["[id]"] = 1
|
||||
message_admins("[key_name_admin(user)] has flagged book #[id] as inappropriate.")
|
||||
|
||||
var/sqlid = text2num(id)
|
||||
if(!sqlid)
|
||||
return
|
||||
var/DBQuery/query = dbcon.NewQuery("UPDATE [format_table_name("library")] SET flagged = flagged + 1 WHERE id=[sqlid]")
|
||||
query.Execute()
|
||||
|
||||
/datum/library_catalog/proc/rmBookByID(mob/user, id)
|
||||
if("[id]" in cached_books)
|
||||
var/datum/cachedbook/CB = cached_books["[id]"]
|
||||
if(CB.programmatic)
|
||||
to_chat(user, "<span class='danger'>That book cannot be removed from the system, as it does not actually exist in the database.</span>")
|
||||
return
|
||||
|
||||
var/sqlid = text2num(id)
|
||||
if(!sqlid)
|
||||
return
|
||||
var/DBQuery/query = dbcon.NewQuery("DELETE FROM [format_table_name("library")] WHERE id=[sqlid]")
|
||||
query.Execute()
|
||||
|
||||
/datum/library_catalog/proc/getBookByID(id)
|
||||
if("[id]" in cached_books)
|
||||
return cached_books["[id]"]
|
||||
|
||||
var/sqlid = text2num(id)
|
||||
if(!sqlid)
|
||||
return
|
||||
var/DBQuery/query = dbcon.NewQuery("SELECT id, author, title, category, content, ckey, flagged FROM [format_table_name("library")] WHERE id=[sqlid]")
|
||||
query.Execute()
|
||||
|
||||
var/list/results=list()
|
||||
while(query.NextRow())
|
||||
var/datum/cachedbook/CB = new()
|
||||
CB.LoadFromRow(list(
|
||||
"id" =query.item[1],
|
||||
"author" =query.item[2],
|
||||
"title" =query.item[3],
|
||||
"category"=query.item[4],
|
||||
"content" =query.item[5],
|
||||
"ckey" =query.item[6],
|
||||
"flagged" =query.item[7]
|
||||
))
|
||||
results += CB
|
||||
cached_books["[id]"]=CB
|
||||
return CB
|
||||
return results
|
||||
|
||||
/** Scanner **/
|
||||
/obj/machinery/libraryscanner
|
||||
name = "scanner"
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state = "bigscanner"
|
||||
anchored = 1
|
||||
density = 1
|
||||
var/obj/item/book/cache // Last scanned book
|
||||
|
||||
/obj/machinery/libraryscanner/attackby(obj/item/I, mob/user)
|
||||
if(default_unfasten_wrench(user, I))
|
||||
power_change()
|
||||
return
|
||||
if(istype(I, /obj/item/book))
|
||||
user.drop_item()
|
||||
I.forceMove(src)
|
||||
return 1
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/machinery/libraryscanner/attack_hand(mob/user)
|
||||
if(istype(user,/mob/dead))
|
||||
to_chat(user, "<span class='danger'>Nope.</span>")
|
||||
return
|
||||
usr.set_machine(src)
|
||||
var/dat = "<HEAD><TITLE>Scanner Control Interface</TITLE></HEAD><BODY>\n" // <META HTTP-EQUIV='Refresh' CONTENT='10'>
|
||||
if(cache)
|
||||
dat += "<FONT color=#005500>Data stored in memory.</FONT><BR>"
|
||||
else
|
||||
dat += "No data stored in memory.<BR>"
|
||||
dat += "<A href='?src=[UID()];scan=1'>\[Scan\]</A>"
|
||||
if(cache)
|
||||
dat += " <A href='?src=[UID()];clear=1'>\[Clear Memory\]</A><BR><BR><A href='?src=[UID()];eject=1'>\[Remove Book\]</A>"
|
||||
else
|
||||
dat += "<BR>"
|
||||
user << browse(dat, "window=scanner")
|
||||
onclose(user, "scanner")
|
||||
|
||||
/obj/machinery/libraryscanner/Topic(href, href_list)
|
||||
if(..())
|
||||
usr << browse(null, "window=scanner")
|
||||
onclose(usr, "scanner")
|
||||
return
|
||||
|
||||
if(href_list["scan"])
|
||||
for(var/obj/item/book/B in contents)
|
||||
cache = B
|
||||
break
|
||||
if(href_list["clear"])
|
||||
cache = null
|
||||
if(href_list["eject"])
|
||||
for(var/obj/item/book/B in contents)
|
||||
B.loc = src.loc
|
||||
src.add_fingerprint(usr)
|
||||
src.updateUsrDialog()
|
||||
return
|
||||
|
||||
|
||||
/*
|
||||
* Book binder
|
||||
*/
|
||||
/obj/machinery/bookbinder
|
||||
name = "Book Binder"
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state = "binder"
|
||||
anchored = 1
|
||||
density = 1
|
||||
|
||||
/obj/machinery/bookbinder/attackby(obj/item/I, mob/user)
|
||||
var/obj/item/paper/P = I
|
||||
if(default_unfasten_wrench(user, I))
|
||||
power_change()
|
||||
return
|
||||
if(istype(P))
|
||||
user.drop_item()
|
||||
user.visible_message("[user] loads some paper into [src].", "You load some paper into [src].")
|
||||
src.visible_message("[src] begins to hum as it warms up its printing drums.")
|
||||
sleep(rand(200,400))
|
||||
src.visible_message("[src] whirs as it prints and binds a new book.")
|
||||
var/obj/item/book/b = new(loc)
|
||||
b.dat = P.info
|
||||
b.name = "Print Job #[rand(100, 999)]"
|
||||
b.icon_state = "book[rand(1,16)]"
|
||||
qdel(P)
|
||||
return 1
|
||||
else
|
||||
return ..()
|
||||
#define LIBRARY_BOOKS_PER_PAGE 25
|
||||
|
||||
var/global/datum/library_catalog/library_catalog = new()
|
||||
var/global/list/library_section_names = list("Any", "Fiction", "Non-Fiction", "Adult", "Reference", "Religion")
|
||||
|
||||
|
||||
/hook/startup/proc/load_manuals()
|
||||
library_catalog.initialize()
|
||||
return 1
|
||||
|
||||
/*
|
||||
* Borrowbook datum
|
||||
*/
|
||||
/datum/borrowbook // Datum used to keep track of who has borrowed what when and for how long.
|
||||
var/bookname
|
||||
var/mobname
|
||||
var/getdate
|
||||
var/duedate
|
||||
|
||||
/*
|
||||
* Cachedbook datum
|
||||
*/
|
||||
/datum/cachedbook // Datum used to cache the SQL DB books locally in order to achieve a performance gain.
|
||||
var/id
|
||||
var/title
|
||||
var/author
|
||||
var/ckey // ADDED 24/2/2015 - N3X
|
||||
var/category
|
||||
var/content
|
||||
var/programmatic=0 // Is the book programmatically added to the catalog?
|
||||
var/forbidden=0
|
||||
var/path = /obj/item/book // Type path of the book to generate
|
||||
var/flagged = 0
|
||||
|
||||
/datum/cachedbook/proc/LoadFromRow(var/list/row)
|
||||
id = row["id"]
|
||||
author = row["author"]
|
||||
title = row["title"]
|
||||
category = row["category"]
|
||||
ckey = row["ckey"]
|
||||
flagged = row["flagged"]
|
||||
if("content" in row)
|
||||
content = row["content"]
|
||||
programmatic=0
|
||||
|
||||
// Builds a SQL statement
|
||||
/datum/library_query
|
||||
var/author
|
||||
var/category
|
||||
var/title
|
||||
|
||||
/datum/library_query/proc/toSQL()
|
||||
var/list/where = list()
|
||||
if(author || title || category)
|
||||
if(author)
|
||||
where.Add("author LIKE '%[sanitizeSQL(author)]%'")
|
||||
if(category)
|
||||
where.Add("category = '[sanitizeSQL(category)]'")
|
||||
if(title)
|
||||
where.Add("title LIKE '%[sanitizeSQL(title)]%'")
|
||||
return " WHERE " + jointext(where, " AND ")
|
||||
return ""
|
||||
|
||||
// So we can have catalogs of books that are programmatic, and ones that aren't.
|
||||
/datum/library_catalog
|
||||
var/list/cached_books = list()
|
||||
|
||||
/datum/library_catalog/proc/initialize()
|
||||
var/newid=1
|
||||
for(var/typepath in subtypesof(/obj/item/book/manual))
|
||||
var/obj/item/book/B = new typepath(null)
|
||||
var/datum/cachedbook/CB = new()
|
||||
CB.forbidden = B.forbidden
|
||||
CB.title = B.name
|
||||
CB.author = B.author
|
||||
CB.programmatic=1
|
||||
CB.path=typepath
|
||||
CB.id = "M[newid]"
|
||||
newid++
|
||||
cached_books["[CB.id]"]=CB
|
||||
|
||||
/datum/library_catalog/proc/flag_book_by_id(mob/user, id)
|
||||
var/global/books_flagged_this_round[0]
|
||||
|
||||
if("[id]" in cached_books)
|
||||
var/datum/cachedbook/CB = cached_books["[id]"]
|
||||
if(CB.programmatic)
|
||||
to_chat(user, "<span class='danger'>That book cannot be flagged in the system, as it does not actually exist in the database.</span>")
|
||||
return
|
||||
|
||||
if("[id]" in books_flagged_this_round)
|
||||
to_chat(user, "<span class='danger'>This book has already been flagged this shift.</span>")
|
||||
return
|
||||
|
||||
books_flagged_this_round["[id]"] = 1
|
||||
message_admins("[key_name_admin(user)] has flagged book #[id] as inappropriate.")
|
||||
|
||||
var/sqlid = text2num(id)
|
||||
if(!sqlid)
|
||||
return
|
||||
var/DBQuery/query = dbcon.NewQuery("UPDATE [format_table_name("library")] SET flagged = flagged + 1 WHERE id=[sqlid]")
|
||||
query.Execute()
|
||||
|
||||
/datum/library_catalog/proc/rmBookByID(mob/user, id)
|
||||
if("[id]" in cached_books)
|
||||
var/datum/cachedbook/CB = cached_books["[id]"]
|
||||
if(CB.programmatic)
|
||||
to_chat(user, "<span class='danger'>That book cannot be removed from the system, as it does not actually exist in the database.</span>")
|
||||
return
|
||||
|
||||
var/sqlid = text2num(id)
|
||||
if(!sqlid)
|
||||
return
|
||||
var/DBQuery/query = dbcon.NewQuery("DELETE FROM [format_table_name("library")] WHERE id=[sqlid]")
|
||||
query.Execute()
|
||||
|
||||
/datum/library_catalog/proc/getBookByID(id)
|
||||
if("[id]" in cached_books)
|
||||
return cached_books["[id]"]
|
||||
|
||||
var/sqlid = text2num(id)
|
||||
if(!sqlid)
|
||||
return
|
||||
var/DBQuery/query = dbcon.NewQuery("SELECT id, author, title, category, content, ckey, flagged FROM [format_table_name("library")] WHERE id=[sqlid]")
|
||||
query.Execute()
|
||||
|
||||
var/list/results=list()
|
||||
while(query.NextRow())
|
||||
var/datum/cachedbook/CB = new()
|
||||
CB.LoadFromRow(list(
|
||||
"id" =query.item[1],
|
||||
"author" =query.item[2],
|
||||
"title" =query.item[3],
|
||||
"category"=query.item[4],
|
||||
"content" =query.item[5],
|
||||
"ckey" =query.item[6],
|
||||
"flagged" =query.item[7]
|
||||
))
|
||||
results += CB
|
||||
cached_books["[id]"]=CB
|
||||
return CB
|
||||
return results
|
||||
|
||||
/** Scanner **/
|
||||
/obj/machinery/libraryscanner
|
||||
name = "scanner"
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state = "bigscanner"
|
||||
anchored = 1
|
||||
density = 1
|
||||
var/obj/item/book/cache // Last scanned book
|
||||
|
||||
/obj/machinery/libraryscanner/attackby(obj/item/I, mob/user)
|
||||
if(default_unfasten_wrench(user, I))
|
||||
power_change()
|
||||
return
|
||||
if(istype(I, /obj/item/book))
|
||||
user.drop_item()
|
||||
I.forceMove(src)
|
||||
return 1
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/machinery/libraryscanner/attack_hand(mob/user)
|
||||
if(istype(user,/mob/dead))
|
||||
to_chat(user, "<span class='danger'>Nope.</span>")
|
||||
return
|
||||
usr.set_machine(src)
|
||||
var/dat = "<HEAD><TITLE>Scanner Control Interface</TITLE></HEAD><BODY>\n" // <META HTTP-EQUIV='Refresh' CONTENT='10'>
|
||||
if(cache)
|
||||
dat += "<FONT color=#005500>Data stored in memory.</FONT><BR>"
|
||||
else
|
||||
dat += "No data stored in memory.<BR>"
|
||||
dat += "<A href='?src=[UID()];scan=1'>\[Scan\]</A>"
|
||||
if(cache)
|
||||
dat += " <A href='?src=[UID()];clear=1'>\[Clear Memory\]</A><BR><BR><A href='?src=[UID()];eject=1'>\[Remove Book\]</A>"
|
||||
else
|
||||
dat += "<BR>"
|
||||
user << browse(dat, "window=scanner")
|
||||
onclose(user, "scanner")
|
||||
|
||||
/obj/machinery/libraryscanner/Topic(href, href_list)
|
||||
if(..())
|
||||
usr << browse(null, "window=scanner")
|
||||
onclose(usr, "scanner")
|
||||
return
|
||||
|
||||
if(href_list["scan"])
|
||||
for(var/obj/item/book/B in contents)
|
||||
cache = B
|
||||
break
|
||||
if(href_list["clear"])
|
||||
cache = null
|
||||
if(href_list["eject"])
|
||||
for(var/obj/item/book/B in contents)
|
||||
B.loc = src.loc
|
||||
src.add_fingerprint(usr)
|
||||
src.updateUsrDialog()
|
||||
return
|
||||
|
||||
|
||||
/*
|
||||
* Book binder
|
||||
*/
|
||||
/obj/machinery/bookbinder
|
||||
name = "Book Binder"
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state = "binder"
|
||||
anchored = 1
|
||||
density = 1
|
||||
|
||||
/obj/machinery/bookbinder/attackby(obj/item/I, mob/user)
|
||||
var/obj/item/paper/P = I
|
||||
if(default_unfasten_wrench(user, I))
|
||||
power_change()
|
||||
return
|
||||
if(istype(P))
|
||||
user.drop_item()
|
||||
user.visible_message("[user] loads some paper into [src].", "You load some paper into [src].")
|
||||
src.visible_message("[src] begins to hum as it warms up its printing drums.")
|
||||
sleep(rand(200,400))
|
||||
src.visible_message("[src] whirs as it prints and binds a new book.")
|
||||
var/obj/item/book/b = new(loc)
|
||||
b.dat = P.info
|
||||
b.name = "Print Job #[rand(100, 999)]"
|
||||
b.icon_state = "book[rand(1,16)]"
|
||||
qdel(P)
|
||||
return 1
|
||||
else
|
||||
return ..()
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
//*******************************
|
||||
//
|
||||
// Library SQL Configuration
|
||||
//
|
||||
//*******************************
|
||||
|
||||
// Deprecated! See global.dm for new SQL config vars -- TLE
|
||||
/*
|
||||
#define SQL_ADDRESS ""
|
||||
#define SQL_DB ""
|
||||
#define SQL_PORT "3306"
|
||||
#define SQL_LOGIN ""
|
||||
#define SQL_PASS ""
|
||||
*/
|
||||
|
||||
//*******************************
|
||||
// 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.
|
||||
//*******************************
|
||||
//
|
||||
// Library SQL Configuration
|
||||
//
|
||||
//*******************************
|
||||
|
||||
// Deprecated! See global.dm for new SQL config vars -- TLE
|
||||
/*
|
||||
#define SQL_ADDRESS ""
|
||||
#define SQL_DB ""
|
||||
#define SQL_PORT "3306"
|
||||
#define SQL_LOGIN ""
|
||||
#define SQL_PASS ""
|
||||
*/
|
||||
|
||||
//*******************************
|
||||
// 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.
|
||||
|
||||
@@ -82,4 +82,4 @@
|
||||
. = ..()
|
||||
while(book_count > 0 && prob(ref_book_prob))
|
||||
book_count--
|
||||
new /obj/item/book/manual/random(src)
|
||||
new /obj/item/book/manual/random(src)
|
||||
|
||||
Reference in New Issue
Block a user