786 lines
18 KiB
JavaScript
786 lines
18 KiB
JavaScript
globalThis.prosperon = {}
|
||
var io = os.use('io')
|
||
|
||
os.mem_limit.doc = "Set the memory limit of the runtime in bytes.";
|
||
os.gc_threshold.doc = "Set the threshold before a GC pass is triggered in bytes. This is set to malloc_size + malloc_size>>1 after a GC pass.";
|
||
os.max_stacksize.doc = "Set the max stack size in bytes.";
|
||
|
||
globalThis.Resources = {};
|
||
|
||
prosperon.SIGINT = function() {
|
||
os.exit();
|
||
}
|
||
|
||
Resources.rm_fn = function rm_fn(fn, text) {
|
||
var reg = new RegExp(fn.source + "\\s*\\(");
|
||
var match;
|
||
while ((match = text.match(reg))) {
|
||
var last = match.index + match[0].length;
|
||
var par = 1;
|
||
while (par !== 0) {
|
||
if (text[last] === "(") par++;
|
||
if (text[last] === ")") par--;
|
||
last++;
|
||
}
|
||
text = text.rm(match.index, last);
|
||
}
|
||
|
||
return text;
|
||
};
|
||
Resources.rm_fn.doc = "Remove calls to a given function from a given text script.";
|
||
|
||
// Normalizes paths for use in prosperon
|
||
Resources.replpath = function replpath(str, path) {
|
||
if (!str) return str;
|
||
if (str[0] === "/") return str.rm(0);
|
||
|
||
if (!path) return str;
|
||
|
||
var stem = path.dir();
|
||
while (stem) {
|
||
var tr = stem + "/" + str;
|
||
if (io.exists(tr)) return tr;
|
||
stem = stem.updir();
|
||
}
|
||
return str;
|
||
};
|
||
|
||
// Given a script path, loads it, and replaces certain function calls to conform to environment
|
||
Resources.replstrs = function replstrs(path) {
|
||
if (!path) return;
|
||
var script = io.slurp(path);
|
||
if (!script) return;
|
||
var regexp = /"[^"\s]*?\.[^"\s]+?"/g;
|
||
|
||
var stem = path.dir();
|
||
|
||
// if (!console.enabled) script = Resources.rm_fn(/console\.(spam|info|warn|error)/, script);
|
||
/* if (!profile.enabled) script = Resources.rm_fn(/profile\.(cache|frame|endcache|endframe)/, script);
|
||
|
||
if (!debug.enabled) {
|
||
script = Resources.rm_fn(/assert/, script);
|
||
script = Resources.rm_fn(/debug\.(build|fn_break)/, script);
|
||
}
|
||
*/
|
||
script = script.replace(regexp, function (str) {
|
||
var newstr = Resources.replpath(os.trimchr(str,'"'), path);
|
||
return `"${newstr}"`;
|
||
});
|
||
|
||
return script;
|
||
}
|
||
|
||
Resources.is_sound = function (path) {
|
||
var ext = path.ext();
|
||
return Resources.sounds.any(x => x === ext);
|
||
};
|
||
|
||
Resources.is_animation = function (path) {
|
||
if (path.ext() === "gif" && Resources.gif.frames(path) > 1) return true;
|
||
if (path.ext() === "ase") return true;
|
||
|
||
return false;
|
||
};
|
||
|
||
Resources.is_path = function (str) {
|
||
return !/[\\\/:*?"<>|]/.test(str);
|
||
};
|
||
|
||
globalThis.json = {};
|
||
json.encode = function json_encode(value, replacer, space = 1) {
|
||
return JSON.stringify(value, replacer, space);
|
||
};
|
||
|
||
json.decode = function json_decode(text, reviver) {
|
||
if (!text) return undefined;
|
||
return JSON.parse(text, reviver);
|
||
};
|
||
|
||
json.readout = function (obj) {
|
||
var j = {};
|
||
for (var k in obj)
|
||
if (typeof obj[k] === "function") j[k] = "function " + obj[k].toString();
|
||
else j[k] = obj[k];
|
||
|
||
return json.encode(j);
|
||
};
|
||
|
||
json.doc = {
|
||
doc: "json implementation.",
|
||
encode: "Encode a value to json.",
|
||
decode: "Decode a json string to a value.",
|
||
readout: "Encode an object fully, including function definitions.",
|
||
};
|
||
|
||
Resources.scripts = ["jsoc", "jsc", "jso", "js"];
|
||
Resources.images = ["qoi", "png", "gif", "jpg", "jpeg", "ase", "aseprite"];
|
||
Resources.sounds = ["wav", "flac", "mp3", "qoa"];
|
||
Resources.fonts = ["ttf"];
|
||
Resources.is_image = function (path) {
|
||
var ext = path.ext();
|
||
return Resources.images.some(x => x === ext);
|
||
};
|
||
|
||
Resources.shaders = ["hlsl", "glsl", "cg"]
|
||
Resources.is_shader = function(path) {
|
||
var ext = path.ext();
|
||
return Resources.shaders.some(x => x === ext)
|
||
}
|
||
|
||
var res_cache = {};
|
||
|
||
// ext is a list of extensions to search
|
||
function find_ext(file, ext) {
|
||
if (!file) return;
|
||
|
||
var file_ext = file.ext();
|
||
var has_ext = file_ext.length > 0;
|
||
|
||
for (var e of ext) {
|
||
var nf = `${file}.${e}`;
|
||
if (io.exists(nf)) return nf;
|
||
}
|
||
|
||
var glob_pat = has_ext ? `**/${file}` : `**/${file}.*`;
|
||
|
||
var all_files = io.glob(glob_pat);
|
||
var find = undefined;
|
||
for (var e of ext) {
|
||
var finds = all_files.filter(x => x.ext() === e);
|
||
if (finds.length > 1) {
|
||
console.warn(`Found conflicting files when searching for '${file}': ${json.encode(finds)}. Returning the topmost one.`);
|
||
finds.sort((a,b) => a.length-b.length);
|
||
return finds[0];
|
||
}
|
||
if (finds.length === 1) return finds[0];
|
||
}
|
||
|
||
return find;
|
||
}
|
||
|
||
Object.defineProperty(Function.prototype, "hashify", {
|
||
value: function () {
|
||
var hash = new Map();
|
||
var fn = this;
|
||
function hashified(...args) {
|
||
var key = args[0];
|
||
if (!hash.has(key)) hash.set(key, fn(...args));
|
||
|
||
return hash.get(key);
|
||
}
|
||
return hashified;
|
||
},
|
||
});
|
||
|
||
Resources.find_image = function (file, root = "") {
|
||
return find_ext(file, Resources.images, root);
|
||
}.hashify();
|
||
|
||
Resources.find_sound = function (file, root = "") {
|
||
return find_ext(file, Resources.sounds, root);
|
||
}.hashify();
|
||
|
||
Resources.find_script = function (file, root = "") {
|
||
return find_ext(file, Resources.scripts, root);
|
||
}.hashify();
|
||
|
||
Resources.find_font = function(file, root = "") {
|
||
return find_ext(file, Resources.fonts, root);
|
||
}.hashify();
|
||
|
||
var tmpslurp = io.slurp;
|
||
io.slurp = function slurp(path)
|
||
{
|
||
var findpath = Resources.replpath(path);
|
||
var ret = tmpslurp(findpath, true); //|| core_db.slurp(findpath, true);
|
||
return ret;
|
||
}
|
||
|
||
io.slurpbytes = function(path)
|
||
{
|
||
path = Resources.replpath(path);
|
||
var ret = tmpslurp(path);// || core_db.slurp(path);
|
||
if (!ret) throw new Error(`Could not find file ${path} anywhere`);
|
||
return ret;
|
||
}
|
||
|
||
var ignore = io.slurp('.prosperonignore').split('\n');
|
||
var allpaths;
|
||
io.glob = function glob(pat) {
|
||
if (!allpaths)
|
||
allpaths = io.globfs(ignore);
|
||
|
||
return allpaths.filter(str => io.match(pat,str)).sort();
|
||
}
|
||
|
||
io.invalidate = function()
|
||
{
|
||
allpaths = undefined;
|
||
}
|
||
|
||
|
||
console.transcript = "";
|
||
console.say = function (msg) {
|
||
console.print(msg);
|
||
if (debug.termout) console.term_print(msg);
|
||
console.transcript += msg;
|
||
};
|
||
|
||
console.rec = function(category, priority, line, file, msg)
|
||
{
|
||
return `${file}:${line}: [${category} ${priority}]: ${msg}` + "\n";
|
||
}
|
||
|
||
|
||
|
||
var logfile = io.open('.prosperon/log.txt')
|
||
//logfile.buffer(1024*1024) // 1MB buffer
|
||
|
||
console.stdout_lvl = 0;
|
||
|
||
function pprint(msg, lvl = 0) {
|
||
if (lvl < console.stdout_lvl && !logfile) return;
|
||
|
||
if (typeof msg === "object") msg = JSON.stringify(msg, null, 2);
|
||
|
||
var file = "nofile";
|
||
var line = 0;
|
||
|
||
var caller = new Error().stack.split("\n")[2];
|
||
if (caller) {
|
||
var md = caller.match(/\((.*)\:/);
|
||
var m = md ? md[1] : "SCRIPT";
|
||
if (m) file = m;
|
||
md = caller.match(/\:(\d*)\)/);
|
||
m = md ? md[1] : 0;
|
||
if (m) line = m;
|
||
}
|
||
var fmt = console.rec("script", lvl, line,file, msg);
|
||
|
||
if (lvl >= console.stdout_lvl)
|
||
console.print(fmt)
|
||
|
||
if (logfile)
|
||
logfile.write(fmt)
|
||
|
||
if (tracy) tracy.message(fmt);
|
||
};
|
||
|
||
console.spam = function spam(msg) {
|
||
pprint(msg, 0);
|
||
};
|
||
console.debug = function debug(msg) {
|
||
pprint(msg, 1);
|
||
};
|
||
console.info = function info(msg) {
|
||
pprint(msg, 2);
|
||
};
|
||
console.warn = function warn(msg) {
|
||
pprint(msg, 3);
|
||
};
|
||
|
||
console.log = function(msg)
|
||
{
|
||
pprint(msg, 2)
|
||
}
|
||
|
||
console.error = function(e) {
|
||
console.print(e.message)
|
||
if (!e)
|
||
e = new Error();
|
||
|
||
pprint(`${e.name} : ${e.message}
|
||
${e.stack}`, 4)
|
||
};
|
||
|
||
console.panic = function (e) {
|
||
console.pprint(e , 5)
|
||
os.quit();
|
||
};
|
||
|
||
console.assert = function (op, str = `assertion failed [value '${op}']`) {
|
||
if (!op) console.panic(str);
|
||
};
|
||
|
||
os.on('uncaught_exception', function(e) { console.error(e); });
|
||
|
||
console.doc = {
|
||
log: "Output directly to in game console.",
|
||
level: "Set level to output logging to console.",
|
||
info: "Output info level message.",
|
||
warn: "Output warn level message.",
|
||
error: "Output error level message, and print stacktrace.",
|
||
critical: "Output critical level message, and exit game immediately.",
|
||
write: "Write raw text to console.",
|
||
say: "Write raw text to console, plus a newline.",
|
||
stack: "Output a stacktrace to console.",
|
||
clear: "Clear console.",
|
||
};
|
||
|
||
var script = io.slurp("core/scripts/base.js")
|
||
var fnname = "base"
|
||
script = `(function ${fnname}() { ${script}; })`
|
||
os.eval('core/scripts/base.js', script)()
|
||
|
||
prosperon.SIGABRT = function()
|
||
{
|
||
console.error(new Error('SIGABRT'));
|
||
os.exit(1);
|
||
}
|
||
|
||
prosperon.SIGSEGV = function()
|
||
{
|
||
console.error(new Error('SIGSEGV'));
|
||
os.exit(1);
|
||
}
|
||
|
||
function add_timer(obj, fn, seconds)
|
||
{
|
||
var timers = obj.timers;
|
||
|
||
var stop = function () {
|
||
if (!timer) return
|
||
timers.delete(stop);
|
||
timer.fn = undefined;
|
||
timer = undefined;
|
||
};
|
||
|
||
function execute() {
|
||
if (fn)
|
||
timer.remain = fn(stop.seconds);
|
||
|
||
if (!timer) return
|
||
if (!timer.remain)
|
||
stop();
|
||
else
|
||
stop.seconds = timer.remain;
|
||
}
|
||
|
||
var timer = os.make_timer(execute);
|
||
timer.remain = seconds;
|
||
|
||
stop.remain = seconds;
|
||
stop.seconds = seconds;
|
||
|
||
timers.push(stop);
|
||
return stop;
|
||
}
|
||
|
||
var actor = {};
|
||
|
||
var so_ext;
|
||
switch(os.sys()) {
|
||
case 'Windows':
|
||
so_ext = '.dll';
|
||
break;
|
||
default:
|
||
so_ext = '.so';
|
||
break;
|
||
}
|
||
|
||
var use_cache = {}
|
||
|
||
function load_mod(file)
|
||
{
|
||
try {
|
||
var par = script_fn(file)
|
||
if (par?.module_ret) {
|
||
use_cache[file] = par.module_
|
||
return par.module_ret
|
||
}
|
||
} catch(e) {
|
||
console.error(e)
|
||
}
|
||
|
||
try {
|
||
return os.use('./lib' + file + so_ext)
|
||
} catch(e) { console.error(e) }
|
||
|
||
return os.use(file)
|
||
}
|
||
|
||
var use = function use(file) {
|
||
if (use_cache[file]) return use_cache[file];
|
||
|
||
use_cache[file] = load_mod(file)
|
||
return use_cache[file]
|
||
}
|
||
|
||
use.hotreload = function(file)
|
||
{
|
||
console.log(`hot reloading ${file}`)
|
||
var oldval = use_cache[file]
|
||
var newval = load_mod(file)
|
||
if (!oldval) {
|
||
use_cache[file] = newval;
|
||
return newval;
|
||
}
|
||
|
||
if (typeof oldval !== 'object' || typeof newval !== 'object' || !oldval || !newval) {
|
||
use_cache[file] = newval;
|
||
return newval;
|
||
}
|
||
|
||
use_patch(oldval, newval);
|
||
return oldval;
|
||
}
|
||
|
||
function use_patch(target, source)
|
||
{
|
||
// First remove properties that aren’t in source at all
|
||
for (let key of Object.keys(target)) {
|
||
if (!(key in source)) {
|
||
delete target[key];
|
||
}
|
||
}
|
||
|
||
// Then shallow-copy the source’s own properties
|
||
for (let key of Object.keys(source)) {
|
||
target[key] = source[key];
|
||
}
|
||
|
||
// Update the prototype if needed so that new or changed methods come along
|
||
let oldProto = Object.getPrototypeOf(target);
|
||
let newProto = Object.getPrototypeOf(source);
|
||
if (oldProto !== newProto) {
|
||
Object.setPrototypeOf(target, newProto);
|
||
}
|
||
}
|
||
|
||
var script_fn = function script_fn(path) {
|
||
var file = Resources.find_script(path)
|
||
if (!file) throw new Error(`File ${path} could not be found`)
|
||
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; var $ = this; var exports = {}; var module = {exports:exports}; var define = undefined; ${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;
|
||
} else
|
||
parsed.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 parsed;
|
||
}.hashify()
|
||
|
||
function parse_file(content) {
|
||
if (!content) return {}
|
||
var parts = content.split(/\n\s*---\s*\n/)
|
||
if (parts.length === 1) {
|
||
var part = parts[0]
|
||
if (part.match(/return\s+[^;]+;?\s*$/))
|
||
return { module: part }
|
||
return { program: part }
|
||
}
|
||
var module = parts[0]
|
||
if (!module.match(/return\s+[^;]+;?\s*$/))
|
||
throw new Error("Module section must end with a return statement")
|
||
|
||
var pad = '\n'.repeat(module.split('\n').length+2) // add 2 from the split search
|
||
return {
|
||
module,
|
||
program: pad+parts[1]
|
||
}
|
||
}
|
||
|
||
actor.__stats = function () {
|
||
var total = 0;
|
||
var stats = {};
|
||
search.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);
|
||
Register.check_registers(a);
|
||
}
|
||
*/
|
||
};
|
||
|
||
//////////
|
||
/// EVENT
|
||
/////////
|
||
|
||
var Event = {
|
||
events: {},
|
||
|
||
observe(name, obj, fn) {
|
||
this.events[name] ??= [];
|
||
this.events[name].push([obj, fn]);
|
||
},
|
||
|
||
unobserve(name, obj) {
|
||
this.events[name] = this.events[name].filter(x => x[0] !== obj);
|
||
},
|
||
|
||
rm_obj(obj) {
|
||
Object.keys(this.events).forEach(name => Event.unobserve(name, obj));
|
||
},
|
||
|
||
notify(name, ...args) {
|
||
if (!this.events[name]) return;
|
||
this.events[name].forEach(function (x) {
|
||
x[1].call(x[0], ...args);
|
||
});
|
||
},
|
||
};
|
||
|
||
//////////////////
|
||
///////REGISTRANT
|
||
/////////////////
|
||
/*
|
||
Factory for creating registries. Register one with 'X.register',
|
||
which returns a function that, when invoked, cancels the registry.
|
||
*/
|
||
var Register = {
|
||
registries: [],
|
||
|
||
add_cb(name) {
|
||
var n = {};
|
||
var fns = [];
|
||
|
||
n.register = function (fn, oname) {
|
||
if (!(fn instanceof Function)) return;
|
||
|
||
var dofn = function (...args) {
|
||
fn(...args);
|
||
};
|
||
Object.defineProperty(dofn, 'name', {value:`do_${oname}`});
|
||
|
||
var left = 0;
|
||
var right = fns.length - 1;
|
||
dofn.layer = fn.layer;
|
||
dofn.layer ??= 0;
|
||
|
||
while (left <= right) {
|
||
var mid = Math.floor((left + right) / 2);
|
||
if (fns[mid] === dofn.layer) {
|
||
left = mid;
|
||
break;
|
||
} else if (fns[mid].layer < dofn.layer) left = mid + 1;
|
||
else right = mid - 1;
|
||
}
|
||
|
||
fns.splice(left, 0, dofn);
|
||
|
||
return function () {
|
||
fns.delete(dofn)
|
||
};
|
||
};
|
||
|
||
prosperon[name] = function (...args) {
|
||
// tracy.fiber_enter(vector.fib);
|
||
fns.forEach(fn => {
|
||
fn(...args)
|
||
});
|
||
// tracy.fiber_leave(vector.fib);
|
||
};
|
||
|
||
Object.defineProperty(prosperon[name], 'name', {value:name});
|
||
prosperon[name].fns = fns;
|
||
n.clear = function () {
|
||
fns = [];
|
||
};
|
||
|
||
Register[name] = n;
|
||
Register.registries[name] = n;
|
||
|
||
return n;
|
||
},
|
||
};
|
||
|
||
Register.pull_registers = function pull_registers(obj)
|
||
{
|
||
var reggies = [];
|
||
for (var reg in Register.registries) {
|
||
if (typeof obj[reg] === "function")
|
||
reggies.push(reg);
|
||
}
|
||
return reggies;
|
||
}
|
||
|
||
Register.register_obj = function register_obj(obj, reg)
|
||
{
|
||
var fn = obj[reg].bind(obj);
|
||
fn.layer = obj[reg].layer;
|
||
var name = obj.ur ? obj.ur.name : obj.toString();
|
||
obj.timers.push(Register.registries[reg].register(fn, name));
|
||
if (!obj[reg].name) Object.defineProperty(obj[reg], 'name', {value:`${obj._file}_${reg}`});
|
||
}
|
||
|
||
Register.check_registers = function check_registers(obj) {
|
||
if (obj.__reggies) {
|
||
if (obj.__reggies.length == 0) return;
|
||
// fast path
|
||
for (var reg of obj.__reggies)
|
||
Register.register_obj(obj,reg);
|
||
|
||
return;
|
||
}
|
||
|
||
for (var reg in Register.registries) {
|
||
if (typeof obj[reg] === "function")
|
||
Register.register_obj(obj,reg);
|
||
}
|
||
|
||
/* for (var k in obj) {
|
||
if (!k.startsWith("on_")) continue;
|
||
var signal = k.fromfirst("on_");
|
||
Event.observe(signal, obj, obj[k]);
|
||
}*/
|
||
};
|
||
|
||
Register.add_cb("appupdate");
|
||
Register.add_cb("update").doc = "Called once per frame.";
|
||
Register.add_cb("physupdate");
|
||
Register.add_cb("gui");
|
||
Register.add_cb("hud");
|
||
Register.add_cb("draw");
|
||
Register.add_cb("imgui");
|
||
Register.add_cb("app");
|
||
Register.add_cb("prerender");
|
||
|
||
function cant_kill()
|
||
{
|
||
throw Error("Can't kill an object in its spawning code. Move the kill command to awake.");
|
||
}
|
||
|
||
actor.toString = function() { return this.__file }
|
||
actor.spawn = function spawn(script, config, callback) {
|
||
if (this.__dead__) throw Error("Attempting to spawn on a dead actor")
|
||
var prog
|
||
if (!script) {
|
||
prog = {}
|
||
prog.module_ret = {}
|
||
prog.prog_fn = function() {}
|
||
} else {
|
||
prog = script_fn(script);
|
||
if (!prog.prog_fn) throw new Error(`Script ${script} is not an actor script or has no actor component`)
|
||
}
|
||
|
||
var underling;
|
||
prog.module_ret.__proto__ = actor;
|
||
underling = Object.create(prog.module_ret);
|
||
underling.overling = this;
|
||
underling.__file = script
|
||
|
||
underling.timers = []
|
||
underling.underlings = new Set()
|
||
|
||
if (callback) callback(underling, {
|
||
message:"created"
|
||
})
|
||
|
||
try{
|
||
prog.prog_fn.call(underling)
|
||
} catch(e) {throw e}
|
||
if (underling.__dead__)
|
||
return undefined
|
||
|
||
if (typeof config === 'object') Object.assign(underling,config);
|
||
|
||
if (!underling.__reggies)
|
||
underling.__proto__.__reggies = Register.pull_registers(underling)
|
||
|
||
Register.check_registers(underling);
|
||
|
||
if (underling.awake) underling.awake();
|
||
|
||
this.underlings.add(underling);
|
||
if (underling.tag)
|
||
search.tag_add(underling.tag, underling)
|
||
|
||
Object.defineProperty(underling, 'garbage', {
|
||
configurable: false,
|
||
writable: false,
|
||
value: underling.garbage
|
||
})
|
||
|
||
return underling;
|
||
};
|
||
|
||
actor.spawn.doc = `Create a new actor, using this actor as the overling, initializing it with 'script' and with data (as a JSON or Nota file) from 'config'.`;
|
||
|
||
actor.clear = function actor_clear()
|
||
{
|
||
this.underlings.forEach(p => {
|
||
p.kill();
|
||
})
|
||
this.underlings.clear()
|
||
}
|
||
|
||
var input = use('input')
|
||
|
||
actor.kill = function kill() {
|
||
if (this.__dead__) return;
|
||
this.__dead__ = true;
|
||
this.timers.slice().forEach(t => t()) // slice in case something is removed from timers while running
|
||
delete this.timers
|
||
input.do_uncontrol(this);
|
||
Event.rm_obj(this);
|
||
|
||
this.clear()
|
||
this.overling.underlings.delete(this)
|
||
delete this.underlings
|
||
|
||
if (typeof this.garbage === "function") this.garbage();
|
||
if (typeof this.then === "function") this.then();
|
||
|
||
search.tag_clear_guid(this)
|
||
};
|
||
|
||
actor.kill.doc = `Remove this actor and all its underlings from existence.`;
|
||
|
||
actor.delay = function (fn, seconds) { add_timer(this, fn, seconds) }
|
||
actor.delay.doc = `Call 'fn' after 'seconds' with 'this' set to the actor.`;
|
||
|
||
actor.interval = function interval(fn, seconds) {
|
||
var self = this;
|
||
var stop;
|
||
var usefn = function () {
|
||
fn();
|
||
stop = self.delay(usefn, seconds);
|
||
};
|
||
stop = self.delay(usefn, seconds);
|
||
|
||
return stop;
|
||
};
|
||
|
||
var search = use('search')
|
||
|
||
actor.underlings = new Set()
|
||
|
||
globalThis.mixin("color");
|
||
globalThis.mixin("std")
|