init python in wheelmenu: import math reference_num_buttons = 32 reference_radius = 0.5 def pos(num_buttons): num_buttons = max(num_buttons, 2) radius = reference_radius * (num_buttons / reference_num_buttons) ** 0.5 angle_step = 2 * math.pi / num_buttons start_angle = angle_step / 2 if num_buttons % 2 else 0 positions = [(min(max(0.5 + math.cos(start_angle + i * angle_step) * radius, 0.0), 1.0), min(max(0.5 + math.sin(start_angle + i * angle_step) * radius, 0.0), 1.0)) for i in range(num_buttons)] return positions def button(displayable, action, condition, disabled, **kwargs): """ displayable - Ren'Py displayable action - Ren'Py screen action condition - Boolean disabled - Boolean """ if condition: if action is None or disabled: return renpy.store.Button(renpy.store.Transform(displayable, matrixcolor=renpy.store.SaturationMatrix(0)*renpy.store.BrightnessMatrix(-0.15)), action=action, style="wheelmenu_disabled_button", **kwargs) return renpy.store.Button(displayable, action=action, style="wheelmenu_button", **kwargs) else: return None def clamp_to_screen(num_buttons, pos): num_buttons = max(num_buttons, 2) radius = reference_radius * (num_buttons / reference_num_buttons) ** 0.5 diameter = int(radius * min(renpy.store.config.screen_width, renpy.store.config.screen_height)) x = max(diameter, min(renpy.store.config.screen_width - diameter, pos[0])) y = max(diameter, min(renpy.store.config.screen_height - diameter, pos[1])) return (x, y) init python: def create_wheelmenu(elements): # Proxy function; We cannot evaluate in a named store without side effects. buttons = [] for name, (displayable, action, *tail) in elements.items(): if tail: condition = eval(tail[0]) disabled = eval(tail[1]) if len(tail) > 1 else False else: condition = True disabled = False if not condition and disabled: continue buttons.append(wheelmenu.button(displayable, action, condition, disabled, tooltip=name)) if not buttons: return None return None if not buttons else tuple(zip(buttons, wheelmenu.pos(len(buttons)))) # Nonhashable types cannot be used in a screen, so we use a tuple instead. config.per_frame_screens.append("wheelmenu") label wheelmenu(btns, ret, pos=None): play sound "sounds/qubodup-click1.ogg" call screen wheelmenu(btns, pos) jump expression ret screen wheelmenu(btns, pos=None, close_action=Return("Close")): layer "interface" tag wheelmenu zorder 1 style_prefix "wheelmenu" $ mpos = renpy.get_mouse_pos() default start_pos = wheelmenu.clamp_to_screen(len(btns), pos or mpos) use close_button_background(action=close_action, keysym="game_menu") window at wheelmenu_anim: id "wheelmenu" pos start_pos for btn, pos in btns: add btn pos pos add "wheelmenu_genie" align (0.5, 0.5) at transform: subpixel True xysize (48, 48) yzoom (1 if mpos[0] < start_pos[0] else -1) rotate (math.degrees(math.atan2(mpos[1] - start_pos[1], mpos[0] - start_pos[0])) + 360) % 360 - 180 style wheelmenu_window is empty: background Transform("wheelmenu_gradient", align=(0.5, 0.5), xysize=(200, 200)) maximum (400, 400) anchor (0.5, 0.5) style wheelmenu_button is empty: background Transform("wheelmenu_button", xysize=(48,48)) hover_background At(Transform("wheelmenu_button_opaque", xysize=(48,48)), wheelmenu_hover_anim) hover_sound "sounds/qubodup-hover1.ogg" activate_sound "sounds/qubodup-click2.ogg" xysize (48, 48) anchor (0.5, 0.5) style wheelmenu_disabled_button is wheelmenu_button: background Transform("wheelmenu_button", xysize=(48,48), matrixcolor=BrightnessMatrix(0.2)) hover_background At(Transform("wheelmenu_button_opaque", xysize=(48,48), matrixcolor=BrightnessMatrix(0.2)), wheelmenu_hover_anim) xysize (48, 48) anchor (0.5, 0.5) style wheelmenu_window_text is default: anchor (0.5, 0.5) color "#fff" size 10 outlines [(1, "#00000080", 1, 0)] style wheelmenu_button_text is wheelmenu_text transform wheelmenu_anim: on show: zoom 0.0 alpha 0.0 easein_back 0.2 zoom 1.0 alpha 1.0 on hide: easeout 0.15 zoom 0.0 alpha 0.0 transform wheelmenu_hover_anim(t=2.0, strength=0.2, pause=0.0): matrixcolor BrightnessMatrix(value=0.0) linear t/2 matrixcolor BrightnessMatrix(value=strength) linear t/2 matrixcolor BrightnessMatrix(value=0.0) pause pause repeat # transform tooltip_follow: # events False # function tooltip_func # init python: # def tooltip_func(trans, st, at): # x, y = renpy.get_mouse_pos() # if trans.pos is not (x, y): # cw, ch = trans.child.window_size # xanchor = 1.0 if (x + int(cw)) > (config.screen_width) else 0.0 # yanchor = 1.0 if (y + int(ch)) > (config.screen_height) else 0.0 # xoffset = 18 if xanchor else 0 # yoffset = 24 if yanchor else 0 # trans.pos = (x, y) # trans.anchor = (xanchor, yanchor) # trans.offset = (xoffset, yoffset) # return 0 # if not renpy.android: # config.always_shown_screens.append("tooltip") # config.per_frame_screens.append("tooltip")