Modding support

* Resolved CHARACTERS constant to allow adding custom characters without having to rely on monkey patching.
This commit is contained in:
LoafyLemon 2023-04-11 20:36:22 +01:00
parent e4c8e2de73
commit d3d2f6c8aa
13 changed files with 60 additions and 56 deletions

View File

@ -10,7 +10,7 @@ init -999 python:
def narrator_fade(ev, interact=True, **kwargs):
if ev == "begin":
behind = [f"{i}_main" for i in CHARACTERS] + ["snape_main", "genie_main", "cg"]
behind = [f"{i}_main" for i in states.dolls] + ["snape_main", "genie_main", "cg"]
renpy.show("fade", zorder=15, behind=behind)
elif ev == "end":
renpy.hide("fade")

View File

@ -1,4 +1,2 @@
define CHARACTERS = {"hermione", "tonks", "astoria", "cho", "luna", "susan", "hooch"}
define SAYERS = {i[:3]:i for i in CHARACTERS}
define ALLOWED_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz "

View File

@ -2,7 +2,7 @@
init offset = 2
init python:
def wardrobe_init():
for c in CHARACTERS:
for c in states.dolls:
char = get_character_object(c)
body_default = get_character_body(c, type="default")

View File

@ -62,7 +62,7 @@ init python:
self.zorder = 15
self.layer = "screens"
self.animation = None
self.tag = get_character_tag(name)
self.tag = f"{name}_main"
# Transform properties
self.pos = (0, 0)
@ -72,6 +72,12 @@ init python:
self.modpath = "mods/" + posixpath.normpath(modpath) if modpath else ""
# Add doll name to global doll states store
try:
renpy.store.states.dolls.add(name)
except AttributeError:
renpy.store.states.dolls = {name}
def generate_hash(self):
clothes_hash = str([x[0]._hash for x in self.states.values() if istype(x[0], (DollCloth, DollClothDynamic, DollMakeup)) and x[2]])
salt = str( [self.name, self.pose, str(self.body._hash), str(self.face._hash), str(self.cum._hash), clothes_hash] )

View File

@ -130,7 +130,7 @@ screen choice(items):
style_prefix gui.theme("menu")
default blacklist_screens = {"say", "letter", "bld1"} # Combine sets
default blacklist_tags = set(get_character_tag(x) for x in CHARACTERS)
default blacklist_tags = set(get_character_tag(x) for x in states.dolls)
# Dont add the fade if character or saybox is present (They have their own triggers for fading)
if not any(renpy.get_screen(x) for x in blacklist_screens) and not any(renpy.showing(x, layer="screens") for x in blacklist_tags):

View File

@ -70,7 +70,7 @@ label cheats:
jump cheats.general
python:
for i in CHARACTERS:
for i in states.dolls:
for x in getattr(renpy.store, i).outfits:
if not x.hidden:
x.unlock()
@ -326,7 +326,7 @@ label cheats:
"-Unequip all clothes-":
python:
for i in CHARACTERS:
for i in states.dolls:
getattr(renpy.store, i).unequip("clothes")
nar "All characters are now naked."

View File

