Engine, Wheel menu, intro, bug fixes

* Added IfExpr Action.
* Added string evals for If func.
* Added reset_action method for RoomObject class.
* Added new actions appropriate for room relevant objects.
* Fixed a potential issue with reset_variables method within Events class.
* Fixed single button radius calculation in wheel menu
* Fixed crash if a null list of actions was supplied to create_wheelmenu method.
* Converted intro to use wheel menu actions.
* Split existing room object labels where it made sense.
This commit is contained in:
LoafyLemon 2024-04-22 18:11:01 +01:00
parent 8e589f45b6
commit 9d76d6088f
13 changed files with 211 additions and 133 deletions

View File

@ -125,10 +125,15 @@ label genie_intro_E1:
# Highlight important objects # Highlight important objects
python: python:
fireplace_OBJ.idle = At("fireplace_idle_shadow", pulse_hover) fireplace_OBJ.idle = At("fireplace_idle_shadow", pulse_hover)
fireplace_OBJ.action = {"Examine": (Text("🔍", align=(0.5, 0.5)), Jump("examine_fireplace"), "True")}
cupboard_OBJ.idle = At("cupboard_idle", pulse_hover) cupboard_OBJ.idle = At("cupboard_idle", pulse_hover)
cupboard_OBJ.action = {"Examine": (Text("🔍", align=(0.5, 0.5)), Jump("examine_cupboard"), "True")}
phoenix_OBJ.idle = At("phoenix_idle", pulse_hover) phoenix_OBJ.idle = At("phoenix_idle", pulse_hover)
phoenix_OBJ.action = {"Examine": (Text("🔍", align=(0.5, 0.5)), Jump("examine_phoenix"), "True")}
door_OBJ.idle = At("door_idle", pulse_hover) door_OBJ.idle = At("door_idle", pulse_hover)
door_OBJ.action = {"Examine": (Text("🔍", align=(0.5, 0.5)), Jump("examine_door"), "True")}
desk_OBJ.idle = At("ch_gen sit_behind_desk", pulse_hover) desk_OBJ.idle = At("ch_gen sit_behind_desk", pulse_hover)
desk_OBJ.action = {"Examine": (Text("🔍", align=(0.5, 0.5)), Jump("examine_desk"), "True")}
achievements.unlock("start") achievements.unlock("start")
states.gen.ev.intro.e1_complete = True states.gen.ev.intro.e1_complete = True
@ -141,6 +146,12 @@ label genie_intro_E2:
gen "Did I just spend an entire day examining this room?" ("base", xpos="far_left", ypos="head") gen "Did I just spend an entire day examining this room?" ("base", xpos="far_left", ypos="head")
call bld("hide") call bld("hide")
$ fireplace_OBJ.reset_action()
$ cupboard_OBJ.reset_action()
$ phoenix_OBJ.reset_action()
$ door_OBJ.reset_action()
$ desk_OBJ.reset_action()
$ states.gen.ev.intro.e2_complete = True $ states.gen.ev.intro.e2_complete = True
# Next is Snape intro E1 # Next is Snape intro E1

View File

@ -339,7 +339,9 @@ init -1 python:
return return
# We need to add these after defaults are finished. # We need to add these after defaults are finished.
renpy.config.label_callbacks.append(execute_event_callbacks) # Explicitly check callback existence to avoid duplication in case a default var is reset.
if not execute_event_callbacks in renpy.config.label_callbacks:
renpy.config.label_callbacks.append(execute_event_callbacks)
def show_events_menu(queues, filter=False, report_progress=True, **kwargs): def show_events_menu(queues, filter=False, report_progress=True, **kwargs):
# This function is a stop gap until we update interfaces. Because it's not tied to any internals, # This function is a stop gap until we update interfaces. Because it's not tied to any internals,

View File

@ -120,7 +120,7 @@ style imagemap:
activate_sound "sounds/qubodup-click2.ogg" activate_sound "sounds/qubodup-click2.ogg"
style button: style button:
activate_sound "sounds/qubodup-click1.ogg" activate_sound "sounds/qubodup-click2.ogg"
hover_sound "sounds/qubodup-hover1.ogg" hover_sound "sounds/qubodup-hover1.ogg"
insensitive_background "#463b3be6" insensitive_background "#463b3be6"
selected_background "#766a6ae6" selected_background "#766a6ae6"

View File

