WTS/game/scripts/interface/book.rpy

254 lines
9.3 KiB
Plaintext

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 kinds of entries:
- the first kind is to store the possible pages which can be added to the dict.
These entries are the form {str page_id : (str|None page_title, str page_text)}.
`page_title` and `page_text` may contain interpolation fields, such as {code}.
- the second kind is to store the for interpolation codes to fill in those blanks.
These entries are of the form {str interpo_id : str interpo_text}.
`interpo_text` is the text to be used for the interpolation.
See the diary_append method to match interpolation fields with interpolation ids when adding the pages.
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 # type: dict[str, tuple[str|None, str]|str]|str
self.entry_ids = set() # set[str]
@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 - a first-kind key.
`day` sets the day number for the entry, defaulting to the current day.
`branches` is a dict of {code : interpo_id} for every happened sub-event specializing the `id` event.
The specified interpolation ids will be looked for in the dictionary, and the original entry of id `id`
will be formatted by replacing {code} with the page title and page text found in dictionary[interpo_id].
If `interpo_id` is not a valid entry in the dict, the passed value itself will be interpolated instead,
or nothing if it is a false value (like None).
For example, if the entry associated with "id1549" is ("Tittle", "Today I met {a} and did {b}. I liked it{c}.").
Calling `diary_append("id1549", a="Alice", b="nothing", c=" a lot")` will add the page
"Today I met Alice and did nothing. I liked it a lot." to the diary, with the title "Tittle".
Passing c="" or c=None or c=False will all result in "Today I met Alice and did nothing. I liked it.".
If the page and title for the key `id` contain interpolation fields, it is a mistake
not to pass all interpolation fields as kwargs.
It is benign to specify keys which are not interpolation fields in the entry : in the previous example,
passing d=whatever will not change anything to the result.
Passing the same page `id` several times will only work the first time, subsequent tries will be ignored.
The entry_id attribute (a set) can be accessed, read only, to check if an entry has already been added.
"""
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 page_title:
page_title = page_title.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
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