feat: mousearea, datetime dates, dir(), renpy.image_size and style.rebuild for Ren'Py games
Screens get an invisible mousearea that reports the pointer entering and leaving without taking clicks. datetime.date/datetime/timedelta support construction and date arithmetic. dir() lists the game's variables, renpy.style.rebuild() is a no-op, and renpy.image_size answers only for pictures the browser already holds (otherwise an IOError the game can catch). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6c10d06d90
commit
4dc9836a8b
7 files changed
+119
-4
No files matched your search
@@ -123,7 +123,58 @@ function strftime(format: string, d: Date): string {
|
||||
}
|
||||
|
||||
const now = () => new DateTime(new Date());
|
||||
const DAY_MS = 86_400_000;
|
||||
|
||||
/** `datetime.timedelta`: kept in milliseconds. */
|
||||
class TimeDelta {
|
||||
constructor(public ms: number) {}
|
||||
get days() { return Math.floor(this.ms / DAY_MS); }
|
||||
get seconds() { return Math.floor((this.ms - this.days * DAY_MS) / 1000); }
|
||||
total_seconds() { return this.ms / 1000; }
|
||||
__binop__(op: string, other: any) {
|
||||
if (op === "+" && other instanceof TimeDelta) return new TimeDelta(this.ms + other.ms);
|
||||
if (op === "-" && other instanceof TimeDelta) return new TimeDelta(this.ms - other.ms);
|
||||
if (op === "*" && typeof other === "number") return new TimeDelta(this.ms * other);
|
||||
throw new TypeError(`timedelta ${op}`);
|
||||
}
|
||||
}
|
||||
|
||||
function timedelta(...a: any[]) {
|
||||
const last = a[a.length - 1];
|
||||
const kw = last && typeof last === "object" && last.__kw ? last.__kw : {};
|
||||
const unit: Record<string, number> = { days: DAY_MS, seconds: 1000, minutes: 60_000, hours: 3_600_000,
|
||||
weeks: 7 * DAY_MS, milliseconds: 1 };
|
||||
let ms = typeof a[0] === "number" ? a[0] * DAY_MS : 0;
|
||||
for (const [name, factor] of Object.entries(unit)) if (typeof kw[name] === "number") ms += kw[name] * factor;
|
||||
return new TimeDelta(ms);
|
||||
}
|
||||
|
||||
/** `date(y, m, d)` and `datetime(y, m, d, ...)`: a DateTime at a given moment, with date arithmetic. */
|
||||
const fromParts = (...a: any[]) => {
|
||||
const [y, m, d, h = 0, min = 0, s = 0] = a.filter((x) => typeof x === "number");
|
||||
const date = new Date(y, m - 1, d, h, min, s);
|
||||
if (date.getMonth() !== m - 1 || date.getDate() !== d) throw new Error("ValueError: day is out of range for month");
|
||||
return new DateTime(date);
|
||||
};
|
||||
const datetimeClass = { now, today: now, utcnow: now };
|
||||
const dateClass = Object.assign(fromParts, datetimeClass);
|
||||
Object.assign(DateTime.prototype, {
|
||||
__binop__(this: DateTime, op: string, other: any) {
|
||||
if (other instanceof TimeDelta && (op === "+" || op === "-")) {
|
||||
const moved = new Date(this.d.getTime());
|
||||
moved.setTime(moved.getTime() + (op === "+" ? other.ms : -other.ms));
|
||||
// whole days keep the wall-clock time across daylight-saving changes
|
||||
if (other.ms % DAY_MS === 0) {
|
||||
const shifted = new Date(this.d.getTime());
|
||||
shifted.setDate(shifted.getDate() + (op === "+" ? 1 : -1) * (other.ms / DAY_MS));
|
||||
return new DateTime(shifted);
|
||||
}
|
||||
return new DateTime(moved);
|
||||
}
|
||||
if (other instanceof DateTime && op === "-") return new TimeDelta(this.d.getTime() - other.d.getTime());
|
||||
throw new TypeError(`datetime ${op}`);
|
||||
},
|
||||
});
|
||||
|
||||
/** Namespaces added to the sandbox's builtins. */
|
||||
export function pythonModules(): Record<string, any> {
|
||||
@@ -172,7 +223,7 @@ export function pythonModules(): Record<string, any> {
|
||||
sleep: () => null,
|
||||
strftime: (format: string, t?: any) => strftime(format, t instanceof DateTime ? t.d : new Date()),
|
||||
},
|
||||
datetime: Object.assign({ datetime: datetimeClass, date: datetimeClass }, datetimeClass),
|
||||
datetime: Object.assign({ datetime: dateClass, date: dateClass, timedelta }, datetimeClass),
|
||||
collections: {
|
||||
defaultdict: (factory?: any) => new PyDefaultDict(typeof factory === "function" ? () => factory() : null),
|
||||
OrderedDict: () => new PyDict(),
|
||||
|
||||
Reference in new issue
Block a user