@ -2,6 +2,7 @@ init python in wheelmenu:
import math import math
def pos(num_buttons): def pos(num_buttons):
num_buttons = max(num_buttons, 2)
reference_num_buttons = 32 reference_num_buttons = 32
reference_radius = 0.5 reference_radius = 0.5
radius = reference_radius * (num_buttons / reference_num_buttons) ** 0.5 radius = reference_radius * (num_buttons / reference_num_buttons) ** 0.5
@ -30,6 +31,8 @@ init python in wheelmenu:
if condition: if condition:
return renpy.store.Button(displayable, action=action, style="wheelmenu_button", **kwargs) return renpy.store.Button(displayable, action=action, style="wheelmenu_button", **kwargs)
elif action is None:
return renpy.store.Button(displayable, action=action, style="wheelmenu_disabled_button", **kwargs)
else: else:
return None return None
@ -46,8 +49,10 @@ init python:
buttons.append(wheelmenu.button(displayable, action, condition, tooltip=name)) buttons.append(wheelmenu.button(displayable, action, condition, tooltip=name))
positions = wheelmenu.pos(len(buttons)) if not buttons:
return None
positions = wheelmenu.pos(len(buttons))
return tuple(zip(buttons, positions)) # Nonhashable types cannot be used in a screen, so we use a tuple instead. return tuple(zip(buttons, positions)) # Nonhashable types cannot be used in a screen, so we use a tuple instead.
config.per_frame_screens.append("wheelmenu") config.per_frame_screens.append("wheelmenu")
@ -97,6 +102,7 @@ style wheelmenu_button is empty:
style wheelmenu_disabled_button is wheelmenu_button: style wheelmenu_disabled_button is wheelmenu_button:
background "#ffffff80" background "#ffffff80"
foreground "#ff00ff"
xysize (48, 48) xysize (48, 48)
anchor (0.5, 0.5) anchor (0.5, 0.5)

View File

