WTS/game/scripts/01cds.rpy

186 lines
6.0 KiB
Plaintext

python early hide:
import inspect
try:
from renpy.lint import python_builtins, renpy_builtins
except ImportError:
import builtins
python_builtins = set(dir(builtins))
del builtins
renpy_builtins = set()
__register_params = frozenset(inspect.signature(renpy.register_statement).parameters).difference({"name", "parse"})
def register_decorator(cls):
"""
A class decorator which registers a new statement.
The name of the statement will be the class name unless a `name` class attribute is present, which should be a string.
The `parse` method should be a static method that returns an object which will be passed to the other methods as `self`.
Returning an instance of the class is disabled for now.
"""
# security
def raiser(*args, **kwargs):
raise TypeError("Returning an instance of the class is disabled")
cls.__init__ = raiser
name = getattr(cls, "name", cls.__name__)
parse = getattr(cls, "parse", cls) # left in
renpy.register_statement(name,
parse=parse,
**{k:getattr(cls, k) for k in __register_params.intersection(vars(cls))})
return cls
@register_decorator
class dynamic:
block = "possible"
@staticmethod
def parse(l):
rv = {}
def parse_simple(ll):
target = ll.require(ll.name, "variable name")
if target in rv:
ll.error(f"Variable {target} already set in the same dynamic block")
ll.require("=", "equals sign")
expression = ll.simple_expression()
ll.expect_eol()
rv[target] = expression
if l.match(":"):
l.expect_block("dynamic block")
l.expect_eol()
ll = l.subblock_lexer()
while ll.advance():
parse_simple(ll)
else:
parse_simple(l)
return rv
def execute(self):
evaled = {n: eval(e) for n, e in self.items()}
renpy.dynamic(**evaled)
def lint(self):
for domain, st in (("Python", python_builtins), ("Ren'Py", renpy_builtins)):
inter = st.intersection(self)
if inter:
renpy.error(f"Dynamic statement redefines one or several {domain} builtins: {', '.join(map(repr, inter))}")
@register_decorator
class chibi:
@staticmethod
def parse(l):
who = l.require(l.name)
action = l.require(l.name)
arguments = l.arguments() # may be None
l.expect_eol()
return (who, action, arguments)
def execute(self):
print(self)
who, action, arguments = self
chibi = DollChibi.instances[who]
method = getattr(chibi, action)
if arguments is None:
args, kwargs = (), {}
else:
args, kwargs = arguments.evaluate()
method(*args, **kwargs)
# print(f"Execution: {who} {action}")
def lint(self):
who, action, arguments = self
if who not in DollChibi.instances:
renpy.error(f"Character chibi not defined: {who}")
chibi = DollChibi.instances[who]
if not hasattr(chibi, action):
renpy.error(f"Chibi action not defined: {who} {action}")
def predict(self):
who, action, arguments = self
chibi = DollChibi.instances[who]
doll = states.dolls[who]
layers = (
l[0] for pose in chibi.poses.keys()
for k in doll.states.values() if k[0] and k[2]
for l in k[0].get_layers(k[0]._hash, subpath=posixpath.join("chibi", pose)).values()
)
return layers
@register_decorator
class random:
block = True
predict_all = True
@staticmethod
def parse(l):
l.require(":")
l.expect_eol()
ll = l.subblock_lexer()
blocks = []
while ll.advance():
with ll.catch_error():
weight = 1.0
condition = "True"
if ll.keyword("block"):
ll.expect_block("block")
block = ll.subblock_lexer().renpy_block()
if ll.keyword("weight"):
weight = float(ll.require(ll.float))
if ll.keyword("if"):
ll.expect_block("if block")
condition = ll.require(ll.python_expression)
else:
block = ll.renpy_statement()
blocks.append((block, weight, condition))
return {"blocks": blocks}
def lint(self):
any_true = False
for block, weight, condition in self["blocks"]:
if not isinstance(weight, (int, float)):
renpy.error(f"Weight must be a number, not {weight!r}")
if condition == "True":
any_true = True
else:
try:
eval(condition)
except Exception:
renpy.error(f"Condition could not be evaluated: {condition!r}")
if not any_true:
renpy.error("All blocks have a condition, which will raise an exception if all conditions are False at the same time at runtime")
def next(self):
blocks = [(block, weight) for block, weight, condition in self["blocks"] if eval(condition)]
total_weight = sum(weight for _, weight in blocks)
n = renpy.random.random() * total_weight
for block, weight in blocks:
if n <= weight:
break
else:
n -= weight
return block