WTS/game/scripts/events/queue.rpy

274 lines
8.1 KiB
Plaintext

default event_callbacks = []
init -1 python:
class EventQueue(object):
def __init__(self, id=None):
self.id = id
self.queue = []
self.freeze = False
def freeze(self):
self.freeze = True
def unfreeze(self):
self.freeze = False
def delay(self, n):
for i in self.queue:
i.wait += n
def tick(self):
"""Causes time to pass."""
if self.freeze:
return
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)
return queue
def start(self, allow_repeat=True):
global _ev_queue_completed, _ev_queue_any_completed
queue = self.list_applicable()
_ev_queue_completed = all(ev.completed for ev in queue)
_ev_queue_any_completed = any(ev.completed for ev in queue)
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 = []
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")
s = "(" + "".join(hints) + f") {completed}/{total_applicable}"
if config.developer:
s += f"/{total_events}"
return s
def _debug(self):
queue = self.queue
queue.sort(key=lambda x: x.priority)
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}"
print(s)
def _debug_menu(self):
queue = self.queue
queue.sort(key=lambda x: x.priority)
menu = []
for ev in queue:
req = f"{{color=#00ff00}}{ev.req}{{/color}}" if ev.requirements_met() else f"{{color=#ff0000}}{ev.req}{{/color}}"
s = f"{{size=12}}id:{ev.id} label:{ev.label}{{/size}}{{size=8}}\npriority:{ev.priority} Daytime:{ev.daytime} Req:{req} AutoENQ:{ev.autoenqueue} AutoDEQ:{ev.autodequeue}{{/size}}"
l = ev.label
menu.append( (s, l) )
renpy.display_menu(menu)
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=[]):
self.id = id
self.wait = wait
self.priority = priority
self.daytime = daytime
self.req = req
self.label = label
self.func = func
self.queue = queue
self.autoenqueue = autoenqueue
self.autodequeue = autodequeue
self.repeat = repeat
self.fail_suffixes = tuple(fail_suffixes)
self.ignore_labels = ignore_labels
self.queued = False
self.started = False
self.completed = False
self.completed_failed = False
self.disabled = False
if not renpy.has_label(self.label):
raise Exception("Supplied label does not exist.")
if autoenqueue:
self.enqueue()
def disable(self):
self.disabled = True
def enable(self):
self.disabled = False
@property
def queue(self):
return getattr(store, self._queue).queue if self._queue else None
@queue.setter
def queue(self, name):
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
if not self in self.queue:
self.queue.append(self)
def requirements_met(self):
if self.req:
return eval(self.req)
return True
def start(self):
global _ev_completed, _ev_completed_failed
self.started = True
_ev_completed = self.completed
_ev_completed_failed = self.completed_failed
if self.autodequeue and self in self.queue:
self.queue.remove(self)
self.queued = False
## additional requirements check
if not self._track_completion in event_callbacks:
event_callbacks.append(self._track_completion)
if self.func:
self.func()
if self.label:
return renpy.jump(self.label)
def _track_completion(self, label, abnormal):
if renpy.is_init_phase():
return
if label == self.label:
# Ignore jumps to self.
return
if label in self.ignore_labels:
# Postpone completing the event until end label returns
return
# if _last_label_call == label:
# # Ignore calls.
# return
# if abnormal:
# return
if renpy.game.context().return_stack:
# 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._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}")
# global _last_label_call
# _last_label_call = label
def execute_event_callbacks(label, abnormal):
if renpy.is_init_phase() or not hasattr(store, "event_callbacks"):
return
for callback in event_callbacks: callback(label, abnormal)
def initialize_callbacks():
if renpy.in_rollback():
return
# We need to add these after defaults are finished.
renpy.config.label_callbacks.append(execute_event_callbacks)
# renpy.config.call_callbacks.append(catch_label_call)
def show_events_menu(queues, **kwargs):
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()}"
l.append( (title, queue) )
return renpy.display_menu(l, **kwargs)
config.after_default_callbacks.append(initialize_callbacks)
init offset = -5
default eventqueue = EventQueue()
# default _last_label_call = None