mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-22 04:22:39 +01:00
Library Update (#22120)
Updates the Library to TGUI and the new DBCore The GUI was created using Claude Code Public Library Computer <img width="652" height="501" alt="image" src="https://github.com/user-attachments/assets/eba1841a-5784-4fe4-b337-6a9e9503bb58" /> Library Management Computer: Inventory Page: <img width="750" height="605" alt="image" src="https://github.com/user-attachments/assets/e4f38fcb-7299-4baf-a8a2-0974297a544c" /> Check Out Page with Book Picker active: <img width="750" height="607" alt="image" src="https://github.com/user-attachments/assets/e9189157-1617-4535-a053-942b17378614" /> Library Order Page with pagination: <img width="747" height="598" alt="image" src="https://github.com/user-attachments/assets/07d3abaf-5a16-4229-8ab8-e64e2f73de24" /> Emag action: <img width="752" height="602" alt="image" src="https://github.com/user-attachments/assets/285cd4bf-0ed3-4cf2-a9cb-4469e6466ad0" /> Book Uploader: <img width="747" height="602" alt="image" src="https://github.com/user-attachments/assets/b4b648ab-59e4-48f1-90a3-77e8440f01e4" /> And its orderable instantly: <img width="750" height="602" alt="image" src="https://github.com/user-attachments/assets/1c1c21fa-28fd-42fe-82b5-1c859cd74fdf" /> --------- Co-authored-by: Werner <Arrow768@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Book Binder
|
||||
*/
|
||||
/obj/machinery/bookbinder
|
||||
name = "book binder"
|
||||
desc = "A machine that takes paper and binds them into books. Fascinating!"
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state = "binder"
|
||||
anchored = TRUE
|
||||
density = TRUE
|
||||
var/binding = FALSE
|
||||
|
||||
/obj/machinery/bookbinder/attackby(obj/item/attacking_item, mob/user)
|
||||
if(istype(attacking_item, /obj/item/paper))
|
||||
var/obj/item/paper/paper = attacking_item
|
||||
if(!anchored)
|
||||
to_chat(user, SPAN_WARNING("\The [src] must be secured to the floor first!"))
|
||||
return
|
||||
if(binding)
|
||||
to_chat(user, SPAN_WARNING("You must wait for \the [src] to finish its current operation!"))
|
||||
return
|
||||
var/turf/work_turf = get_turf(src)
|
||||
user.drop_from_inventory(paper, src)
|
||||
user.visible_message(
|
||||
SPAN_NOTICE("\The [user] loads some paper into \the [src]."),
|
||||
SPAN_NOTICE("You load some paper into \the [src]."))
|
||||
visible_message(SPAN_NOTICE("\The [src] begins to hum as it warms up its printing drums."))
|
||||
playsound(work_turf, 'sound/items/bureaucracy/binder.ogg', 75, 1)
|
||||
binding = TRUE
|
||||
sleep(rand(20 SECONDS, 40 SECONDS))
|
||||
binding = FALSE
|
||||
if(!anchored)
|
||||
visible_message(SPAN_WARNING("\The [src] buzzes and flashes an error light."))
|
||||
paper.forceMove(work_turf)
|
||||
return
|
||||
visible_message(SPAN_NOTICE("\The [src] whirs as it prints and binds a new book."))
|
||||
playsound(work_turf, 'sound/items/bureaucracy/print.ogg', 75, 1)
|
||||
var/obj/item/book/bound_book = new(work_turf)
|
||||
bound_book.dat = paper.info
|
||||
bound_book.name = "blank book"
|
||||
bound_book.icon_state = "book[rand(1,7)]"
|
||||
bound_book.item_state = icon_state
|
||||
qdel(paper)
|
||||
return
|
||||
|
||||
if(attacking_item.tool_behaviour == TOOL_WRENCH)
|
||||
attacking_item.play_tool_sound(get_turf(src), 75)
|
||||
if(anchored)
|
||||
user.visible_message(
|
||||
SPAN_NOTICE("\The [user] unsecures \the [src] from the floor."),
|
||||
SPAN_NOTICE("You unsecure \the [src] from the floor."),
|
||||
SPAN_WARNING("You hear a ratcheting noise."))
|
||||
else
|
||||
user.visible_message(
|
||||
SPAN_NOTICE("\The [user] secures \the [src] to the floor."),
|
||||
SPAN_NOTICE("You secure \the [src] to the floor."),
|
||||
SPAN_WARNING("You hear a ratcheting noise."))
|
||||
anchored = !anchored
|
||||
@@ -0,0 +1,425 @@
|
||||
/*
|
||||
* Borrowbook datum
|
||||
*/
|
||||
/// Tracks a single book checkout: what was borrowed, by whom, and when it is due.
|
||||
/datum/borrowbook
|
||||
var/book_name
|
||||
var/mob_name
|
||||
var/get_date
|
||||
var/due_date
|
||||
|
||||
/*
|
||||
* Library Computer
|
||||
*/
|
||||
/obj/machinery/librarycomp
|
||||
name = "library computer"
|
||||
desc = "A computer."
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state = "computer"
|
||||
anchored = TRUE
|
||||
density = TRUE
|
||||
var/upload_category = "Fiction"
|
||||
/// Active book checkouts
|
||||
var/list/datum/borrowbook/checkouts = list()
|
||||
/// Weakrefs to physical books currently in the library's physical inventory
|
||||
var/list/datum/weakref/inventory = list()
|
||||
/// How long a checkout lasts, in minutes
|
||||
var/checkout_period_minutes = 5
|
||||
var/bible_on_cooldown = FALSE
|
||||
var/is_public = FALSE
|
||||
var/buffer_book // Set by barcode scanner mode 1
|
||||
/// Title of the last successfully uploaded book, shown in the UI as confirmation
|
||||
var/last_uploaded_title
|
||||
var/datum/weakref/scanner_ref // Weakref to the nearest anchored book scanner
|
||||
/// Current page of archive results (one page = 20 entries)
|
||||
var/list/archive_results = list()
|
||||
var/archive_loading = FALSE
|
||||
var/archive_error = FALSE
|
||||
var/archive_page = 1
|
||||
/// Total number of archive entries matching the current search
|
||||
var/archive_total = 0
|
||||
var/archive_search = ""
|
||||
var/archive_sort_field = "title"
|
||||
var/archive_sort_dir = "asc"
|
||||
|
||||
/obj/machinery/librarycomp/attack_hand(var/mob/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
return TRUE
|
||||
ui_interact(user)
|
||||
|
||||
/obj/machinery/librarycomp/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
var/title = is_public ? "[SSatlas.current_map.station_name] Library" : "[SSatlas.current_map.station_name] Library Management"
|
||||
ui = new(user, src, "LibraryComputer", title, 750, 600)
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/librarycomp/ui_data(mob/user)
|
||||
var/obj/machinery/libraryscanner/scanner = scanner_ref?.resolve()
|
||||
if(!scanner)
|
||||
scanner_ref = null
|
||||
for(var/obj/machinery/libraryscanner/nearby_scanner in range(9))
|
||||
if(nearby_scanner.anchored)
|
||||
scanner_ref = WEAKREF(nearby_scanner)
|
||||
scanner = nearby_scanner
|
||||
break
|
||||
|
||||
var/list/data = list()
|
||||
data["is_public"] = is_public
|
||||
data["is_emagged"] = emagged
|
||||
data["bible_on_cooldown"] = bible_on_cooldown
|
||||
data["checkout_period_minutes"] = checkout_period_minutes
|
||||
data["archive_loading"] = archive_loading
|
||||
data["archive_error"] = archive_error
|
||||
data["archive_results"] = archive_results
|
||||
data["archive_page"] = archive_page
|
||||
data["archive_total"] = archive_total
|
||||
data["archive_search"] = archive_search
|
||||
data["archive_sort_field"] = archive_sort_field
|
||||
data["archive_sort_dir"] = archive_sort_dir
|
||||
data["archive_page_size"] = 20
|
||||
data["upload_category"] = upload_category
|
||||
data["buffer_book"] = buffer_book
|
||||
data["last_uploaded_title"] = last_uploaded_title
|
||||
|
||||
var/list/inv = list()
|
||||
var/list/stale = list()
|
||||
for(var/datum/weakref/wref in inventory)
|
||||
var/obj/item/book/book = wref.resolve()
|
||||
if(book)
|
||||
inv += list(list("ref" = REF(book), "name" = book.name))
|
||||
else
|
||||
stale += wref
|
||||
inventory -= stale
|
||||
data["inventory"] = inv
|
||||
|
||||
var/list/co = list()
|
||||
for(var/datum/borrowbook/checkout in checkouts)
|
||||
var/taken_minutes = round((world.time - checkout.get_date) / 1 MINUTES)
|
||||
var/due_raw = (checkout.due_date - world.time) / 1 MINUTES
|
||||
co += list(list(
|
||||
"ref" = REF(checkout),
|
||||
"book_name" = checkout.book_name,
|
||||
"mob_name" = checkout.mob_name,
|
||||
"taken_minutes" = taken_minutes,
|
||||
"due_minutes" = abs(round(due_raw)),
|
||||
"overdue" = (due_raw <= 0)
|
||||
))
|
||||
data["checkouts"] = co
|
||||
|
||||
var/list/scanner_data = list()
|
||||
scanner_data["found"] = !!(scanner?.anchored)
|
||||
scanner_data["title"] = scanner?.cache ? scanner.cache.name : null
|
||||
scanner_data["author"] = scanner?.cache ? (scanner.cache.author ? scanner.cache.author : "Anonymous") : null
|
||||
data["scanner"] = scanner_data
|
||||
|
||||
var/list/crew_names = list()
|
||||
var/list/manifest = SSrecords.get_manifest_list()
|
||||
for(var/dept in manifest)
|
||||
for(var/list/entry in manifest[dept])
|
||||
if(!entry["ooc_role"] && entry["name"])
|
||||
crew_names |= entry["name"]
|
||||
data["crew_names"] = crew_names
|
||||
|
||||
return data
|
||||
|
||||
/obj/machinery/librarycomp/emag_act(var/remaining_charges, var/mob/user)
|
||||
if(src.density && !src.emagged)
|
||||
src.emagged = TRUE
|
||||
return 1
|
||||
|
||||
/obj/machinery/librarycomp/attackby(obj/item/attacking_item, mob/user)
|
||||
if(istype(attacking_item, /obj/item/barcodescanner))
|
||||
var/obj/item/barcodescanner/barcode_scanner = attacking_item
|
||||
barcode_scanner.computer_ref = REF(src)
|
||||
to_chat(user, "[barcode_scanner]'s associated machine has been set to [src].")
|
||||
for(var/mob/hearer in hearers(src))
|
||||
hearer.show_message("[src] lets out a low, short blip.", 2)
|
||||
else
|
||||
..()
|
||||
|
||||
/obj/machinery/librarycomp/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
add_fingerprint(usr)
|
||||
switch(action)
|
||||
if("print_bible")
|
||||
if(!bible_on_cooldown)
|
||||
var/obj/item/storage/bible/bible = new /obj/item/storage/bible(src.loc)
|
||||
bible.verbs += /obj/item/storage/bible/verb/Set_Religion
|
||||
var/rand_book = "book" + pick("1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16")
|
||||
bible.icon_state = rand_book
|
||||
bible.item_state = rand_book
|
||||
bible.name = "religious book"
|
||||
bible_on_cooldown = TRUE
|
||||
addtimer(CALLBACK(src, PROC_REF(end_bible_cooldown)), 6 SECONDS, TIMER_UNIQUE|TIMER_STOPPABLE)
|
||||
. = TRUE
|
||||
if("arcane_confirm")
|
||||
if(emagged && !bible_on_cooldown)
|
||||
new /obj/item/book/tome(get_turf(src))
|
||||
to_chat(usr, SPAN_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."))
|
||||
usr.visible_message(
|
||||
SPAN_NOTICE("\The [usr] stares at the blank screen for a few moments, [usr.get_pronoun("his")] expression frozen in fear. When [usr.get_pronoun("he")] finally awakens from it, [usr.get_pronoun("he")] looks a lot older."),
|
||||
range = 2)
|
||||
bible_on_cooldown = TRUE
|
||||
addtimer(CALLBACK(src, PROC_REF(end_bible_cooldown)), 6 SECONDS, TIMER_UNIQUE|TIMER_STOPPABLE)
|
||||
. = TRUE
|
||||
if("increase_checkout_period")
|
||||
checkout_period_minutes += 1
|
||||
. = TRUE
|
||||
if("decrease_checkout_period")
|
||||
checkout_period_minutes = max(1, checkout_period_minutes - 1)
|
||||
. = TRUE
|
||||
if("checkout_book")
|
||||
var/book_title = sanitizeSafe(params["book_title"])
|
||||
var/recipient = sanitize(params["recipient"], MAX_NAME_LEN)
|
||||
if(book_title && recipient)
|
||||
var/datum/borrowbook/new_checkout = new /datum/borrowbook
|
||||
new_checkout.book_name = book_title
|
||||
new_checkout.mob_name = recipient
|
||||
new_checkout.get_date = world.time
|
||||
new_checkout.due_date = world.time + (checkout_period_minutes * 1 MINUTES)
|
||||
checkouts.Add(new_checkout)
|
||||
. = TRUE
|
||||
if("checkin_book")
|
||||
var/datum/borrowbook/checkout = locate(params["ref"])
|
||||
if(checkout && (checkout in checkouts))
|
||||
checkouts.Remove(checkout)
|
||||
. = TRUE
|
||||
if("delete_inventory_book")
|
||||
var/obj/item/book/book = locate(params["ref"])
|
||||
if(book && !QDELETED(book))
|
||||
inventory.Remove(WEAKREF(book))
|
||||
. = TRUE
|
||||
if("set_upload_category")
|
||||
var/new_category = params["value"]
|
||||
if(new_category in list("Fiction", "Non-Fiction", "Reference", "Religion"))
|
||||
upload_category = new_category
|
||||
. = TRUE
|
||||
if("set_upload_author")
|
||||
var/obj/machinery/libraryscanner/sc = get_scanner()
|
||||
if(sc?.cache)
|
||||
sc.cache.author = sanitize(params["value"])
|
||||
. = TRUE
|
||||
if("clear_scanner_cache")
|
||||
var/obj/machinery/libraryscanner/sc = get_scanner()
|
||||
if(sc)
|
||||
sc.cache = null
|
||||
SStgui.update_uis(sc)
|
||||
last_uploaded_title = null
|
||||
. = TRUE
|
||||
if("upload_book")
|
||||
var/obj/machinery/libraryscanner/sc = get_scanner()
|
||||
if(!is_public && sc?.anchored && sc.cache)
|
||||
if(sc.cache.unique)
|
||||
to_chat(usr, SPAN_WARNING("This book has been rejected from the database."))
|
||||
else
|
||||
INVOKE_ASYNC(src, PROC_REF(async_upload_book), usr)
|
||||
. = TRUE
|
||||
if("fetch_archive")
|
||||
if(!archive_loading)
|
||||
archive_page = 1
|
||||
archive_search = ""
|
||||
INVOKE_ASYNC(src, PROC_REF(async_fetch_archive))
|
||||
. = TRUE
|
||||
if("archive_go_to_page")
|
||||
var/new_page = text2num(params["page"])
|
||||
if(isnum(new_page) && new_page >= 1 && !archive_loading)
|
||||
archive_page = round(new_page)
|
||||
INVOKE_ASYNC(src, PROC_REF(async_fetch_archive))
|
||||
. = TRUE
|
||||
if("archive_set_search")
|
||||
if(!archive_loading)
|
||||
archive_search = sanitize(params["query"])
|
||||
archive_page = 1
|
||||
INVOKE_ASYNC(src, PROC_REF(async_fetch_archive))
|
||||
. = TRUE
|
||||
if("archive_set_sort")
|
||||
var/field = params["field"]
|
||||
if(field in list("title", "author", "category"))
|
||||
var/direction = params["dir"]
|
||||
archive_sort_field = field
|
||||
archive_sort_dir = (direction == "desc") ? "desc" : "asc"
|
||||
archive_page = 1
|
||||
if(!archive_loading)
|
||||
INVOKE_ASYNC(src, PROC_REF(async_fetch_archive))
|
||||
. = TRUE
|
||||
if("order_book")
|
||||
var/book_id = text2num(params["id"])
|
||||
if(isnum(book_id) && book_id > 0)
|
||||
if(!bible_on_cooldown)
|
||||
INVOKE_ASYNC(src, PROC_REF(async_order_book), book_id)
|
||||
else
|
||||
visible_message("\The [src]'s monitor flashes: \"Printer unavailable. Please allow a short time before attempting to print.\"")
|
||||
. = TRUE
|
||||
if("extend_checkout")
|
||||
var/datum/borrowbook/checkout = locate(params["ref"])
|
||||
if(checkout && (checkout in checkouts))
|
||||
checkout.due_date += checkout_period_minutes * 1 MINUTES
|
||||
. = TRUE
|
||||
if("print_inventory")
|
||||
var/dat = "<B>Library Inventory</B><BR><BR>"
|
||||
var/printed = 0
|
||||
for(var/datum/weakref/wref in inventory)
|
||||
var/obj/item/book/book = wref.resolve()
|
||||
if(book)
|
||||
dat += "- [book.name]<BR>"
|
||||
printed++
|
||||
if(!printed)
|
||||
dat += "No books in inventory.<BR>"
|
||||
var/obj/item/paper/printout = new /obj/item/paper(src.loc)
|
||||
printout.info = dat
|
||||
printout.name = "paper- 'Library Inventory'"
|
||||
. = TRUE
|
||||
if("print_checkouts")
|
||||
var/dat = "<B>Active Checkouts</B><BR><BR>"
|
||||
if(checkouts.len)
|
||||
for(var/datum/borrowbook/checkout in checkouts)
|
||||
var/taken_minutes = round((world.time - checkout.get_date) / 1 MINUTES)
|
||||
var/due_raw = (checkout.due_date - world.time) / 1 MINUTES
|
||||
var/status = due_raw <= 0 ? "OVERDUE by [abs(round(due_raw))] min" : "due in [round(due_raw)] min"
|
||||
dat += "- <B>[checkout.book_name]</B> ([checkout.mob_name]) — taken [taken_minutes] min ago, [status]<BR>"
|
||||
else
|
||||
dat += "No books currently checked out.<BR>"
|
||||
var/obj/item/paper/printout = new /obj/item/paper(src.loc)
|
||||
printout.info = dat
|
||||
printout.name = "paper- 'Active Checkouts'"
|
||||
. = TRUE
|
||||
|
||||
/obj/machinery/librarycomp/proc/end_bible_cooldown()
|
||||
bible_on_cooldown = FALSE
|
||||
SStgui.update_uis(src)
|
||||
|
||||
/obj/machinery/librarycomp/proc/get_scanner()
|
||||
var/obj/machinery/libraryscanner/scanner = scanner_ref?.resolve()
|
||||
if(!scanner)
|
||||
scanner_ref = null
|
||||
for(var/obj/machinery/libraryscanner/nearby_scanner in range(9))
|
||||
if(nearby_scanner.anchored)
|
||||
scanner_ref = WEAKREF(nearby_scanner)
|
||||
return nearby_scanner
|
||||
return null
|
||||
return scanner
|
||||
|
||||
/obj/machinery/librarycomp/proc/async_fetch_archive()
|
||||
archive_loading = TRUE
|
||||
archive_error = FALSE
|
||||
archive_total = 0
|
||||
archive_results = list()
|
||||
SStgui.update_uis(src)
|
||||
|
||||
// Build WHERE clause — keep % wildcards out of params via CONCAT so
|
||||
// the param value is the raw search term without special characters
|
||||
var/sql_where = ""
|
||||
var/list/count_args
|
||||
var/list/select_args
|
||||
if(length(archive_search))
|
||||
sql_where = "WHERE (title LIKE CONCAT('%', :search, '%') OR author LIKE CONCAT('%', :search, '%') OR category LIKE CONCAT('%', :search, '%'))"
|
||||
count_args = list("search" = archive_search)
|
||||
select_args = list("search" = archive_search)
|
||||
else
|
||||
count_args = list()
|
||||
select_args = list()
|
||||
|
||||
// Validate sort field and direction before interpolating into SQL
|
||||
var/safe_field
|
||||
switch(archive_sort_field)
|
||||
if("author") safe_field = "author"
|
||||
if("category") safe_field = "category"
|
||||
else safe_field = "title"
|
||||
var/safe_dir = (archive_sort_dir == "desc") ? "DESC" : "ASC"
|
||||
|
||||
// COUNT query to get total matching entries
|
||||
var/datum/db_query/count_query = SSdbcore.NewQuery(
|
||||
"SELECT COUNT(*) FROM ss13_library [sql_where]",
|
||||
count_args)
|
||||
if(!count_query.Execute())
|
||||
archive_error = TRUE
|
||||
qdel(count_query)
|
||||
archive_loading = FALSE
|
||||
SStgui.update_uis(src)
|
||||
return
|
||||
archive_total = count_query.NextRow() ? text2num(count_query.item[1]) : 0
|
||||
qdel(count_query)
|
||||
|
||||
// Clamp page to valid range now that we know the total
|
||||
var/total_pages = max(1, CEILING(archive_total, 20) / 20)
|
||||
archive_page = clamp(archive_page, 1, total_pages)
|
||||
var/sql_offset = (archive_page - 1) * 20
|
||||
|
||||
// Paginated SELECT
|
||||
var/datum/db_query/query = SSdbcore.NewQuery(
|
||||
"SELECT id, author, title, category FROM ss13_library [sql_where] ORDER BY [safe_field] [safe_dir] LIMIT 20 OFFSET [sql_offset]",
|
||||
select_args)
|
||||
if(!query.Execute())
|
||||
archive_error = TRUE
|
||||
qdel(query)
|
||||
archive_loading = FALSE
|
||||
SStgui.update_uis(src)
|
||||
return
|
||||
|
||||
var/list/results = list()
|
||||
while(query.NextRow())
|
||||
results += list(list(
|
||||
"id" = text2num(query.item[1]),
|
||||
"author" = query.item[2],
|
||||
"title" = query.item[3],
|
||||
"category" = query.item[4]
|
||||
))
|
||||
qdel(query)
|
||||
archive_results = results
|
||||
archive_loading = FALSE
|
||||
SStgui.update_uis(src)
|
||||
|
||||
/obj/machinery/librarycomp/proc/async_upload_book(mob/uploader)
|
||||
var/obj/machinery/libraryscanner/scanner = get_scanner()
|
||||
var/obj/item/book/book = scanner?.cache
|
||||
if(!book)
|
||||
return
|
||||
var/datum/db_query/query = SSdbcore.NewQuery(
|
||||
"INSERT INTO ss13_library (author, title, content, category, uploadtime, uploader) VALUES (:author, :title, :content, :category, NOW(), :uploader)",
|
||||
list(
|
||||
"author" = book.author ? book.author : "Anonymous",
|
||||
"title" = book.name,
|
||||
"content" = book.dat,
|
||||
"category" = upload_category,
|
||||
"uploader" = ckey(uploader.client.ckey)
|
||||
))
|
||||
if(!query.Execute())
|
||||
to_chat(uploader, SPAN_WARNING("Upload failed: [query.last_error]"))
|
||||
qdel(query)
|
||||
return
|
||||
qdel(query)
|
||||
log_and_message_admins("has uploaded the book titled [book.name], [length(book.dat)] signs")
|
||||
log_game("[uploader.name]/[uploader.key] has uploaded the book titled [book.name], [length(book.dat)] signs")
|
||||
to_chat(uploader, SPAN_NOTICE("Upload complete."))
|
||||
last_uploaded_title = book.name
|
||||
SStgui.update_uis(src)
|
||||
|
||||
/obj/machinery/librarycomp/proc/async_order_book(var/book_id)
|
||||
bible_on_cooldown = TRUE
|
||||
addtimer(CALLBACK(src, PROC_REF(end_bible_cooldown)), 6 SECONDS, TIMER_UNIQUE|TIMER_STOPPABLE)
|
||||
SStgui.update_uis(src)
|
||||
|
||||
var/datum/db_query/query = SSdbcore.NewQuery(
|
||||
"SELECT id, author, title, content FROM ss13_library WHERE id = :id",
|
||||
list("id" = book_id))
|
||||
if(!query.Execute() || !query.NextRow())
|
||||
qdel(query)
|
||||
return
|
||||
var/obj/item/book/ordered_book = new(src.loc)
|
||||
ordered_book.author = query.item[2]
|
||||
ordered_book.title = query.item[3]
|
||||
ordered_book.name = "Book: [query.item[3]]"
|
||||
ordered_book.dat = query.item[4]
|
||||
ordered_book.icon_state = "book[rand(1,16)]"
|
||||
ordered_book.item_state = ordered_book.icon_state
|
||||
qdel(query)
|
||||
src.visible_message("\The [src]'s printer hums as it produces a book.")
|
||||
|
||||
// Public Related Code
|
||||
/obj/machinery/librarycomp/public
|
||||
name = "public library computer"
|
||||
is_public = TRUE
|
||||
@@ -183,6 +183,14 @@
|
||||
update_icon()
|
||||
|
||||
|
||||
/*
|
||||
* Barcode Scanner mode defines — used in book/attackby and barcodescanner/attack_self
|
||||
*/
|
||||
#define BARCODE_SCAN_BUFFER_ONLY 0
|
||||
#define BARCODE_SCAN_BUFFER_COMPUTER 1
|
||||
#define BARCODE_SCAN_CHECKIN 2
|
||||
#define BARCODE_SCAN_ADD_INVENTORY 3
|
||||
|
||||
/*
|
||||
* Book
|
||||
*/
|
||||
@@ -273,35 +281,37 @@
|
||||
return
|
||||
else if(istype(attacking_item, /obj/item/barcodescanner))
|
||||
var/obj/item/barcodescanner/scanner = attacking_item
|
||||
if(!scanner.computer)
|
||||
var/obj/machinery/librarycomp/comp = locate(scanner.computer_ref)
|
||||
if(!comp || QDELETED(comp))
|
||||
to_chat(user, "[attacking_item]'s screen flashes: 'No associated computer found!'")
|
||||
else
|
||||
switch(scanner.mode)
|
||||
if(0)
|
||||
scanner.book = src
|
||||
if(BARCODE_SCAN_BUFFER_ONLY)
|
||||
scanner.book_ref = REF(src)
|
||||
to_chat(user, "[attacking_item]'s screen flashes: 'Book stored in buffer.'")
|
||||
if(1)
|
||||
scanner.book = src
|
||||
scanner.computer.buffer_book = src.name
|
||||
if(BARCODE_SCAN_BUFFER_COMPUTER)
|
||||
scanner.book_ref = REF(src)
|
||||
comp.buffer_book = src.name
|
||||
to_chat(user, "[attacking_item]'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)
|
||||
if(BARCODE_SCAN_CHECKIN)
|
||||
scanner.book_ref = REF(src)
|
||||
for(var/datum/borrowbook/checkout in comp.checkouts)
|
||||
if(checkout.book_name == src.name)
|
||||
comp.checkouts.Remove(checkout)
|
||||
to_chat(user, "[attacking_item]'s screen flashes: 'Book stored in buffer. Book has been checked in.'")
|
||||
return
|
||||
to_chat(user, "[attacking_item]'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, "[attacking_item]'s screen flashes: 'Book stored in buffer. Title already present in inventory, aborting to avoid duplicate entry.'")
|
||||
return
|
||||
scanner.computer.inventory.Add(src)
|
||||
if(BARCODE_SCAN_ADD_INVENTORY)
|
||||
scanner.book_ref = REF(src)
|
||||
var/datum/weakref/src_wref = WEAKREF(src)
|
||||
if(src_wref in comp.inventory)
|
||||
to_chat(user, "[attacking_item]'s screen flashes: 'Book stored in buffer. Title already present in inventory, aborting to avoid duplicate entry.'")
|
||||
return
|
||||
comp.inventory.Add(src_wref)
|
||||
to_chat(user, "[attacking_item]'s screen flashes: 'Book stored in buffer. Title added to general inventory.'")
|
||||
else if(istype(attacking_item, /obj/item/material/knife) || attacking_item.tool_behaviour == TOOL_WIRECUTTER)
|
||||
if(carved) return
|
||||
if(carved)
|
||||
return
|
||||
to_chat(user, SPAN_NOTICE("You begin to carve out [title]."))
|
||||
if(attacking_item.use_tool(src, user, 30, volume = 50))
|
||||
to_chat(user, SPAN_NOTICE("You carve out the pages from [title]! You didn't want to read it anyway."))
|
||||
@@ -326,34 +336,40 @@
|
||||
/obj/item/barcodescanner
|
||||
name = "book scanner"
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state ="scanner"
|
||||
icon_state = "scanner"
|
||||
throw_speed = 1
|
||||
throw_range = 5
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
var/obj/machinery/librarycomp/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
|
||||
var/computer_ref // REF string of the associated library computer
|
||||
var/book_ref // REF string of the currently scanned book
|
||||
var/mode = BARCODE_SCAN_BUFFER_ONLY
|
||||
|
||||
/obj/item/barcodescanner/attack_self(mob/user as mob)
|
||||
/obj/item/barcodescanner/attack_self(mob/user)
|
||||
mode += 1
|
||||
if(mode > 3)
|
||||
mode = 0
|
||||
if(mode > BARCODE_SCAN_ADD_INVENTORY)
|
||||
mode = BARCODE_SCAN_BUFFER_ONLY
|
||||
to_chat(user, "[src] Status Display:")
|
||||
var/modedesc
|
||||
var/mode_desc
|
||||
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."
|
||||
if(BARCODE_SCAN_BUFFER_ONLY)
|
||||
mode_desc = "Scan book to local buffer."
|
||||
if(BARCODE_SCAN_BUFFER_COMPUTER)
|
||||
mode_desc = "Scan book to local buffer and set associated computer buffer to match."
|
||||
if(BARCODE_SCAN_CHECKIN)
|
||||
mode_desc = "Scan book to local buffer, attempt to check in scanned book."
|
||||
if(BARCODE_SCAN_ADD_INVENTORY)
|
||||
mode_desc = "Scan book to local buffer, attempt to add book to general inventory."
|
||||
else
|
||||
modedesc = "ERROR"
|
||||
to_chat(user, " - Mode [mode] : [modedesc]")
|
||||
if(src.computer)
|
||||
mode_desc = "ERROR"
|
||||
to_chat(user, " - Mode [mode] : [mode_desc]")
|
||||
var/obj/machinery/librarycomp/comp = locate(src.computer_ref)
|
||||
if(comp && !QDELETED(comp))
|
||||
to_chat(user, SPAN_NOTICE("Computer has been associated with this unit."))
|
||||
else
|
||||
to_chat(user, SPAN_WARNING("No associated computer found. Only local scans will function properly."))
|
||||
to_chat(user, "\n")
|
||||
|
||||
#undef BARCODE_SCAN_BUFFER_ONLY
|
||||
#undef BARCODE_SCAN_BUFFER_COMPUTER
|
||||
#undef BARCODE_SCAN_CHECKIN
|
||||
#undef BARCODE_SCAN_ADD_INVENTORY
|
||||
|
||||
@@ -1,517 +0,0 @@
|
||||
/* Library Machines
|
||||
*
|
||||
* Contains:
|
||||
* * Borrowbook datum
|
||||
* * Library Public Computer
|
||||
* * Library Computer
|
||||
* * Library Scanner
|
||||
* * Book Binder
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
/*
|
||||
* Library Public Computer
|
||||
*/
|
||||
/obj/machinery/librarypubliccomp
|
||||
name = "public library computer"
|
||||
desc = "A computer."
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state = "computer"
|
||||
anchored = TRUE
|
||||
density = TRUE
|
||||
var/screenstate = 0
|
||||
var/title
|
||||
var/category = "Any"
|
||||
var/author
|
||||
var/SQLquery
|
||||
|
||||
/obj/machinery/librarypubliccomp/attack_hand(var/mob/user)
|
||||
usr.set_machine(src)
|
||||
var/dat = ""
|
||||
switch(screenstate)
|
||||
if(0)
|
||||
dat += {"<h2>Search Settings</h2><br>
|
||||
<a href='byond://?src=[REF(src)];settitle=1'>Filter by Title: [title]</a><br>
|
||||
<a href='byond://?src=[REF(src)];setcategory=1'>Filter by Category: [category]</a><br>
|
||||
<a href='byond://?src=[REF(src)];setauthor=1'>Filter by Author: [author]</a><br>
|
||||
<a href='byond://?src=[REF(src)];search=1'>\[Start Search\]</a><br>"}
|
||||
if(1)
|
||||
if(!establish_db_connection(GLOB.dbcon))
|
||||
dat += "<font color=red><b>ERROR</b>: Unable to contact External Archive. Please contact your system administrator for assistance.</font><br>"
|
||||
else if(!SQLquery)
|
||||
dat += "<font color=red><b>ERROR</b>: Malformed search request. Please contact your system administrator for assistance.</font><br>"
|
||||
else
|
||||
dat += {"<table>
|
||||
<tr><td>AUTHOR</td><td>TITLE</td><td>CATEGORY</td><td>SS<sup>13</sup>BN</td></tr>"}
|
||||
|
||||
var/DBQuery/query = GLOB.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 += "<tr><td>[author]</td><td>[title]</td><td>[category]</td><td>[id]</td></tr>"
|
||||
dat += "</table><br>"
|
||||
dat += "<a href='byond://?src=[REF(src)];back=1'>\[Go Back\]</a><br>"
|
||||
user << browse(HTML_SKELETON_TITLE("Library Visitor", dat), "window=publiclibrary")
|
||||
onclose(user, "publiclibrary")
|
||||
|
||||
/obj/machinery/librarypubliccomp/Topic(href, href_list)
|
||||
if(..())
|
||||
usr << browse(null, "window=publiclibrary")
|
||||
onclose(usr, "publiclibrary")
|
||||
return
|
||||
|
||||
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 = sanitizeSQL(title)
|
||||
if(href_list["setcategory"])
|
||||
var/newcategory = input("Choose a category to search for:") in list("Any", "Fiction", "Non-Fiction", "Reference", "Religion")
|
||||
if(newcategory)
|
||||
category = sanitize(newcategory)
|
||||
else
|
||||
category = "Any"
|
||||
category = sanitizeSQL(category)
|
||||
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 = sanitizeSQL(author)
|
||||
if(href_list["search"])
|
||||
SQLquery = "SELECT author, title, category, id FROM ss13_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
|
||||
|
||||
/*
|
||||
* Library Computer
|
||||
*/
|
||||
/obj/machinery/librarycomp
|
||||
name = "library computer"
|
||||
desc = "A computer."
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state = "computer"
|
||||
anchored = TRUE
|
||||
density = TRUE
|
||||
var/arcanecheckout = FALSE
|
||||
var/screenstate = 0 // 0: Main Menu - 1: Inventory - 2: Checked Out - 3: Check Out
|
||||
var/sortby = "author"
|
||||
var/buffer_book
|
||||
var/buffer_mob
|
||||
var/upload_category = "Fiction"
|
||||
var/list/checkouts = list()
|
||||
var/list/inventory = list()
|
||||
var/checkoutperiod = 5 // In minutes
|
||||
var/bibledelay = 0
|
||||
var/is_public = FALSE
|
||||
var/obj/machinery/libraryscanner/scanner // Book scanner that will be used when uploading books to the Archive
|
||||
|
||||
/obj/machinery/librarycomp/attack_hand(var/mob/user)
|
||||
user.set_machine(src)
|
||||
var/dat = ""
|
||||
switch(screenstate)
|
||||
if(0)
|
||||
// Main Menu
|
||||
dat += "<a href='byond://?src=[REF(src)];switchscreen=1'>View Stock</a><br>"
|
||||
dat += "<a href='byond://?src=[REF(src)];switchscreen=2'>View Checked Out Books</a><br>"
|
||||
dat += "<a href='byond://?src=[REF(src)];switchscreen=3'>Check out a Book</a><br>"
|
||||
dat += "<a href='byond://?src=[REF(src)];switchscreen=4'>Order From Library Database</a><br>"
|
||||
if(!is_public)
|
||||
dat += "<a href='byond://?src=[REF(src)];switchscreen=5'>Upload New Title to Library Database</a><br>"
|
||||
dat += "<a href='byond://?src=[REF(src)];switchscreen=6'>Print a Bible</a><br>"
|
||||
if(emagged)
|
||||
dat += "<a href='byond://?src=[REF(src)];switchscreen=7'>7. Access the Forbidden Lore Vault</a><br>"
|
||||
if(arcanecheckout)
|
||||
new /obj/item/book/tome(get_turf(src))
|
||||
to_chat(user, SPAN_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."))
|
||||
user.visible_message(SPAN_NOTICE("\The [user] stares at the blank screen for a few moments, [user.get_pronoun("his")] expression frozen in fear. When [user.get_pronoun("he")] finally awakens from it, [user.get_pronoun("he")] looks a lot older."), range = 2)
|
||||
arcanecheckout = FALSE
|
||||
if(1)
|
||||
// Inventory
|
||||
dat += "<H3>Inventory</H3><br>"
|
||||
for(var/obj/item/book/b in inventory)
|
||||
dat += "[b.name] <a href='byond://?src=[REF(src)];delbook=[REF(b)]'>(Delete)</a><br>"
|
||||
dat += "<a href='byond://?src=[REF(src)];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='byond://?src=[REF(src)];checkin=[REF(b)]'>(Check In)</a><br><br>"}
|
||||
dat += "<a href='byond://?src=[REF(src)];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='byond://?src=[REF(src)];editbook=1'>\[Edit\]</a><br>
|
||||
Recipient: [src.buffer_mob]
|
||||
<a href='byond://?src=[REF(src)];editmob=1'>\[Edit\]</a><br>
|
||||
Checkout Date: [world.time / 600]<br>
|
||||
Due Date: [(world.time + checkoutperiod) / 600]<br>
|
||||
(Checkout Period: [checkoutperiod] minutes) (<a href='byond://?src=[REF(src)];increasetime=1'>+</a>/<a href='byond://?src=[REF(src)];decreasetime=1'>-</a>)<br>
|
||||
<a href='byond://?src=[REF(src)];checkout=1'>(Commit Entry)</a><br>
|
||||
<a href='byond://?src=[REF(src)];switchscreen=0'>(<-- Return to Main Menu)</a><br>"}
|
||||
if(4)
|
||||
dat += "<h3>External Archive</h3>"
|
||||
if(!establish_db_connection(GLOB.dbcon))
|
||||
dat += "<font color=red><b>ERROR</b>: Unable to contact External Archive. Please contact your system administrator for assistance.</font>"
|
||||
else
|
||||
dat += {"<a href='byond://?src=[REF(src)];orderbyid=1'>(Order Book by ISBN)</a><br><br>
|
||||
<table>
|
||||
<tr><td><a href='byond://?src=[REF(src)];sort=author'>AUTHOR</a></td><td><a href='byond://?src=[REF(src)];sort=title'>TITLE</a></td><td><a href='byond://?src=[REF(src)];sort=category'>CATEGORY</a></td><td></td></tr>"}
|
||||
var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT id, author, title, category FROM ss13_library ORDER BY [sortby]")
|
||||
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 += "<tr><td>[author]</td><td>[title]</td><td>[category]</td><td><a href='byond://?src=[REF(src)];targetid=[id]'>\[Order\]</a></td></tr>"
|
||||
dat += "</table>"
|
||||
dat += "<br><a href='byond://?src=[REF(src)];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))
|
||||
if(S.anchored)
|
||||
scanner = S
|
||||
break
|
||||
if(!(scanner?.anchored))
|
||||
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='byond://?src=[REF(src)];setauthor=1'>[scanner.cache.author]</a><br>
|
||||
<TT>Category: </TT><a href='byond://?src=[REF(src)];setcategory=1'>[upload_category]</a><br>
|
||||
<a href='byond://?src=[REF(src)];upload=1'>\[Upload\]</a><br>"}
|
||||
dat += "<a href='byond://?src=[REF(src)];switchscreen=0'>(<-- Return to Main Menu)</a><br>"
|
||||
if(7)
|
||||
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='byond://?src=[REF(src)];arccheckout=1'>Yes.</a><br>
|
||||
<a href='byond://?src=[REF(src)];switchscreen=0'>No.</a><br>"}
|
||||
|
||||
//dat += "<a href='byond://?src=[REF(user)];mach_close=library'>Close</a><br><br>"
|
||||
user << browse(HTML_SKELETON_TITLE(is_public ? "[SSatlas.current_map.station_name] Library" : "[SSatlas.current_map.station_name] Library Management", dat), "window=library")
|
||||
onclose(user, "library")
|
||||
|
||||
/obj/machinery/librarycomp/emag_act(var/remaining_charges, var/mob/user)
|
||||
if (src.density && !src.emagged)
|
||||
src.emagged = 1
|
||||
return 1
|
||||
|
||||
/obj/machinery/librarycomp/attackby(obj/item/attacking_item, mob/user)
|
||||
if(istype(attacking_item, /obj/item/barcodescanner))
|
||||
var/obj/item/barcodescanner/scanner = attacking_item
|
||||
scanner.computer = src
|
||||
to_chat(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(..())
|
||||
usr << browse(null, "window=library")
|
||||
onclose(usr, "library")
|
||||
return
|
||||
|
||||
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)
|
||||
B.verbs += /obj/item/storage/bible/verb/Set_Religion
|
||||
var/randbook = "book" + pick("1", "2", "3", "4", "5", "6" , "7", "8", "9", "10", "11", "12", "13" , "14", "15" , "16")
|
||||
B.icon_state = randbook
|
||||
B.item_state = randbook
|
||||
B.name = "religious book"
|
||||
|
||||
bibledelay = 1
|
||||
spawn(60)
|
||||
bibledelay = 0
|
||||
|
||||
else
|
||||
for (var/mob/V in hearers(src))
|
||||
V.show_message("<b>[src]</b>'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 = sanitizeSafe(input("Enter the book's title:") as text|null)
|
||||
if(href_list["editmob"])
|
||||
buffer_mob = sanitize(input("Enter the recipient's name:") as text|null, MAX_NAME_LEN)
|
||||
if(href_list["checkout"])
|
||||
var/datum/borrowbook/b = new /datum/borrowbook
|
||||
b.bookname = sanitizeSafe(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["setauthor"])
|
||||
var/newauthor = sanitize(input("Enter the author's name: ") as text|null)
|
||||
if(newauthor)
|
||||
scanner.cache.author = newauthor
|
||||
if(href_list["setcategory"])
|
||||
var/newcategory = input("Choose a category: ") in list("Fiction", "Non-Fiction", "Reference", "Religion")
|
||||
if(newcategory)
|
||||
upload_category = newcategory
|
||||
if(href_list["upload"])
|
||||
if(scanner?.anchored)
|
||||
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(scanner.cache.unique)
|
||||
alert("This book has been rejected from the database. Aborting!")
|
||||
else
|
||||
if(!establish_db_connection(GLOB.dbcon))
|
||||
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/sqlckey = sanitizeSQL(ckey(usr.client.ckey))
|
||||
var/DBQuery/query = GLOB.dbcon.NewQuery("INSERT INTO ss13_library (author, title, content, category, uploadtime, uploader) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory]', NOW(), '[sqlckey]')")
|
||||
if(!query.Execute())
|
||||
to_chat(usr, query.ErrorMsg())
|
||||
else
|
||||
log_and_message_admins("has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] signs")
|
||||
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"])
|
||||
var/sqlid = sanitizeSQL(href_list["targetid"])
|
||||
if(!establish_db_connection(GLOB.dbcon))
|
||||
alert("Connection to Archive has been severed. Aborting.")
|
||||
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
|
||||
var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT * FROM ss13_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/book/B = new(src.loc)
|
||||
B.name = "Book: [title]"
|
||||
B.title = title
|
||||
B.author = author
|
||||
B.dat = content
|
||||
B.icon_state = "book[rand(1,16)]"
|
||||
B.item_state = B.icon_state
|
||||
src.visible_message("\The [src]\s printer hums as it produces a book.")
|
||||
break
|
||||
if(href_list["orderbyid"])
|
||||
var/orderid = input("Enter your order:") as num|null
|
||||
if(orderid)
|
||||
if(isnum(orderid))
|
||||
var/nhref = "src=[REF(src)];targetid=[orderid]"
|
||||
spawn() src.Topic(nhref, params2list(nhref), src)
|
||||
if(href_list["sort"] in list("author", "title", "category"))
|
||||
sortby = href_list["sort"]
|
||||
src.add_fingerprint(usr)
|
||||
src.updateUsrDialog()
|
||||
return
|
||||
|
||||
// Public Related Code
|
||||
/obj/machinery/librarycomp/public
|
||||
name = "public library computer"
|
||||
is_public = TRUE
|
||||
|
||||
/*
|
||||
* Library Scanner
|
||||
*/
|
||||
/obj/machinery/libraryscanner
|
||||
name = "book scanner"
|
||||
desc = "A machine that scans books for upload to the library database."
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state = "bigscanner"
|
||||
var/insert_anim = "bigscanner1"
|
||||
anchored = TRUE
|
||||
density = TRUE
|
||||
var/obj/item/book/cache // Last scanned book
|
||||
|
||||
/obj/machinery/libraryscanner/attackby(obj/item/attacking_item, mob/user)
|
||||
if(istype(attacking_item, /obj/item/book))
|
||||
if(!anchored)
|
||||
to_chat(user, SPAN_WARNING("\The [src] must be secured to the floor first!"))
|
||||
return
|
||||
user.drop_from_inventory(attacking_item,src)
|
||||
if(attacking_item.tool_behaviour == TOOL_WRENCH)
|
||||
attacking_item.play_tool_sound(get_turf(src), 75)
|
||||
if(anchored)
|
||||
user.visible_message(SPAN_NOTICE("\The [user] unsecures \the [src] from the floor."),
|
||||
SPAN_NOTICE("You unsecure \the [src] from the floor."),
|
||||
SPAN_WARNING("You hear a ratcheting noise."))
|
||||
else
|
||||
user.visible_message(SPAN_NOTICE("\The [user] secures \the [src] to the floor."),
|
||||
SPAN_NOTICE("You secure \the [src] to the floor."),
|
||||
SPAN_WARNING("You hear a ratcheting noise."))
|
||||
anchored = !anchored
|
||||
|
||||
/obj/machinery/libraryscanner/attack_hand(var/mob/user)
|
||||
usr.set_machine(src)
|
||||
var/dat = ""
|
||||
if(cache)
|
||||
dat += "<FONT color=#005500>Data stored in memory.</FONT><br>"
|
||||
else
|
||||
dat += "No data stored in memory.<br>"
|
||||
dat += "<a href='byond://?src=[REF(src)];scan=1'>\[Scan\]</a>"
|
||||
if(cache)
|
||||
dat += " <a href='byond://?src=[REF(src)];clear=1'>\[Clear Memory\]</a><br><br><a href='byond://?src=[REF(src)];eject=1'>\[Remove Book\]</a>"
|
||||
else
|
||||
dat += "<br>"
|
||||
user << browse(HTML_SKELETON_TITLE("Scanner Control Interface", 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"])
|
||||
flick(insert_anim, src)
|
||||
playsound(loc, 'sound/items/bureaucracy/scan.ogg', 75, 1)
|
||||
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.forceMove(src.loc)
|
||||
src.add_fingerprint(usr)
|
||||
src.updateUsrDialog()
|
||||
return
|
||||
|
||||
/*
|
||||
* Book binder
|
||||
*/
|
||||
/obj/machinery/bookbinder
|
||||
name = "book binder"
|
||||
desc = "A machine that takes paper and binds them into books. Fascinating!"
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state = "binder"
|
||||
anchored = TRUE
|
||||
density = TRUE
|
||||
var/binding = FALSE
|
||||
|
||||
/obj/machinery/bookbinder/attackby(obj/item/attacking_item, mob/user)
|
||||
if(istype(attacking_item, /obj/item/paper))
|
||||
var/obj/item/paper/paper = attacking_item
|
||||
if(!anchored)
|
||||
to_chat(user, SPAN_WARNING("\The [src] must be secured to the floor first!"))
|
||||
return
|
||||
if(binding)
|
||||
to_chat(user, SPAN_WARNING("You must wait for \the [src] to finish its current operation!"))
|
||||
return
|
||||
var/turf/T = get_turf(src)
|
||||
user.drop_from_inventory(paper,src)
|
||||
user.visible_message(SPAN_NOTICE("\The [user] loads some paper into \the [src]."), SPAN_NOTICE("You load some paper into \the [src]."))
|
||||
visible_message(SPAN_NOTICE("\The [src] begins to hum as it warms up its printing drums."))
|
||||
playsound(T, 'sound/items/bureaucracy/binder.ogg', 75, 1)
|
||||
binding = TRUE
|
||||
sleep(rand(200,400))
|
||||
binding = FALSE
|
||||
if(!anchored)
|
||||
visible_message(SPAN_WARNING("\The [src] buzzes and flashes an error light."))
|
||||
paper.forceMove(T)
|
||||
return
|
||||
visible_message(SPAN_NOTICE("\The [src] whirs as it prints and binds a new book."))
|
||||
playsound(T, 'sound/items/bureaucracy/print.ogg', 75, 1)
|
||||
var/obj/item/book/b = new(T)
|
||||
b.dat = paper.info
|
||||
b.name = "blank book"
|
||||
b.icon_state = "book[rand(1,7)]"
|
||||
b.item_state = icon_state
|
||||
qdel(paper)
|
||||
return
|
||||
|
||||
if(attacking_item.tool_behaviour == TOOL_WRENCH)
|
||||
attacking_item.play_tool_sound(get_turf(src), 75)
|
||||
if(anchored)
|
||||
user.visible_message(SPAN_NOTICE("\The [user] unsecures \the [src] from the floor."), \
|
||||
SPAN_NOTICE("You unsecure \the [src] from the floor."), \
|
||||
SPAN_WARNING("You hear a ratcheting noise."))
|
||||
else
|
||||
user.visible_message(SPAN_NOTICE("\The [user] secures \the [src] to the floor."), \
|
||||
SPAN_NOTICE("You secure \the [src] to the floor."), \
|
||||
SPAN_WARNING("You hear a ratcheting noise."))
|
||||
anchored = !anchored
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Library Public Computer
|
||||
*/
|
||||
/obj/machinery/librarypubliccomp
|
||||
name = "public library computer"
|
||||
desc = "A computer."
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state = "computer"
|
||||
anchored = TRUE
|
||||
density = TRUE
|
||||
var/list/search_results = list()
|
||||
var/search_title = ""
|
||||
var/search_author = ""
|
||||
var/search_category = "Any"
|
||||
var/db_loading = FALSE
|
||||
var/db_error = FALSE
|
||||
|
||||
/obj/machinery/librarypubliccomp/attack_hand(var/mob/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
return TRUE
|
||||
ui_interact(user)
|
||||
|
||||
/obj/machinery/librarypubliccomp/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "LibraryPublicComputer", "Public Library Terminal", 650, 500)
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/librarypubliccomp/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["search_title"] = search_title
|
||||
data["search_author"] = search_author
|
||||
data["search_category"] = search_category
|
||||
data["db_loading"] = db_loading
|
||||
data["db_error"] = db_error
|
||||
data["search_results"] = search_results
|
||||
return data
|
||||
|
||||
/obj/machinery/librarypubliccomp/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
add_fingerprint(usr)
|
||||
switch(action)
|
||||
if("search")
|
||||
if(!db_loading)
|
||||
search_title = sanitize(params["title"])
|
||||
search_author = sanitize(params["author"])
|
||||
var/new_category = params["category"]
|
||||
if(new_category in list("Any", "Fiction", "Non-Fiction", "Reference", "Religion"))
|
||||
search_category = new_category
|
||||
INVOKE_ASYNC(src, PROC_REF(async_search))
|
||||
. = TRUE
|
||||
|
||||
/obj/machinery/librarypubliccomp/proc/async_search()
|
||||
db_loading = TRUE
|
||||
db_error = FALSE
|
||||
search_results = list()
|
||||
SStgui.update_uis(src)
|
||||
|
||||
var/sql
|
||||
var/list/query_args
|
||||
if(search_category != "Any")
|
||||
sql = "SELECT author, title, category, id FROM ss13_library WHERE author LIKE :author AND title LIKE :title AND category = :category"
|
||||
query_args = list(
|
||||
"title" = "%[search_title]%",
|
||||
"author" = "%[search_author]%",
|
||||
"category" = search_category
|
||||
)
|
||||
else
|
||||
sql = "SELECT author, title, category, id FROM ss13_library WHERE author LIKE :author AND title LIKE :title"
|
||||
query_args = list(
|
||||
"title" = "%[search_title]%",
|
||||
"author" = "%[search_author]%"
|
||||
)
|
||||
|
||||
var/datum/db_query/query = SSdbcore.NewQuery(sql, query_args)
|
||||
if(!query.Execute())
|
||||
db_error = TRUE
|
||||
qdel(query)
|
||||
db_loading = FALSE
|
||||
SStgui.update_uis(src)
|
||||
return
|
||||
|
||||
var/list/results = list()
|
||||
while(query.NextRow())
|
||||
results += list(list(
|
||||
"author" = query.item[1],
|
||||
"title" = query.item[2],
|
||||
"category" = query.item[3],
|
||||
"id" = text2num(query.item[4])
|
||||
))
|
||||
qdel(query)
|
||||
search_results = results
|
||||
db_loading = FALSE
|
||||
SStgui.update_uis(src)
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Library Scanner
|
||||
*/
|
||||
/obj/machinery/libraryscanner
|
||||
name = "book scanner"
|
||||
desc = "A machine that scans books for upload to the library database."
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state = "bigscanner"
|
||||
var/insert_animation = "bigscanner1"
|
||||
anchored = TRUE
|
||||
density = TRUE
|
||||
var/obj/item/book/cache // Last scanned book
|
||||
|
||||
/obj/machinery/libraryscanner/attackby(obj/item/attacking_item, mob/user)
|
||||
if(istype(attacking_item, /obj/item/book))
|
||||
if(!anchored)
|
||||
to_chat(user, SPAN_WARNING("\The [src] must be secured to the floor first!"))
|
||||
return
|
||||
user.drop_from_inventory(attacking_item, src)
|
||||
if(attacking_item.tool_behaviour == TOOL_WRENCH)
|
||||
attacking_item.play_tool_sound(get_turf(src), 75)
|
||||
if(anchored)
|
||||
user.visible_message(
|
||||
SPAN_NOTICE("\The [user] unsecures \the [src] from the floor."),
|
||||
SPAN_NOTICE("You unsecure \the [src] from the floor."),
|
||||
SPAN_WARNING("You hear a ratcheting noise."))
|
||||
else
|
||||
user.visible_message(
|
||||
SPAN_NOTICE("\The [user] secures \the [src] to the floor."),
|
||||
SPAN_NOTICE("You secure \the [src] to the floor."),
|
||||
SPAN_WARNING("You hear a ratcheting noise."))
|
||||
anchored = !anchored
|
||||
|
||||
/obj/machinery/libraryscanner/attack_hand(var/mob/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
return TRUE
|
||||
ui_interact(user)
|
||||
|
||||
/obj/machinery/libraryscanner/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "LibraryScanner", "Book Scanner", 400, 250)
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/libraryscanner/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["has_book"] = !!cache
|
||||
data["book_title"] = cache ? cache.name : null
|
||||
data["book_author"] = cache ? cache.author : null
|
||||
data["is_anchored"] = anchored
|
||||
return data
|
||||
|
||||
/obj/machinery/libraryscanner/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
add_fingerprint(usr)
|
||||
switch(action)
|
||||
if("scan")
|
||||
flick(insert_animation, src)
|
||||
playsound(loc, 'sound/items/bureaucracy/scan.ogg', 75, 1)
|
||||
for(var/obj/item/book/book in contents)
|
||||
cache = book
|
||||
break
|
||||
. = TRUE
|
||||
if("clear")
|
||||
cache = null
|
||||
. = TRUE
|
||||
if("eject")
|
||||
for(var/obj/item/book/book in contents)
|
||||
book.forceMove(src.loc)
|
||||
cache = null
|
||||
. = TRUE
|
||||
Reference in New Issue
Block a user