feat: drag boxes, transforms as values, Matrix() colours and offline renpy.fetch in the player
screens: `drag` with draggable True moves with the pointer (the place is kept while the screen is shown); `at icon["at"]` picks a transform that Python code stored in a dict (transform names now evaluate to a reference); PreviewSlowText and Move(...) exist as values. Python: Matrix([16 numbers]) for matrixcolor, renpy.has_live2d() is false, renpy.fetch fails like a missing network (games catch it), cache.get_total_size and the texture helpers are no-ops. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
dcddd42aca
commit
353cd5e49e
4 files changed
+63
-4
No files matched your search
@@ -66,6 +66,11 @@ type Props = Record<string, any>;
|
||||
|
||||
class ActionAbort extends Error {}
|
||||
|
||||
/** A transform used as a value: `{"at": move_in}`; `at icon["at"]` then applies it. */
|
||||
export class TransformRef {
|
||||
constructor(public name: string) {}
|
||||
}
|
||||
|
||||
/** A screen action such as Jump("x"); run when the player activates a widget. */
|
||||
export class ScreenAction {
|
||||
/** Actions are callable in Ren'Py: `MainMenu(confirm=False)()` performs the action at once. */
|
||||
@@ -571,6 +576,7 @@ export class ScreenManager {
|
||||
private widget(node: any, ctx: Ctx): HTMLElement | null {
|
||||
switch (node.w) {
|
||||
case "fixed": return this.container(node, ctx, "div", "abs", ["fixed"], true);
|
||||
case "drag": return this.dragBox(node, ctx);
|
||||
case "frame":
|
||||
case "window": return this.container(node, ctx, "div", "stack", [node.w, "frame"]);
|
||||
case "vbox": return this.container(node, ctx, "div", "col", ["vbox"]);
|
||||
@@ -594,6 +600,31 @@ export class ScreenManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** A box the player can move with the pointer; where it was left is kept while the screen is shown. */
|
||||
private dragBox(node: any, ctx: Ctx): HTMLElement {
|
||||
const el = this.container(node, ctx, "div", "abs", ["fixed"]);
|
||||
const props = this.resolve(node, ctx, ["fixed"]);
|
||||
if (!truthy(this.pick(props, "draggable"))) return el;
|
||||
const key = `__drag:${pyStr(this.pick(props, "drag_name") ?? "")}`;
|
||||
const place = (ctx.inst.vars.get(key) ?? [0, 0]) as [number, number];
|
||||
el.style.translate = `${place[0]}px ${place[1]}px`;
|
||||
el.style.cursor = "grab";
|
||||
el.onpointerdown = (down) => {
|
||||
if ((down.target as HTMLElement).closest("button, [role=button], .pg-sbutton")) return;
|
||||
down.stopPropagation();
|
||||
el.setPointerCapture(down.pointerId);
|
||||
const start = [...place] as [number, number];
|
||||
el.onpointermove = (move) => {
|
||||
place[0] = start[0] + (move.clientX - down.clientX);
|
||||
place[1] = start[1] + (move.clientY - down.clientY);
|
||||
el.style.translate = `${place[0]}px ${place[1]}px`;
|
||||
ctx.inst.vars.set(key, [...place]);
|
||||
};
|
||||
el.onpointerup = () => { el.onpointermove = null; el.onpointerup = null; };
|
||||
};
|
||||
return el;
|
||||
}
|
||||
|
||||
private container(node: any, ctx: Ctx, tag: string, layout: Ctx["layout"], kinds: string[], fill = false): HTMLElement {
|
||||
const el = document.createElement(tag);
|
||||
const props = this.resolve(node, ctx, kinds);
|
||||
@@ -1345,7 +1376,12 @@ export class ScreenManager {
|
||||
|
||||
/** `at` transforms: static properties now, animated ones through the ATL runner. */
|
||||
private transforms(el: HTMLElement, props: Record<string, Expr>, ctx: Ctx) {
|
||||
const pick = (spec: AtSpec): AtSpec => (spec.choose ? pick(truthy(this.ev(spec.choose.c)) ? spec.choose.then : spec.choose.else) : spec);
|
||||
const named = (value: any): AtSpec => {
|
||||
const name = value instanceof TransformRef ? value.name : typeof value === "string" ? value : "";
|
||||
return name ? { name, atl: this.host.atl(name) ? name : undefined } : {};
|
||||
};
|
||||
const pick = (spec: AtSpec): AtSpec => (spec.choose ? pick(truthy(this.ev(spec.choose.c)) ? spec.choose.then : spec.choose.else)
|
||||
: spec.dyn ? named(this.ev(spec.dyn)) : spec);
|
||||
const specs: AtSpec[] = Array.isArray((props as any).at) ? (props as any).at.map(pick) : [];
|
||||
const live: Props = {};
|
||||
const apply = (p: Props) => {
|
||||
@@ -1714,6 +1750,9 @@ export class ScreenManager {
|
||||
out.Flatten = (child: any) => child; // flattening only changes how the browser composites, not what is drawn
|
||||
out.AlphaMask = shape("alphamask");
|
||||
out.TrackCursor = shape("trackcursor");
|
||||
out.PreviewSlowText = shape("text");
|
||||
out.Move = (...a: any[]) => ({ __transition: "move", args: a }); // `default vpunch = Move(...)`: the effect itself is chosen by `with`
|
||||
out.absolute = (x: any) => Number(x);
|
||||
out.At = (child: any) => child;
|
||||
Object.assign(out, colorMatrices);
|
||||
out.im = {
|
||||
|
||||
Reference in new issue
Block a user