init python: class book_readable_class(object): def __init__(self, title, contents=(), **kwargs): self.title = title self.page = 0 self.overflow = None self.title = title self.contents = list(contents) # list of (title, text) pairs self.__dict__.update(**kwargs) self.npages = len(self.contents) @property def maxpage(self): # maximum value for the `page` attribute if self.npages %2: return self.npages-1 return self.npages-2 def Open(self, page=None): """ Returns a list of actions that populate the variables of the book_menu screen. If passed a `page`, does NOT change the page attribute of the book, instead acts as though that page was the current one. """ if page is None: page = self.page page_title, page_text = self.contents[page] page_text = page_text[:880] if self.npages > page+1: next_page_title, next_page_text = self.contents[page+1] next_page_text = next_page_text[:880] else: next_page_title, next_page_text = None, None return [ SetScreenVariable("page_title", page_title), SetScreenVariable("page_text", page_text), SetScreenVariable("next_page_title", next_page_title), SetScreenVariable("next_page_text", next_page_text), ] def Next(self): page = min(self.page+2, self.maxpage) return self.OpenPage(page) def Prev(self): page = max(self.page-2, 0) return self.OpenPage(page) def OpenPage(self, page): return [ SetField(self, "page", page), self.Open(page), ] def append(self, page_title, page_text): self.contents.append((page_title, page_text)) self.npages = len(self.contents) return class diary_class(book_readable_class): def __init__(self, *args, dictionary, **kwargs): """ `dictionary` is a dict containing two types of entries: - for normal text pages, id: (title, text). `text` may contain interpolation fields, such as {code}. When that's the case, there must be an 2nd-type entry for "code" in this dict. `title` may be None. - for interpolation codes, code: text. `text` is the text to be used for the interpolation. Alternatively, `dictionary` may be the name of a store variable containing the actual dictionary (that's better when not in a testing phase, for pickling/saving/updating reasons) """ super().__init__(*args, **kwargs) self.dictionary_ = dictionary # str id : either (title, text) or text self.entry_ids = set() # str id @property def dictionary(self): rv = self.dictionary_ if isinstance(rv, str): rv = getattr(renpy.store, rv) return rv # append still exists, so that you can manually add pages without going through the dictionary def diary_append(self, id, day=None, **branches): """ Adds a page to the diary. `id` is the key of the event that just happened, in the dictionary. `day` sets the day number for the entry, defaulting to the current day. `branches` is a dict of {sub-event id : code} for every happened sub-event specializing event `id`. the specified codes will be looked for in the dictionary, and the original entry of id `id` will be formatted by associating {sub-event id} with dictionary[code]. If `code` is not a valid entry in the dict, the `code` value itself will be interpolated instead, or nothing if v is a false value (like None). If the page for the key `id` contains interpolation fields, it is a mistake to not specify all interpolation fields in `branches`. It is benign to specify keys which are not interpolation fields. """ if id in self.entry_ids: return dictionary = self.dictionary page_title, page_text = dictionary[id] if branches: branches = {k : dictionary.get(v, v or "") for k, v in branches.items()} page_text = page_text.format(**branches) if day is None: day = game.day if page_title: page_title = "Day {}\n{}".format(day, page_title) else: page_title = "Day {}".format(day) self.append(page_title, page_text) self.entry_ids.add(id) label book_handle(book=None): call screen book_menu(book) # with BookAnimatorFade(0.5, book_wrapped("book_page_next")) return screen book_menu(book=None): zorder 30 # button style "empty" action NullAction() modal True add Color("#000", alpha=0.5) add "interface/book/book_open.webp" default page_title = None # on "show" action book.Open() # sets up all the screen variables we want, including page_title which serves as a trigger timer 0.00000000001 action book.Open() # sets up all the screen variables we want, including page_title which serves as a trigger if page_title is not None: use book_content(book, page_title, page_text, next_page_title, next_page_text) use close_button screen book_content(book, page_title, page_text, next_page_title, next_page_text): frame: style "empty" pos (280, 130) xsize 250 ysize 300 text page_title ypos -20 size 16 xalign 0.5 textalign .5 text page_text size 12 ypos 40 text str(book.page+1) bold True xalign 0.5 ypos 350 size 11 frame: style "empty" xpos 600 ypos 130 xsize 250 ysize 300 if next_page_title is not None: text next_page_title ypos -20 size 16 xalign 0.5 textalign .5 if next_page_text is not None: text next_page_text size 12 ypos 40 text str(book.page+2) bold True xalign 0.5 ypos 350 size 11 if book.page+2 < book.npages: # Next imagebutton: pos (721, 100) idle Transform("interface/book/hover.webp", alpha=0) hover "interface/book/hover.webp" action [book.Next(), With(BookAnimatorFade(.48, book_wrapped("book_page_next")))] elif book.page > 0: # Fast Back to start imagebutton: pos (721, 100) idle "interface/book/back.webp" hover "interface/book/back.webp" action [book.OpenPage(0), With(BookAnimatorFade(.42, book_wrapped("book_page_start")))] # Previous imagebutton: pos (242, 100) idle Transform("interface/book/hover.webp", xzoom=-1.0, alpha=0) hover Transform("interface/book/hover.webp", xzoom=-1.0, alpha=1) action [book.Prev(), With(BookAnimatorFade(.48, book_wrapped("book_page_prev")))] transform BookAnimatorFade(hold_time, widget, old_widget=None, new_widget=None): delay hold_time contains: # old_widget # new_widget with Dissolve(hold_time) new_widget events False contains: widget time hold_time new_widget events True transform book_wrapped(child): contains: "interface/book/book_open.webp" contains: child image book_page_next: #"interface/book_of_secrets/book_anim_01.webp" #pause.1 "interface/book/page_02.webp" pause.08 "interface/book/page_03.webp" pause.08 "interface/book/page_04.webp" pause.08 "interface/book/page_05.webp" pause.08 "interface/book/page_06.webp" pause.08 "interface/book/page_07.webp" pause.08 image book_page_prev: xoffset 40 xzoom -1 "book_page_next" image book_page_start: "interface/book/reverse_01.webp" pause.07 "interface/book/reverse_02.webp" pause.07 repeat 3 #book_page_max was too slow