@ -40,6 +40,11 @@ init -1 python:
# Backwards compatibility, to be resolved if possible. # Backwards compatibility, to be resolved if possible.
self.xpos, self.ypos = self.pos self.xpos, self.ypos = self.pos
if isinstance(self.action, dict):
self._default_action = self.action.copy()
else:
self._default_action = self.action
def generate_hash(self): def generate_hash(self):
salt = str( [self.id, self.pos, self.idle, self.hover, self.foreground, self.background, self.anchor, self.focus_mask, salt = str( [self.id, self.pos, self.idle, self.hover, self.foreground, self.background, self.anchor, self.focus_mask,
self.action, self.hovered, self.unhovered, self.tooltip, self.decoration, self.zorder, self.hidden] self.action, self.hovered, self.unhovered, self.tooltip, self.decoration, self.zorder, self.hidden]
@ -105,10 +110,24 @@ init -1 python:
raise IndexError(f"Action dict was provided for '{self.id}' but it is empty.") raise IndexError(f"Action dict was provided for '{self.id}' but it is empty.")
btns = create_wheelmenu(action) btns = create_wheelmenu(action)
if not btns:
return None
return Call("wheelmenu", btns=btns, ret=self.room.menu) return Call("wheelmenu", btns=btns, ret=self.room.menu)
return action return action
def reset_action(self):
if isinstance(self._default_action, dict):
self.action = self._default_action.copy()
elif isinstance(self._default_action, set):
self.action = set(self._default_action)
elif isinstance(self._default_action, list):
self.action = self._default_action[:]
else:
self.action = self._default_action
def get_anchor(self): def get_anchor(self):
deco = self.decoration deco = self.decoration

View File

@ -32,14 +32,14 @@ default door_OBJ = RoomObject(
idle="door_idle", idle="door_idle",
focus_mask="door_hover", focus_mask="door_hover",
action={ action={
"Summon Snape": (Transform("wheelmenu_snape", align=(0.5, 0.5), xysize=(32, 32)), If(states.sna.busy, None, Jump("summon_snape")), "states.sna.unlocked"), "Summon Snape": (Transform("wheelmenu_snape", align=(0.5, 0.5), xysize=(32, 32)), IfExpr("states.sna.busy", None, Jump("summon_snape")), "states.sna.unlocked"),
"Summon Tonks": (Transform("wheelmenu_tonks", align=(0.5, 0.5), xysize=(32, 32)), If(states.ton.busy, None, Jump("summon_tonks")), "states.ton.unlocked"), "Summon Tonks": (Transform("wheelmenu_tonks", align=(0.5, 0.5), xysize=(32, 32)), IfExpr("states.ton.busy", None, Jump("summon_tonks")), "states.ton.unlocked"),
"Summon Hermione": (Transform("wheelmenu_hermione", align=(0.5, 0.5), xysize=(32, 32)), If(states.her.busy, None, Jump("summon_hermione")), "states.her.unlocked"), "Summon Hermione": (Transform("wheelmenu_hermione", align=(0.5, 0.5), xysize=(32, 32)), IfExpr("states.her.busy", None, Jump("summon_hermione")), "states.her.unlocked"),
"Summon Cho": (Transform("wheelmenu_cho", align=(0.5, 0.5), xysize=(32, 32)), If(states.cho.busy, None, Jump("summon_cho")), "states.cho.unlocked"), "Summon Cho": (Transform("wheelmenu_cho", align=(0.5, 0.5), xysize=(32, 32)), IfExpr("states.cho.busy", None, Jump("summon_cho")), "states.cho.unlocked"),
"Summon Luna": (Transform("wheelmenu_luna", align=(0.5, 0.5), xysize=(32, 32)), If(states.lun.busy, None, Jump("summon_luna")), "states.lun.unlocked"), "Summon Luna": (Transform("wheelmenu_luna", align=(0.5, 0.5), xysize=(32, 32)), IfExpr("states.lun.busy", None, Jump("summon_luna")), "states.lun.unlocked"),
"Summon Astoria": (Transform("wheelmenu_astoria", align=(0.5, 0.5), xysize=(32, 32)), If(states.ast.busy, None, Jump("summon_astoria")), "states.ast.unlocked"), "Summon Astoria": (Transform("wheelmenu_astoria", align=(0.5, 0.5), xysize=(32, 32)), IfExpr("states.ast.busy", None, Jump("summon_astoria")), "states.ast.unlocked"),
"Summon Susan": (Transform("wheelmenu_susan", align=(0.5, 0.5), xysize=(32, 32)), If(states.sus.busy, None, Jump("summon_susan")), "states.sus.unlocked"), "Summon Susan": (Transform("wheelmenu_susan", align=(0.5, 0.5), xysize=(32, 32)), IfExpr("states.sus.busy", None, Jump("summon_susan")), "states.sus.unlocked"),
"Exit": (Text("🚪", align=(0.5, 0.5)), Jump("desk"), "states.map.unlocked"), "Exit": (Text("🚪", align=(0.5, 0.5)), IfExpr("states.map.unlocked", Jump("desk"), Jump("door")), "True"),
}, },
tooltip="Door" tooltip="Door"
) )
@ -52,7 +52,7 @@ default desk_OBJ = RoomObject(
hover="ch_gen sit_behind_desk_hover", hover="ch_gen sit_behind_desk_hover",
focus_mask="ch_gen sit_behind_desk", focus_mask="ch_gen sit_behind_desk",
action={ action={
"Sleep": (Text("💤", align=(0.5, 0.5)), If(game.daytime, Return("night_start"), Return("day_start")), "True"), "Sleep": (Text("💤", align=(0.5, 0.5)), IfExpr("game.daytime", Jump("night_start"), Jump("day_start")), "True"),
"Jerk Off": (Text("🍆", align=(0.5, 0.5)), Jump("jerk_off"), "True"), "Jerk Off": (Text("🍆", align=(0.5, 0.5)), Jump("jerk_off"), "True"),
"Do Paperwork": (Text("📝", align=(0.5, 0.5)), Jump("paperwork"), "True"), "Do Paperwork": (Text("📝", align=(0.5, 0.5)), Jump("paperwork"), "True"),
"Open Deck Builder": (Text("🃏", align=(0.5, 0.5)), Jump("deck_builder"), "states.cardgame.unlocked"), "Open Deck Builder": (Text("🃏", align=(0.5, 0.5)), Jump("deck_builder"), "states.cardgame.unlocked"),

View File

@ -1,26 +1,5 @@
label cupboard: label cupboard:
if game.day == 1:
if not states.gen.ev.intro.cupboard_examined:
$ states.gen.ev.intro.cupboard_examined = True
$ cupboard_OBJ.idle = "cupboard_idle"
call gen_chibi("stand","behind_desk","base", flip=False)
with d5
pause.2
call bld
gen "*Hmm*..." ("base", xpos="far_left", ypos="head")
gen "A cupboard..." ("base", xpos="far_left", ypos="head")
gen "Maybe I should rummage through this one later..." ("base", xpos="far_left", ypos="head")
call gen_chibi("sit_behind_desk")
else:
gen "Looks like any other cupboard, maybe a bit dustier." ("base", xpos="far_left", ypos="head")
if states.gen.ev.intro.bird_examined and states.gen.ev.intro.desk_examined and states.gen.ev.intro.cupboard_examined and states.gen.ev.intro.door_examined and states.gen.ev.intro.fireplace_examined:
jump genie_intro_E2
else:
jump main_room_menu
if states.cupboard_rummaged: if states.cupboard_rummaged:
if game.daytime: if game.daytime:
nar "You have already searched the cupboard today." nar "You have already searched the cupboard today."
@ -77,6 +56,25 @@ label cupboard:
call gen_chibi("sit_behind_desk") call gen_chibi("sit_behind_desk")
jump main_room_menu jump main_room_menu
label examine_cupboard:
if not states.gen.ev.intro.cupboard_examined:
$ states.gen.ev.intro.cupboard_examined = True
$ cupboard_OBJ.idle = "cupboard_idle"
$ cupboard_OBJ.action = Jump("examine_cupboard")
call gen_chibi("stand","behind_desk","base", flip=False)
with d5
pause.2
call bld
gen "*Hmm*..." ("base", xpos="far_left", ypos="head")
gen "A cupboard..." ("base", xpos="far_left", ypos="head")
gen "Maybe I should rummage through this one later..." ("base", xpos="far_left", ypos="head")
call gen_chibi("sit_behind_desk")
else:
gen "Looks like any other cupboard, maybe a bit dustier." ("base", xpos="far_left", ypos="head")
jump main_room_menu
label rum_block(item): label rum_block(item):
if isinstance(item, int): if isinstance(item, int):
$ game.gold += item $ game.gold += item

View File

@ -265,6 +265,38 @@ screen watch():
else: else:
add "interface/desk/watch/night.webp" xpos watch_x+40 ypos watch_y+6 xanchor 0.5 add "interface/desk/watch/night.webp" xpos watch_x+40 ypos watch_y+6 xanchor 0.5
label examine_desk:
if not states.gen.ev.intro.desk_examined:
$ states.gen.ev.intro.desk_examined = True
$ desk_OBJ.idle = "ch_gen sit_behind_desk"
$ desk_OBJ.action = Jump("examine_desk")
call bld
gen "A desk of some sort..." ("base", xpos="far_left", ypos="head")
gen "And a letter..." ("base", xpos="far_left", ypos="head")
gen "Mailed to a certain \"Albus Dumbledore\"." ("base", xpos="far_left", ypos="head")
menu:
gen "Should I open it?" ("base", xpos="far_left", ypos="head")
"-Read the letter-":
call bld
gen "Of course I will!" ("grin", xpos="far_left", ypos="head")
"-Leave it be-":
call bld
gen "Hell no!" ("angry", xpos="far_left", ypos="head")
gen "Of course I will read it!" ("grin", xpos="far_left", ypos="head")
# First letter from Hermione
$ desk_OBJ.foreground = None
$ letter_hg_1.open()
gen "*Ehm*........." ("base", xpos="far_left", ypos="head")
gen "What?" ("base", xpos="far_left", ypos="head")
gen "................................." ("base", xpos="far_left", ypos="head")
else:
gen "I've already checked the desk." ("base", xpos="far_left", ypos="head")
jump main_room_menu
label paperwork: label paperwork:
if letter_work_report in mailbox.get_letters(): if letter_work_report in mailbox.get_letters():
gen "(I need to get paid first.)" ("base", xpos="far_left", ypos="head") gen "(I need to get paid first.)" ("base", xpos="far_left", ypos="head")

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +1,4 @@
label fireplace: label fireplace:
if game.day == 1:
if not states.gen.ev.intro.fireplace_examined:
$ states.gen.ev.intro.fireplace_examined = True
$ fireplace_OBJ.idle = "fireplace_idle_shadow"
call gen_chibi("stand","mid","base")
with d5
gen "*Hmm*... Looks like an ordinary fireplace..." ("base", xpos="far_left", ypos="head")
call gen_chibi("sit_behind_desk")
with d5
else:
gen "Looks like a normal fireplace to me." ("base", xpos="far_left", ypos="head")
if states.gen.ev.intro.bird_examined and states.gen.ev.intro.desk_examined and states.gen.ev.intro.cupboard_examined and states.gen.ev.intro.door_examined and states.gen.ev.intro.fireplace_examined:
jump genie_intro_E2
else:
jump main_room_menu
if is_puzzle_box_in_fireplace(): if is_puzzle_box_in_fireplace():
call gen_chibi("stand", "fireplace", "fireplace") call gen_chibi("stand", "fireplace", "fireplace")
with d3 with d3
@ -55,6 +38,21 @@ label fireplace:
jump main_room_menu jump main_room_menu
label examine_fireplace:
if not states.gen.ev.intro.fireplace_examined:
$ states.gen.ev.intro.fireplace_examined = True
$ fireplace_OBJ.idle = "fireplace_idle_shadow"
$ fireplace_OBJ.action = Jump("examine_fireplace")
call gen_chibi("stand","mid","base")
with d5
gen "*Hmm*... Looks like an ordinary fireplace..." ("base", xpos="far_left", ypos="head")
call gen_chibi("sit_behind_desk")
with d5
else:
gen "Already checked it out." ("base", xpos="far_left", ypos="head")
jump main_room_menu
init python: init python:
def is_puzzle_box_in_fireplace(): def is_puzzle_box_in_fireplace():
return game.day >= 25 and not game.daytime and game.moon and not puzzle_box_ITEM.unlocked and not states.map.seventh_floor.unlocked return game.day >= 25 and not game.daytime and game.moon and not puzzle_box_ITEM.unlocked and not states.map.seventh_floor.unlocked

View File

@ -1,20 +1,16 @@
label phoenix: label examine_phoenix:
if not states.gen.ev.intro.bird_examined:
if game.day == 1: $ states.gen.ev.intro.bird_examined = True
if not states.gen.ev.intro.bird_examined: $ phoenix_OBJ.idle = "phoenix_idle"
$ states.gen.ev.intro.bird_examined = True $ phoenix_OBJ.action = Jump("examine_phoenix")
$ phoenix_OBJ.idle = "phoenix_idle" call gen_chibi("stand","mid","base",flip=False)
call gen_chibi("stand","mid","base",flip=False) with d5
with d5 gen "*Hmm*..." ("base", xpos="far_left", ypos="head")
gen "*Hmm*..." ("base", xpos="far_left", ypos="head") gen "Even this weird-looking bird radiates magic..." ("base", xpos="far_left", ypos="head")
gen "Even this weird-looking bird radiates magic..." ("base", xpos="far_left", ypos="head") call gen_chibi("sit_behind_desk")
call gen_chibi("sit_behind_desk") with d5
with d5 else:
else: gen "It's just a bird. Nothing more to say." ("base", xpos="far_left", ypos="head")
gen "It's just a bird. Nothing more to say." ("base", xpos="far_left", ypos="head")
if states.gen.ev.intro.bird_examined and states.gen.ev.intro.desk_examined and states.gen.ev.intro.cupboard_examined and states.gen.ev.intro.door_examined and states.gen.ev.intro.fireplace_examined:
jump genie_intro_E2
jump main_room_menu jump main_room_menu

View File

@ -101,7 +101,9 @@ init python early:
def reset_variables(*args): def reset_variables(*args):
""" """
Resets the given variables to their default values. Resets the given variables to their default values.
Should not be used : instead, define the base value and have a function set the defaulted one to the defined value (or a copy of it).
As of Ren'py version 8.2.1:
After reevaluating this function and the underlying Ren'py code, this function is deemed safe to use because Ren'Py employs a similar implementation internally, and it is also utilized for user-defined statements. Essentially, it calls execute_default on a node marked as ever_been_changed, followed by running py_eval_bytecode to add it to the global store. The potential side effects to consider is when config.after_default_callbacks is provided, or direct object references are in use. However, this can be easily patched if necessary.
""" """
# Refer to renpy.ast.Default.set_default for implementation details # Refer to renpy.ast.Default.set_default for implementation details
defaults_set = renpy.store._defaults_set defaults_set = renpy.store._defaults_set

View File

@ -329,14 +329,31 @@ init -100 python:
setattr(self, k, v) setattr(self, k, v)
# The original does not support nested actions. # The original does not support nested actions.
# Nor does it support string expressions evaluated at runtime.
@renpy.pure @renpy.pure
def If(expression, true=None, false=None): def If(expression, true=None, false=None):
if isinstance(expression, Action): if isinstance(expression, Action):
expression = expression() expression = expression()
elif isinstance(expression, str):
expression = eval(expression)
return true if expression else false return true if expression else false
@renpy.pure
class IfExpr(Action):
def __init__(self, expr, true=None, false=None):
self.expr = expr
self.true = true
self.false = false
def __call__(self):
result = If(self.expr, self.true, self.false)
if isinstance(result, Action):
return result()
return result
# Adds support for nested stores for replay scope # Adds support for nested stores for replay scope
def _call_replay(label, scope={}): def _call_replay(label, scope={}):
renpy.display.focus.clear_focus() renpy.display.focus.clear_focus()