2022-05-16 23:48:22 +00:00
|
|
|
init python:
|
|
|
|
def inventory_sortfilter(item, sortby="A-z", filtering=None):
|
|
|
|
if filtering == "Owned":
|
2023-11-16 01:17:25 +00:00
|
|
|
item = (x for x in item if x.owned > 0)
|
2022-05-16 23:48:22 +00:00
|
|
|
|
|
|
|
# Always sort alphabetically first.
|
|
|
|
item = sorted(item, key=lambda x: natsort_key(x.name))
|
|
|
|
|
|
|
|
if sortby == "z-A":
|
2023-11-16 01:17:25 +00:00
|
|
|
item.sort(key=lambda x: natsort_key(x.name), reverse=True)
|
2023-11-17 01:07:57 +00:00
|
|
|
elif sortby == "Available":
|
2023-11-16 01:17:25 +00:00
|
|
|
item.sort(key=lambda x: x.owned is True, reverse=True)
|
2023-11-17 01:07:57 +00:00
|
|
|
elif sortby == "Unavailable":
|
2023-11-16 01:17:25 +00:00
|
|
|
item.sort(key=lambda x: x.owned is False)
|
2022-05-16 23:48:22 +00:00
|
|
|
|
|
|
|
return item
|
|
|
|
|
2023-11-17 00:44:02 +00:00
|
|
|
class __Sort(Action):
|
2023-11-17 01:12:26 +00:00
|
|
|
# TODO: replace with CycleVariable("current_sorting", ("A-z", "z-A", "Available", "Unavailable"))
|
2023-11-17 00:44:02 +00:00
|
|
|
def __call__(self):
|
|
|
|
global current_sorting
|
|
|
|
|
2022-09-29 21:19:55 +00:00
|
|
|
if current_sorting == "A-z":
|
|
|
|
current_sorting = "z-A"
|
|
|
|
elif current_sorting == "z-A":
|
|
|
|
current_sorting = "Available"
|
|
|
|
elif current_sorting == "Available":
|
|
|
|
current_sorting = "Unavailable"
|
|
|
|
else:
|
|
|
|
current_sorting = "A-z"
|
2023-11-17 00:44:02 +00:00
|
|
|
|
|
|
|
class __Filter(Action):
|
2023-11-17 01:12:26 +00:00
|
|
|
# TODO: replace with CycleVariable("current_filter", ("Owned", None))
|
|
|
|
# (ToggleVariable would cause selectedness problems)
|
2023-11-17 00:44:02 +00:00
|
|
|
def __call__(self):
|
|
|
|
global current_filter
|
|
|
|
|
2023-11-16 16:53:29 +00:00
|
|
|
if current_filter is None:
|
2022-09-29 21:19:55 +00:00
|
|
|
current_filter = "Owned"
|
|
|
|
else:
|
|
|
|
current_filter = None
|
2023-11-17 01:12:26 +00:00
|
|
|
|
|
|
|
class __Actuate(Action):
|
|
|
|
def __call__(self):
|
|
|
|
global menu_items
|
|
|
|
global menu_items_length
|
|
|
|
global current_page
|
|
|
|
global current_item
|
|
|
|
|
2023-11-17 00:44:02 +00:00
|
|
|
menu_items = inventory_sortfilter(inventory_dict[current_category], current_sorting, current_filter)
|
2022-09-29 21:19:55 +00:00
|
|
|
menu_items_length = len(menu_items)
|
|
|
|
|
|
|
|
if current_category == "Decorations":
|
2023-11-17 00:44:02 +00:00
|
|
|
menu_items.sort(key=lambda x: x.placement.id)
|
2022-05-16 23:48:22 +00:00
|
|
|
|
2022-09-29 21:19:55 +00:00
|
|
|
current_page = 0
|
2022-05-16 23:48:22 +00:00
|
|
|
|
2023-11-17 00:44:02 +00:00
|
|
|
if current_item not in menu_items:
|
2022-09-29 21:19:55 +00:00
|
|
|
current_item = next(iter(menu_items), None)
|
2022-05-16 23:48:22 +00:00
|
|
|
|
2023-11-17 00:44:02 +00:00
|
|
|
renpy.restart_interaction()
|
2022-05-16 23:48:22 +00:00
|
|
|
|
2023-11-17 00:44:02 +00:00
|
|
|
def __init_scope():
|
|
|
|
if inventory_mode == 0:
|
|
|
|
inventory_dict = {
|
|
|
|
"Gifts": inventory.get_instances_of_type("gift"),
|
|
|
|
"Books": inventory.get_instances_of_type("book"),
|
|
|
|
"Scrolls": inventory.get_instances_of_type("scroll"),
|
|
|
|
"Ingredients": inventory.get_instances_of_type("ingredient"),
|
|
|
|
"Potions": inventory.get_instances_of_type("potion"),
|
|
|
|
"Decorations": inventory.get_instances_of_type("decoration"),
|
|
|
|
"Quest Items": inventory.get_instances_of_type("quest"),
|
|
|
|
}
|
|
|
|
elif inventory_mode == 1:
|
|
|
|
inventory_dict = {
|
|
|
|
"Gifts": inventory.get_instances_of_type("gift"),
|
|
|
|
"Potions": inventory.get_instances_of_type("potion"),
|
|
|
|
"Quest Items": inventory.get_instances_of_type("quest"),
|
|
|
|
}
|
|
|
|
|
|
|
|
renpy.dynamic(
|
|
|
|
inventory_dict=inventory_dict,
|
|
|
|
current_page=0,
|
|
|
|
current_category="Gifts",
|
|
|
|
current_filter="Owned",
|
|
|
|
current_sorting="Available",
|
|
|
|
)
|
|
|
|
|
|
|
|
menu_items = inventory_sortfilter(inventory_dict[current_category], current_sorting, current_filter)
|
|
|
|
renpy.dynamic(
|
|
|
|
menu_items=menu_items,
|
|
|
|
menu_items_length=len(menu_items),
|
|
|
|
current_item=next(iter(menu_items), None),
|
|
|
|
)
|
|
|
|
|
|
|
|
define items_shown = 36
|
|
|
|
|
|
|
|
####################################
|
|
|
|
############# Menu #################
|
|
|
|
####################################
|
|
|
|
|
|
|
|
label inventory:
|
|
|
|
$ gui.in_context("inventory_menu")
|
|
|
|
jump main_room_menu
|
|
|
|
|
|
|
|
label .use_item(item):
|
|
|
|
python:
|
|
|
|
enable_game_menu()
|
|
|
|
item.use()
|
|
|
|
return
|
|
|
|
|
|
|
|
label .give_item(item):
|
|
|
|
if item.type == "gift":
|
|
|
|
if get_character_gifted(states.active_girl):
|
|
|
|
show screen blktone
|
|
|
|
with d3
|
|
|
|
gen "I already gave her a gift today. Don't want to spoil her too much..." ("base", xpos="far_left", ypos="head")
|
|
|
|
hide screen blktone
|
|
|
|
with d3
|
|
|
|
else:
|
|
|
|
call expression get_character_gift_label(states.active_girl) pass (item)
|
|
|
|
|
|
|
|
elif item.type == "potion":
|
|
|
|
if not states.active_girl in item.usable_on:
|
|
|
|
show screen blktone
|
|
|
|
with d3
|
|
|
|
gen "(Something tells me this potion won't work on [states.active_girl].)" ("base", xpos="far_left", ypos="head")
|
|
|
|
nar "Perhaps in the future..."
|
|
|
|
hide screen blktone
|
|
|
|
with d3
|
|
|
|
elif not game.daytime:
|
|
|
|
show screen blktone
|
|
|
|
with d3
|
|
|
|
gen "(Some grander force tells me I should give it to her during daytime only.)" ("base", xpos="far_left", ypos="head")
|
|
|
|
hide screen blktone
|
|
|
|
with d3
|
|
|
|
elif get_character_mood(states.active_girl) > 0:
|
|
|
|
show screen blktone
|
|
|
|
with d3
|
|
|
|
gen "(I don't think it's a good idea to give it to her when she's still upset with me...)" ("base", xpos="far_left", ypos="head")
|
|
|
|
gen "(I should wait for her to calm down first.)" ("base", xpos="far_left", ypos="head")
|
|
|
|
hide screen blktone
|
|
|
|
with d3
|
|
|
|
elif states.active_girl == "hermione" and not states.her.favors_convinced_stage == 2 and is_in_lead(gryffindor):
|
|
|
|
show screen blktone
|
|
|
|
with d3
|
|
|
|
|
|
|
|
gen "[name_hermione_genie], what would you say--" ("base", xpos="far_left", ypos="head")
|
|
|
|
her "I'm sorry professor but I don't need your help at the moment."
|
|
|
|
her "My house is already in the lead points wise."
|
|
|
|
gen "Figures..." ("base", xpos="far_left", ypos="head")
|
|
|
|
|
|
|
|
call tutorial("points")
|
|
|
|
|
|
|
|
hide screen blktone
|
|
|
|
with d3
|
|
|
|
else:
|
|
|
|
python:
|
|
|
|
enable_game_menu()
|
|
|
|
inventory_mode = 0
|
|
|
|
item.give(states.active_girl)
|
|
|
|
|
|
|
|
elif item.type == "quest":
|
|
|
|
if not states.active_girl in item.usable_on:
|
|
|
|
show screen blktone
|
|
|
|
with d3
|
|
|
|
gen "(Something tells me I cannot give this item to [states.active_girl].)" ("base", xpos="far_left", ypos="head")
|
|
|
|
nar "Perhaps in the future..."
|
|
|
|
hide screen blktone
|
|
|
|
with d3
|
|
|
|
#TODO Add daytime check for buttplug give item event
|
|
|
|
#show screen blktone
|
|
|
|
#with d3
|
|
|
|
#gen "(Some grander force tells me I should give it to her during daytime only.)" ("base", xpos="far_left", ypos="head")
|
|
|
|
#hide screen blktone
|
|
|
|
#with d3
|
|
|
|
elif get_character_mood(states.active_girl) > 0:
|
|
|
|
show screen blktone
|
|
|
|
with d3
|
|
|
|
gen "(I don't think it's a good idea to give it to her when she's still upset with me...)" ("base", xpos="far_left", ypos="head")
|
|
|
|
gen "(I should wait for her to calm down first.)" ("base", xpos="far_left", ypos="head")
|
|
|
|
hide screen blktone
|
|
|
|
with d3
|
|
|
|
elif states.active_girl == "hermione" and not states.her.favors_convinced_stage == 2 and is_in_lead(gryffindor):
|
|
|
|
show screen blktone
|
|
|
|
with d3
|
|
|
|
|
|
|
|
gen "[name_hermione_genie], I have something for you--" ("base", xpos="far_left", ypos="head")
|
|
|
|
her "I'm sorry professor but I don't need your help at the moment."
|
|
|
|
her "My house is already in the lead points wise."
|
|
|
|
gen "Figures..." ("base", xpos="far_left", ypos="head")
|
|
|
|
|
|
|
|
call tutorial("points")
|
|
|
|
|
|
|
|
hide screen blktone
|
|
|
|
with d3
|
|
|
|
else:
|
|
|
|
python:
|
|
|
|
enable_game_menu()
|
|
|
|
inventory_mode = 0
|
|
|
|
item.give(states.active_girl)
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
label inventory_menu(xx=150, yy=90, inventory_mode=0): # 0 - Inventory, 1 - gifts
|
|
|
|
# Inventory dictionary
|
|
|
|
|
|
|
|
$ __init_scope()
|
|
|
|
|
|
|
|
call screen inventory(xx, yy)
|
|
|
|
|
|
|
|
return
|
2022-05-16 23:48:22 +00:00
|
|
|
|
|
|
|
screen inventory(xx, yy):
|
|
|
|
tag inventory
|
|
|
|
zorder 15
|
|
|
|
modal True
|
|
|
|
|
|
|
|
add "gui_fade"
|
|
|
|
|
|
|
|
if renpy.mobile:
|
|
|
|
use close_button_background
|
|
|
|
use close_button(key=["inventory", "game_menu"])
|
|
|
|
|
|
|
|
fixed:
|
|
|
|
if settings.get("animations"):
|
|
|
|
at gui_animation
|
|
|
|
use inventory_menu(xx, yy)
|
|
|
|
use inventory_menuitem(xx, yy)
|
|
|
|
|
|
|
|
screen inventory_menu(xx, yy):
|
|
|
|
window:
|
|
|
|
style "empty"
|
|
|
|
style_prefix gui.theme('achievements')
|
|
|
|
pos (xx, yy)
|
|
|
|
xysize (207, 454)
|
|
|
|
|
|
|
|
use invisible_button()
|
|
|
|
|
|
|
|
add gui.format("interface/achievements/{}/panel_left.webp")
|
|
|
|
|
|
|
|
vbox:
|
|
|
|
pos (6, 41)
|
2023-11-17 00:44:02 +00:00
|
|
|
for category in inventory_dict:
|
2022-05-16 23:48:22 +00:00
|
|
|
vbox:
|
|
|
|
textbutton category:
|
|
|
|
style "empty"
|
|
|
|
xsize 195 ysize 16
|
|
|
|
text_xalign 0.5
|
|
|
|
if current_category == category:
|
|
|
|
background gui.format("interface/achievements/{}/highlight_left.webp")
|
|
|
|
else:
|
|
|
|
hover_background gui.format("interface/achievements/{}/highlight_left.webp")
|
2023-11-17 01:12:26 +00:00
|
|
|
action [SetVariable("current_category", category), __Actuate()]
|
2022-05-16 23:48:22 +00:00
|
|
|
add gui.format("interface/achievements/{}/spacer_left.webp")
|
|
|
|
|
|
|
|
# Gold & Tokens
|
|
|
|
null height 16
|
|
|
|
text "{color=#daa520}Gold{/color} {outlinecolor=#ffffff00}[game.gold]{/outlinecolor}" size 12 outlines [ (2, "#000", 0, 0) ] xalign 0.1 xanchor 0
|
|
|
|
add gui.format("interface/achievements/{}/spacer_left.webp")
|
|
|
|
text "{color=#2055da}Tokens{/color} {outlinecolor=#ffffff00}[tokens]{/outlinecolor}" size 12 outlines [ (2, "#000", 0, 0) ] xalign 0.1 xanchor 0
|
|
|
|
add gui.format("interface/achievements/{}/spacer_left.webp")
|
|
|
|
|
|
|
|
vbox:
|
|
|
|
style_prefix gui.theme('achievements_filters')
|
|
|
|
pos (6, 384)
|
2023-11-16 16:53:29 +00:00
|
|
|
if current_filter is None:
|
2023-11-17 01:12:26 +00:00
|
|
|
textbutton "Show: All" action [__Filter(), __Actuate()]
|
2022-05-16 23:48:22 +00:00
|
|
|
else:
|
2023-11-17 01:12:26 +00:00
|
|
|
textbutton "Show: [current_filter]" action [__Filter(), __Actuate()]
|
|
|
|
textbutton "Sort by: [current_sorting]" action [__Sort(), __Actuate()]
|
2022-05-16 23:48:22 +00:00
|
|
|
|
|
|
|
screen inventory_menuitem(xx, yy):
|
|
|
|
window:
|
|
|
|
style "empty"
|
|
|
|
style_prefix gui.theme()
|
|
|
|
pos (xx+217, yy-53)
|
|
|
|
xysize (560, 507)
|
|
|
|
|
|
|
|
use invisible_button()
|
|
|
|
|
|
|
|
add "interface/achievements/inventory.webp"
|
|
|
|
add gui.format("interface/achievements/{}/panel.webp")
|
|
|
|
|
|
|
|
#Western Egg
|
2023-03-11 22:01:39 +00:00
|
|
|
button xsize 90 ysize 60 action Play("sound", "sounds/plushie.ogg") xalign 0.5 style "empty"
|
2022-05-16 23:48:22 +00:00
|
|
|
|
|
|
|
text "Inventory" size 22 xalign 0.5 ypos 65
|
|
|
|
|
2022-09-29 21:19:55 +00:00
|
|
|
#text "Unlocked: "+str(len([x for x in menu_items if x[1][3] is True]))+"/[menu_items_length]" size 12 pos (24, 70)
|
2022-05-16 23:48:22 +00:00
|
|
|
|
|
|
|
# Page counter
|
|
|
|
if menu_items_length > items_shown:
|
|
|
|
hbox:
|
|
|
|
xanchor 1.0
|
|
|
|
pos (540, 24)
|
|
|
|
spacing 5
|
|
|
|
add "interface/page.webp" yanchor 0.5 ypos 53
|
2023-03-11 17:41:07 +00:00
|
|
|
text str(current_page+1)+"/"+str(int(math.ceil(menu_items_length/items_shown))) ypos 44 size 16
|
2022-05-16 23:48:22 +00:00
|
|
|
vbox:
|
|
|
|
pos (570, 186)
|
|
|
|
spacing 10
|
|
|
|
|
|
|
|
imagebutton:
|
|
|
|
idle gui.format("interface/frames/{}/arrow_up.webp")
|
2023-11-17 00:44:02 +00:00
|
|
|
if current_page > 0:
|
2022-05-16 23:48:22 +00:00
|
|
|
hover image_hover(gui.format("interface/frames/{}/arrow_up.webp"))
|
2023-11-17 01:12:26 +00:00
|
|
|
action SetVariable("current_page", current_page-1) # TODO: replace with IncrementVariable("current_page", -1)
|
2022-05-16 23:48:22 +00:00
|
|
|
|
|
|
|
imagebutton:
|
2022-12-17 21:57:06 +00:00
|
|
|
idle Transform(gui.format("interface/frames/{}/arrow_up.webp"), yzoom=-1)
|
2023-03-11 17:41:07 +00:00
|
|
|
if current_page < math.ceil((menu_items_length-1)/items_shown)-1:
|
2022-12-17 21:57:06 +00:00
|
|
|
hover Transform(image_hover(gui.format("interface/frames/{}/arrow_up.webp")), yzoom=-1)
|
2023-11-17 01:12:26 +00:00
|
|
|
action SetVariable("current_page", current_page+1) # TODO: replace with IncrementVariable("current_page")
|
2022-05-16 23:48:22 +00:00
|
|
|
|
|
|
|
# Add items
|
2023-11-16 17:01:43 +00:00
|
|
|
grid 9 4:
|
|
|
|
style "empty"
|
|
|
|
pos (24, 113)
|
|
|
|
|
2023-11-16 17:05:57 +00:00
|
|
|
for it_item in menu_items[current_page*items_shown:(current_page+1)*items_shown]:
|
|
|
|
frame:
|
|
|
|
style "empty"
|
|
|
|
xsize 48
|
|
|
|
ysize 48
|
|
|
|
add gui.format("interface/achievements/{}/iconbox.webp")
|
|
|
|
|
|
|
|
if current_item is not None and current_item.id == it_item.id:
|
|
|
|
add "interface/achievements/glow.webp" align (0.5, 0.5) zoom 0.105 alpha 0.7 at rotate_circular
|
|
|
|
|
|
|
|
add crop_image_zoom(it_item.get_image(), 42, 42, it_item.owned <= 0) align (0.5, 0.5)
|
|
|
|
|
|
|
|
button:
|
|
|
|
style gui.theme("overlay_button")
|
|
|
|
background "interface/achievements/glass_iconbox.webp"
|
|
|
|
xsize 46 ysize 46
|
2023-11-17 01:12:26 +00:00
|
|
|
action SetVariable("current_item", it_item)
|
2023-11-16 17:05:57 +00:00
|
|
|
tooltip it_item.name
|
|
|
|
|
|
|
|
if it_item.limit > 1 and it_item.owned > 0:
|
|
|
|
if it_item.infinite:
|
|
|
|
text "{unicode}∞{/unicode}" size 20 align (0.1, 0.1) offset(-1, -9) color "#FFFFFF" outlines [ (1, "#000", 0, 0) ]
|
|
|
|
else:
|
2023-11-17 00:44:02 +00:00
|
|
|
text "[it_item.owned]" size 10 align (0.1, 0.1) color "#FFFFFF" outlines [ (1, "#000", 0, 0) ]
|
2023-11-16 17:05:57 +00:00
|
|
|
elif current_category == "Decorations":
|
|
|
|
if it_item.in_use:
|
|
|
|
add "interface/topbar/icon_check.webp" anchor (1.0, 1.0) align (1.0, 1.0) offset (-3, -3) zoom 0.5
|
|
|
|
elif current_category in ("Books", "Quest Items"):
|
|
|
|
if it_item.used:
|
|
|
|
add "interface/topbar/icon_check.webp" anchor (1.0, 1.0) align (1.0, 1.0) offset (-3, -3) zoom 0.5
|
|
|
|
|
|
|
|
if inventory_mode == 1 and (not it_item.givable or not states.active_girl in it_item.usable_on):
|
|
|
|
add "#b2000040"
|
2022-05-16 23:48:22 +00:00
|
|
|
|
|
|
|
if menu_items_length <= 0:
|
|
|
|
text "Nothing here yet" align (0.5, 0.5) anchor (0.5, 0.5) size 24
|
|
|
|
|
|
|
|
if current_item:
|
|
|
|
frame:
|
|
|
|
style "empty"
|
|
|
|
xsize 96
|
|
|
|
ysize 96
|
|
|
|
pos (24, 375)
|
|
|
|
add gui.format("interface/achievements/{}/icon_selected.webp")
|
2023-11-16 16:40:48 +00:00
|
|
|
add crop_image_zoom(current_item.get_image(), 84, 84, current_item.owned <= 0) align (0.5, 0.5)
|
2022-05-16 23:48:22 +00:00
|
|
|
add "interface/achievements/glass_selected.webp" pos (6, 6)
|
|
|
|
|
|
|
|
if current_category in {"Gifts", "Ingredients", "Potions"}:
|
|
|
|
if current_item.owned > 0:
|
|
|
|
if current_item.infinite:
|
|
|
|
text "{unicode}∞{/unicode}" size 30 align (0.1, 0.1) offset(-2, -10) color "#FFFFFF" outlines [ (1, "#000", 0, 0) ]
|
|
|
|
else:
|
2023-11-16 16:42:55 +00:00
|
|
|
text "[current_item.owned]" size 14 align (0.1, 0.1) color "#FFFFFF" outlines [ (1, "#000", 0, 0) ]
|
2022-05-16 23:48:22 +00:00
|
|
|
elif current_category == "Decorations":
|
|
|
|
if current_item.in_use:
|
|
|
|
add "interface/topbar/icon_check.webp" anchor (1.0, 1.0) align (1.0, 1.0) offset (-6, -6)
|
|
|
|
elif current_category in ("Books", "Quest Items"):
|
|
|
|
if current_item.used:
|
|
|
|
add "interface/topbar/icon_check.webp" anchor (1.0, 1.0) align (1.0, 1.0) offset (-6, -6)
|
|
|
|
|
2023-03-31 22:41:48 +00:00
|
|
|
if inventory_mode == 1 and (not current_item.givable or not states.active_girl in current_item.usable_on):
|
2022-05-16 23:48:22 +00:00
|
|
|
add "#b2000040"
|
|
|
|
|
|
|
|
add gui.format("interface/achievements/{}/highlight.webp") pos (112, 375)
|
|
|
|
add gui.format("interface/achievements/{}/spacer.webp") pos (120, 398)
|
|
|
|
hbox:
|
|
|
|
spacing 5
|
|
|
|
xalign 0.5
|
|
|
|
text current_item.name ypos 380 size 16 xoffset 45
|
|
|
|
|
|
|
|
if (inventory_mode == 0 and current_item.usable) or (inventory_mode == 1 and current_item.givable):
|
|
|
|
textbutton "[current_item.caption]":
|
|
|
|
style "inventory_button"
|
2023-11-17 00:44:02 +00:00
|
|
|
background Frame(gui.format("interface/frames/{}/iconframe.webp"), 6, 6)
|
2022-05-16 23:48:22 +00:00
|
|
|
xalign 0.89
|
|
|
|
xoffset 45
|
|
|
|
ypos 374
|
|
|
|
sensitive (current_item.owned > 0)
|
|
|
|
if inventory_mode == 0:
|
2023-11-17 01:12:26 +00:00
|
|
|
action Call("inventory.use_item", current_item, from_current=True)
|
2022-05-16 23:48:22 +00:00
|
|
|
elif inventory_mode == 1:
|
2023-11-17 01:12:26 +00:00
|
|
|
action Call("inventory.use_item", current_item, from_current=True)
|
2022-05-16 23:48:22 +00:00
|
|
|
|
|
|
|
hbox:
|
|
|
|
pos (132, 407)
|
|
|
|
xsize 410
|
|
|
|
text current_item.desc size 12
|
|
|
|
|
|
|
|
style inventory_button:
|
|
|
|
xysize (90, 26)
|
|
|
|
hover_foreground "#ffffff1F"
|
|
|
|
|
|
|
|
style inventory_button_text:
|
|
|
|
outlines []
|
|
|
|
align (0.5, 0.5)
|
|
|
|
size 16
|