From a5d78748d71c0944ea16c9be16a378459a509ec1 Mon Sep 17 00:00:00 2001 From: LoafyLemon Date: Thu, 26 Sep 2024 16:58:42 +0100 Subject: [PATCH] Refactor say functions, Bug fixes * Refactored say functions to be more clear and easier to maintain * Fixed Tonks' hair colour not updating after the temporary change * Fixed character sprites being displayed above CGs when they shouldn't. --- game/scripts/characters/astoria/common.rpy | 103 +++++++++------- game/scripts/characters/cho/common.rpy | 105 +++++++++------- game/scripts/characters/hermione/common.rpy | 104 +++++++++------- game/scripts/characters/hooch/common.rpy | 94 +++++++++------ game/scripts/characters/luna/common.rpy | 103 +++++++++------- game/scripts/characters/susan/common.rpy | 104 +++++++++------- game/scripts/characters/tonks/common.rpy | 126 ++++++++++++-------- 7 files changed, 451 insertions(+), 288 deletions(-) diff --git a/game/scripts/characters/astoria/common.rpy b/game/scripts/characters/astoria/common.rpy index 551a28ab..626c815b 100644 --- a/game/scripts/characters/astoria/common.rpy +++ b/game/scripts/characters/astoria/common.rpy @@ -35,43 +35,66 @@ define character.astoria_say = Character("name_astoria_genie", show_icon="astori init python: def ast(what, mouth=None, eyes=None, eyebrows=None, pupils=None, cheeks=None, tears=None, - emote=None, xpos=None, ypos=None, pos=None, flip=None, trans=None, animation=False, **kwargs): + emote=None, face=None, xpos=None, ypos=None, pos=None, flip=None, trans=None, + animation=False, **kwargs): - def show(): - astoria.show(force=True) + def update_face(face_attrs): + """Update Astoria's face with provided attributes.""" + if any(face_attrs.values()): + astoria.set_face(**face_attrs) + return True + return False - renpy.with_statement({"dolls": trans or d1}) + def update_position(xpos, ypos): + """Update Astoria's position based on xpos and ypos.""" + if xpos is not None or ypos is not None: + xpos = astoria.pos[0] if xpos is None else sprite_pos.get("x", {}).get(xpos, xpos) + ypos = astoria.pos[1] if ypos is None else sprite_pos.get("y", {}).get(ypos, ypos) + astoria.pos = (xpos, ypos) + return True + return False - face = {"mouth": mouth, "eyes": eyes, "eyebrows": eyebrows, "pupils": pupils, "cheeks": cheeks, "tears": tears} - temp_face = renpy.game.context().temporary_attributes - redraw = False - tag = astoria.tag - layer = astoria.layer + def handle_temp_attributes(temp_face): + """Handle temporary face attributes.""" + if temp_face: + last_face = astoria.get_face() + temp_data = dict(zip(temp_face[::2], temp_face[1::2])) - if xpos is not None or ypos is not None: - xpos = astoria.pos[0] if xpos is None else sprite_pos.get("x").get(xpos, xpos) - ypos = astoria.pos[1] if ypos is None else sprite_pos.get("y").get(ypos, ypos) - astoria.pos = (xpos, ypos) - redraw = True + astoria.set_face(**temp_data) + return last_face + return None - head_only = astoria.pos[1] == sprite_pos.get("y").get("head") + def show_astoria(redraw, head_only): + """Show or hide Astoria depending on the state.""" + if redraw and not head_only: + astoria.show(force=True) + renpy.with_statement({"dolls": trans or d1}) + elif head_only: + astoria.hide() - if any(face.values()): - astoria.set_face(**face) - redraw = True + def reset_attributes(last_face): + """Reset temporary face attributes.""" + if last_face: + astoria.set_face(**last_face) - if temp_face: - last_face = astoria.get_face() + # Determine name prefix and suffix + name_prefix = f'Astoria {{size=-20}}"' if name_astoria_genie != "Astoria" else "" + name_suffix = '"{/size}' if name_prefix else "" - d = dict(zip(temp_face[::2], temp_face[1::2])) - astoria.set_face(**d) - redraw = True + # If no visual changes, just make Astoria speak + if not any((mouth, eyes, eyebrows, pupils, cheeks, tears, emote, xpos, ypos, flip, trans, animation)): + character.astoria_say(what, who_prefix=name_prefix, who_suffix=name_suffix, **kwargs) + return + # Handle position update + redraw = update_position(xpos, ypos) + + # Handle emote, animation, and flipping if emote: astoria.set_emote(emote) redraw = True - if animation != False: + if animation: astoria.animation = animation redraw = True @@ -79,29 +102,27 @@ init python: astoria.xzoom = -1 if flip else 1 redraw = True - if redraw and not head_only: - show() - elif head_only: - astoria.hide() + # Handle face update and temporary attributes + face_attrs = {"mouth": mouth, "eyes": eyes, "eyebrows": eyebrows, "pupils": pupils, "cheeks": cheeks, "tears": tears} + redraw = redraw or update_face(face_attrs) - # Figure out nickname - if name_astoria_genie != "Astoria": - name_prefix = "Astoria {size=-20}\"" - name_suffix = "\"{/size}" - else: - name_prefix = "" - name_suffix = "" + temp_face = renpy.game.context().temporary_attributes + last_face = handle_temp_attributes(temp_face) + redraw = redraw or bool(temp_face) + # Handle display logic + head_only = astoria.pos[1] == sprite_pos.get("y", {}).get("head") + show_astoria(redraw, head_only) + + # Handle dialog and doll positioning if what: + side_doll = None if head_only: - xpos = sprite_pos.get("x").get("right_corner") if astoria.xzoom == 1 else sprite_pos.get("x").get("left_corner") + xpos = sprite_pos.get("x", {}).get("right_corner", 0) - 25 if astoria.xzoom == 1 else sprite_pos.get("x", {}).get("left_corner", 0) + 25 ypos = astoria.pos[1] side_doll = At(astoria.image, doll_transform((xpos, ypos), astoria.zoom, astoria.xzoom)) - else: - side_doll = None character.astoria_say(what, who_prefix=name_prefix, who_suffix=name_suffix, show_side_doll=side_doll, **kwargs) - if temp_face: - astoria.set_face(**last_face) - + # Reset face if modified temporarily + reset_attributes(last_face) diff --git a/game/scripts/characters/cho/common.rpy b/game/scripts/characters/cho/common.rpy index eeff5aad..682a27a9 100644 --- a/game/scripts/characters/cho/common.rpy +++ b/game/scripts/characters/cho/common.rpy @@ -32,44 +32,68 @@ define character.cho_say = Character("name_cho_genie", show_icon="cho", dynamic= init python in character: # Cho's name is short, therefore it needs to be initialised in character scope, # otherwise we won't be able to use the same name for both Doll and Character calls. + def cho(what, mouth=None, eyes=None, eyebrows=None, pupils=None, cheeks=None, tears=None, - emote=None, face=None, xpos=None, ypos=None, pos=None, flip=None, trans=None, animation=False, **kwargs): + emote=None, face=None, xpos=None, ypos=None, pos=None, flip=None, trans=None, + animation=False, **kwargs): - def show(): - renpy.store.cho.show(force=True) + def update_face(face_attrs): + """Update Cho's face with provided attributes.""" + if any(face_attrs.values()): + renpy.store.cho.set_face(**face_attrs) + return True + return False - renpy.with_statement({"dolls": trans or renpy.store.d1}) + def update_position(xpos, ypos): + """Update Cho's position based on xpos and ypos.""" + if xpos is not None or ypos is not None: + xpos = renpy.store.cho.pos[0] if xpos is None else renpy.store.sprite_pos.get("x", {}).get(xpos, xpos) + ypos = renpy.store.cho.pos[1] if ypos is None else renpy.store.sprite_pos.get("y", {}).get(ypos, ypos) + renpy.store.cho.pos = (xpos, ypos) + return True + return False - face = {"mouth": mouth, "eyes": eyes, "eyebrows": eyebrows, "pupils": pupils, "cheeks": cheeks, "tears": tears} - temp_face = renpy.game.context().temporary_attributes - redraw = False - tag = renpy.store.cho.tag - layer = renpy.store.cho.layer + def handle_temp_attributes(temp_face): + """Handle temporary face attributes.""" + if temp_face: + last_face = renpy.store.cho.get_face() + temp_data = dict(zip(temp_face[::2], temp_face[1::2])) - if xpos is not None or ypos is not None: - xpos = renpy.store.cho.pos[0] if xpos is None else renpy.store.sprite_pos.get("x").get(xpos, xpos) - ypos = renpy.store.cho.pos[1] if ypos is None else renpy.store.sprite_pos.get("y").get(ypos, ypos) - renpy.store.cho.pos = (xpos, ypos) - redraw = True + renpy.store.cho.set_face(**temp_data) + return last_face + return None - head_only = renpy.store.cho.pos[1] == renpy.store.sprite_pos.get("y").get("head") + def show_cho(redraw, head_only): + """Show or hide Cho depending on the state.""" + if redraw and not head_only: + renpy.store.cho.show(force=True) + renpy.with_statement({"dolls": trans or renpy.store.d1}) + elif head_only: + renpy.store.cho.hide() - if any(face.values()): - renpy.store.cho.set_face(**face) - redraw = True + def reset_attributes(last_face): + """Reset temporary face attributes.""" + if last_face: + renpy.store.cho.set_face(**last_face) - if temp_face: - last_face = renpy.store.cho.get_face() + # Determine name prefix and suffix + name_prefix = f'Cho {{size=-20}}"' if renpy.store.name_cho_genie != "Cho" else "" + name_suffix = '"{/size}' if name_prefix else "" - d = dict(zip(temp_face[::2], temp_face[1::2])) - renpy.store.cho.set_face(**d) - redraw = True + # If no visual changes, just make Cho speak + if not any((mouth, eyes, eyebrows, pupils, cheeks, tears, emote, xpos, ypos, flip, trans, animation)): + renpy.store.character.cho_say(what, who_prefix=name_prefix, who_suffix=name_suffix, **kwargs) + return + # Handle position update + redraw = update_position(xpos, ypos) + + # Handle emote, animation, and flipping if emote: renpy.store.cho.set_emote(emote) redraw = True - if animation != False: + if animation: renpy.store.cho.animation = animation redraw = True @@ -77,28 +101,27 @@ init python in character: renpy.store.cho.xzoom = -1 if flip else 1 redraw = True - if redraw and not head_only: - show() - elif head_only: - renpy.store.cho.hide() + # Handle face update and temporary attributes + face_attrs = {"mouth": mouth, "eyes": eyes, "eyebrows": eyebrows, "pupils": pupils, "cheeks": cheeks, "tears": tears} + redraw = redraw or update_face(face_attrs) - # Figure out nickname - if renpy.store.name_cho_genie != "Cho": - name_prefix = "Cho {size=-20}\"" - name_suffix = "\"{/size}" - else: - name_prefix = "" - name_suffix = "" + temp_face = renpy.game.context().temporary_attributes + last_face = handle_temp_attributes(temp_face) + redraw = redraw or bool(temp_face) + # Handle display logic + head_only = renpy.store.cho.pos[1] == renpy.store.sprite_pos.get("y", {}).get("head") + show_cho(redraw, head_only) + + # Handle dialog and doll positioning if what: + side_doll = None if head_only: - xpos = renpy.store.sprite_pos.get("x").get("right_corner") if renpy.store.cho.xzoom == 1 else renpy.store.sprite_pos.get("x").get("left_corner") + xpos = renpy.store.sprite_pos.get("x", {}).get("right_corner", 0) - 25 if renpy.store.cho.xzoom == 1 else renpy.store.sprite_pos.get("x", {}).get("left_corner", 0) + 25 ypos = renpy.store.cho.pos[1] side_doll = renpy.store.At(renpy.store.cho.image, renpy.store.doll_transform((xpos, ypos), renpy.store.cho.zoom, renpy.store.cho.xzoom)) - else: - side_doll = None - cho_say(what, who_prefix=name_prefix, who_suffix=name_suffix, show_side_doll=side_doll, **kwargs) + renpy.store.character.cho_say(what, who_prefix=name_prefix, who_suffix=name_suffix, show_side_doll=side_doll, **kwargs) - if temp_face: - renpy.store.cho.set_face(**last_face) + # Reset face if modified temporarily + reset_attributes(last_face) diff --git a/game/scripts/characters/hermione/common.rpy b/game/scripts/characters/hermione/common.rpy index f92b8cf2..598ff94b 100644 --- a/game/scripts/characters/hermione/common.rpy +++ b/game/scripts/characters/hermione/common.rpy @@ -28,43 +28,66 @@ define character.hermione_say = Character("name_hermione_genie", show_icon="herm init python: def her(what, mouth=None, eyes=None, eyebrows=None, pupils=None, cheeks=None, tears=None, - emote=None, face=None, xpos=None, ypos=None, pos=None, flip=None, trans=None, animation=False, **kwargs): + emote=None, face=None, xpos=None, ypos=None, pos=None, flip=None, trans=None, + animation=False, **kwargs): - def show(): - hermione.show(force=True) + def update_face(face_attrs): + """Update Hermione's face with provided attributes.""" + if any(face_attrs.values()): + hermione.set_face(**face_attrs) + return True + return False - renpy.with_statement({"dolls": trans or d1}) + def update_position(xpos, ypos): + """Update Hermione's position based on xpos and ypos.""" + if xpos is not None or ypos is not None: + xpos = hermione.pos[0] if xpos is None else sprite_pos.get("x", {}).get(xpos, xpos) + ypos = hermione.pos[1] if ypos is None else sprite_pos.get("y", {}).get(ypos, ypos) + hermione.pos = (xpos, ypos) + return True + return False - face = {"mouth": mouth, "eyes": eyes, "eyebrows": eyebrows, "pupils": pupils, "cheeks": cheeks, "tears": tears} - temp_face = renpy.game.context().temporary_attributes - redraw = False - tag = hermione.tag - layer = hermione.layer + def handle_temp_attributes(temp_face): + """Handle temporary face attributes.""" + if temp_face: + last_face = hermione.get_face() + temp_data = dict(zip(temp_face[::2], temp_face[1::2])) - if xpos is not None or ypos is not None: - xpos = hermione.pos[0] if xpos is None else sprite_pos.get("x").get(xpos, xpos) - ypos = hermione.pos[1] if ypos is None else sprite_pos.get("y").get(ypos, ypos) - hermione.pos = (xpos, ypos) - redraw = True + hermione.set_face(**temp_data) + return last_face + return None - head_only = hermione.pos[1] == sprite_pos.get("y").get("head") + def show_hermione(redraw, head_only): + """Show or hide Hermione depending on the state.""" + if redraw and not head_only: + hermione.show(force=True) + renpy.with_statement({"dolls": trans or d1}) + elif head_only: + hermione.hide() - if any(face.values()): - hermione.set_face(**face) - redraw = True + def reset_attributes(last_face): + """Reset temporary face attributes.""" + if last_face: + hermione.set_face(**last_face) - if temp_face: - last_face = hermione.get_face() + # Determine name prefix and suffix + name_prefix = f'Hermione {{size=-20}}"' if name_hermione_genie != "Hermione" else "" + name_suffix = '"{/size}' if name_prefix else "" - d = dict(zip(temp_face[::2], temp_face[1::2])) - hermione.set_face(**d) - redraw = True + # If no visual changes, just make Hermione speak + if not any((mouth, eyes, eyebrows, pupils, cheeks, tears, emote, xpos, ypos, flip, trans, animation)): + character.hermione_say(what, who_prefix=name_prefix, who_suffix=name_suffix, **kwargs) + return + # Handle position update + redraw = update_position(xpos, ypos) + + # Handle emote, animation, and flipping if emote: hermione.set_emote(emote) redraw = True - if animation != False: + if animation: hermione.animation = animation redraw = True @@ -72,30 +95,31 @@ init python: hermione.xzoom = -1 if flip else 1 redraw = True - if redraw and not head_only: - show() - elif head_only: - hermione.hide() + # Handle face update and temporary attributes + face_attrs = {"mouth": mouth, "eyes": eyes, "eyebrows": eyebrows, "pupils": pupils, "cheeks": cheeks, "tears": tears} + redraw = redraw or update_face(face_attrs) - # Figure out nickname - if name_hermione_genie != "Hermione": - name_prefix = "Hermione {size=-20}\"" - name_suffix = "\"{/size}" - else: - name_prefix = "" - name_suffix = "" + temp_face = renpy.game.context().temporary_attributes + last_face = handle_temp_attributes(temp_face) + redraw = redraw or bool(temp_face) + # Handle display logic + head_only = hermione.pos[1] == sprite_pos.get("y", {}).get("head") + show_hermione(redraw, head_only) + + # Handle dialog and doll positioning if what: + side_doll = None if head_only: - xpos = sprite_pos.get("x").get("right_corner") -20 if hermione.xzoom == 1 else sprite_pos.get("x").get("left_corner") +20 + xpos = sprite_pos.get("x", {}).get("right_corner", 0) - 25 if hermione.xzoom == 1 else sprite_pos.get("x", {}).get("left_corner", 0) + 25 ypos = hermione.pos[1] side_doll = At(hermione.image, doll_transform((xpos, ypos), hermione.zoom, hermione.xzoom)) - else: - side_doll = None + character.hermione_say(what, who_prefix=name_prefix, who_suffix=name_suffix, show_side_doll=side_doll, **kwargs) - if temp_face: - hermione.set_face(**last_face) + # Reset face if modified temporarily + reset_attributes(last_face) + label too_much: diff --git a/game/scripts/characters/hooch/common.rpy b/game/scripts/characters/hooch/common.rpy index b162bf17..51ebac21 100644 --- a/game/scripts/characters/hooch/common.rpy +++ b/game/scripts/characters/hooch/common.rpy @@ -19,43 +19,62 @@ define character.hooch_say = Character("name_hooch_genie", show_icon="hooch", dy init python: def hoo(what, mouth=None, eyes=None, eyebrows=None, pupils=None, cheeks=None, tears=None, - emote=None, face=None, xpos=None, ypos=None, pos=None, flip=None, trans=None, animation=False, **kwargs): + emote=None, face=None, xpos=None, ypos=None, pos=None, flip=None, trans=None, + animation=False, **kwargs): - def show(): - hooch.show(force=True) + def update_face(face_attrs): + """Update Hooch's face with provided attributes.""" + if any(face_attrs.values()): + hooch.set_face(**face_attrs) + return True + return False - renpy.with_statement({"dolls": trans or d1}) + def update_position(xpos, ypos): + """Update Hooch's position based on xpos and ypos.""" + if xpos is not None or ypos is not None: + xpos = hooch.pos[0] if xpos is None else sprite_pos.get("x", {}).get(xpos, xpos) + ypos = hooch.pos[1] if ypos is None else sprite_pos.get("y", {}).get(ypos, ypos) + hooch.pos = (xpos, ypos) + return True + return False - face = {"mouth": mouth, "eyes": eyes, "eyebrows": eyebrows, "pupils": pupils, "cheeks": cheeks, "tears": tears} - temp_face = renpy.game.context().temporary_attributes - redraw = False - tag = hooch.tag - layer = hooch.layer + def handle_temp_attributes(temp_face): + """Handle temporary face attributes.""" + if temp_face: + last_face = hooch.get_face() + temp_data = dict(zip(temp_face[::2], temp_face[1::2])) - if xpos is not None or ypos is not None: - xpos = hooch.pos[0] if xpos is None else sprite_pos.get("x").get(xpos, xpos) - ypos = hooch.pos[1] if ypos is None else sprite_pos.get("y").get(ypos, ypos) - hooch.pos = (xpos, ypos) - redraw = True + hooch.set_face(**temp_data) + return last_face + return None - head_only = hooch.pos[1] == sprite_pos.get("y").get("head") + def show_hooch(redraw, head_only): + """Show or hide Hooch depending on the state.""" + if redraw and not head_only: + hooch.show(force=True) + renpy.with_statement({"dolls": trans or d1}) + elif head_only: + hooch.hide() - if any(face.values()): - hooch.set_face(**face) - redraw = True + def reset_attributes(last_face): + """Reset temporary face attributes.""" + if last_face: + hooch.set_face(**last_face) - if temp_face: - last_face = hooch.get_face() + # If no visual changes, just make Hooch speak + if not any((mouth, eyes, eyebrows, pupils, cheeks, tears, emote, xpos, ypos, flip, trans, animation)): + character.hooch_say(what, **kwargs) + return - d = dict(zip(temp_face[::2], temp_face[1::2])) - hooch.set_face(**d) - redraw = True + # Handle position update + redraw = update_position(xpos, ypos) + # Handle emote, animation, and flipping if emote: hooch.set_emote(emote) redraw = True - if animation != False: + if animation: hooch.animation = animation redraw = True @@ -63,20 +82,27 @@ init python: hooch.xzoom = -1 if flip else 1 redraw = True - if redraw and not head_only: - show() - elif head_only: - hooch.hide() + # Handle face update and temporary attributes + face_attrs = {"mouth": mouth, "eyes": eyes, "eyebrows": eyebrows, "pupils": pupils, "cheeks": cheeks, "tears": tears} + redraw = redraw or update_face(face_attrs) + temp_face = renpy.game.context().temporary_attributes + last_face = handle_temp_attributes(temp_face) + redraw = redraw or bool(temp_face) + + # Handle display logic + head_only = hooch.pos[1] == sprite_pos.get("y", {}).get("head") + show_hooch(redraw, head_only) + + # Handle dialog and doll positioning if what: + side_doll = None if head_only: - xpos = sprite_pos.get("x").get("right_corner") if hooch.xzoom == 1 else sprite_pos.get("x").get("left_corner") + xpos = sprite_pos.get("x", {}).get("right_corner", 0) - 25 if hooch.xzoom == 1 else sprite_pos.get("x", {}).get("left_corner", 0) + 25 ypos = hooch.pos[1] side_doll = At(hooch.image, doll_transform((xpos, ypos), hooch.zoom, hooch.xzoom)) - else: - side_doll = None - character.hooch_say(what, show_side_doll=side_doll, **kwargs) - if temp_face: - hooch.set_face(**last_face) + character.hooch_say(what, who_prefix=name_prefix, who_suffix=name_suffix, show_side_doll=side_doll, **kwargs) + # Reset face if modified temporarily + reset_attributes(last_face) diff --git a/game/scripts/characters/luna/common.rpy b/game/scripts/characters/luna/common.rpy index cd4a1d12..d5f096f5 100644 --- a/game/scripts/characters/luna/common.rpy +++ b/game/scripts/characters/luna/common.rpy @@ -31,43 +31,66 @@ define character.luna_say = Character("name_luna_genie", show_icon="luna", dynam init python: def lun(what, mouth=None, eyes=None, eyebrows=None, pupils=None, cheeks=None, tears=None, - emote=None, face=None, xpos=None, ypos=None, pos=None, flip=None, trans=None, animation=False, **kwargs): + emote=None, face=None, xpos=None, ypos=None, pos=None, flip=None, trans=None, + animation=False, **kwargs): - def show(): - luna.show(force=True) + def update_face(face_attrs): + """Update Luna's face with provided attributes.""" + if any(face_attrs.values()): + luna.set_face(**face_attrs) + return True + return False - renpy.with_statement({"dolls": trans or d1}) + def update_position(xpos, ypos): + """Update Luna's position based on xpos and ypos.""" + if xpos is not None or ypos is not None: + xpos = luna.pos[0] if xpos is None else sprite_pos.get("x", {}).get(xpos, xpos) + ypos = luna.pos[1] if ypos is None else sprite_pos.get("y", {}).get(ypos, ypos) + luna.pos = (xpos, ypos) + return True + return False - face = {"mouth": mouth, "eyes": eyes, "eyebrows": eyebrows, "pupils": pupils, "cheeks": cheeks, "tears": tears} - temp_face = renpy.game.context().temporary_attributes - redraw = False - tag = luna.tag - layer = luna.layer + def handle_temp_attributes(temp_face): + """Handle temporary face attributes.""" + if temp_face: + last_face = luna.get_face() + temp_data = dict(zip(temp_face[::2], temp_face[1::2])) - if xpos is not None or ypos is not None: - xpos = luna.pos[0] if xpos is None else sprite_pos.get("x").get(xpos, xpos) - ypos = luna.pos[1] if ypos is None else sprite_pos.get("y").get(ypos, ypos) - luna.pos = (xpos, ypos) - redraw = True + luna.set_face(**temp_data) + return last_face + return None - head_only = luna.pos[1] == sprite_pos.get("y").get("head") + def show_luna(redraw, head_only): + """Show or hide Luna depending on the state.""" + if redraw and not head_only: + luna.show(force=True) + renpy.with_statement({"dolls": trans or d1}) + elif head_only: + luna.hide() - if any(face.values()): - luna.set_face(**face) - redraw = True + def reset_attributes(last_face): + """Reset temporary face attributes.""" + if last_face: + luna.set_face(**last_face) - if temp_face: - last_face = luna.get_face() + # Determine name prefix and suffix + name_prefix = f'Luna {{size=-20}}"' if name_luna_genie != "Luna" else "" + name_suffix = '"{/size}' if name_prefix else "" - d = dict(zip(temp_face[::2], temp_face[1::2])) - luna.set_face(**d) - redraw = True + # If no visual changes, just make Luna speak + if not any((mouth, eyes, eyebrows, pupils, cheeks, tears, emote, xpos, ypos, flip, trans, animation)): + character.luna_say(what, who_prefix=name_prefix, who_suffix=name_suffix, **kwargs) + return + # Handle position update + redraw = update_position(xpos, ypos) + + # Handle emote, animation, and flipping if emote: luna.set_emote(emote) redraw = True - if animation != False: + if animation: luna.animation = animation redraw = True @@ -75,27 +98,27 @@ init python: luna.xzoom = -1 if flip else 1 redraw = True - if redraw and not head_only: - show() - elif head_only: - luna.hide() + # Handle face update and temporary attributes + face_attrs = {"mouth": mouth, "eyes": eyes, "eyebrows": eyebrows, "pupils": pupils, "cheeks": cheeks, "tears": tears} + redraw = redraw or update_face(face_attrs) - # Figure out nickname - if name_luna_genie != "Luna": - name_prefix = "Luna {size=-20}\"" - name_suffix = "\"{/size}" - else: - name_prefix = "" - name_suffix = "" + temp_face = renpy.game.context().temporary_attributes + last_face = handle_temp_attributes(temp_face) + redraw = redraw or bool(temp_face) + # Handle display logic + head_only = luna.pos[1] == sprite_pos.get("y", {}).get("head") + show_luna(redraw, head_only) + + # Handle dialog and doll positioning if what: + side_doll = None if head_only: - xpos = sprite_pos.get("x").get("right_corner") - 46 if luna.xzoom == 1 else sprite_pos.get("x").get("left_corner") + 46 + xpos = sprite_pos.get("x", {}).get("right_corner", 0) - 25 if luna.xzoom == 1 else sprite_pos.get("x", {}).get("left_corner", 0) + 25 ypos = luna.pos[1] side_doll = At(luna.image, doll_transform((xpos, ypos), luna.zoom, luna.xzoom)) - else: - side_doll = None + character.luna_say(what, who_prefix=name_prefix, who_suffix=name_suffix, show_side_doll=side_doll, **kwargs) - if temp_face: - luna.set_face(**last_face) + # Reset face if modified temporarily + reset_attributes(last_face) diff --git a/game/scripts/characters/susan/common.rpy b/game/scripts/characters/susan/common.rpy index 6715ab27..4e0963b5 100644 --- a/game/scripts/characters/susan/common.rpy +++ b/game/scripts/characters/susan/common.rpy @@ -30,43 +30,66 @@ define character.susan_say = Character("name_susan_genie", show_icon="susan", dy init python: def sus(what, mouth=None, eyes=None, eyebrows=None, pupils=None, cheeks=None, tears=None, - emote=None, face=None, xpos=None, ypos=None, pos=None, flip=None, trans=None, animation=False, **kwargs): + emote=None, face=None, xpos=None, ypos=None, pos=None, flip=None, trans=None, + animation=False, **kwargs): - def show(): - susan.show(force=True) + def update_face(face_attrs): + """Update Susan's face with provided attributes.""" + if any(face_attrs.values()): + susan.set_face(**face_attrs) + return True + return False - renpy.with_statement({"dolls": trans or d1}) + def update_position(xpos, ypos): + """Update Susan's position based on xpos and ypos.""" + if xpos is not None or ypos is not None: + xpos = susan.pos[0] if xpos is None else sprite_pos.get("x", {}).get(xpos, xpos) + ypos = susan.pos[1] if ypos is None else sprite_pos.get("y", {}).get(ypos, ypos) + susan.pos = (xpos, ypos) + return True + return False - face = {"mouth": mouth, "eyes": eyes, "eyebrows": eyebrows, "pupils": pupils, "cheeks": cheeks, "tears": tears} - temp_face = renpy.game.context().temporary_attributes - redraw = False - tag = susan.tag - layer = susan.layer + def handle_temp_attributes(temp_face): + """Handle temporary face attributes.""" + if temp_face: + last_face = susan.get_face() + temp_data = dict(zip(temp_face[::2], temp_face[1::2])) - if xpos is not None or ypos is not None: - xpos = susan.pos[0] if xpos is None else sprite_pos.get("x").get(xpos, xpos) - ypos = susan.pos[1] if ypos is None else sprite_pos.get("y").get(ypos, ypos) - susan.pos = (xpos, ypos) - redraw = True + susan.set_face(**temp_data) + return last_face + return None - head_only = susan.pos[1] == sprite_pos.get("y").get("head") + def show_susan(redraw, head_only): + """Show or hide Susan depending on the state.""" + if redraw and not head_only: + susan.show(force=True) + renpy.with_statement({"dolls": trans or d1}) + elif head_only: + susan.hide() - if any(face.values()): - susan.set_face(**face) - redraw = True + def reset_attributes(last_face): + """Reset temporary face attributes.""" + if last_face: + susan.set_face(**last_face) - if temp_face: - last_face = susan.get_face() + # Determine name prefix and suffix + name_prefix = f'Susan {{size=-20}}"' if name_susan_genie != "Susan" else "" + name_suffix = '"{/size}' if name_prefix else "" - d = dict(zip(temp_face[::2], temp_face[1::2])) - susan.set_face(**d) - redraw = True + # If no visual changes, just make Susan speak + if not any((mouth, eyes, eyebrows, pupils, cheeks, tears, emote, xpos, ypos, flip, trans, animation)): + character.susan_say(what, who_prefix=name_prefix, who_suffix=name_suffix, **kwargs) + return + # Handle position update + redraw = update_position(xpos, ypos) + + # Handle emote, animation, and flipping if emote: susan.set_emote(emote) redraw = True - if animation != False: + if animation: susan.animation = animation redraw = True @@ -74,28 +97,27 @@ init python: susan.xzoom = -1 if flip else 1 redraw = True - if redraw and not head_only: - show() - elif head_only: - tonks.hide() + # Handle face update and temporary attributes + face_attrs = {"mouth": mouth, "eyes": eyes, "eyebrows": eyebrows, "pupils": pupils, "cheeks": cheeks, "tears": tears} + redraw = redraw or update_face(face_attrs) - # Figure out nickname - if name_susan_genie != "Susan": - name_prefix = "Susan {size=-20}\"" - name_suffix = "\"{/size}" - else: - name_prefix = "" - name_suffix = "" + temp_face = renpy.game.context().temporary_attributes + last_face = handle_temp_attributes(temp_face) + redraw = redraw or bool(temp_face) + # Handle display logic + head_only = susan.pos[1] == sprite_pos.get("y", {}).get("head") + show_susan(redraw, head_only) + + # Handle dialog and doll positioning if what: + side_doll = None if head_only: - xpos = sprite_pos.get("x").get("right_corner") if susan.xzoom == 1 else sprite_pos.get("x").get("left_corner") + xpos = sprite_pos.get("x", {}).get("right_corner", 0) - 25 if susan.xzoom == 1 else sprite_pos.get("x", {}).get("left_corner", 0) + 25 ypos = susan.pos[1] side_doll = At(susan.image, doll_transform((xpos, ypos), susan.zoom, susan.xzoom)) - else: - side_doll = None + character.susan_say(what, who_prefix=name_prefix, who_suffix=name_suffix, show_side_doll=side_doll, **kwargs) - if temp_face: - susan.set_face(**last_face) - + # Reset face if modified temporarily + reset_attributes(last_face) diff --git a/game/scripts/characters/tonks/common.rpy b/game/scripts/characters/tonks/common.rpy index c8769315..7437b665 100644 --- a/game/scripts/characters/tonks/common.rpy +++ b/game/scripts/characters/tonks/common.rpy @@ -41,52 +41,79 @@ define character.tonks_say = Character("name_tonks_genie", show_icon="tonks", dy init python: def ton(what, mouth=None, eyes=None, eyebrows=None, pupils=None, cheeks=None, tears=None, - emote=None, face=None, xpos=None, ypos=None, pos=None, flip=None, trans=None, animation=False, **kwargs): + emote=None, face=None, xpos=None, ypos=None, pos=None, flip=None, trans=None, + animation=False, **kwargs): - def show(): - tonks.show(force=True) + def update_face(face_attrs): + """Update Tonks' face with provided attributes.""" + if any(face_attrs.values()): + tonks.set_face(**face_attrs) + return True + return False - renpy.with_statement({"dolls": trans or d1}) + def update_position(xpos, ypos): + """Update Tonks' position based on xpos and ypos.""" + if xpos is not None or ypos is not None: + xpos = tonks.pos[0] if xpos is None else sprite_pos.get("x", {}).get(xpos, xpos) + ypos = tonks.pos[1] if ypos is None else sprite_pos.get("y", {}).get(ypos, ypos) + tonks.pos = (xpos, ypos) + return True + return False - face = {"mouth": mouth, "eyes": eyes, "eyebrows": eyebrows, "pupils": pupils, "cheeks": cheeks, "tears": tears} - temp_face = renpy.game.context().temporary_attributes - temp_hair = None - redraw = False - tag = tonks.tag - layer = tonks.layer + def handle_temp_attributes(temp_face): + """Handle temporary face and hair attributes.""" + if temp_face: + last_face = tonks.get_face() + temp_data = dict(zip(temp_face[::2], temp_face[1::2])) + temp_hair = temp_data.pop("hair", None) - if xpos is not None or ypos is not None: - xpos = tonks.pos[0] if xpos is None else sprite_pos.get("x").get(xpos, xpos) - ypos = tonks.pos[1] if ypos is None else sprite_pos.get("y").get(ypos, ypos) - tonks.pos = (xpos, ypos) - redraw = True + tonks.set_face(**temp_data) + return last_face, temp_hair + return None, None - head_only = tonks.pos[1] == sprite_pos.get("y").get("head") + def update_hair(temp_hair): + """Update Tonks' hair color if necessary.""" + if temp_hair: + last_hair = tonks.get_equipped("hair").color + new_color = tonks_haircolor_table.get(temp_hair) + tonks.get_equipped("hair").set_color(new_color) + return last_hair + return None - if any(face.values()): - tonks.set_face(**face) - redraw = True + def show_tonks(redraw, head_only): + """Show or hide Tonks depending on the state.""" + if redraw and not head_only: + tonks.show(force=True) + renpy.with_statement({"dolls": trans or d1}) + elif head_only: + tonks.hide() - if temp_face: - last_face = tonks.get_face() + def reset_attributes(last_face, last_hair, head_only): + """Reset temporary face and hair attributes.""" + if last_face: + tonks.set_face(**last_face) + if last_hair: + tonks.get_equipped("hair").set_color(last_hair) + show_tonks(True, head_only) - d = dict(zip(temp_face[::2], temp_face[1::2])) - temp_hair = d.pop("hair", None) + # Determine name prefix and suffix + name_prefix = f'Tonks {{size=-20}}"' if name_tonks_genie != "Tonks" else "" + name_suffix = '"{/size}' if name_prefix else "" - tonks.set_face(**d) - redraw = True + # If no visual changes, just make Tonks speak + if not any((mouth, eyes, eyebrows, pupils, cheeks, tears, emote, xpos, ypos, flip, trans, animation)): + character.tonks_say(what, who_prefix=name_prefix, who_suffix=name_suffix, **kwargs) + return - if temp_hair: - last_hair = tonks.get_equipped("hair").color - col = tonks_haircolor_table.get(temp_hair) - tonks.get_equipped("hair").set_color(col) - redraw = True + # Handle position update + redraw = update_position(xpos, ypos) + # Handle emote, animation, and flipping if emote: tonks.set_emote(emote) redraw = True - if animation != False: + if animation: tonks.animation = animation redraw = True @@ -94,31 +121,28 @@ init python: tonks.xzoom = -1 if flip else 1 redraw = True - if redraw and not head_only: - show() - elif head_only: - tonks.hide() + # Handle face update and temporary attributes + face_attrs = {"mouth": mouth, "eyes": eyes, "eyebrows": eyebrows, "pupils": pupils, "cheeks": cheeks, "tears": tears} + redraw = redraw or update_face(face_attrs) - # Figure out nickname - if name_tonks_genie != "Tonks": - name_prefix = "Tonks {size=-20}\"" - name_suffix = "\"{/size}" - else: - name_prefix = "" - name_suffix = "" + temp_face = renpy.game.context().temporary_attributes + last_face, temp_hair = handle_temp_attributes(temp_face) + last_hair = update_hair(temp_hair) + redraw = redraw or bool(temp_face) + # Handle display logic + head_only = tonks.pos[1] == sprite_pos.get("y", {}).get("head") + show_tonks(redraw, head_only) + + # Handle dialog and doll positioning if what: + side_doll = None if head_only: - xpos = sprite_pos.get("x").get("right_corner") -25 if tonks.xzoom == 1 else sprite_pos.get("x").get("left_corner") + 25 + xpos = sprite_pos.get("x", {}).get("right_corner", 0) - 25 if tonks.xzoom == 1 else sprite_pos.get("x", {}).get("left_corner", 0) + 25 ypos = tonks.pos[1] side_doll = At(tonks.image, doll_transform((xpos, ypos), tonks.zoom, tonks.xzoom)) - else: - side_doll = None - character.tonks_say(what, who_prefix=name_prefix, who_suffix=name_suffix, show_side_doll=side_doll, **kwargs) # Weird Engine quirk; `show_` prefix required to pass arguments to screen. - if temp_face: - tonks.set_face(**last_face) - - if temp_hair: - tonks.get_equipped("hair").set_color(last_hair) + character.tonks_say(what, who_prefix=name_prefix, who_suffix=name_suffix, show_side_doll=side_doll, **kwargs) + # Reset face and hair if modified temporarily + reset_attributes(last_face, last_hair, head_only)