Files
cell/scripts/actor.js

202 lines
4.8 KiB
JavaScript

var actor = {};
var actor_urs = {};
var actor_spawns = {};
var script_fns = {};
function use(file)
{
var par = script_fn(file)
if (!par.module_ret)
throw new Error(`File ${file} has no valid module definition`)
return par.module_ret;
}
var script_fn = function script_fn(file) {
var content = Resources.replstrs(file);
var parsed = parse_file(content)
var module_name = file.name()
if (parsed.module) {
var mod_script = `(function setup_${module_name}_module(){ var self = this; ${parsed.module}})`;
var module_fn = os.eval(file, mod_script)
var module_ret = module_fn.call();
if (module_ret === undefined || module_ret === null)
throw new Error(`Module ${module_name} must return a value`);
parsed.module_fn = module_fn;
parsed.module_ret = module_ret;
}
if (parsed.program) {
var prog_script = `(function use_${module_name}() { var self = this; var $ = this.__proto__; ${parsed.program}})`;
parsed.prog_fn = os.eval(file, prog_script);
}
return fn;
}
function parse_file(content) {
var parts = content.split(/\n\s*---\s*\n/)
if (parts.length === 1)
return {
program: parts[0]
}
}
var module = parts[0].trim();
if (!moduleCode.match(/return\s+[^;]+;?\s*$/))
throw new Error("Module section must end with a return statement");
return {
module,
program: parts[1]
}
}
globalThis.actor_use = function actor_use(script)
{
var file = Resources.find_script(script);
if (!file)
return;
return use(file)
}
globalThis.class_use = function class_use(script, config, base, callback) {
var file = Resources.find_script(script);
if (!file) {
var ret = Object.create(base);
if (callback) callback(ret);
return ret;
}
var prog = script_fn(file);
var padawan;
if (prog.module_ret)
padawan = Object.create(prog.module_ret);
else
padawan = {};
if (callback) callback(padawan)
prog.prog_fn.call(padawan)
if (typeof config === 'object') Object.merge(padawan,config);
return padawan;
};
globalThis.rmactor = function (e) {
if (!actor_spawns[e._file]) return;
actor_spawns[e._file].remove(e);
};
actor.__stats = function () {
var total = 0;
var stats = {};
game.all_objects(obj => {
if (!actor_spawns[obj._file]) return;
stats[obj._file] ??= 0;
stats[obj._file]++;
total++;
});
/* for (var i in actor_spawns) {
stats[i] = actor_spawns[i].length;
total += stats[i];
}*/
// stats.total = total;
return stats;
};
actor.hotreload = function hotreload(file) {
var script = Resources.replstrs(file);
script = `(function() { var self = this; var $ = this.__proto__;${script};})`;
var fn = os.eval(file, script);
for (var obj of actor_spawns[file]) {
var a = obj;
a.timers.forEachRight(t=>t());
a.timers = [];
var save = json.decode(json.encode(a));
fn.call(a);
Object.merge(a, save);
check_registers(a);
}
};
actor.spawn = function (script, config) {
if (typeof script !== "string") return undefined;
var padawan = class_use(script, config, actor);
padawan.padawans = [];
padawan.timers = [];
padawan.master = this;
Object.hide(padawan, "master", "padawans");
padawan.toString = function () {
return script;
};
check_registers(padawan);
this.padawans.push(padawan);
if (padawan.awake) padawan.awake();
return padawan;
};
actor.tween = function (from, to, time, fn) {
var stop = tween(from, to, time, fn);
this.timers.push(stop);
return stop;
};
actor.spawn.doc = `Create a new actor, using this actor as the master, initializing it with 'script' and with data (as a JSON or Nota file) from 'config'.`;
actor.rm_pawn = function (pawn) {
this.padawans.remove(pawn);
};
actor.timers = [];
actor.kill = function () {
if (this.__dead__) return;
this.timers.forEach(t => t());
input.do_uncontrol(this);
Event.rm_obj(this);
if (this.master) this.master.rm_pawn(this);
this.padawans.forEach(p => p.kill());
this.padawans = [];
this.__dead__ = true;
actor_spawns[this._file].remove(this);
if (typeof this.die === "function") this.die();
if (typeof this.stop === "function") this.stop();
if (typeof this.garbage === "function") this.garbage();
if (typeof this.then === "function") this.then();
};
actor.kill.doc = `Remove this actor and all its padawans from existence.`;
actor.delay = function (fn, seconds) { prosperon.add_timer(this, fn, seconds); }
actor.delay.doc = `Call 'fn' after 'seconds' with 'this' set to the actor.`;
actor.interval = function (fn, seconds) {
var self = this;
var stop;
var usefn = function () {
fn();
stop = self.delay(usefn, seconds);
};
stop = self.delay(usefn, seconds);
return stop;
};
actor.padawans = [];
global.app = Object.create(actor);
app.die = function () {
os.exit(0);
};
return { actor, app };