From 99f174cfbb45e49e6fc254a9b9dbabb86f909c5a Mon Sep 17 00:00:00 2001 From: LoafyLemon Date: Wed, 18 Jan 2023 20:22:59 +0000 Subject: [PATCH] Refactoring, Optimizations, Bug fixes * Fixed Hermione's hslut panties * Refactored image calls * Refactored character poses (partially) * Added hash generation to all Doll-type displayables * Hashed and cached Doll layers (Greatly improves rollback performance) * Fixed outfit and colour randomization * Added is_stale method to doll-type displayables to reduce code complexity * Removed doll-related redundant global methods * Added AVIF format support * Simplified Doll posing * and more... --- .../clothes/accessory/protectors1/0.webp | 3 - .../clothes/accessory/protectors2/0.webp | 3 - .../accessory/snitch_in_hand/extra.webp} | 0 .../expression.webp} | 0 .../clothes/panties/hslut_panties/1.webp | 3 - .../poses/hold_book/body/armleft/down.webp | 3 - .../poses/hold_book/body/armright/down.webp | 3 - .../poses/hold_book/body/breasts/normal.webp | 3 - .../hold_book/body/{base => }/front.webp | 0 .../{blush.webp => blush/expression.webp} | 0 .../{angry.webp => angry/expression.webp} | 0 .../{angry_skin.webp => angry/skin.webp} | 0 .../{annoyed.webp => annoyed/expression.webp} | 0 .../{annoyed_skin.webp => annoyed/skin.webp} | 0 .../{base.webp => base/expression.webp} | 0 .../{base_skin.webp => base/skin.webp} | 0 .../{low.webp => low/expression.webp} | 0 .../eyebrows/{low_skin.webp => low/skin.webp} | 0 .../{worried.webp => worried/expression.webp} | 0 .../{worried_skin.webp => worried/skin.webp} | 0 .../eyes/{base.webp => base/expression.webp} | 0 .../{base_mask.webp => base/eyemask.webp} | 0 .../{closed.webp => closed/expression.webp} | 0 .../{happy.webp => happy/expression.webp} | 0 .../{happy_mask.webp => happy/eyemask.webp} | 0 .../{happyCl.webp => happyCl/expression.webp} | 0 .../{narrow.webp => narrow/expression.webp} | 0 .../{narrow_mask.webp => narrow/eyemask.webp} | 0 .../eyes/{slit.webp => slit/expression.webp} | 0 .../{slit_mask.webp => slit/eyemask.webp} | 0 .../{squint.webp => squint/expression.webp} | 0 .../{squint_mask.webp => squint/eyemask.webp} | 0 .../eyes/{wide.webp => wide/expression.webp} | 0 .../{wide_mask.webp => wide/eyemask.webp} | 0 .../eyes/{wink.webp => wink/expression.webp} | 0 .../{wink_mask.webp => wink/eyemask.webp} | 0 .../{angry.webp => angry/expression.webp} | 0 .../{annoyed.webp => annoyed/expression.webp} | 0 .../mouth/{base.webp => base/expression.webp} | 0 .../{clench.webp => clench/expression.webp} | 0 .../smile.webp} | 0 .../mouth/{cum.webp => cum/expression.webp} | 0 .../{disgust.webp => disgust/expression.webp} | 0 .../mouth/{full.webp => full/expression.webp} | 0 .../expression.webp} | 0 .../mouth/{grin.webp => grin/expression.webp} | 0 .../mouth/{mad.webp => mad/expression.webp} | 0 .../{normal.webp => normal/expression.webp} | 0 .../mouth/{open.webp => open/expression.webp} | 0 .../expression.webp} | 0 .../expression.webp} | 0 .../face/mouth/open_wide_tongue_cum.webp | 3 - .../{scream.webp => scream/expression.webp} | 0 .../{shock.webp => shock/expression.webp} | 0 .../{silly.webp => silly/expression.webp} | 0 .../{smile.webp => smile/expression.webp} | 0 .../mouth/{soft.webp => soft/expression.webp} | 0 .../{upset.webp => upset/expression.webp} | 0 .../face/pupils/{L.webp => L/expression.webp} | 0 .../{L_soft.webp => L_soft/expression.webp} | 0 .../face/pupils/{R.webp => R/expression.webp} | 0 .../{R_soft.webp => R_soft/expression.webp} | 0 .../{ahegao.webp => ahegao/expression.webp} | 0 .../{dead.webp => dead/expression.webp} | 0 .../{down.webp => down/expression.webp} | 0 .../expression.webp} | 0 .../pupils/{mid.webp => mid/expression.webp} | 0 .../expression.webp} | 0 .../{shocked.webp => shocked/expression.webp} | 0 .../{squint.webp => squint/expression.webp} | 0 .../expression.webp} | 0 .../{stare.webp => stare/expression.webp} | 0 .../expression.webp} | 0 .../pupils/{up.webp => up/expression.webp} | 0 .../{up_soft.webp => up_soft/expression.webp} | 0 .../{crying.webp => crying/expression.webp} | 0 .../expression.webp} | 0 .../{mascara.webp => mascara/expression.webp} | 0 .../expression.webp} | 0 .../expression.webp} | 0 .../face/tears/mascara_soft_blink.webp | 3 - .../{messy.webp => messy/expression.webp} | 0 .../tears/{soft.webp => soft/expression.webp} | 0 .../expression.webp} | 0 .../{sweat.webp => sweat/expression.webp} | 0 .../expression.webp} | 0 .../expression.webp} | 0 .../expression.webp} | 0 .../expression.webp} | 0 .../hermione/poses/hold_book/loadable.webp | 3 - .../hermione/poses/masturbate/loadable.webp | 3 - game/scripts/cg/common.rpy | 12 +- game/scripts/characters/cho/common.rpy | 3 - game/scripts/characters/cho/wardrobe.rpy | 5 +- game/scripts/characters/luna/common.rpy | 3 - game/scripts/characters/susan/common.rpy | 3 - game/scripts/characters/tonks/common.rpy | 3 - game/scripts/doll/body.rpy | 37 ++-- game/scripts/doll/clothes.rpy | 69 +++----- game/scripts/doll/clothes_dynamic.rpy | 18 +- game/scripts/doll/common.rpy | 31 +--- game/scripts/doll/cum.rpy | 69 ++------ game/scripts/doll/face.rpy | 37 ++-- game/scripts/doll/main.rpy | 166 ++++-------------- game/scripts/doll/makeup.rpy | 12 +- game/scripts/doll/outfits.rpy | 82 +++------ game/scripts/interface/stats.rpy | 12 +- game/scripts/shops/dress/menu.rpy | 2 +- game/scripts/utility/skipping.rpy | 10 +- game/scripts/wardrobe/functions.rpy | 7 - game/scripts/wardrobe/studio.rpy | 4 +- game/scripts/wardrobe/wardrobe.rpy | 37 ++-- 112 files changed, 189 insertions(+), 463 deletions(-) delete mode 100644 game/characters/cho/poses/broom/clothes/accessory/protectors1/0.webp delete mode 100644 game/characters/cho/poses/broom/clothes/accessory/protectors2/0.webp rename game/characters/cho/poses/broom/{body/snitch.webp => clothes/accessory/snitch_in_hand/extra.webp} (100%) rename game/characters/cho/poses/broom/face/mouth/{crooked/smile.webp => crooked_smile/expression.webp} (100%) delete mode 100644 game/characters/hermione/clothes/panties/hslut_panties/1.webp delete mode 100644 game/characters/hermione/poses/hold_book/body/armleft/down.webp delete mode 100644 game/characters/hermione/poses/hold_book/body/armright/down.webp delete mode 100644 game/characters/hermione/poses/hold_book/body/breasts/normal.webp rename game/characters/hermione/poses/hold_book/body/{base => }/front.webp (100%) rename game/characters/hermione/poses/hold_book/face/cheeks/{blush.webp => blush/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyebrows/{angry.webp => angry/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyebrows/{angry_skin.webp => angry/skin.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyebrows/{annoyed.webp => annoyed/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyebrows/{annoyed_skin.webp => annoyed/skin.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyebrows/{base.webp => base/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyebrows/{base_skin.webp => base/skin.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyebrows/{low.webp => low/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyebrows/{low_skin.webp => low/skin.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyebrows/{worried.webp => worried/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyebrows/{worried_skin.webp => worried/skin.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyes/{base.webp => base/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyes/{base_mask.webp => base/eyemask.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyes/{closed.webp => closed/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyes/{happy.webp => happy/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyes/{happy_mask.webp => happy/eyemask.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyes/{happyCl.webp => happyCl/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyes/{narrow.webp => narrow/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyes/{narrow_mask.webp => narrow/eyemask.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyes/{slit.webp => slit/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyes/{slit_mask.webp => slit/eyemask.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyes/{squint.webp => squint/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyes/{squint_mask.webp => squint/eyemask.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyes/{wide.webp => wide/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyes/{wide_mask.webp => wide/eyemask.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyes/{wink.webp => wink/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/eyes/{wink_mask.webp => wink/eyemask.webp} (100%) rename game/characters/hermione/poses/hold_book/face/mouth/{angry.webp => angry/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/mouth/{annoyed.webp => annoyed/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/mouth/{base.webp => base/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/mouth/{clench.webp => clench/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/mouth/{crooked_smile.webp => crooked/smile.webp} (100%) rename game/characters/hermione/poses/hold_book/face/mouth/{cum.webp => cum/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/mouth/{disgust.webp => disgust/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/mouth/{full.webp => full/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/mouth/{full_cum.webp => full_cum/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/mouth/{grin.webp => grin/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/mouth/{mad.webp => mad/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/mouth/{normal.webp => normal/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/mouth/{open.webp => open/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/mouth/{open_tongue.webp => open_tongue/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/mouth/{open_wide_tongue.webp => open_wide/expression.webp} (100%) delete mode 100644 game/characters/hermione/poses/hold_book/face/mouth/open_wide_tongue_cum.webp rename game/characters/hermione/poses/hold_book/face/mouth/{scream.webp => scream/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/mouth/{shock.webp => shock/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/mouth/{silly.webp => silly/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/mouth/{smile.webp => smile/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/mouth/{soft.webp => soft/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/mouth/{upset.webp => upset/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/pupils/{L.webp => L/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/pupils/{L_soft.webp => L_soft/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/pupils/{R.webp => R/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/pupils/{R_soft.webp => R_soft/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/pupils/{ahegao.webp => ahegao/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/pupils/{dead.webp => dead/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/pupils/{down.webp => down/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/pupils/{down_soft.webp => down_soft/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/pupils/{mid.webp => mid/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/pupils/{mid_soft.webp => mid_soft/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/pupils/{shocked.webp => shocked/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/pupils/{squint.webp => squint/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/pupils/{squint_soft.webp => squint_soft/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/pupils/{stare.webp => stare/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/pupils/{stare_soft.webp => stare_soft/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/pupils/{up.webp => up/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/pupils/{up_soft.webp => up_soft/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/tears/{crying.webp => crying/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/tears/{crying_blink.webp => crying_blink/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/tears/{mascara.webp => mascara/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/tears/{mascara_crying.webp => mascara_crying/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/tears/{mascara_soft.webp => mascara_soft/expression.webp} (100%) delete mode 100644 game/characters/hermione/poses/hold_book/face/tears/mascara_soft_blink.webp rename game/characters/hermione/poses/hold_book/face/tears/{messy.webp => messy/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/tears/{soft.webp => soft/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/tears/{soft_blink.webp => soft_blink/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/tears/{sweat.webp => sweat/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/tears/{tears_crying_side.webp => tears/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/tears/{tears_down.webp => tears_down/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/tears/{tears_mascara_crying_blink.webp => tears_mascara/expression.webp} (100%) rename game/characters/hermione/poses/hold_book/face/tears/{tears_soft_sweat.webp => tears_soft/expression.webp} (100%) delete mode 100644 game/characters/hermione/poses/hold_book/loadable.webp delete mode 100644 game/characters/hermione/poses/masturbate/loadable.webp diff --git a/game/characters/cho/poses/broom/clothes/accessory/protectors1/0.webp b/game/characters/cho/poses/broom/clothes/accessory/protectors1/0.webp deleted file mode 100644 index 6fbb7bcc..00000000 --- a/game/characters/cho/poses/broom/clothes/accessory/protectors1/0.webp +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e4f16469f76fa22ea43a2346407a802434ae02dff4da1c40bec6af146a42feae -size 94 diff --git a/game/characters/cho/poses/broom/clothes/accessory/protectors2/0.webp b/game/characters/cho/poses/broom/clothes/accessory/protectors2/0.webp deleted file mode 100644 index 6fbb7bcc..00000000 --- a/game/characters/cho/poses/broom/clothes/accessory/protectors2/0.webp +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e4f16469f76fa22ea43a2346407a802434ae02dff4da1c40bec6af146a42feae -size 94 diff --git a/game/characters/cho/poses/broom/body/snitch.webp b/game/characters/cho/poses/broom/clothes/accessory/snitch_in_hand/extra.webp similarity index 100% rename from game/characters/cho/poses/broom/body/snitch.webp rename to game/characters/cho/poses/broom/clothes/accessory/snitch_in_hand/extra.webp diff --git a/game/characters/cho/poses/broom/face/mouth/crooked/smile.webp b/game/characters/cho/poses/broom/face/mouth/crooked_smile/expression.webp similarity index 100% rename from game/characters/cho/poses/broom/face/mouth/crooked/smile.webp rename to game/characters/cho/poses/broom/face/mouth/crooked_smile/expression.webp diff --git a/game/characters/hermione/clothes/panties/hslut_panties/1.webp b/game/characters/hermione/clothes/panties/hslut_panties/1.webp deleted file mode 100644 index d21129e0..00000000 --- a/game/characters/hermione/clothes/panties/hslut_panties/1.webp +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:21a0c1b8af09402e09c803fa8538c62abdcab47778048d62a13a77f1fb205081 -size 1738 diff --git a/game/characters/hermione/poses/hold_book/body/armleft/down.webp b/game/characters/hermione/poses/hold_book/body/armleft/down.webp deleted file mode 100644 index 4845b4e5..00000000 --- a/game/characters/hermione/poses/hold_book/body/armleft/down.webp +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:949d2c3f40b4631abd22a170a9492ed699338013b1cc1f6249021bc298fe18bf -size 3238 diff --git a/game/characters/hermione/poses/hold_book/body/armright/down.webp b/game/characters/hermione/poses/hold_book/body/armright/down.webp deleted file mode 100644 index 40a670dc..00000000 --- a/game/characters/hermione/poses/hold_book/body/armright/down.webp +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:57038791f9fce36c0b8b6d0594ef9562181e9ff64098f4dddb83031e198a638e -size 890 diff --git a/game/characters/hermione/poses/hold_book/body/breasts/normal.webp b/game/characters/hermione/poses/hold_book/body/breasts/normal.webp deleted file mode 100644 index 40a670dc..00000000 --- a/game/characters/hermione/poses/hold_book/body/breasts/normal.webp +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:57038791f9fce36c0b8b6d0594ef9562181e9ff64098f4dddb83031e198a638e -size 890 diff --git a/game/characters/hermione/poses/hold_book/body/base/front.webp b/game/characters/hermione/poses/hold_book/body/front.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/body/base/front.webp rename to game/characters/hermione/poses/hold_book/body/front.webp diff --git a/game/characters/hermione/poses/hold_book/face/cheeks/blush.webp b/game/characters/hermione/poses/hold_book/face/cheeks/blush/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/cheeks/blush.webp rename to game/characters/hermione/poses/hold_book/face/cheeks/blush/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyebrows/angry.webp b/game/characters/hermione/poses/hold_book/face/eyebrows/angry/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyebrows/angry.webp rename to game/characters/hermione/poses/hold_book/face/eyebrows/angry/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyebrows/angry_skin.webp b/game/characters/hermione/poses/hold_book/face/eyebrows/angry/skin.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyebrows/angry_skin.webp rename to game/characters/hermione/poses/hold_book/face/eyebrows/angry/skin.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyebrows/annoyed.webp b/game/characters/hermione/poses/hold_book/face/eyebrows/annoyed/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyebrows/annoyed.webp rename to game/characters/hermione/poses/hold_book/face/eyebrows/annoyed/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyebrows/annoyed_skin.webp b/game/characters/hermione/poses/hold_book/face/eyebrows/annoyed/skin.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyebrows/annoyed_skin.webp rename to game/characters/hermione/poses/hold_book/face/eyebrows/annoyed/skin.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyebrows/base.webp b/game/characters/hermione/poses/hold_book/face/eyebrows/base/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyebrows/base.webp rename to game/characters/hermione/poses/hold_book/face/eyebrows/base/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyebrows/base_skin.webp b/game/characters/hermione/poses/hold_book/face/eyebrows/base/skin.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyebrows/base_skin.webp rename to game/characters/hermione/poses/hold_book/face/eyebrows/base/skin.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyebrows/low.webp b/game/characters/hermione/poses/hold_book/face/eyebrows/low/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyebrows/low.webp rename to game/characters/hermione/poses/hold_book/face/eyebrows/low/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyebrows/low_skin.webp b/game/characters/hermione/poses/hold_book/face/eyebrows/low/skin.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyebrows/low_skin.webp rename to game/characters/hermione/poses/hold_book/face/eyebrows/low/skin.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyebrows/worried.webp b/game/characters/hermione/poses/hold_book/face/eyebrows/worried/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyebrows/worried.webp rename to game/characters/hermione/poses/hold_book/face/eyebrows/worried/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyebrows/worried_skin.webp b/game/characters/hermione/poses/hold_book/face/eyebrows/worried/skin.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyebrows/worried_skin.webp rename to game/characters/hermione/poses/hold_book/face/eyebrows/worried/skin.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyes/base.webp b/game/characters/hermione/poses/hold_book/face/eyes/base/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyes/base.webp rename to game/characters/hermione/poses/hold_book/face/eyes/base/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyes/base_mask.webp b/game/characters/hermione/poses/hold_book/face/eyes/base/eyemask.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyes/base_mask.webp rename to game/characters/hermione/poses/hold_book/face/eyes/base/eyemask.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyes/closed.webp b/game/characters/hermione/poses/hold_book/face/eyes/closed/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyes/closed.webp rename to game/characters/hermione/poses/hold_book/face/eyes/closed/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyes/happy.webp b/game/characters/hermione/poses/hold_book/face/eyes/happy/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyes/happy.webp rename to game/characters/hermione/poses/hold_book/face/eyes/happy/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyes/happy_mask.webp b/game/characters/hermione/poses/hold_book/face/eyes/happy/eyemask.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyes/happy_mask.webp rename to game/characters/hermione/poses/hold_book/face/eyes/happy/eyemask.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyes/happyCl.webp b/game/characters/hermione/poses/hold_book/face/eyes/happyCl/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyes/happyCl.webp rename to game/characters/hermione/poses/hold_book/face/eyes/happyCl/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyes/narrow.webp b/game/characters/hermione/poses/hold_book/face/eyes/narrow/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyes/narrow.webp rename to game/characters/hermione/poses/hold_book/face/eyes/narrow/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyes/narrow_mask.webp b/game/characters/hermione/poses/hold_book/face/eyes/narrow/eyemask.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyes/narrow_mask.webp rename to game/characters/hermione/poses/hold_book/face/eyes/narrow/eyemask.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyes/slit.webp b/game/characters/hermione/poses/hold_book/face/eyes/slit/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyes/slit.webp rename to game/characters/hermione/poses/hold_book/face/eyes/slit/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyes/slit_mask.webp b/game/characters/hermione/poses/hold_book/face/eyes/slit/eyemask.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyes/slit_mask.webp rename to game/characters/hermione/poses/hold_book/face/eyes/slit/eyemask.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyes/squint.webp b/game/characters/hermione/poses/hold_book/face/eyes/squint/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyes/squint.webp rename to game/characters/hermione/poses/hold_book/face/eyes/squint/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyes/squint_mask.webp b/game/characters/hermione/poses/hold_book/face/eyes/squint/eyemask.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyes/squint_mask.webp rename to game/characters/hermione/poses/hold_book/face/eyes/squint/eyemask.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyes/wide.webp b/game/characters/hermione/poses/hold_book/face/eyes/wide/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyes/wide.webp rename to game/characters/hermione/poses/hold_book/face/eyes/wide/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyes/wide_mask.webp b/game/characters/hermione/poses/hold_book/face/eyes/wide/eyemask.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyes/wide_mask.webp rename to game/characters/hermione/poses/hold_book/face/eyes/wide/eyemask.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyes/wink.webp b/game/characters/hermione/poses/hold_book/face/eyes/wink/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyes/wink.webp rename to game/characters/hermione/poses/hold_book/face/eyes/wink/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/eyes/wink_mask.webp b/game/characters/hermione/poses/hold_book/face/eyes/wink/eyemask.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/eyes/wink_mask.webp rename to game/characters/hermione/poses/hold_book/face/eyes/wink/eyemask.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/angry.webp b/game/characters/hermione/poses/hold_book/face/mouth/angry/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/angry.webp rename to game/characters/hermione/poses/hold_book/face/mouth/angry/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/annoyed.webp b/game/characters/hermione/poses/hold_book/face/mouth/annoyed/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/annoyed.webp rename to game/characters/hermione/poses/hold_book/face/mouth/annoyed/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/base.webp b/game/characters/hermione/poses/hold_book/face/mouth/base/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/base.webp rename to game/characters/hermione/poses/hold_book/face/mouth/base/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/clench.webp b/game/characters/hermione/poses/hold_book/face/mouth/clench/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/clench.webp rename to game/characters/hermione/poses/hold_book/face/mouth/clench/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/crooked_smile.webp b/game/characters/hermione/poses/hold_book/face/mouth/crooked/smile.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/crooked_smile.webp rename to game/characters/hermione/poses/hold_book/face/mouth/crooked/smile.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/cum.webp b/game/characters/hermione/poses/hold_book/face/mouth/cum/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/cum.webp rename to game/characters/hermione/poses/hold_book/face/mouth/cum/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/disgust.webp b/game/characters/hermione/poses/hold_book/face/mouth/disgust/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/disgust.webp rename to game/characters/hermione/poses/hold_book/face/mouth/disgust/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/full.webp b/game/characters/hermione/poses/hold_book/face/mouth/full/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/full.webp rename to game/characters/hermione/poses/hold_book/face/mouth/full/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/full_cum.webp b/game/characters/hermione/poses/hold_book/face/mouth/full_cum/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/full_cum.webp rename to game/characters/hermione/poses/hold_book/face/mouth/full_cum/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/grin.webp b/game/characters/hermione/poses/hold_book/face/mouth/grin/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/grin.webp rename to game/characters/hermione/poses/hold_book/face/mouth/grin/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/mad.webp b/game/characters/hermione/poses/hold_book/face/mouth/mad/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/mad.webp rename to game/characters/hermione/poses/hold_book/face/mouth/mad/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/normal.webp b/game/characters/hermione/poses/hold_book/face/mouth/normal/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/normal.webp rename to game/characters/hermione/poses/hold_book/face/mouth/normal/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/open.webp b/game/characters/hermione/poses/hold_book/face/mouth/open/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/open.webp rename to game/characters/hermione/poses/hold_book/face/mouth/open/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/open_tongue.webp b/game/characters/hermione/poses/hold_book/face/mouth/open_tongue/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/open_tongue.webp rename to game/characters/hermione/poses/hold_book/face/mouth/open_tongue/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/open_wide_tongue.webp b/game/characters/hermione/poses/hold_book/face/mouth/open_wide/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/open_wide_tongue.webp rename to game/characters/hermione/poses/hold_book/face/mouth/open_wide/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/open_wide_tongue_cum.webp b/game/characters/hermione/poses/hold_book/face/mouth/open_wide_tongue_cum.webp deleted file mode 100644 index 68b67d54..00000000 --- a/game/characters/hermione/poses/hold_book/face/mouth/open_wide_tongue_cum.webp +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:842cdaed8c60a2bb3cc0bfead4fe36cd131100a4250d44be464cafa1cdd35ba6 -size 6938 diff --git a/game/characters/hermione/poses/hold_book/face/mouth/scream.webp b/game/characters/hermione/poses/hold_book/face/mouth/scream/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/scream.webp rename to game/characters/hermione/poses/hold_book/face/mouth/scream/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/shock.webp b/game/characters/hermione/poses/hold_book/face/mouth/shock/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/shock.webp rename to game/characters/hermione/poses/hold_book/face/mouth/shock/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/silly.webp b/game/characters/hermione/poses/hold_book/face/mouth/silly/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/silly.webp rename to game/characters/hermione/poses/hold_book/face/mouth/silly/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/smile.webp b/game/characters/hermione/poses/hold_book/face/mouth/smile/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/smile.webp rename to game/characters/hermione/poses/hold_book/face/mouth/smile/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/soft.webp b/game/characters/hermione/poses/hold_book/face/mouth/soft/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/soft.webp rename to game/characters/hermione/poses/hold_book/face/mouth/soft/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/mouth/upset.webp b/game/characters/hermione/poses/hold_book/face/mouth/upset/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/mouth/upset.webp rename to game/characters/hermione/poses/hold_book/face/mouth/upset/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/pupils/L.webp b/game/characters/hermione/poses/hold_book/face/pupils/L/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/pupils/L.webp rename to game/characters/hermione/poses/hold_book/face/pupils/L/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/pupils/L_soft.webp b/game/characters/hermione/poses/hold_book/face/pupils/L_soft/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/pupils/L_soft.webp rename to game/characters/hermione/poses/hold_book/face/pupils/L_soft/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/pupils/R.webp b/game/characters/hermione/poses/hold_book/face/pupils/R/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/pupils/R.webp rename to game/characters/hermione/poses/hold_book/face/pupils/R/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/pupils/R_soft.webp b/game/characters/hermione/poses/hold_book/face/pupils/R_soft/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/pupils/R_soft.webp rename to game/characters/hermione/poses/hold_book/face/pupils/R_soft/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/pupils/ahegao.webp b/game/characters/hermione/poses/hold_book/face/pupils/ahegao/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/pupils/ahegao.webp rename to game/characters/hermione/poses/hold_book/face/pupils/ahegao/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/pupils/dead.webp b/game/characters/hermione/poses/hold_book/face/pupils/dead/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/pupils/dead.webp rename to game/characters/hermione/poses/hold_book/face/pupils/dead/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/pupils/down.webp b/game/characters/hermione/poses/hold_book/face/pupils/down/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/pupils/down.webp rename to game/characters/hermione/poses/hold_book/face/pupils/down/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/pupils/down_soft.webp b/game/characters/hermione/poses/hold_book/face/pupils/down_soft/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/pupils/down_soft.webp rename to game/characters/hermione/poses/hold_book/face/pupils/down_soft/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/pupils/mid.webp b/game/characters/hermione/poses/hold_book/face/pupils/mid/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/pupils/mid.webp rename to game/characters/hermione/poses/hold_book/face/pupils/mid/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/pupils/mid_soft.webp b/game/characters/hermione/poses/hold_book/face/pupils/mid_soft/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/pupils/mid_soft.webp rename to game/characters/hermione/poses/hold_book/face/pupils/mid_soft/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/pupils/shocked.webp b/game/characters/hermione/poses/hold_book/face/pupils/shocked/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/pupils/shocked.webp rename to game/characters/hermione/poses/hold_book/face/pupils/shocked/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/pupils/squint.webp b/game/characters/hermione/poses/hold_book/face/pupils/squint/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/pupils/squint.webp rename to game/characters/hermione/poses/hold_book/face/pupils/squint/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/pupils/squint_soft.webp b/game/characters/hermione/poses/hold_book/face/pupils/squint_soft/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/pupils/squint_soft.webp rename to game/characters/hermione/poses/hold_book/face/pupils/squint_soft/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/pupils/stare.webp b/game/characters/hermione/poses/hold_book/face/pupils/stare/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/pupils/stare.webp rename to game/characters/hermione/poses/hold_book/face/pupils/stare/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/pupils/stare_soft.webp b/game/characters/hermione/poses/hold_book/face/pupils/stare_soft/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/pupils/stare_soft.webp rename to game/characters/hermione/poses/hold_book/face/pupils/stare_soft/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/pupils/up.webp b/game/characters/hermione/poses/hold_book/face/pupils/up/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/pupils/up.webp rename to game/characters/hermione/poses/hold_book/face/pupils/up/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/pupils/up_soft.webp b/game/characters/hermione/poses/hold_book/face/pupils/up_soft/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/pupils/up_soft.webp rename to game/characters/hermione/poses/hold_book/face/pupils/up_soft/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/tears/crying.webp b/game/characters/hermione/poses/hold_book/face/tears/crying/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/tears/crying.webp rename to game/characters/hermione/poses/hold_book/face/tears/crying/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/tears/crying_blink.webp b/game/characters/hermione/poses/hold_book/face/tears/crying_blink/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/tears/crying_blink.webp rename to game/characters/hermione/poses/hold_book/face/tears/crying_blink/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/tears/mascara.webp b/game/characters/hermione/poses/hold_book/face/tears/mascara/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/tears/mascara.webp rename to game/characters/hermione/poses/hold_book/face/tears/mascara/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/tears/mascara_crying.webp b/game/characters/hermione/poses/hold_book/face/tears/mascara_crying/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/tears/mascara_crying.webp rename to game/characters/hermione/poses/hold_book/face/tears/mascara_crying/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/tears/mascara_soft.webp b/game/characters/hermione/poses/hold_book/face/tears/mascara_soft/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/tears/mascara_soft.webp rename to game/characters/hermione/poses/hold_book/face/tears/mascara_soft/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/tears/mascara_soft_blink.webp b/game/characters/hermione/poses/hold_book/face/tears/mascara_soft_blink.webp deleted file mode 100644 index 69567c7d..00000000 --- a/game/characters/hermione/poses/hold_book/face/tears/mascara_soft_blink.webp +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b6eb4cb10fd98947c34a12dc3be6bd7f98b3cf2b665ff719e52ec6bff62b9e75 -size 9752 diff --git a/game/characters/hermione/poses/hold_book/face/tears/messy.webp b/game/characters/hermione/poses/hold_book/face/tears/messy/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/tears/messy.webp rename to game/characters/hermione/poses/hold_book/face/tears/messy/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/tears/soft.webp b/game/characters/hermione/poses/hold_book/face/tears/soft/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/tears/soft.webp rename to game/characters/hermione/poses/hold_book/face/tears/soft/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/tears/soft_blink.webp b/game/characters/hermione/poses/hold_book/face/tears/soft_blink/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/tears/soft_blink.webp rename to game/characters/hermione/poses/hold_book/face/tears/soft_blink/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/tears/sweat.webp b/game/characters/hermione/poses/hold_book/face/tears/sweat/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/tears/sweat.webp rename to game/characters/hermione/poses/hold_book/face/tears/sweat/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/tears/tears_crying_side.webp b/game/characters/hermione/poses/hold_book/face/tears/tears/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/tears/tears_crying_side.webp rename to game/characters/hermione/poses/hold_book/face/tears/tears/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/tears/tears_down.webp b/game/characters/hermione/poses/hold_book/face/tears/tears_down/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/tears/tears_down.webp rename to game/characters/hermione/poses/hold_book/face/tears/tears_down/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/tears/tears_mascara_crying_blink.webp b/game/characters/hermione/poses/hold_book/face/tears/tears_mascara/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/tears/tears_mascara_crying_blink.webp rename to game/characters/hermione/poses/hold_book/face/tears/tears_mascara/expression.webp diff --git a/game/characters/hermione/poses/hold_book/face/tears/tears_soft_sweat.webp b/game/characters/hermione/poses/hold_book/face/tears/tears_soft/expression.webp similarity index 100% rename from game/characters/hermione/poses/hold_book/face/tears/tears_soft_sweat.webp rename to game/characters/hermione/poses/hold_book/face/tears/tears_soft/expression.webp diff --git a/game/characters/hermione/poses/hold_book/loadable.webp b/game/characters/hermione/poses/hold_book/loadable.webp deleted file mode 100644 index 9357818b..00000000 --- a/game/characters/hermione/poses/hold_book/loadable.webp +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:86be52bdb7547413cafb3ed175a806a798c65de98b40849e0b974c47d187de65 -size 34 diff --git a/game/characters/hermione/poses/masturbate/loadable.webp b/game/characters/hermione/poses/masturbate/loadable.webp deleted file mode 100644 index 9357818b..00000000 --- a/game/characters/hermione/poses/masturbate/loadable.webp +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:86be52bdb7547413cafb3ed175a806a798c65de98b40849e0b974c47d187de65 -size 34 diff --git a/game/scripts/cg/common.rpy b/game/scripts/cg/common.rpy index 275e1a90..d500b8fd 100644 --- a/game/scripts/cg/common.rpy +++ b/game/scripts/cg/common.rpy @@ -1,22 +1,22 @@ init 5 python: def her_cg_doll(st, at): - return hermione.get_image(), None + return hermione.image, None def lun_cg_doll(st, at): - return luna.get_image(), None + return luna.image, None def ton_cg_doll(st, at): - return tonks.get_image(), None + return tonks.image, None def cho_cg_doll(st, at): - return cho.get_image(), None + return cho.image, None def ast_cg_doll(st, at): - return astoria.get_image(), None + return astoria.image, None def sus_cg_doll(st, at): - return susan.get_image(), None + return susan.image, None image CG her_doll = DynamicDisplayable(her_cg_doll) image CG lun_doll = DynamicDisplayable(lun_cg_doll) diff --git a/game/scripts/characters/cho/common.rpy b/game/scripts/characters/cho/common.rpy index 3cf453d0..00f6881f 100644 --- a/game/scripts/characters/cho/common.rpy +++ b/game/scripts/characters/cho/common.rpy @@ -63,9 +63,6 @@ label end_cho_event: $ cho.wear("all") $ cho.set_cum(None) - $ renpy.stop_predict(cho.get_image()) - $ renpy.stop_predict("characters/cho/face/*.webp") - call music_block jump main_room_menu diff --git a/game/scripts/characters/cho/wardrobe.rpy b/game/scripts/characters/cho/wardrobe.rpy index 8d6c4819..8008554c 100644 --- a/game/scripts/characters/cho/wardrobe.rpy +++ b/game/scripts/characters/cho/wardrobe.rpy @@ -231,8 +231,9 @@ default choq_cloth_schoolskirt2 = DollCloth("cho", ("lower body", "skirts"), "bo default choq_cloth_schoolskirt3 = DollCloth("cho", ("lower body", "skirts"), "bottom", "school_skirt_3", [[103, 90, 108, 255], [232, 177, 13, 255]]) default choq_panties_in_hand = DollCloth("cho", ("misc", "accessory"), "accessory", "panties_in_hand", [[156, 204, 249, 255]]) -default choq_accessory_protectors = DollCloth("cho", ("misc", "accessory"), "accessory", "protectors1", [[0, 0, 0, 255]], zorder=27) -default choq_accessory_protectors2 = DollCloth("cho", ("misc", "accessory"), "accessory", "protectors2", [[0, 0, 0, 255]], zorder=27) +default choq_accessory_protectors = DollCloth("cho", ("misc", "accessory"), "accessory", "protectors1", None, zorder=27) +default choq_accessory_protectors2 = DollCloth("cho", ("misc", "accessory"), "accessory", "protectors2", None, zorder=27) +default choq_accessory_snitch_in_hand = DollCloth("cho", ("hidden", "accessory"), "accessory", "snitch_in_hand", None, zorder=-1) # Add choq_accessory_protectors2 once drawn for normal pose default cho_outfit_quidditch = DollOutfit([cho_hair_ponytail1, choq_cloth_topsweater1, choq_cloth_pantslong2, choq_cloth_robequidditch1, choq_bra_sports1, choq_panties_sport1], hidden=True) diff --git a/game/scripts/characters/luna/common.rpy b/game/scripts/characters/luna/common.rpy index c3f1662d..1cf81c51 100644 --- a/game/scripts/characters/luna/common.rpy +++ b/game/scripts/characters/luna/common.rpy @@ -55,9 +55,6 @@ label end_luna_event: $ luna_busy = True $ luna.wear("all") - $ renpy.stop_predict(luna.get_image()) - $ renpy.stop_predict("characters/luna/face/*.webp") - call music_block jump main_room_menu diff --git a/game/scripts/characters/susan/common.rpy b/game/scripts/characters/susan/common.rpy index a0fa4a3e..dd68ed1f 100644 --- a/game/scripts/characters/susan/common.rpy +++ b/game/scripts/characters/susan/common.rpy @@ -53,9 +53,6 @@ label end_susan_event: $ susan_busy = True $ susan.wear("all") - $ renpy.stop_predict(susan.get_image()) - $ renpy.stop_predict("characters/susan/face/*.webp") - call music_block jump main_room_menu diff --git a/game/scripts/characters/tonks/common.rpy b/game/scripts/characters/tonks/common.rpy index c5657019..02ea23c0 100644 --- a/game/scripts/characters/tonks/common.rpy +++ b/game/scripts/characters/tonks/common.rpy @@ -64,9 +64,6 @@ label end_tonks_event: $ tonks.wear("all") $ tonks.set_cum(None) - $ renpy.stop_predict(tonks.get_image()) - $ renpy.stop_predict("characters/tonks/face/*.webp") - call music_block jump main_room_menu diff --git a/game/scripts/doll/body.rpy b/game/scripts/doll/body.rpy index 8926b546..c9e467a2 100644 --- a/game/scripts/doll/body.rpy +++ b/game/scripts/doll/body.rpy @@ -10,11 +10,20 @@ init python: def __init__(self, obj): self.char = obj - self.layers = None self.hue = HueMatrix(0) self.zorder = 0 + self._hash = None - def get_layers(self): + def set_hue(self, hue): + self.hue = HueMatrix(hue) + self.is_stale() + + def generate_hash(self): + salt = str( [self.char.name + self.char.pose, str(self.hue.__hash__())]) + return hash(salt) + + @functools.cache + def get_layers(self, hash): path = os.path.join("characters", self.char.name, self.char.pose, "body") extensions = self.extensions @@ -47,10 +56,7 @@ init python: return layers @functools.cache - def build_image(self, matrix=None): - self.layers = layers = self.layers or self.get_layers() - sprites = [] - + def build_image(self, hash, matrix=None): if matrix is None: matrix = self.hue @@ -58,6 +64,9 @@ init python: "default": lambda file: Transform(Image(file), matrixcolor=matrix), } + layers = self.get_layers(hash) + + sprites = [] for identifier, (file, zorder) in layers.items(): processor = processors.get(identifier, processors["default"]) processed_file = processor(file) @@ -67,16 +76,12 @@ init python: @property def image(self): - if not renpy.is_skipping(): - if not self._image_cached: - self._image_cached = True + if not renpy.is_skipping() and self.is_stale(): + hash = self._hash - sprites = self.build_image(self.hue) - sprites.sort(key=itemgetter(2)) - sprites = [x[1] for x in sprites] + sprites = self.build_image(hash, self.hue) + sprites.sort(key=itemgetter(2)) + sprites = [x[1] for x in sprites] - self._image = Fixed(*sprites, fit_first=True) + self._image = Fixed(*sprites, fit_first=True) return self._image - - def rebuild_image(self): - self._image_cached = False diff --git a/game/scripts/doll/clothes.rpy b/game/scripts/doll/clothes.rpy index df997dd8..2b42c1df 100644 --- a/game/scripts/doll/clothes.rpy +++ b/game/scripts/doll/clothes.rpy @@ -31,9 +31,6 @@ init python: self.color_default = [x[:] for x in color] if color else None self.zorder = zorder or self.char.clothes[type][1] self.seen = self.unlocked - self.bounds = None - self.layers = None - self.pose = "" self._hash = self.generate_hash() # Add to character wardrobe and unordered list @@ -47,12 +44,18 @@ init python: def __hash__(self): return self._hash + def __eq__(self, obj): + if not isinstance(obj, DollCloth): + return NotImplemented + return self._hash == obj._hash + def generate_hash(self): - salt = str( sorted( [self.name, self.type, self.id, str(self.color)] ) ) + salt = str( [self.name, self.char.pose, self.type, self.id, str(self.color), str(self.char.body._hash)] ) return hash(salt) - def get_layers(self): - path = os.path.join(self.modpath, "characters", self.name, self.pose, "clothes", self.type, self.id) + @functools.cache + def get_layers(self, hash): + path = os.path.join(self.modpath, "characters", self.name, self.char.pose, "clothes", self.type, self.id) extensions = self.extensions types = self.layer_types @@ -115,8 +118,6 @@ init python: @functools.cache def build_image(self, hash, matrix=None): - self.layers = layers = self.layers or self.get_layers() - if matrix is None: matrix = self.char.body.hue @@ -127,6 +128,8 @@ init python: "default": lambda file, _: Image(file), } + layers = self.get_layers(hash) + sprites = [] for identifier, (file, zorder) in layers.items(): @@ -142,28 +145,23 @@ init python: @property def image(self): - if not renpy.is_skipping(): - if not self._image_cached: - self._image_cached = True + if not renpy.is_skipping() and self.is_stale(): + hash = self._hash - sprites = self.build_image(self._hash) - sprites.sort(key=itemgetter(2)) - sprites = [x[1] for x in sprites] + sprites = self.build_image(hash) + sprites.sort(key=itemgetter(2)) + sprites = [x[1] for x in sprites] - self._image = Fixed(*sprites, fit_first=True) + self._image = Fixed(*sprites, fit_first=True) return self._image - def rebuild_image(self): - self._hash = self.generate_hash() - self._image_cached = False - @functools.cache def build_icon(self, hash): matrix = SaturationMatrix(0.0) sprites = [i for i in self.build_image(hash, matrix=matrix) if not i[0] == "mask"] - sprites.extend(self.char.body.build_image(matrix=matrix)) + sprites.extend(self.char.body.build_image(self.char.body._hash, matrix=matrix)) sprites.sort(key=itemgetter(2)) - self.bounds = bounds = self.bounds or self.layers.get("outline", [sprites[0][1]])[0] + bounds = self.get_layers(hash).get("outline", [sprites[0][1]])[0] wmax, hmax = self.sizes wmin = hmin = 96 @@ -219,23 +217,17 @@ init python: cp.default_replace(dcol) elif action == "released": self.color[n] = [int(255*x) for x in value.rgba] - self.rebuild_image() - self.char.rebuild_image() + self.is_stale() elif action == "replace": self.color[n] = [int(255*x) for x in value.rgba] cp.live_replace(value) - self.rebuild_image() - self.char.rebuild_image() + self.is_stale() elif action == "finish": break renpy.hide_screen("colorpickerscreen") elif isinstance(n, list): self.color = [x[:] for x in n] - self.rebuild_image() - - if self.char.is_equipped_item(self): - self.char.rebuild_image() def reset_color(self, n=None): """Reset cloth color. Takes optional int layer number to reset only specific layer color.""" @@ -243,8 +235,7 @@ init python: self.color[n] = [x for x in self.color_default] else: self.color = [x[:] for x in self.color_default] - self.rebuild_image() - self.char.rebuild_image() + self.is_stale() def clone(self): """Creates a clone of this cloth object. Since it requires a parent object it should be used internally only to avoid object depth issue.""" @@ -252,22 +243,6 @@ init python: return self return DollCloth(self.name, self.categories, self.type, self.id, [x[:] for x in self.color] if self.color else None, self.zorder, self.unlocked, self.level, self.blacklist, self.modpath, self) - def set_pose(self, pose): - # TODO: Might not be needed anymore. - last_pose = self.pose - self.pose = pose - layers = self.get_layers() - - if not layers: - self.pose = last_pose - self.char.strip(self.type) - return - - self.pose = pose - self.char.wear(self.type) - self.layers = layers - self.rebuild_image() - def is_modded(self): """Returns True if item comes from a mod.""" return bool(self.modpath) diff --git a/game/scripts/doll/clothes_dynamic.rpy b/game/scripts/doll/clothes_dynamic.rpy index b13dc65a..a9929654 100644 --- a/game/scripts/doll/clothes_dynamic.rpy +++ b/game/scripts/doll/clothes_dynamic.rpy @@ -23,11 +23,12 @@ init python: def generate_hash(self): tracking_object = self.tracking_object tracking_hash = str(tracking_object._hash) if tracking_object else "default" - salt = str( sorted( [self.name, self.type, self.id, str(self.color)] ) ) + tracking_hash + salt = str( [self.name, self.char.pose, self.type, self.id, str(self.color), str(self.char.body._hash)] ) + tracking_hash return hash(salt) - def get_layers(self, _ignore_equipped=False): - path = os.path.join(self.modpath, "characters", self.name, self.pose, "clothes", self.type, self.id) + @functools.cache + def get_layers(self, hash, _ignore_equipped=False): + path = os.path.join(self.modpath, "characters", self.name, self.char.pose, "clothes", self.type, self.id) _tracking = self._tracking def _negative_lookahead(tracking): @@ -103,17 +104,12 @@ init python: return layers - def rebuild_image(self): - self.layers = self.get_layers() - self._hash = self.generate_hash() - self._image_cached = False - @functools.cache def build_icon(self, hash): _tracking = self._tracking if _tracking.startswith("!"): - self.layers = self.get_layers(_ignore_equipped=True) + self.layers = self.get_layers(hash, _ignore_equipped=True) hash = self.generate_hash() tracking_object = None else: @@ -125,9 +121,9 @@ init python: if not tracking_object is None: sprites.extend([i for i in tracking_object.build_image(tracking_object._hash, matrix=matrix) if not i[0] == "mask"]) - sprites.extend(self.char.body.build_image(matrix=matrix)) + sprites.extend(self.char.body.build_image(self.char.body._hash, matrix=matrix)) sprites.sort(key=itemgetter(2)) - self.bounds = bounds = self.bounds or self.layers.get("outline", [sprites[0][1]])[0] + bounds = self.get_layers(hash).get("outline", [sprites[0][1]])[0] wmax, hmax = self.sizes wmin = hmin = 96 diff --git a/game/scripts/doll/common.rpy b/game/scripts/doll/common.rpy index f7ce08aa..256dba11 100644 --- a/game/scripts/doll/common.rpy +++ b/game/scripts/doll/common.rpy @@ -1,26 +1,6 @@ init -1 python: - ### Global Functions ### - - def get_character_emote(char, emote): - return "characters/{}/emotes/{}.webp".format(char, emote) if emote else None - - def get_character_pos(char): - global sprite_pos - - flip = getattr(renpy.store, char+"_flip", None) - use_head = getattr(renpy.store, "use_"+char+"_head", None) - - # Resolve X position for head state - if use_head: - xpos = sprite_pos["x"]["far_right"] if flip == 1 else sprite_pos["x"]["far_left"] - else: - xpos = getattr(renpy.store, char+"_xpos", None) - ypos = getattr(renpy.store, char+"_ypos", None) - - return (xpos, ypos) - ### Classes ### class DollDisplayable(renpy.Displayable): @@ -75,12 +55,9 @@ init -1 python: blacklist_toggles = ("hair", "glasses", "pubes", "piercing", "makeup", "tattoo", "earrings") blacklist_unequip = ("hair",) multislots = ("makeup", "accessory", "piercing", "tattoo") - extensions = {".webp", ".png", ".jxl"} + extensions = {".webp", ".png", ".jxl", ".avif"} sizes = (1010, 1200) # Default sizes used for defining rare cases - def rebuild_image(self): - self._image_cached = True - @property def image(self): if not renpy.is_skipping(): @@ -88,3 +65,9 @@ init -1 python: self._image_cached = True self._image = self.build_image() return self._image + + def is_stale(self): + curr_hash = self.generate_hash() + stale = curr_hash != self._hash + self._hash = curr_hash + return stale diff --git a/game/scripts/doll/cum.rpy b/game/scripts/doll/cum.rpy index 07b164f5..e7849670 100644 --- a/game/scripts/doll/cum.rpy +++ b/game/scripts/doll/cum.rpy @@ -11,18 +11,21 @@ init python: def __init__(self, obj): self.char = obj - self.layers = None self._cum = {k: None for k in {"hair", "face", "breasts", "body", "crotch", "pussy", "legs"}} + self._hash = None + + def generate_hash(self): + salt = str( [self.char.name, self.char.pose, sorted(list(self._cum.items()))] ) + return hash(salt) def set_cum(self, *args, **kwargs): if args: self._cum = {k: args[0] for k in self._cum} self._cum.update(kwargs) - self.layers = self.get_layers() - self.rebuild_image() - def get_layers(self): + @functools.cache + def get_layers(self, hash): cum = self._cum extensions = self.extensions @@ -67,9 +70,8 @@ init python: return layers - def build_image(self, matrix=None): - self.layers = layers = self.layers or self.get_layers() - + @functools.cache + def build_image(self, hash, matrix=None): if matrix is None: matrix = self.char.body.hue @@ -78,6 +80,8 @@ init python: "default": lambda file: Image(file), } + layers = self.get_layers(hash) + sprites = [] for identifier, (file, zorder) in layers.items(): @@ -91,51 +95,12 @@ init python: @property def image(self): - if not renpy.is_skipping(): - if not self._image_cached: - self._image_cached = True + if not renpy.is_skipping() and self.is_stale(): + hash = self._hash - sprites = self.build_image() - sprites.sort(key=itemgetter(2)) - sprites = [x[1] for x in sprites] + sprites = self.build_image(hash) + sprites.sort(key=itemgetter(2)) + sprites = [x[1] for x in sprites] - self._image = Fixed(*sprites, fit_first=True) + self._image = Fixed(*sprites, fit_first=True) return self._image - - def rebuild_image(self): - self._image_cached = False - - # def build_image(self): - # sprites = tuple("{}{}/{}.webp".format(self.imagepath, k, v) for k, v in self.cum.items() if v != None) - # return sprites - - # def get_skin(self): - # return ["{}{}/{}_skin.webp".format(self.imagepath, k, v) for k, v in self.cum.items() if renpy.loadable("{}{}/{}_skin.webp".format(self.imagepath, k, v))] - - # def set_cum(self, *args, **kwargs): - # """Takes keyword argument(s) containing string name(s) of cum layers to apply or None. Returns True if image is changed.""" - # changed = False - - # if args: - # for k, v in self.cum.items(): - # if v != args[0]: - # self.cum[k] = args[0] - # changed = True - - # for arg, value in kwargs.items(): - # if str(arg) != value: - # self.cum[str(arg)] = value - # changed = True - - # if changed: - # self.rebuild_image() - - # return changed - - # def set_pose(self, pose): - # if pose is None: - # self.imagepath = "characters/{}/cum/".format(self.name) - # else: - # self.imagepath = "characters/{}/poses/{}/cum/".format(self.name, pose) - # self.rebuild_image() - # return diff --git a/game/scripts/doll/face.rpy b/game/scripts/doll/face.rpy index 0592e9dc..7903f043 100644 --- a/game/scripts/doll/face.rpy +++ b/game/scripts/doll/face.rpy @@ -13,7 +13,6 @@ init python: def __init__(self, obj): self.char = obj - self.layers = None self._face = {k: None for k in {"cheeks", "eyebrows", "eyes", "mouth", "pupils", "tears"}} self._hash = None @@ -22,14 +21,14 @@ init python: self._face = {k: args[0] for k in self._face} self._face.update(kwargs) - self.layers = self.get_layers() - self.rebuild_image() + self.is_stale() def generate_hash(self): - salt = str( [self.char.name] + sorted( list( self._face.items() ) )) + salt = str( [self.char.name, self.char.pose, str(self.char.body._hash), sorted(list(self._face.items()))] ) return hash(salt) - def get_layers(self): + @functools.cache + def get_layers(self, hash): face = self._face extensions = self.extensions @@ -76,12 +75,8 @@ init python: @functools.cache def build_image(self, hash, matrix=None): - self.layers = layers = self.layers or self.get_layers() - - try: - eyemask = layers.pop(next(k for k in layers if "eyemask" in k), [None])[0] - except StopIteration: - eyemask = None + layers = self.get_layers(hash) + eyemask = next((layers.pop(k, None) for k in layers if "eyemask" in k), [None])[0] if matrix is None: matrix = self.char.body.hue @@ -111,20 +106,12 @@ init python: @property def image(self): - if not renpy.is_skipping(): - if not self._image_cached: - self._image_cached = True + if not renpy.is_skipping() and self.is_stale(): + hash = self._hash - sprites = self.build_image(self._hash) - sprites.sort(key=itemgetter(2)) - sprites = [x[1] for x in sprites] + sprites = self.build_image(hash) + sprites.sort(key=itemgetter(2)) + sprites = [x[1] for x in sprites] - self._image = Fixed(*sprites, fit_first=True) + self._image = Fixed(*sprites, fit_first=True) return self._image - - def rebuild_image(self): - self._hash = self.generate_hash() - self._image_cached = False - - # def get_face(self): - # return dict((k, v[0]) for k, v in self.face.items()) diff --git a/game/scripts/doll/main.rpy b/game/scripts/doll/main.rpy index b2a31edb..099743d9 100644 --- a/game/scripts/doll/main.rpy +++ b/game/scripts/doll/main.rpy @@ -36,6 +36,7 @@ init python: self.cum = DollCum(self) self.pose = "" self.emote = Null() + self._hash = None # Image properties self.zorder = 15 @@ -49,17 +50,9 @@ init python: self.xzoom = 1 self.align = (0.5, 1.0) - def rebuild(self): - """Rebuild character image cache.""" - self.body.rebuild_image() - self.face.rebuild_image() - self.cum.rebuild_image() - for o in self.wardrobe_list: - o.rebuild_image() - o.rebuild_icon() - for o in self.outfits: - o.rebuild_image() - self.rebuild_image() + def generate_hash(self): + salt = str( [self.name, self.pose, str(self.body._hash), str(self.face._hash), str(self.cum._hash), str([x[0]._hash for x in self.clothes.values() if x[0] and x[2]])] ) + return hash(salt) def show(self): if renpy.get_screen(("wardrobe", "animatedCG", "studio")): @@ -78,91 +71,50 @@ init python: def hide(self): renpy.hide(name=self.tag, layer=self.layer) - async def build_image(self): - # Add body, face, cum, clothes, masks + @functools.cache + def build_image(self, hash): + from itertools import chain - async def build_clothes(clothes): - sprites = [] - masks = [] + sprites = list(chain.from_iterable( + (self.body.build_image(self.body._hash), + self.face.build_image(self.face._hash), + self.cum.build_image(self.cum._hash), + *(x[0].build_image(x[0]._hash) for x in self.clothes.values() if x[0] and x[2])) + )) - for i in clothes.values(): - if i[0] and i[2]: - for identifier, layer, zorder in i[0].build_image(i[0]._hash): - if identifier == "mask": - masks.append((layer, zorder)) - else: - sprites.append((layer, zorder)) + masks = [sprites.pop(sprites.index(x)) for x in sprites if x[0] == "mask"] - return (sprites, masks) + sprites.sort(key=itemgetter(2)) + masks.sort(key=itemgetter(2)) - async def build_face(): - return (self.face.image, 1) + back_sprites = [x[1] for x in sprites if x[2] < 0] - async def build_body(): - return (self.body.image, 0) - - async def build_cum(): - return (self.cum.image, 100) - - body, face, cum, (clothes, masks) = await asyncio.gather( - build_body(), - build_face(), - build_cum(), - build_clothes(self.clothes), - ) - - sprites = [ - body, - face, - cum, - *clothes, - (self.emote, 1000) - ] - - # Filter out Nulls - sprites = [x for x in sprites if not isinstance(x[0], Null)] - - sprites.sort(key=itemgetter(1)) - masks.sort(key=itemgetter(1)) - - # Filter out sprites with zorder less than zero, there's no need to iterate over them. - back_sprites = [x[0] for x in sprites if x[1] < 0] - sprites = [x for x in sprites if x[1] > -1] - - # Apply alpha mask + #Apply alpha mask for m in masks: - mask, mask_zorder = m + _, mask, mask_zorder = m for i, s in enumerate(sprites): - sprite, sprite_zorder = s + _, sprite, sprite_zorder = s if i < 1 or mask_zorder > sprite_zorder: continue - c = tuple(x[0] for x in sprites[:i] if not isinstance(x[0], Null)) - masked = AlphaMask(Fixed(*c, fit_first=True), mask) + masked = AlphaMask(Fixed(*(x[1] for x in sprites[:i]), fit_first=True), mask) sprites = sprites[i:] - sprites.insert(0, (masked, mask_zorder)) + sprites.insert(0, (None, masked, mask_zorder)) break - sprites = back_sprites + [x[0] for x in sprites] - self._image = Fixed(*sprites, fit_first=True) - return + sprites = back_sprites + [x[1] for x in sprites] + + return Fixed(*sprites, fit_first=True) @property def image(self): - if not renpy.is_skipping(): - if not self._image_cached: - self._image_cached = True - asyncio.run(self.build_image()) + if not renpy.is_skipping() and self.is_stale(): + if renpy.showing(get_character_tag(self.name), layer=self.layer): + self.show() - return self._image - - def rebuild_image(self): - self._image_cached = False - - if renpy.showing(get_character_tag(self.name), layer=self.layer): - self.show() + return self.build_image(self._hash) def equip(self, obj, remove_old=True): """Takes DollCloth or DollOutfit object to equip.""" @@ -177,8 +129,6 @@ init python: for cloth in obj: self._equip_cloth(cloth) - self.body.rebuild_image() - self.rebuild_image() self.rebuild_blacklist() update_chibi(self.name) @@ -200,25 +150,19 @@ init python: if cloth.blacklist: self.unequip(*cloth.blacklist) - if self.pose: - cloth.set_pose(self.pose) - for tracking in self.get_trackers_list(cloth.type): - tracking.rebuild_image() - - if isinstance(cloth, DollClothDynamic): - cloth.rebuild_image() + tracking.is_stale() if color: cloth.set_color(color) + cloth.is_stale() + def unequip(self, *args): """Takes argument(s) containing string cloth type(s) to unequip.""" if "all" in args: for k, v in self.clothes.items(): if not k in self.blacklist_unequip: - if self.pose and v[0]: - v[0].set_pose(None) v[0], v[2] = None, True else: for arg in args: @@ -229,33 +173,18 @@ init python: if not slot: continue - if self.pose and self.clothes[slot][0]: - self.clothes[slot][0].set_pose(None) self.clothes[slot][0] = None else: - if self.pose and self.clothes[arg.type][0]: - self.clothes[arg.type][0].set_pose(None) self.clothes[arg.type][0] = None - - for tracking in self.get_trackers_list(arg.type): - tracking.rebuild_image() else: if arg in self.multislots: for k, v in self.clothes.items(): if not k in self.blacklist_unequip and any((x in k) for x in self.multislots): - if self.pose and v[0]: - v[0].set_pose(None) v[0], v[2] = None, True else: if not arg in self.blacklist_unequip: - if self.pose and self.clothes[arg][0]: - self.clothes[arg][0].set_pose(None) self.clothes[arg][0] = None - - - self.body.rebuild_image() - self.rebuild_image() self.rebuild_blacklist() update_chibi(self.name) @@ -284,8 +213,7 @@ init python: v[2] = False else: self.clothes[arg][2] = False - self.body.rebuild_image() - self.rebuild_image() + update_chibi(self.name) def wear(self, *args): @@ -304,8 +232,7 @@ init python: v[2] = True else: self.clothes[arg][2] = True - self.body.rebuild_image() - self.rebuild_image() + update_chibi(self.name) def is_equipped(self, *args): @@ -368,15 +295,12 @@ init python: def set_face(self, *args, **kwargs): self.face.set_face(*args, **kwargs) - makeup = next((v[0] for v in self.clothes.values() if v[0] and v[2] and isinstance(v[0], DollMakeup)), None) - if makeup: - makeup.rebuild_image() - self.rebuild_image() + [x[0].is_stale() for x in self.clothes.values() if isinstance(x[0], DollMakeup) and x[2]] def get_face(self): """Returns a dictionary containing currently set facial expressions. Used in character studio.""" - return self.face.get_face() + return self.face._face # def set_body(self, **kwargs): # OBSOLETE! the code in scripts needs to be changed @@ -386,9 +310,7 @@ init python: def set_body_hue(self, arg): """Takes integer between 0 - 359, rotates the character body colour by given amount.""" - self.body.hue = HueMatrix(arg) - self.body.rebuild_image() - self.rebuild_image() + self.body.set_hue(arg) # def set_body_zorder(self, **kwargs): # OBSOLETE! the code in scripts needs to be changed @@ -399,20 +321,10 @@ init python: def set_cum(self, *args, **kwargs): """Takes keyword argument(s) containing string name(s) of cum layers to apply or None.""" self.cum.set_cum(*args, **kwargs) - self.rebuild_image() def set_pose(self, pose): - if pose is None or renpy.loadable("characters/{}/poses/{}/loadable.webp".format(self.name, pose)): - self.pose = pose - self.face.set_pose(pose) - self.body.set_pose(pose) - self.cum.set_pose(pose) - for v in self.clothes.values(): - if v[0]: - v[0].set_pose(pose) - self.rebuild_image() - else: - raise Exception("'{}' pose doesn't exist for character named '{}'.".format(pose, self.name)) + pose = "" if pose is None else os.path.join("poses", pose) + self.pose = pose def rebuild_blacklist(self): blacklist = [] diff --git a/game/scripts/doll/makeup.rpy b/game/scripts/doll/makeup.rpy index 42b921b6..a5df20ca 100644 --- a/game/scripts/doll/makeup.rpy +++ b/game/scripts/doll/makeup.rpy @@ -9,17 +9,18 @@ init python: return f"DollMakeup(name={self.name}, categories={self.categories}, type={self.type}, id={self.id}, color={self.color}, zorder={self.zorder}, unlocked={self.unlocked}, level={self.level}, blacklist={self.blacklist}, parent={self.parent}, modpath={self.modpath or None}, tracking={self.tracking})" def generate_hash(self): - salt = str( sorted( [self.name, self.type, self.id, str(self.color), str(self.char.face._hash)] ) ) + salt = str( [self.name, self.type, self.char.pose, self.id, str(self.color), str(self.char.face._hash), str(self.char.body._hash)] ) return hash(salt) - def get_layers(self): + @functools.cache + def get_layers(self, hash): tracking = self.char.face._face.get(self.tracking, None) if tracking is None: print(f"Invalid tracker for object: {self}") return [] - path = os.path.join(self.modpath, "characters", self.name, self.pose, "clothes", self.type, self.id, tracking) + path = os.path.join(self.modpath, "characters", self.name, self.char.pose, "clothes", self.type, self.id, tracking) extensions = self.extensions types = self.layer_types @@ -58,8 +59,3 @@ init python: layers.setdefault(ltype, [f, zorder]) return layers - - def rebuild_image(self): - self.layers = self.get_layers() - self._hash = self.generate_hash() - self._image_cached = False diff --git a/game/scripts/doll/outfits.rpy b/game/scripts/doll/outfits.rpy index bc0b9de7..3a452031 100644 --- a/game/scripts/doll/outfits.rpy +++ b/game/scripts/doll/outfits.rpy @@ -32,7 +32,7 @@ init python: return self._hash == obj._hash def generate_hash(self): - salt = str( sorted( [sorted([x.name, x.type, x.id, str(x.color)] ) for x in self.group]) ) + salt = str( [self.name, str([x._hash for x in self.group]) ] ) return hash(salt) def delete(self): @@ -40,77 +40,45 @@ init python: self.char.outfits.remove(self) @functools.cache - async def build_image(self): - # Add body, face, cum, clothes, masks + def build_image(self, hash): + from itertools import chain + matrix = SaturationMatrix(0.0) - async def build_clothes(group): - sprites = [] - masks = [] + sprites = list(chain.from_iterable( + (self.char.body.build_image(self.char.body._hash, matrix), + *(x.build_image(x._hash, matrix) for x in self.group)) + )) - for i in group: - for identifier, layer, zorder in i.build_image(i._hash, matrix): - if identifier == "mask": - masks.append((layer, zorder)) - else: - sprites.append((layer, zorder)) + masks = [sprites.pop(sprites.index(x)) for x in sprites if x[0] == "mask"] - return (sprites, masks) + sprites.sort(key=itemgetter(2)) + masks.sort(key=itemgetter(2)) - async def build_mannequin(): - sprites = self.char.body.build_image(matrix) - sprites.sort(key=itemgetter(2)) - sprites = [(x[1], x[2]) for x in sprites] - return sprites + back_sprites = [x[1] for x in sprites if x[2] < 0] - mannequin, (clothes, masks) = await asyncio.gather( - build_mannequin(), - build_clothes(self.group), - ) - - sprites = [ - *mannequin, - *clothes, - ] - - # Filter out Nulls - sprites = [x for x in sprites if not isinstance(x[0], Null)] - - sprites.sort(key=itemgetter(1)) - masks.sort(key=itemgetter(1)) - - # Filter out sprites with zorder less than zero, there's no need to iterate over them. - back_sprites = [x[0] for x in sprites if x[1] < 0] - sprites = [x for x in sprites if x[1] > -1] - - # Apply alpha mask + #Apply alpha mask for m in masks: - mask, mask_zorder = m + _, mask, mask_zorder = m for i, s in enumerate(sprites): - sprite, sprite_zorder = s + _, sprite, sprite_zorder = s if i < 1 or mask_zorder > sprite_zorder: continue - c = tuple(x[0] for x in sprites[:i] if not isinstance(x[0], Null)) - masked = AlphaMask(Fixed(*c, fit_first=True), mask) + masked = AlphaMask(Fixed(*(x[1] for x in sprites[:i]), fit_first=True), mask) sprites = sprites[i:] - sprites.insert(0, (masked, mask_zorder)) + sprites.insert(0, (None, masked, mask_zorder)) break - sprites = back_sprites + [x[0] for x in sprites] - self._image = Fixed(*sprites, fit_first=True) - return + sprites = back_sprites + [x[1] for x in sprites] + + return Fixed(*sprites, fit_first=True) @property def image(self): - if not renpy.is_skipping(): - if not self._image_cached: - self._image_cached = True - asyncio.run(self.build_image()) - - return self._image + return self.build_image(self._hash) def exists(self): return (self in self.char.outfits) @@ -128,7 +96,7 @@ init python: if not os.path.exists(path): os.makedirs(path) - d = Transform(self.get_image(), crop=(210, 200, 700, 1000), anchor=(0.5, 1.0), align=(0.5, 1.0), xsize=310, ysize=470, fit="contain") + d = Transform(self.image, crop=(210, 200, 700, 1000), anchor=(0.5, 1.0), align=(0.5, 1.0), xsize=310, ysize=470, fit="contain") d = Fixed( "interface/wardrobe/export_background.webp", d, @@ -154,11 +122,7 @@ init python: def save(self): """Overwrites this outfit with clothes currently equipped by the character.""" - self.group = [] - for v in self.char.clothes.values(): - if v[0]: - self.group.append(v[0].clone()) - self.rebuild_image() + self.group = [x[0].clone() for x in self.char.clothes.values() if x[0]] return def is_modded(self): diff --git a/game/scripts/interface/stats.rpy b/game/scripts/interface/stats.rpy index 65ab724e..c87dd5c3 100644 --- a/game/scripts/interface/stats.rpy +++ b/game/scripts/interface/stats.rpy @@ -248,32 +248,32 @@ screen stats_menuitem(xx, yy): add "interface/characters/snape_locked.webp" zoom 0.35 align (0.9, 1.0) xzoom 1 elif current_category == "Tonks": if current_item["flag"]: - add tonks.get_image() zoom 0.41 align (0.7, 1.0) xzoom 1 + add tonks.image zoom 0.41 align (0.7, 1.0) xzoom 1 else: add "interface/characters/tonks_locked.webp" zoom 0.41 align (0.7, 1.0) xzoom 1 elif current_category == "Hermione": if current_item["flag"]: - add hermione.get_image() zoom 0.39 align (0.7, 1.0) xzoom 1 + add hermione.image zoom 0.39 align (0.7, 1.0) xzoom 1 else: add "interface/characters/hermione_locked.webp" zoom 0.39 align (0.65, 1.0) xzoom 1 elif current_category == "Cho": if current_item["flag"]: - add cho.get_image() zoom 0.42 align (0.65, 1.0) xzoom 1 + add cho.image zoom 0.42 align (0.65, 1.0) xzoom 1 else: add "interface/characters/cho_locked.webp" zoom 0.42 align (0.65, 1.0) xzoom 1 elif current_category == "Luna": if current_item["flag"]: - add luna.get_image() zoom 0.39 align (0.75, 1.0) xzoom 1 + add luna.image zoom 0.39 align (0.75, 1.0) xzoom 1 else: add "interface/characters/luna_locked.webp" zoom 0.39 align (0.75, 1.0) xzoom 1 elif current_category == "Astoria": if current_item["flag"]: - add astoria.get_image() zoom 0.4 align (0.7, 1.0) xzoom 1 + add astoria.image zoom 0.4 align (0.7, 1.0) xzoom 1 else: add "interface/characters/astoria_locked.webp" zoom 0.4 align (0.7, 1.0) xzoom 1 elif current_category == "Susan": if current_item["flag"]: - add susan.get_image() zoom 0.4 align (0.65, 1.0) xzoom 1 + add susan.image zoom 0.4 align (0.65, 1.0) xzoom 1 else: add "interface/characters/susan_locked.webp" zoom 0.4 align (0.65, 1.0) xzoom 1 diff --git a/game/scripts/shops/dress/menu.rpy b/game/scripts/shops/dress/menu.rpy index 7b5cc99a..bd06771e 100644 --- a/game/scripts/shops/dress/menu.rpy +++ b/game/scripts/shops/dress/menu.rpy @@ -237,7 +237,7 @@ screen shop_dress_menuitem(): mesh True for item in menu_items: - $ icon = Transform(item.get_image(), crop=(215, 0, 680, 1200), mesh=True, gl_pixel_perfect=True) + $ icon = Transform(item.image, crop=(215, 0, 680, 1200), mesh=True, gl_pixel_perfect=True) $ is_modded = item.is_modded() $ is_affordable = bool(game.gold >= item.price) diff --git a/game/scripts/utility/skipping.rpy b/game/scripts/utility/skipping.rpy index f10dfc02..035bea46 100644 --- a/game/scripts/utility/skipping.rpy +++ b/game/scripts/utility/skipping.rpy @@ -22,15 +22,7 @@ init -1 python: for c in end_skip_callbacks: c() - def rebuild_dolls(): - if renpy.in_rollback(): - return - - for c in renpy.store.CHARACTERS: - c = get_character_object(c) - c.rebuild_image() - config.interact_callbacks.append(SkipCallbacksHandler()) - end_skip_callbacks.append(rebuild_dolls) + #end_skip_callbacks.append(rebuild_dolls) diff --git a/game/scripts/wardrobe/functions.rpy b/game/scripts/wardrobe/functions.rpy index 80ebcc45..2afd5f78 100644 --- a/game/scripts/wardrobe/functions.rpy +++ b/game/scripts/wardrobe/functions.rpy @@ -44,13 +44,6 @@ init python: req += ["NO PANTIES: {}".format(get_character_requirement(key, "unequip panties"))] print("\n".join(req)) - def get_character_outfit_hash(key): - ### Untested ### - char = get_character_object(key) - clothes = [x[0] for x in char.clothes.values() if x[0]] - salt = str( sorted([ sorted([x.name, x.type, x.id, x.color]) for x in clothes ]) ) - return hash(salt) - def get_character_tag(key): if not key in CHARACTERS: raise KeyError("'{}' character is undefined.".format(key)) diff --git a/game/scripts/wardrobe/studio.rpy b/game/scripts/wardrobe/studio.rpy index a2c72810..7ed314cf 100644 --- a/game/scripts/wardrobe/studio.rpy +++ b/game/scripts/wardrobe/studio.rpy @@ -92,7 +92,7 @@ init python in studio: char_obj = obj char_name = char_obj.name - d = Transform(Flatten(char_obj.get_image()), zoom=choices[char_name]["zoom"], xzoom=choices[char_name]["flip"], alpha=choices[char_name]["alpha"]) + d = Transform(Flatten(char_obj.image), zoom=choices[char_name]["zoom"], xzoom=choices[char_name]["flip"], alpha=choices[char_name]["alpha"]) pos = (250, 0) drag = Drag(d, activated=drag_activated, drag_offscreen=True, focus_mask=True) @@ -120,7 +120,7 @@ init python in studio: flip = choices[drag.char_name]["flip"] alpha = choices[drag.char_name]["alpha"] - d = Flatten(drag.char_obj.get_image()) + d = Flatten(drag.char_obj.image) d = Transform(d, zoom=zoom, xzoom=flip, alpha=alpha) drag.set_child(d) return diff --git a/game/scripts/wardrobe/wardrobe.rpy b/game/scripts/wardrobe/wardrobe.rpy index 2d3b4bc0..cf212ae9 100644 --- a/game/scripts/wardrobe/wardrobe.rpy +++ b/game/scripts/wardrobe/wardrobe.rpy @@ -193,10 +193,11 @@ label wardrobe_menu(): continue i.set_color(current_item.color) + i.is_stale() rebuild = True if rebuild: - outfit.rebuild_image() + outfit.is_stale() elif _choice[0] == "resetcolor": python: @@ -211,10 +212,11 @@ label wardrobe_menu(): continue i.set_color(current_item.color) + i.is_stale() rebuild = True if rebuild: - outfit.rebuild_image() + outfit.is_stale() elif _choice[0] == "touch": if wardrobe_check_touch(_choice[1]): @@ -299,7 +301,7 @@ label wardrobe_menu(): progress = get_character_progression(active_girl) - for k in char_active.clothes.keys(): + for k in dict(char_active.clothes).keys(): valid_choices = [x for x in char_active.wardrobe_list if (x.type == k and x.unlocked and progress >= x.level)] if k == "panties": @@ -335,10 +337,10 @@ label wardrobe_menu(): if not cloth is None: - if wardrobe_randomise_color: + if wardrobe_randomise_color and cloth.color: col = [] - for i in range(cloth.layers): + for i in range(len(cloth.color)): col.append([random.randint(0, 255), random.randint(0, 255), random.randint(0, 255), 255]) cloth.set_color(col) @@ -348,14 +350,20 @@ label wardrobe_menu(): rebuild = False for i in outfit.group: - if not i.id == cloth.id: + if not (i.id, i.type) == (cloth.id, cloth.type): + continue + + if len(cloth.color) != len(i.color): + print(f"Mismatched color lens:\n{cloth}\n{i}") + renpy.notify("Error!") continue i.set_color(cloth.color) + i.is_stale() rebuild = True if rebuild: - outfit.rebuild_image() + outfit.is_stale() char_active.equip(cloth) else: @@ -386,14 +394,15 @@ label wardrobe_menu(): rebuild = False for i in outfit.group: - if not i.id == cloth.id: + if not (i.id, i.type) == (cloth.id, cloth.type): continue i.set_color(cloth.color) + i.is_stale() rebuild = True if rebuild: - outfit.rebuild_image() + outfit.is_stale() renpy.hide_screen("wardrobe") char_active.wear("all") @@ -589,16 +598,6 @@ screen wardrobe_menuitem(xx, yy): # Item icons if not menu_items: text "Nothing here yet" size 24 align (0.5, 0.6) - # elif not wardrobe_loaded: - # text "Loading..." size 24 align (0.5, 0.6) - - # for i in menu_items: - # $ i.rebuild_image() - # $ i.rebuild_icon() - # #$ renpy.invoke_in_thread(i.rebuild_icon) - - # $ wardrobe_loaded = True - # $ renpy.restart_interaction() else: vpgrid: cols 5