diff --git a/game/scripts/characters/hermione/events/favors/dance_for_me.rpy b/game/scripts/characters/hermione/events/favors/dance_for_me.rpy index aeed9510..88130470 100644 --- a/game/scripts/characters/hermione/events/favors/dance_for_me.rpy +++ b/game/scripts/characters/hermione/events/favors/dance_for_me.rpy @@ -11,6 +11,7 @@ label start_hg_pf_strip: "\"(Yes, let's do it!)\"": pass "\"(Not right now.)\"": + $ _event.cancel() jump hermione_favor_menu $ current_payout = 35 diff --git a/game/scripts/characters/hermione/events/favors/give_me_a_handy.rpy b/game/scripts/characters/hermione/events/favors/give_me_a_handy.rpy index a631d04d..ffe50e21 100644 --- a/game/scripts/characters/hermione/events/favors/give_me_a_handy.rpy +++ b/game/scripts/characters/hermione/events/favors/give_me_a_handy.rpy @@ -11,6 +11,7 @@ label start_hg_pf_handjob: "\"(Yes, let's do it!)\"": pass "\"(Not right now.)\"": + $ _event.cancel() jump hermione_favor_menu $ current_payout = 45 diff --git a/game/scripts/characters/hermione/events/favors/give_me_a_tittyjob.rpy b/game/scripts/characters/hermione/events/favors/give_me_a_tittyjob.rpy index 2930c097..7a3c23b2 100644 --- a/game/scripts/characters/hermione/events/favors/give_me_a_tittyjob.rpy +++ b/game/scripts/characters/hermione/events/favors/give_me_a_tittyjob.rpy @@ -11,6 +11,7 @@ label start_hg_pf_titjob: "\"(Yes, let's do it!)\"": pass "\"(Not right now.)\"": + $ _event.cancel() jump hermione_favor_menu $ current_payout = 45 diff --git a/game/scripts/characters/hermione/events/favors/grope_her.rpy b/game/scripts/characters/hermione/events/favors/grope_her.rpy index 828dd4af..7d5eb19c 100644 --- a/game/scripts/characters/hermione/events/favors/grope_her.rpy +++ b/game/scripts/characters/hermione/events/favors/grope_her.rpy @@ -11,6 +11,7 @@ label start_hg_pf_grope: "\"(Yes, let's do it!)\"": pass "\"(Not right now.)\"": + $ _event.cancel() jump hermione_favor_menu $ current_payout = 15 @@ -98,15 +99,16 @@ label end_hg_pf_grope: # Those events still prgress, but Hermione will run off and get mad. # The heart icons for these events are 'black' -label hg_pf_grope_T1_fail_intro: - gen "[name_hermione_genie], would you mind if I play with your tits a little?" ("base", xpos="far_left", ypos="head") - her "Play with...?" ("shock", "wide", "base", "stare") - her "My tits?!" ("angry", "wide", "base", "mid") - gen "Or your butt! I haven't fully decided yet!" ("grin", xpos="far_left", ypos="head") +label hg_pf_grope_T1_fail: - jump too_much + if not _events_filtered_completed_any: + gen "[name_hermione_genie], would you mind if I play with your tits a little?" ("base", xpos="far_left", ypos="head") + her "Play with...?" ("shock", "wide", "base", "stare") + her "My tits?!" ("angry", "wide", "base", "mid") + gen "Or your butt! I haven't fully decided yet!" ("grin", xpos="far_left", ypos="head") + + jump too_much -label hg_pf_grope_T1_fail_repeat: gen "[name_hermione_genie], I'd like to grope you a little!" ("grin", xpos="far_left", ypos="head") her "This again...?" ("angry", "base", "angry", "mid") her "I've told you before, [name_genie_hermione], absolutely not!!" ("scream", "closed", "angry", "mid") diff --git a/game/scripts/characters/hermione/events/favors/lets_have_sex.rpy b/game/scripts/characters/hermione/events/favors/lets_have_sex.rpy index 1cbeab55..c2557862 100644 --- a/game/scripts/characters/hermione/events/favors/lets_have_sex.rpy +++ b/game/scripts/characters/hermione/events/favors/lets_have_sex.rpy @@ -11,6 +11,7 @@ label start_hg_pf_sex: "\"(Yes, let's do it!)\"": pass "\"(Not right now.)\"": + $ _event.cancel() jump hermione_favor_menu # Start Event diff --git a/game/scripts/characters/hermione/events/favors/show_me_your_panties.rpy b/game/scripts/characters/hermione/events/favors/show_me_your_panties.rpy index dcf15b5e..a88fbd03 100644 --- a/game/scripts/characters/hermione/events/favors/show_me_your_panties.rpy +++ b/game/scripts/characters/hermione/events/favors/show_me_your_panties.rpy @@ -13,6 +13,7 @@ label start_hg_pf_admire_panties: pass "\"(Not right now.)\"": + $ _event.cancel() jump hermione_favor_menu $ current_payout = 10 diff --git a/game/scripts/characters/hermione/events/favors/show_me_your_tits.rpy b/game/scripts/characters/hermione/events/favors/show_me_your_tits.rpy index afb8a9ad..32f3e18a 100644 --- a/game/scripts/characters/hermione/events/favors/show_me_your_tits.rpy +++ b/game/scripts/characters/hermione/events/favors/show_me_your_tits.rpy @@ -10,6 +10,7 @@ label start_hg_pf_admire_breasts: "\"(Yes, let's do it!)\"": pass "\"(Not right now.)\"": + $ _event.cancel() jump hermione_favor_menu $ current_payout = 10 diff --git a/game/scripts/characters/hermione/events/favors/suck_it.rpy b/game/scripts/characters/hermione/events/favors/suck_it.rpy index 35516cc2..d09d7f57 100644 --- a/game/scripts/characters/hermione/events/favors/suck_it.rpy +++ b/game/scripts/characters/hermione/events/favors/suck_it.rpy @@ -11,6 +11,7 @@ label start_hg_pf_blowjob: "\"(Yes, let's do it!)\"": pass "\"(Not right now.)\"": + $ _event.cancel() jump hermione_favor_menu # Start Event diff --git a/game/scripts/characters/hermione/events/favors/talk_to_me.rpy b/game/scripts/characters/hermione/events/favors/talk_to_me.rpy index 556a48ef..d5dac088 100644 --- a/game/scripts/characters/hermione/events/favors/talk_to_me.rpy +++ b/game/scripts/characters/hermione/events/favors/talk_to_me.rpy @@ -9,6 +9,7 @@ label start_hg_pf_talk: "\"(Yes, let's do it!)\"": pass "\"(Not right now.)\"": + $ _event.cancel() jump hermione_favor_menu $ current_payout = 5 diff --git a/game/scripts/characters/hermione/events/items/butt_plugs.rpy b/game/scripts/characters/hermione/events/items/butt_plugs.rpy index b08cd781..352e23d6 100644 --- a/game/scripts/characters/hermione/events/items/butt_plugs.rpy +++ b/game/scripts/characters/hermione/events/items/butt_plugs.rpy @@ -1,6 +1,6 @@ -default ev_her_small_plug = Event(id="her_small_plug", label="hg_butt_plugs_small_return", daytime=False) -default ev_her_medium_plug = Event(id="her_medium_plug", label="hg_butt_plugs_medium_return", daytime=False) -default ev_her_large_plug = Event(id="her_large_plug", label="hg_butt_plugs_large_return", daytime=False) +default ev_her_small_plug = Event(id="her_small_plug", label="hg_butt_plugs_small_return", req="game.daytime==False") +default ev_her_medium_plug = Event(id="her_medium_plug", label="hg_butt_plugs_medium_return", req="game.daytime==False") +default ev_her_large_plug = Event(id="her_large_plug", label="hg_butt_plugs_large_return", req="game.daytime==False") label hg_butt_plugs: diff --git a/game/scripts/characters/hermione/events/items/vibrators.rpy b/game/scripts/characters/hermione/events/items/vibrators.rpy index 81fe35c9..cfcba914 100644 --- a/game/scripts/characters/hermione/events/items/vibrators.rpy +++ b/game/scripts/characters/hermione/events/items/vibrators.rpy @@ -1,5 +1,5 @@ -default ev_her_vibrators_public_return = Event(id="her_vibrators_public_return", label="hg_vibrators_public_return", daytime=False) +default ev_her_vibrators_public_return = Event(id="her_vibrators_public_return", label="hg_vibrators_public_return", req="game.daytime==False") label hg_vibrators: diff --git a/game/scripts/characters/hermione/favors.rpy b/game/scripts/characters/hermione/favors.rpy index 181a9cce..3429f100 100644 --- a/game/scripts/characters/hermione/favors.rpy +++ b/game/scripts/characters/hermione/favors.rpy @@ -32,8 +32,7 @@ default her_ev_admire_breasts_t4_e1 = Event(id="admire_breasts_t4_e1", label="hg default her_ev_admire_breasts_t4_e2 = Event(id="admire_breasts_t4_e2", label="hg_pf_admire_breasts_T4_E1", priority=6, req="states.her.tier >= 4", queue="her_eventqueue_admire_breasts", autoenqueue=True, autodequeue=False, ignore_labels=["end_hg_pf_admire_breasts", "hermione_favor_menu", "hg_pf_admire_breasts_T4"]) default her_ev_admire_breasts_t4_e3 = Event(id="admire_breasts_t4_e3", label="hg_pf_admire_breasts_T4_E2", priority=7, req="states.her.tier >= 4", queue="her_eventqueue_admire_breasts", autoenqueue=True, autodequeue=False, ignore_labels=["end_hg_pf_admire_breasts", "hermione_favor_menu", "hg_pf_admire_breasts_T4"]) -default her_ev_grope_t1_e1 = Event(id="grope_t1_e1", label="hg_pf_grope_T1_fail_intro", priority=5, req="states.her.tier == 1", queue="her_eventqueue_grope", autoenqueue=True, autodequeue=False, repeat=False, ignore_labels=["hermione_favor_menu"]) -default her_ev_grope_t1_e2 = Event(id="grope_t1_e2", label="hg_pf_grope_T1_fail_repeat", priority=6, req="states.her.tier == 1", queue="her_eventqueue_grope", autoenqueue=True, autodequeue=False, ignore_labels=["hermione_favor_menu"]) +default her_ev_grope_t1_e1 = Event(id="grope_t1_e1", label="hg_pf_grope_T1_fail", priority=5, req="states.her.tier == 1", queue="her_eventqueue_grope", autoenqueue=True, autodequeue=False, repeat=False, ignore_labels=["hermione_favor_menu"]) default her_ev_grope_t2_e1 = Event(id="grope_t2_e1", label="hg_pf_grope_T2_intro_E1", priority=5, req="states.her.tier == 2", queue="her_eventqueue_grope", autoenqueue=True, autodequeue=False, repeat=False, ignore_labels=["hermione_favor_menu", "hg_pf_grope_breasts_T2", "hg_pf_grope_ass_T2", "hg_pf_grope_ass_T2_back", "hg_pf_grope_ass_T2_front", "hg_pf_grope_ass_T2_continue", "hg_pf_grope_breasts_T2_continue"]) default her_ev_grope_t2_e2 = Event(id="grope_t2_e2", label="hg_pf_grope_T2_E1", priority=6, req="states.her.tier == 2", queue="her_eventqueue_grope", autoenqueue=True, autodequeue=False, ignore_labels=["hermione_favor_menu", "hg_pf_grope_breasts_T2", "hg_pf_grope_ass_T2", "hg_pf_grope_ass_T2_back", "hg_pf_grope_ass_T2_front", "hg_pf_grope_ass_T2_continue", "hg_pf_grope_breasts_T2_continue"]) default her_ev_grope_t3_e1 = Event(id="grope_t3_e1", label="hg_pf_grope_T3_intro_E1", priority=5, req="states.her.tier == 3", queue="her_eventqueue_grope", autoenqueue=True, autodequeue=False, repeat=False, ignore_labels=["hermione_favor_menu", "hg_pf_grope_breasts_T3", "hg_pf_grope_ass_T3", "hg_pf_grope_ass_T3_back", "hg_pf_grope_ass_T3_front", "hg_pf_grope_ass_T3_continue", "hg_pf_grope_breasts_T3_continue"]) diff --git a/game/scripts/events/queue.rpy b/game/scripts/events/queue.rpy index cd3803b9..a436f539 100644 --- a/game/scripts/events/queue.rpy +++ b/game/scripts/events/queue.rpy @@ -7,17 +7,23 @@ default _events_filtered_completed_all = None default _events_filtered_completed_any = None default _event_completed = None default _event_completed_failed = None +default _event_queue = None +default _event = None init -1 python: class EventQueue(object): + ignore_globals = ("mainloop",) + def __init__(self, id=None): self.id = id self.queue = [] self.freeze = False - self.counter = 0 + if config.developer: + rollback = f"{stdcol.UNDERLINE}(Rollback){stdcol.END} " if renpy.in_rollback() else "" + print(f"{rollback}Creating EventQueue object '{stdcol.BLUE}{id}{stdcol.END}' ...") def freeze(self): self.freeze = True @@ -37,77 +43,57 @@ init -1 python: for i in self.queue: i.wait -= 1 - def list_applicable(self): - queue = [ev for ev in self.queue if (ev.wait <= 0) and ( ev.daytime is None or (ev.daytime == game.daytime) ) and not ev.disabled and ev.requirements_met() ] - queue.sort(key=lambda x: x.priority) + def list_filtered(self, func=lambda ev: (ev.wait <= 0) and not ev.disabled and ev.requirements_met()): + # queue = [ev for ev in self.queue if (ev.wait <= 0) and not ev.disabled and ev.requirements_met() ] + # queue.sort(key=lambda x: x.priority) - return queue + filtered = sorted(filter(func, self.queue), key=lambda x: x.priority) - def start(self, allow_repeat=True): - global _events_completed_all, _events_completed_any, _events_filtered_completed_all, _events_filtered_completed_any + return filtered + + def initialize_globals(self): + global _event_queue, _events_completed_all, _events_completed_any, _events_filtered_completed_all, _events_filtered_completed_any + + _event_queue = self queue = self.queue _events_completed_all = all(ev.completed for ev in queue) _events_completed_any = any(ev.completed for ev in queue) - queue = self.list_applicable() + queue = self.list_filtered() _events_filtered_completed_all = all(ev.completed for ev in queue) _events_filtered_completed_any = any(ev.completed for ev in queue) - self.counter += 1 - - for ev in queue: - if not ev.repeat and ev.completed: - continue - - if config.developer: - print(f"Starting event id '{ev.id}' ... ") - - return ev.start() - - if queue and allow_repeat: - return renpy.random.choice(queue).start() - - def is_in_queue(self, ev): - if isinstance(ev, str): - return any(i.id == ev for i in self.queue) - return ev in self.queue - - # def menu(self, title, **kwargs): - # hints = self.menu_hints() - # title += hints - - # return renpy.display_menu( [ (title, self) ], **kwargs) - - def menu_hints(self): - queue = self.list_applicable() - total_applicable = len(queue) - total_events = len(self.queue) - completed = 0 - hints = [] + def next(self): + queue = self.list_filtered() + repeatable = [] for ev in queue: if ev.completed: - hints.append("v") - completed += 1 - elif ev.completed_failed: - hints.append("x") - completed += 1 - else: - hints.append("o") + if ev.repeat: + repeatable.append(ev) + + continue - s = "(" + "".join(hints) + f") {completed}/{total_applicable}" + return ev - if config.developer: - s += f"/{total_events}" - - return s + if repeatable: + return renpy.random.choice(repeatable) - def _debug(self): + def start(self): + if not self.id in self.ignore_globals: + self.initialize_globals() + + ev = self.next() + + if ev: + return ev.start(self) + + def _debug(self, sort=lambda x: x.priority): queue = self.queue - queue.sort(key=lambda x: x.priority) + queue.sort(key=sort) for ev in queue: s = f"ID:{ev.id} QUE:{ev.queued} STA:{ev.started} COM:{ev.completed} COMF:{ev.completed_failed} DIS:{ev.disabled}" @@ -133,7 +119,8 @@ init -1 python: class Event(object): _queue = None - def __init__(self, id, wait=0, priority=5, daytime=None, req=None, label=None, func=None, queue="eventqueue", autoenqueue=False, autodequeue=True, repeat=True, fail_suffixes=("_fail", "too_much", "too_much_public"), ignore_labels=[]): + def __init__(self, id, wait=0, priority=5, daytime=None, req=None, label=None, func=None, queue="eventqueue", autoenqueue=False, autodequeue=True, + repeat=True, fail_suffixes=("_fail", "too_much", "too_much_public"), ignore_labels=[], subevents=[]): self.id = id self.wait = wait self.priority = priority @@ -147,6 +134,7 @@ init -1 python: self.repeat = repeat self.fail_suffixes = tuple(fail_suffixes) self.ignore_labels = ignore_labels + self.subevents = subevents self.queued = False self.started = False @@ -160,6 +148,19 @@ init -1 python: if autoenqueue: self.enqueue() + if config.developer: + def __setattr__(self, attr, value): + if hasattr(self, attr) and getattr(self, attr) != value: + id = getattr(self, "id") + rollback = f"{stdcol.UNDERLINE}(Rollback){stdcol.END} " if renpy.in_rollback() else "" + caller_id = renpy.get_filename_line() + + if not "/00start.rpy" in caller_id[0]: + # Ignore init + print(f"{rollback}Setting '{stdcol.GREEN}{id}{stdcol.END}' attribute '{stdcol.RED}{attr}{stdcol.END}' to '{stdcol.YELLOW}{value}{stdcol.END}' caller '{stdcol.BLUE}{caller_id}{stdcol.END}'...") + + super().__setattr__(attr, value) + def disable(self): self.disabled = True @@ -175,32 +176,48 @@ init -1 python: self._queue = name if name is not None and not hasattr(store, name): - if config.developer: - print(f"Creating EventQueue object named '{name}' ...") - setattr(store, name, EventQueue(name)) - def enqueue(self): - self.queued = True + def enqueue(self, queue=None): + if queue: + queue = getattr(store, queue).queue + else: + queue = self.queue - if not self in self.queue: - self.queue.append(self) + if not self in queue: + if config.developer: + rollback = f"{stdcol.UNDERLINE}(Rollback){stdcol.END} " if renpy.in_rollback() else "" + name = getattr(store, self._queue).id + caller_id = renpy.get_filename_line() + + if not "/00start.rpy" in caller_id[0]: + print(f"{rollback}Enqueueing '{stdcol.GREEN}{self.id}{stdcol.END}' into '{stdcol.BLUE}{name}{stdcol.END}' ...") + + queue.append(self) + self.queued = True def requirements_met(self): if self.req: return eval(self.req) return True - def start(self): - global _event_completed, _event_completed_failed + def start(self, _caller=None): + global _event, _event_completed, _event_completed_failed + + if config.developer: + rollback = f"{stdcol.UNDERLINE}(Rollback){stdcol.END} " if renpy.in_rollback() else "" + caller_id = _caller.id if _caller else renpy.get_filename_line() + print(f"{rollback}Starting '{stdcol.GREEN}{self.id}{stdcol.END}' caller '{stdcol.BLUE}{caller_id}{stdcol.END}' ... ") self.started = True + _event = self _event_completed = self.completed _event_completed_failed = self.completed_failed - if self.autodequeue and self in self.queue: - self.queue.remove(self) + if self.autodequeue: + queue = _caller.queue or self.queue + queue.remove(self) self.queued = False ## additional requirements check @@ -214,6 +231,16 @@ init -1 python: if self.label: return renpy.jump(self.label) + def cancel(self, _caller=None): + if config.developer: + rollback = f"{stdcol.UNDERLINE}(Rollback){stdcol.END} " if renpy.in_rollback() else "" + caller_id = _caller.id if _caller else renpy.get_filename_line() + print(f"{rollback}Cancelling '{stdcol.GREEN}{self.id}{stdcol.END}' caller '{stdcol.BLUE}{caller_id}{stdcol.END}'... ") + self.started = False + + if self._track_completion in event_callbacks: + event_callbacks.remove(self._track_completion) + def _track_completion(self, label, abnormal): if renpy.is_init_phase(): return @@ -237,14 +264,25 @@ init -1 python: # If return stack exists, ignore, because we're probably in a call label. return - if label.endswith(self.fail_suffixes): - self.completed_failed = True - else: - self.completed = True + if self.started: # Event cancelled abnormally? + + if label.endswith(self.fail_suffixes): + self.completed_failed = True + else: + self.completed = True + + self.started = False + + for ev in self.subevents: + if isinstance(ev, str): + ev = getattr(store, ev) + ev.enqueue() if self._track_completion in event_callbacks: event_callbacks.remove(self._track_completion) + + # def catch_label_call(label, args, kwargs): # if config.developer: # print(f"Called '{label}' with ARGS: {args} KWARGS: {kwargs}") @@ -258,7 +296,7 @@ init -1 python: for callback in event_callbacks: callback(label, abnormal) - def initialize_callbacks(): + def initialize_event_callbacks(): if renpy.in_rollback(): return @@ -266,25 +304,46 @@ init -1 python: renpy.config.label_callbacks.append(execute_event_callbacks) # renpy.config.call_callbacks.append(catch_label_call) - def show_events_menu(queues, **kwargs): + def show_events_menu(queues, filter=False, **kwargs): + def menu_hints(queue, filter): + filtered_queue = queue.list_filtered() if filter is False else queue.list_filtered(filter) + total_applicable = len(filtered_queue) + total_events = len(queue.queue) + completed = 0 + hints = [] + + for ev in filtered_queue: + if ev.completed: + hints.append("v") + completed += 1 + elif ev.completed_failed: + hints.append("x") + completed += 1 + else: + hints.append("o") + + s = "(" + "".join(hints) + f") {completed}/{total_applicable}" + + if config.developer: + s += f"/{total_events}" + + return s + l = [] for queue in queues: if isinstance(queue, str): queue = getattr(store, queue) - if not queue.list_applicable(): - continue - - title = f"-{queue.id}- {queue.menu_hints()}" + title = f"-{queue.id}- {menu_hints(queue, filter)}" l.append( (title, queue) ) return renpy.display_menu(l, **kwargs) - config.after_default_callbacks.append(initialize_callbacks) + config.after_default_callbacks.append(initialize_event_callbacks) init offset = -5 -default eventqueue = EventQueue() +default eventqueue = EventQueue("mainloop") # default _last_label_call = None \ No newline at end of file diff --git a/game/scripts/utility/devtools.rpy b/game/scripts/utility/devtools.rpy index 7d12d970..2f96aaa6 100644 --- a/game/scripts/utility/devtools.rpy +++ b/game/scripts/utility/devtools.rpy @@ -34,6 +34,31 @@ init -999 python early: detect_orphaned_rpyc_files() + + # class InstanceDebugger(object): + + # def __init__(self, obj): + # object.__setattr__(self, 'instance', obj) + + # def __get__(self, obj, type=None): + # return functools.partial(self, obj) + + # def __call__(self, *args, **kwargs): + # return self.instance(*args,**kwargs) + + # def __getattr__(self, attr): + # attr = getattr(self.instance, attr) + + # return self.__class__(attr) if hasattr(attr, '__dict__') else attr + + # def __setattr__(self, attr, value): + # instance = self.instance + + # if getattr(instance, attr, None) != value: + # print(f"'{self.instance.id}' setting attribute '{attr}' to '{value}' ...") + + # instance.attr = value + init python: config.missing_image_callback = missing_image_func @@ -43,7 +68,7 @@ init python: config.missing_label_callback = missing_label_func config.return_not_found_label = "missing_return" -init -1 python: +init -2 python: def missing_image_func(path): global systemerror @@ -100,6 +125,18 @@ init -1 python: print("\rCalculating whitespace... Done!") return False + class stdcol: + PURPLE = '\033[1;35;48m' + CYAN = '\033[1;36;48m' + BOLD = '\033[1;37;48m' + BLUE = '\033[1;34;48m' + GREEN = '\033[1;32;48m' + YELLOW = '\033[1;33;48m' + RED = '\033[1;31;48m' + BLACK = '\033[1;30;48m' + UNDERLINE = '\033[4;37;48m' + END = '\033[1;37;0m' + label missing_label(): $ renpy.choice_for_skipping() $ err_msg1 = systemerror[0]