@ -1,24 +1,24 @@
default lollipop_ITEM = Item("lollipop", "gift", "Lollipop Candy", 20, "A lollipop candy. An adult candy for kids or kids candy for adults?", givable=True, caption="Give", usable_on=list(CHARACTERS))
default chocolate_ITEM = Item("chocolate", "gift", "Chocolate", 40, "The recipe for this delicious milk chocolate is kept a secret. (Rumoured to contain dried faeries).", givable=True, caption="Give", usable_on=list(CHARACTERS))
default plush_owl_ITEM = Item("plush_owl", "gift", "Plush owl", 35, "A Toy owl stuffed with feathers of an actual owl. It's so cuddly!", givable=True, caption="Give", usable_on=list(CHARACTERS))
default butterbeer_ITEM = Item("butterbeer", "gift", "Butterbeer", 50, "Girls can't resist this beverage's buttery texture. Therefore it's always in high demand among the boys.", givable=True, caption="Give", usable_on=list(CHARACTERS))
default science_mag_ITEM = Item("science_mag", "gift", "Educational Magazines", 30, "Educational magazines.\nthe Trusty companions of every social outcast.", givable=True, caption="Give", usable_on=list(CHARACTERS))
default girls_mag_ITEM = Item("girls_mag", "gift", "Girly Magazines", 45, "Girly magazines.\nAll cool girls are reading these.", givable=True, caption="Give", usable_on=list(CHARACTERS))
default adult_mag_ITEM = Item("adult_mag", "gift", "Adult magazines", 60, "Your boyfriend is turning into a nice guy?\nYour husband won't abuse you anymore?\nAll you wanted to know about relationships, love and sex. Mostly about sex.", givable=True, caption="Give", usable_on=list(CHARACTERS))
default porn_mag_ITEM = Item("porn_mag", "gift", "Porn magazines", 80, "Give these to your girlfriend to test her, to your wife to shame her and to your daughter to avoid \"the talk\".", givable=True, caption="Give", usable_on=list(CHARACTERS))
default krum_poster_ITEM = Item("krum_poster", "gift", "Viktor Krum Poster", 25, "A skilled Quidditch Seeker, Viktor has been selected to play for the Bulgarian National Quidditch team despite still going to school, and is widely regarded as one of the best players in the world.", givable=True, caption="Give", usable_on=list(CHARACTERS))
default sexy_lingerie_ITEM = Item("sexy_lingerie", "gift", "Sexy Lingerie", 75, "Sexy lingerie \"Fairy Godmother\". Charm your wizard in bed or empress your sisters at a Sabbath.", givable=True, caption="Give", usable_on=list(CHARACTERS))
default sexy_stockings_ITEM = Item("sexy_stockings", "gift", "Sexy Stockings", 50, "Somewhere between now and the dark-ages came the invention of stockings, when you want to show some skin but not too much.", givable=True, caption="Give", usable_on=list(CHARACTERS))
default pink_condoms_ITEM = Item("condoms", "gift", "A Pack Of Condoms", 50, "Unleash the one-horned beast!\n{size=-4}May contain traces of actual unicorn saliva.{/size}", givable=True, caption="Give", usable_on=list(CHARACTERS))
default vibrator_ITEM = Item("vibrator", "gift", "Vibrator", 55, "A magnificent, magically enhanced vibrator made of vine wood, with a dragon heartstring core.", givable=True, caption="Give", usable_on=list(CHARACTERS))
default anal_lube_ITEM = Item("lube", "gift", "Jar of lubricant", 60, "A Jar full of lube, Buy this for your loved one - show that you care.", givable=True, caption="Give", usable_on=list(CHARACTERS))
default ballgag_and_cuffs_ITEM = Item("ballgag_and_cuffs", "gift", "Ball gag and cuffs", 70, "Ball gag and cuffs, Turn your soulmate into your cellmate.", givable=True, caption="Give", usable_on=list(CHARACTERS))
default anal_plugs_ITEM = Item("buttplugs", "gift", "Anal plugs", 85, "Anal plugs decorated with actual tails. Sizes vary to satisfy expert practitioners and beginner alike.", givable=True, caption="Give", usable_on=list(CHARACTERS))
default testral_strapon_ITEM = Item("strapon", "gift", "Thestral Strap-on", 200, "Thestral strap-on.\nWhen you see it, you'll shit bricks.", givable=True, caption="Give", usable_on=list(CHARACTERS))
default broom_2000_ITEM = Item("broom", "gift", "Lady Speed Stick-2000", 500, "{size=-2}The \"Lady Speed Stick-2000\", an elegant way of transportation for passionate witches. The trademarked saddle guarantees full satisfaction. Get one for your witch and she won't use her boring old broom ever again!{/size}", givable=True, caption="Give", usable_on=list(CHARACTERS))
default sexdoll_ITEM = Item("sexdoll", "gift", "Sex doll \"Joanne\"", 350, "It's so realistic. Almost looks like a real human under the influence of a spell of some sort.", givable=True, caption="Give", usable_on=list(CHARACTERS))
default anal_beads_ITEM = Item("beads", "gift", "Anal beads", 65, "Anal beads engraved with a strange inscription \"Property of L.C.\".", givable=True, caption="Give", usable_on=list(CHARACTERS))
default lollipop_ITEM = Item("lollipop", "gift", "Lollipop Candy", 20, "A lollipop candy. An adult candy for kids or kids candy for adults?", givable=True, caption="Give", usable_on=list(states.dolls))
default chocolate_ITEM = Item("chocolate", "gift", "Chocolate", 40, "The recipe for this delicious milk chocolate is kept a secret. (Rumoured to contain dried faeries).", givable=True, caption="Give", usable_on=list(states.dolls))
default plush_owl_ITEM = Item("plush_owl", "gift", "Plush owl", 35, "A Toy owl stuffed with feathers of an actual owl. It's so cuddly!", givable=True, caption="Give", usable_on=list(states.dolls))
default butterbeer_ITEM = Item("butterbeer", "gift", "Butterbeer", 50, "Girls can't resist this beverage's buttery texture. Therefore it's always in high demand among the boys.", givable=True, caption="Give", usable_on=list(states.dolls))
default science_mag_ITEM = Item("science_mag", "gift", "Educational Magazines", 30, "Educational magazines.\nthe Trusty companions of every social outcast.", givable=True, caption="Give", usable_on=list(states.dolls))
default girls_mag_ITEM = Item("girls_mag", "gift", "Girly Magazines", 45, "Girly magazines.\nAll cool girls are reading these.", givable=True, caption="Give", usable_on=list(states.dolls))
default adult_mag_ITEM = Item("adult_mag", "gift", "Adult magazines", 60, "Your boyfriend is turning into a nice guy?\nYour husband won't abuse you anymore?\nAll you wanted to know about relationships, love and sex. Mostly about sex.", givable=True, caption="Give", usable_on=list(states.dolls))
default porn_mag_ITEM = Item("porn_mag", "gift", "Porn magazines", 80, "Give these to your girlfriend to test her, to your wife to shame her and to your daughter to avoid \"the talk\".", givable=True, caption="Give", usable_on=list(states.dolls))
default krum_poster_ITEM = Item("krum_poster", "gift", "Viktor Krum Poster", 25, "A skilled Quidditch Seeker, Viktor has been selected to play for the Bulgarian National Quidditch team despite still going to school, and is widely regarded as one of the best players in the world.", givable=True, caption="Give", usable_on=list(states.dolls))
default sexy_lingerie_ITEM = Item("sexy_lingerie", "gift", "Sexy Lingerie", 75, "Sexy lingerie \"Fairy Godmother\". Charm your wizard in bed or empress your sisters at a Sabbath.", givable=True, caption="Give", usable_on=list(states.dolls))
default sexy_stockings_ITEM = Item("sexy_stockings", "gift", "Sexy Stockings", 50, "Somewhere between now and the dark-ages came the invention of stockings, when you want to show some skin but not too much.", givable=True, caption="Give", usable_on=list(states.dolls))
default pink_condoms_ITEM = Item("condoms", "gift", "A Pack Of Condoms", 50, "Unleash the one-horned beast!\n{size=-4}May contain traces of actual unicorn saliva.{/size}", givable=True, caption="Give", usable_on=list(states.dolls))
default vibrator_ITEM = Item("vibrator", "gift", "Vibrator", 55, "A magnificent, magically enhanced vibrator made of vine wood, with a dragon heartstring core.", givable=True, caption="Give", usable_on=list(states.dolls))
default anal_lube_ITEM = Item("lube", "gift", "Jar of lubricant", 60, "A Jar full of lube, Buy this for your loved one - show that you care.", givable=True, caption="Give", usable_on=list(states.dolls))
default ballgag_and_cuffs_ITEM = Item("ballgag_and_cuffs", "gift", "Ball gag and cuffs", 70, "Ball gag and cuffs, Turn your soulmate into your cellmate.", givable=True, caption="Give", usable_on=list(states.dolls))
default anal_plugs_ITEM = Item("buttplugs", "gift", "Anal plugs", 85, "Anal plugs decorated with actual tails. Sizes vary to satisfy expert practitioners and beginner alike.", givable=True, caption="Give", usable_on=list(states.dolls))
default testral_strapon_ITEM = Item("strapon", "gift", "Thestral Strap-on", 200, "Thestral strap-on.\nWhen you see it, you'll shit bricks.", givable=True, caption="Give", usable_on=list(states.dolls))
default broom_2000_ITEM = Item("broom", "gift", "Lady Speed Stick-2000", 500, "{size=-2}The \"Lady Speed Stick-2000\", an elegant way of transportation for passionate witches. The trademarked saddle guarantees full satisfaction. Get one for your witch and she won't use her boring old broom ever again!{/size}", givable=True, caption="Give", usable_on=list(states.dolls))
default sexdoll_ITEM = Item("sexdoll", "gift", "Sex doll \"Joanne\"", 350, "It's so realistic. Almost looks like a real human under the influence of a spell of some sort.", givable=True, caption="Give", usable_on=list(states.dolls))
default anal_beads_ITEM = Item("beads", "gift", "Anal beads", 65, "Anal beads engraved with a strange inscription \"Property of L.C.\".", givable=True, caption="Give", usable_on=list(states.dolls))
default wine_ITEM = Item("wine", "gift", "Wine", 60, "For the more refined palate.", givable=True, caption="Give", usable_on=list(CHARACTERS))
default firewhisky_ITEM = Item("firewhisky", "gift", "Firewhisky", 80, "Great taste with a fiery burn.", givable=True, caption="Give", unlocked=False, usable_on=list(CHARACTERS))
default wine_ITEM = Item("wine", "gift", "Wine", 60, "For the more refined palate.", givable=True, caption="Give", usable_on=list(states.dolls))
default firewhisky_ITEM = Item("firewhisky", "gift", "Firewhisky", 80, "Great taste with a fiery burn.", givable=True, caption="Give", unlocked=False, usable_on=list(states.dolls))

