diff --git a/README.md b/README.md index b21eeca..ab6c290 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,65 @@ -# SlippyCheeze's Read It Once - -Reduce the effort and memorization required to read books throughout the -world. When you encounter a book you have previously seen, it will display an -alert informing you of that, rather than opening the book for reading. - -The very first time you encounter a book, however, it will be shown as normal. - -I prefer this to blocking all books, all the time, as I enjoy the lore ... I -just don't want to be trying to remember if I read the book, and dislike the -time taken to close it again when searching a bazillion bookshelves in -a delve. - -## Inventory and Journal books are displayed normally - -Anything in your inventory, or accessed through the Lore Journal, will display -normally. Accessing books through non-standard book collecting addons should -work normally, but has not been tested. Please report issues appropriately. - -## Configuration - -There is no configuration for the addon. - -This is an account-wide addon, so any book seen on any character will count as -"read". You should, however, still receive full credit for skill -points regardless. - -## Help! I have a problem! - -### I need to read a book again, for a quest! - -If you try and read the exact same book twice within one second, you bypass -the block. We still play the alert sound and display the warning the first -time you interact, but the second will open it as it would without the -addon installed. - -I use this for the occasional quest note that I need to reference more than -once, and found that one second was more than enough time to make this -work smoothly. - -### I found a different problem, what should I do? - -Please report bugs [through the GitHub issue tracker][gh-bugs]. You can also -post them in the comments section here, if you must, though I'm less likely to -notice and fix them that way. - -## Can I contribute? - -I'm happy to accept contributions, though I strongly prefer keeping addons -small, and very single purpose, so do try and stick with the single theme. - -Please also stick to one change per submission: if you make multiple, -unrelated changes, I'm not going to be very enthusiastic about adding that, -compared to someone who sent them separately. - -The [code is available on GitHub][gh-repo], and git patches and/or GitHub pull -requests are the best possible way to contribute. - -Sending, or linking, modified files, is much more difficult, but if you must, -you must. If you go this path, please make absolutely certain that you tell -me which version the change was made against, so that I can integrate -it safely. - -[gh-bugs]: https://github.com/slippycheeze/eso-SlippyCheezeReadItOnce/issues/new -[gh-repo]: https://github.com/slippycheeze/eso-SlippyCheezeReadItOnce +# SlippyCheeze's Read It Once + +Reduce the effort and memorization required to read books throughout the +world. When you encounter a book you have previously seen, it will display an +alert informing you of that, rather than opening the book for reading. + +The very first time you encounter a book, however, it will be shown as normal. + +I prefer this to blocking all books, all the time, as I enjoy the lore ... I +just don't want to be trying to remember if I read the book, and dislike the +time taken to close it again when searching a bazillion bookshelves in +a delve. + +## Inventory and Journal books are displayed normally + +Anything in your inventory, or accessed through the Lore Journal, will display +normally. Accessing books through non-standard book collecting addons should +work normally, but has not been tested. Please report issues appropriately. + +## Configuration + +There is no configuration for the addon. + +This is an account-wide addon, so any book seen on any character will count as +"read". You should, however, still receive full credit for skill +points regardless. + +## Help! I have a problem! + +### I need to read a book again, for a quest! + +If you try and read the exact same book twice within one second, you bypass +the block. We still play the alert sound and display the warning the first +time you interact, but the second will open it as it would without the +addon installed. + +I use this for the occasional quest note that I need to reference more than +once, and found that one second was more than enough time to make this +work smoothly. + +### I found a different problem, what should I do? + +Please report bugs [through the GitHub issue tracker][gh-bugs]. You can also +post them in the comments section here, if you must, though I'm less likely to +notice and fix them that way. + +## Can I contribute? + +I'm happy to accept contributions, though I strongly prefer keeping addons +small, and very single purpose, so do try and stick with the single theme. + +Please also stick to one change per submission: if you make multiple, +unrelated changes, I'm not going to be very enthusiastic about adding that, +compared to someone who sent them separately. + +The [code is available on GitHub][gh-repo], and git patches and/or GitHub pull +requests are the best possible way to contribute. + +Sending, or linking, modified files, is much more difficult, but if you must, +you must. If you go this path, please make absolutely certain that you tell +me which version the change was made against, so that I can integrate +it safely. + +[gh-bugs]: https://github.com/slippycheeze/eso-SlippyCheezeReadItOnce/issues/new +[gh-repo]: https://github.com/slippycheeze/eso-SlippyCheezeReadItOnce diff --git a/SlippyCheezeReadItOnce.lua b/SlippyCheezeReadItOnce.lua index ecb7cd8..2066ce9 100644 --- a/SlippyCheezeReadItOnce.lua +++ b/SlippyCheezeReadItOnce.lua @@ -1,127 +1,141 @@ --- Copyright © 2018 Daniel Pittman <daniel@rimspace.net> --- See LICENSE for more details. - --- addon core object. -local ADDON_NAME = "SlippyCheezeReadItOnce" -local DISPLAY_NAME = "Read It Once" - -local DOUBLE_TAP_TIME = 1000 -local previousBook = {id=nil, time=0} - --- saved var: which books have been seen. -local seen - -local unpack = table.unpack or unpack -local insert = table.insert -local function dmsg(msg, ...) - local args = {} - for n=1, select('#', ...) do - insert(args, tostring(select(n, ...))) - end - - d(zo_strformat(msg, unpack(args))) -end - --- return bool, have we seen this before. never called before saved variables --- are loaded and initialized. -local function HaveSeenBookBefore(id, title, body) - if type(id) ~= "number" then - dmsg("ReadItOnce: id is <<1>> (<<2>>)", type(id), id) - return false - end - - -- ensure that we index by string, not number, in the table. - local id = tostring(id) - local bodyHash = HashString(body) - - local record = seen[id] - if record then - -- probably have seen it before, but check for changes - if record.id ~= id then - d("ReadItOnce: book id changed from <<1>> to <<2>>", record.id, id) - end - if record.title ~= title then - d("ReadItOnce: book title changed from '<<1>>' to '<<2>>'", record.title, title) - end - if record.bodyHash ~= bodyHash then - d("ReadItOnce: book body changed") - end - - -- don't show. - return true - end - - -- have not seen, record it, and return that fact - seen[id] = {id=id, title=title, bodyHash=bodyHash} - return false -end - --- Sadly, we have to override the original method, which is a local anonymous --- function, and which we have apparently no access to in order to hook nicely. --- --- The bulk of this is a direct copy-paste from the lore reader, as of USOUI --- 100023 --- --- The HaveSeenBook logic is my addition. -local function OnShowBookOverride(eventCode, title, body, medium, showTitle, bookId) - -- by default, only block books when we are in the default in-game - -- interaction mode, so that inventory, lore journal, etc, activations do - -- not get blocked. - local force_show = not SCENE_MANAGER:IsShowingBaseScene() - - -- handle the case of double-activation to bypass the restriction. - local now = GetGameTimeMilliseconds() - if previousBook.id == bookId then - if (now - previousBook.time) < DOUBLE_TAP_TIME then - force_show = true - end - else - previousBook.id = bookId - end - -- dmsg("force_show <<1>> now <<2>> prev.time <<3>> deltaT <<4>> prev.id <<5>> id <<6>>", - -- force_show, now, previousBook.time, now - previousBook.time, previousBook.id, id) - previousBook.time = now - - -- implement the block, if appropriate. - if HaveSeenBookBefore(bookId, title, body) and not force_show then - PlaySound(SOUNDS.NEGATIVE_CLICK) - - -- local msg = zo_strformat("<<1>>: You have already read \"<<2>>\"", DISPLAY_NAME, title) - local msg = zo_strformat("You have already read \"<<1>>\"", title) - - -- ZO_AlertNoSuppression(UI_ALERT_CATEGORY_ALERT, nil, ) - - local params = CENTER_SCREEN_ANNOUNCE:CreateMessageParams(CSA_CATEGORY_SMALL_TEXT, nil) - params:SetText(msg) - params:SetCSAType(CENTER_SCREEN_ANNOUNCE_TYPE_LORE_BOOK_LEARNED) - params:SetLifespanMS(850) - CENTER_SCREEN_ANNOUNCE:AddMessageWithParams(params) - - EndInteraction(INTERACTION_BOOK) - return - end - - -- meh, this is copied from the local function in the ZOS code. :( - if LORE_READER:Show(title, body, medium, showTitle) then - PlaySound(LORE_READER.OpenSound) - else - EndInteraction(INTERACTION_BOOK) - end -end - -local function OnAddonLoaded(_, name) - if name ~= ADDON_NAME then return end - EVENT_MANAGER:UnregisterForEvent(ADDON_NAME, EVENT_ADD_ON_LOADED) - - -- if the second argument, the version, changes then the data is wiped and - -- replaced with the defaults. - seen = ZO_SavedVars:NewAccountWide("SlippyCheezeReadItOnceData", 1) - - -- replace the original event handler with ours; sadly, we don't have - -- access to the original implementation to do anything nicer. :/ - LORE_READER.control:UnregisterForEvent(EVENT_SHOW_BOOK) - LORE_READER.control:RegisterForEvent(EVENT_SHOW_BOOK, OnShowBookOverride) -end - --- bootstrapping -EVENT_MANAGER:RegisterForEvent(ADDON_NAME, EVENT_ADD_ON_LOADED, OnAddonLoaded) +-- Copyright © 2018 Daniel Pittman <daniel@rimspace.net> +-- See LICENSE for more details. +if not SlippyCheezeReadItOnce then + SlippyCheezeReadItOnce = { + ADDON_NAME="SlippyCheezeReadItOnce", + DISPLAY_NAME = "|c798BD2ReadItOnce|r", + DOUBLE_TAP_TIME = 1000, + previousBook = {id=nil, time=0}, + -- seen holds our saved variables. + seen = {} + } +end + +-- my local alias for the addon itself. +local M = SlippyCheezeReadItOnce + +local unpack = table.unpack or unpack +local insert = table.insert + +local function msg(msg, ...) + local args = {} + for n=1, select('#', ...) do + insert(args, tostring(select(n, ...))) + end + + d(DISPLAY_NAME..": "..zo_strformat(msg, unpack(args))) +end + +-- return bool, have we seen this before. never called before saved variables +-- are loaded and initialized. +function M:HaveSeenBookBefore(self, id, title, body) + if type(id) ~= "number" then + msg("ReadItOnce: id is <<1>> (<<2>>)", type(id), id) + return false + end + + -- ensure that we index by string, not number, in the table. + local id = tostring(id) + local bodyHash = HashString(body) + + local record = self.seen[id] + if record then + -- probably have seen it before, but check for changes + if record.id ~= id then + d("ReadItOnce: book id changed from <<1>> to <<2>>", record.id, id) + end + if record.title ~= title then + d("ReadItOnce: book title changed from '<<1>>' to '<<2>>'", record.title, title) + end + if record.bodyHash ~= bodyHash then + d("ReadItOnce: book body changed") + end + + -- don't show. + return true + end + + -- have not seen, record it, and return that fact + self.seen[id] = {id=id, title=title, bodyHash=bodyHash} + return false +end + +-- Called when we want to skip showing a book. Probably going to be very +-- strange if you call it any other time! +function M:DoNotShowThisBook(self) + PlaySound(SOUNDS.NEGATIVE_CLICK) + + -- local msg = zo_strformat("<<1>>: You have already read \"<<2>>\"", DISPLAY_NAME, title) + local msg = zo_strformat("You have already read \"<<1>>\"", title) + + -- ZO_AlertNoSuppression(UI_ALERT_CATEGORY_ALERT, nil, ) + + local params = CENTER_SCREEN_ANNOUNCE:CreateMessageParams(CSA_CATEGORY_SMALL_TEXT, nil) + params:SetText(msg) + params:SetCSAType(CENTER_SCREEN_ANNOUNCE_TYPE_LORE_BOOK_LEARNED) + params:SetLifespanMS(850) + CENTER_SCREEN_ANNOUNCE:AddMessageWithParams(params) + + EndInteraction(INTERACTION_BOOK) +end + +-- Sadly, we have to override the original method, which is a local anonymous +-- function, and which we have apparently no access to in order to hook nicely. +-- +-- The bulk of this is a direct copy-paste from the lore reader, as of USOUI +-- 100023 +-- +-- The HaveSeenBook logic is my addition. +function M:OnShowBookOverride(eventCode, title, body, medium, showTitle, bookId) + local do_not_show = false + + -- never block a book if we are not in the most basic state, which is the + -- world interaction state. + if SCENE_MANAGER:IsShowingBaseScene() then + -- handle the case of double-activation to bypass the restriction. + local now = GetGameTimeMilliseconds() + if previousBook.id == bookId then + if (now - previousBook.time) > DOUBLE_TAP_TIME then + do_not_show = true + end + else + previousBook.id = bookId + end + -- msg("force_show <<1>> now <<2>> prev.time <<3>> deltaT <<4>> prev.id <<5>> id <<6>>", + -- force_show, now, previousBook.time, now - previousBook.time, previousBook.id, id) + previousBook.time = now + else -- not in the base scene + do_not_show = true + end + + -- implement the block, if appropriate. + if HaveSeenBookBefore(bookId, title, body) and not force_show then + end + + -- meh, this is copied from the local function in the ZOS code. :( + if LORE_READER:Show(title, body, medium, showTitle) then + PlaySound(LORE_READER.OpenSound) + else + EndInteraction(INTERACTION_BOOK) + end +end + +local function ScanKnowLoreBooks() +end + +local function OnAddonLoaded(_, name) + if name ~= ADDON_NAME then return end + EVENT_MANAGER:UnregisterForEvent(ADDON_NAME, EVENT_ADD_ON_LOADED) + + -- if the second argument, the version, changes then the data is wiped and + -- replaced with the defaults. + seen = ZO_SavedVars:NewAccountWide("SlippyCheezeReadItOnceData", 1) + + -- replace the original event handler with ours; sadly, we don't have + -- access to the original implementation to do anything nicer. :/ + LORE_READER.control:UnregisterForEvent(EVENT_SHOW_BOOK) + LORE_READER.control:RegisterForEvent(EVENT_SHOW_BOOK, OnShowBookOverride) +end + +-- bootstrapping +EVENT_MANAGER:RegisterForEvent(ADDON_NAME, EVENT_ADD_ON_LOADED, OnAddonLoaded) diff --git a/SlippyCheezeReadItOnce.txt b/SlippyCheezeReadItOnce.txt index d9c9853..ceb5f2f 100644 --- a/SlippyCheezeReadItOnce.txt +++ b/SlippyCheezeReadItOnce.txt @@ -1,16 +1,16 @@ -## Title: |c8794C5SlippyCheeze's|r Read It Once -## Description: Skips reading books when you open them for the second time. -## Author: |c8794C5SlippyCheeze|r -## Version: 1000000-DEV -## AddOnVersion: 1000000 -## APIVersion: 100024 -## SavedVariables: SlippyCheezeReadItOnceData - -SlippyCheezeReadItOnce.lua - - -# This Add-on is not created by, affiliated with or sponsored by ZeniMax -# Media Inc. or its affiliates. The Elder Scrolls® and related logos are -# registered trademarks or trademarks of ZeniMax Media Inc. in the United -# States and/or other countries. All rights reserved. -# You can read the full terms at https://account.elderscrollsonline.com/add-on-terms +## Title: |c8794C5SlippyCheeze's|r Read It Once +## Description: Skips reading books when you open them for the second time. +## Author: |c8794C5SlippyCheeze|r +## Version: 1000000-DEV +## AddOnVersion: 1000000 +## APIVersion: 100024 +## SavedVariables: SlippyCheezeReadItOnceData + +SlippyCheezeReadItOnce.lua + + +# This Add-on is not created by, affiliated with or sponsored by ZeniMax +# Media Inc. or its affiliates. The Elder Scrolls® and related logos are +# registered trademarks or trademarks of ZeniMax Media Inc. in the United +# States and/or other countries. All rights reserved. +# You can read the full terms at https://account.elderscrollsonline.com/add-on-terms