2024-03-26 23:26:14 +00:00
|
|
|
python early hide:
|
|
|
|
import inspect
|
2024-04-01 20:05:24 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
from renpy.lint import python_builtins, renpy_builtins
|
|
|
|
except ImportError:
|
|
|
|
import builtins
|
|
|
|
python_builtins = set(dir(builtins))
|
|
|
|
del builtins
|
|
|
|
renpy_builtins = set()
|
|
|
|
|
2024-03-26 23:26:14 +00:00
|
|
|
__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):
|
2024-03-28 21:27:10 +00:00
|
|
|
raise TypeError("Returning an instance of the class is disabled")
|
2024-03-26 23:26:14 +00:00
|
|
|
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
|
|
|
|
|
2024-03-26 23:26:23 +00:00
|
|
|
@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)
|
|
|
|
|
2024-04-01 20:05:24 +00:00
|
|
|
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))}")
|
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
@register_decorator
|
|
|
|
class chibi:
|
|
|
|
@staticmethod
|
|
|
|
def parse(l):
|
|
|
|
who = l.simple_expression()
|
|
|
|
action = l.simple_expression()
|
2023-02-21 02:53:16 +00:00
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
return (who, action)
|
2023-02-21 02:53:16 +00:00
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
def execute(self):
|
|
|
|
print(f"{self}")
|
|
|
|
who, action = self
|
|
|
|
func = eval(f"{who}_chibi.{action}")
|
2023-02-21 02:53:16 +00:00
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
# print(f"Execution: {who} {action}")
|
2023-02-21 02:53:16 +00:00
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
def lint(self):
|
|
|
|
who, action = self
|
|
|
|
try:
|
|
|
|
chibi = eval(f"{who}_chibi")
|
|
|
|
except Exception:
|
|
|
|
renpy.error(f"Character chibi not defined: {who}")
|
2023-02-21 02:53:16 +00:00
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
def predict(self):
|
|
|
|
who, action = self
|
2023-02-21 02:53:16 +00:00
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
chibi = eval(f"{who}_chibi")
|
|
|
|
doll = eval(f"{who}")
|
2023-02-21 02:53:16 +00:00
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
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()
|
|
|
|
)
|
2023-02-21 02:53:16 +00:00
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
return layers
|
2023-02-21 02:53:16 +00:00
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
@register_decorator
|
|
|
|
class random:
|
|
|
|
block = True
|
|
|
|
predict_all = True
|
2023-03-10 23:09:32 +00:00
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
@staticmethod
|
|
|
|
def parse(l):
|
|
|
|
l.require(":")
|
|
|
|
l.expect_eol()
|
2023-03-10 23:09:32 +00:00
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
ll = l.subblock_lexer()
|
|
|
|
blocks = []
|
2023-03-10 23:09:32 +00:00
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
while ll.advance():
|
|
|
|
with ll.catch_error():
|
|
|
|
weight = 1.0
|
|
|
|
condition = "True"
|
2023-03-10 23:09:32 +00:00
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
if ll.keyword("block"):
|
|
|
|
ll.expect_block("block")
|
2023-03-13 20:29:05 +00:00
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
block = ll.subblock_lexer().renpy_block()
|
2023-03-13 20:29:05 +00:00
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
if ll.keyword("weight"):
|
|
|
|
weight = float(ll.require(ll.float))
|
2023-03-13 20:29:05 +00:00
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
if ll.keyword("if"):
|
|
|
|
ll.expect_block("if block")
|
|
|
|
condition = ll.require(ll.python_expression)
|
|
|
|
else:
|
|
|
|
block = ll.renpy_statement()
|
2023-03-10 23:09:32 +00:00
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
blocks.append((block, weight, condition))
|
2023-03-13 20:29:05 +00:00
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
return {"blocks": blocks}
|
2023-03-13 20:29:05 +00:00
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
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
|
2023-03-10 23:09:32 +00:00
|
|
|
|
2024-03-28 21:28:53 +00:00
|
|
|
for block, weight in blocks:
|
|
|
|
if n <= weight:
|
2024-08-09 14:49:21 +00:00
|
|
|
return block
|
|
|
|
n -= weight
|
2023-03-10 23:09:32 +00:00
|
|
|
|
2024-08-09 14:49:21 +00:00
|
|
|
# In the rare event where weights are incorrectly assigned,
|
|
|
|
# return a random available block, and in any other case None.
|
|
|
|
return renpy.random.choice(blocks)[0] if blocks else None
|