View File

@ -20,7 +20,7 @@ label naughty_list:
# This code retrieves user name and displays it on a leaderboard-like
# list for funsies, the variable is discarded afterwards.
_username = None
_d = [(i, get_character_progression(i)) for i in CHARACTERS if get_character_unlock(i)]
_d = [(i, get_character_progression(i)) for i in states.dolls if get_character_unlock(i)]
_d.append(["Snape", states.sna.level])
try:

View File

@ -78,7 +78,7 @@ label start_dev:
for i in inventory.items:
i.owned = i.limit
for i in CHARACTERS:
for i in states.dolls:
for x in getattr(renpy.store, i).outfits:
if not x.hidden:
x.unlock()

View File

@ -14,6 +14,7 @@ init python:
self.persistent_expressions = _dict()
self.last_expressions = _dict()
self.active = False
self.sayers = {i[:3]:i for i in states.dolls}
def catch(self, *args, **kwargs):
if not self.active or renpy.is_init_phase():
@ -258,7 +259,7 @@ init python:
all_files = renpy.list_files()
d = _dict()
for charname in CHARACTERS:
for charname in states.dolls:
charobj = get_character_object(charname)
extensions = charobj.extensions
@ -306,7 +307,7 @@ init python:
who = node.who
args = node.arguments
if who in SAYERS:
if who in self.sayers:
keywords = ["mouth", "eyes", "eyebrows", "pupils", "cheeks", "tears", "emote",
"face", "xpos", "ypos", "pos", "flip", "trans", "animation", "hair"]
else:
@ -465,7 +466,7 @@ screen editor():
action [CaptureFocus(expr_type), SetScreenVariable("focused", expr_type)]
selected GetFocusRect(expr_type)
if e.node.who in SAYERS:
if e.node.who in e.sayers:
textbutton "😊":
action Function(e.toggle_expressions_persistent_type, e.node.who, "cheeks")
selected (e.get_expressions_persistent_type(e.node.who, "cheeks"))

View File

@ -19,7 +19,7 @@ init -1 python:
renpy.execute_default_statement(False)
# Add images to linting list to avoid undefined errors
for i in CHARACTERS:
for i in states.dolls:
prefix = "{}_main".format(i)
renpy.lint.image_prefixes[prefix] = True
@ -34,7 +34,7 @@ init -1 python:
OTHER = ["flip", "trans", "animation", "hair", "emote"]
KEYS = EXPRESSIONS + POSITIONS + OTHER
INTERPUNCT = (".", "!", "?", "--", "*", ")", "}")
SAYERS = {i[:3]:i for i in states.dolls}
RE_SPACES = re.compile(r"\s[^\S\r\n]")
RE_COMMA = re.compile(r"\w+, \w+, \w+ and")
RE_BRACKET = re.compile(r"\([^)]*$")

View File

@ -1,41 +1,41 @@
init -1 python:
def get_character_progression(key):
if not key in CHARACTERS:
if not key in states.dolls:
raise KeyError("'{}' character is undefined.".format(key))
return getattr(states, f"{key[:3]}").level
def get_character_scheduling(key):
if not key in CHARACTERS:
if not key in states.dolls:
raise KeyError("'{}' character is undefined.".format(key))
return getattr(states, f"{key[:3]}").wardrobe_scheduling
def get_character_requirement(key, type):
if not key in CHARACTERS:
if not key in states.dolls:
raise KeyError("'{}' character is undefined.".format(key))
return getattr(renpy.store, key[:3]+"_requirements").get(type, 0)
def get_character_response(key, type):
if not key in CHARACTERS:
if not key in states.dolls:
raise KeyError("'{}' character is undefined.".format(key))
return getattr(renpy.store, key[:3]+"_responses").get(type)
def get_character_object(key):
if not key in CHARACTERS:
if not key in states.dolls:
raise KeyError("'{}' character is undefined.".format(key))
return getattr(store, key)
def get_character_outfit(key, type="default"):
if not key in CHARACTERS:
if not key in states.dolls:
raise KeyError("'{}' character is undefined.".format(key))
return getattr(store, "{}_outfit_{}".format(key[:3], type))
def get_character_body(key, type="default"):
if not key in CHARACTERS:
if not key in states.dolls:
raise KeyError("'{}' character is undefined.".format(key))
return getattr(store, "{}_body_{}".format(key[:3], type))
def get_character_outfit_req(key, item):
if not key in CHARACTERS:
if not key in states.dolls:
raise KeyError("'{}' character is undefined.".format(key))
if not isinstance(item, DollOutfit):
@ -53,37 +53,37 @@ init -1 python:
print("\n".join(req))
def get_character_tag(key):
if not key in CHARACTERS:
if not key in states.dolls:
raise KeyError("'{}' character is undefined.".format(key))
return "{}_main".format(key)
def get_character_sayer(key):
if not key in CHARACTERS:
if not key in states.dolls:
raise KeyError("'{}' character is undefined.".format(key))
return getattr(store, key[:3])
def get_character_gift_label(key):
if not key in CHARACTERS:
if not key in states.dolls:
raise KeyError("'{}' character is undefined.".format(key))
return "give_{}_gift".format(key[:3])
def get_character_potion_check_label(key):
if not key in CHARACTERS:
if not key in states.dolls:
raise KeyError("'{}' character is undefined.".format(key))
return "{}_potion_check".format(key[:3])
def get_character_potion_check(key):
if not key in CHARACTERS:
if not key in states.dolls:
raise KeyError("'{}' character is undefined.".format(key))
return getattr(store, "{}_potion_check".format(key[:3]))
def get_character_unlock(key):
if not key in CHARACTERS:
if not key in states.dolls:
raise KeyError("'{}' character is undefined.".format(key))
return getattr(store, "{}_unlocked".format(key))
def get_character_mood(key):
if not key in CHARACTERS:
if not key in states.dolls:
raise KeyError("'{}' character is undefined.".format(key))
return getattr(store, "{}_mood".format(key[:3]))

View File

@ -12,14 +12,13 @@ init python in studio:
Transform = renpy.store.Transform
Flatten = renpy.store.Flatten
Drag = renpy.store.Drag
CHARACTERS = renpy.store.CHARACTERS
get_character_object = renpy.store.get_character_object
@functools.cache
def get_faces():
d = _dict()
for charname in CHARACTERS:
for charname in renpy.store.states.dolls:
charobj = get_character_object(charname)
extensions = charobj.extensions
@ -43,7 +42,7 @@ init python in studio:
def get_choices():
d = {}
for i in CHARACTERS:
for i in renpy.store.states.dolls:
d[i] = {}
d[i]["eyebrows"] = faces[i].get("eyebrows", [None]).index("base")
d[i]["eyes"] = faces[i].get("eyes", [None]).index("base")
@ -80,7 +79,7 @@ init python in studio:
active_girl = renpy.store.states.active_girl
d = {}
for i in CHARACTERS:
for i in renpy.store.states.dolls:
d[i] = [drag_init(getattr(renpy.store, i)), (i == active_girl)]
return d