separate the idea of misty actor and scene tree actor
Some checks failed
Build and Deploy / build-macos (push) Failing after 5s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Failing after 1m30s
Build and Deploy / package-dist (push) Has been skipped
Build and Deploy / deploy-itch (push) Has been skipped
Build and Deploy / deploy-gitea (push) Has been skipped
Some checks failed
Build and Deploy / build-macos (push) Failing after 5s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Failing after 1m30s
Build and Deploy / package-dist (push) Has been skipped
Build and Deploy / deploy-itch (push) Has been skipped
Build and Deploy / deploy-gitea (push) Has been skipped
This commit is contained in:
@@ -327,6 +327,7 @@ function joinServer() {
|
||||
|
||||
var os = use('os')
|
||||
var actor = use('actor')
|
||||
for (var i in actor) console.log(i)
|
||||
|
||||
// Set up IO actor subscription
|
||||
var ioguy = {
|
||||
|
||||
@@ -23,12 +23,6 @@ prosperon.dispatch = function(type, data) {
|
||||
|
||||
var os = use_embed('os')
|
||||
var actor_mod = use_embed('actor')
|
||||
var tracy = use_embed('tracy')
|
||||
|
||||
os.trace = true;
|
||||
|
||||
if (os.trace)
|
||||
tracy.level(1);
|
||||
|
||||
var js = use_embed('js')
|
||||
|
||||
@@ -46,19 +40,6 @@ prosperon.on('SIGSEGV', function() {
|
||||
os.exit(1)
|
||||
})
|
||||
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
var io = use_embed('io')
|
||||
|
||||
globalThis.console = use_embed('console')
|
||||
@@ -70,7 +51,7 @@ var resources = js.eval(RESPATH, `(function setup_resources(){${content}})`).cal
|
||||
|
||||
var use_cache = {}
|
||||
|
||||
use_cache[resources.canonical('resources.js')] = resources
|
||||
use_cache['resources'] = resources
|
||||
|
||||
function print_api(obj) {
|
||||
for (var prop in obj) {
|
||||
@@ -86,61 +67,14 @@ function print_api(obj) {
|
||||
|
||||
prosperon.PATH = [
|
||||
"/",
|
||||
"scripts/modules/",
|
||||
"scripts/modules/ext/",
|
||||
"scripts/modules/"
|
||||
]
|
||||
|
||||
// path is the path of a module or script to resolve
|
||||
var script_fn = function script_fn(path, args) {
|
||||
var parsed = {}
|
||||
var file = resources.find_script(path)
|
||||
|
||||
if (!file) {
|
||||
parsed.module_ret = bare_load(path)
|
||||
if (!parsed.module_ret) throw new Error(`Module ${path} could not be created`)
|
||||
return parsed
|
||||
}
|
||||
|
||||
var content = io.slurp(file)
|
||||
var parsed = parse_file(content, file)
|
||||
var module_name = file.name()
|
||||
parsed.module_ret = bare_load(path)
|
||||
parsed.module_ret ??= {}
|
||||
|
||||
if (parsed.module) {
|
||||
// Create a context object with args
|
||||
var context = Object.create(parsed.module_ret)
|
||||
context.__args__ = args || []
|
||||
var mod_script = `(function setup_${module_name}_module(){ var self = this; var $ = this; var exports = {}; var module = {exports: exports}; var define = undefined; var arg = this.__args__; ${parsed.module}})`
|
||||
var module_fn = js.eval(file, mod_script)
|
||||
parsed.module_ret = module_fn.call(context)
|
||||
if (parsed.module_ret === undefined || parsed.module_ret === null)
|
||||
throw new Error(`Module ${module_name} must return a value`)
|
||||
parsed.module_fn = module_fn
|
||||
}
|
||||
|
||||
parsed.program ??= ""
|
||||
var prog_script = `(function use_${module_name}($_) { var self = this; var $ = this.__proto__; ${parsed.program}})`
|
||||
parsed.prog_fn = js.eval(file, prog_script)
|
||||
|
||||
return parsed
|
||||
}.hashify()
|
||||
|
||||
function bare_load(file) {
|
||||
try {
|
||||
return use_embed(file)
|
||||
} catch (e) { }
|
||||
try {
|
||||
return use_dyn(file + so_ext)
|
||||
} catch(e) { }
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
||||
|
||||
var res_cache = {}
|
||||
|
||||
function console_rec(category, priority, line, file, msg) {
|
||||
return `[${file}:${line}: [${category} ${priority}]: ${msg}\n`
|
||||
|
||||
var now = time.now()
|
||||
|
||||
var id = prosperon.name ? prosperon.name : prosperon.id
|
||||
@@ -149,12 +83,7 @@ function console_rec(category, priority, line, file, msg) {
|
||||
return `[${id}] [${time.text(now, "mb d yyyy h:nn:ss")}] ${file}:${line}: [${category} ${priority}]: ${msg}\n`
|
||||
}
|
||||
|
||||
io.mkdir('.prosperon')
|
||||
var logfile //= io.open('.prosperon/log.txt')
|
||||
|
||||
function pprint(msg, lvl = 0) {
|
||||
if (!logfile) return
|
||||
|
||||
var file = "nofile"
|
||||
var line = 0
|
||||
|
||||
@@ -169,11 +98,6 @@ function pprint(msg, lvl = 0) {
|
||||
}
|
||||
var fmt = console_rec("script", lvl, line, file, msg)
|
||||
console.print(fmt)
|
||||
|
||||
if (logfile)
|
||||
logfile.write(fmt)
|
||||
|
||||
if (tracy) tracy.message(fmt)
|
||||
}
|
||||
|
||||
function format_args(...args) {
|
||||
@@ -248,64 +172,28 @@ var fnname = "base"
|
||||
script = `(function ${fnname}() { ${script}; })`
|
||||
js.eval(BASEPATH, script)()
|
||||
|
||||
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 DEAD = Symbol()
|
||||
var GARBAGE = Symbol()
|
||||
var FILE = Symbol()
|
||||
var TIMERS = Symbol()
|
||||
var REGGIES = Symbol()
|
||||
var UNDERLINGS = Symbol()
|
||||
var OVERLING = Symbol()
|
||||
|
||||
var actor = {}
|
||||
globalThis.actor = actor
|
||||
|
||||
var so_ext
|
||||
switch(os.platform()) {
|
||||
case 'Windows':
|
||||
so_ext = '.dll'
|
||||
break
|
||||
default:
|
||||
so_ext = '.so'
|
||||
break
|
||||
}
|
||||
|
||||
var use_cache = {}
|
||||
var inProgress = {}
|
||||
var loadingStack = []
|
||||
|
||||
globalThis.use = function use(file, ...args) {
|
||||
// If we've already begun loading this file in this chain, show the cycle
|
||||
// Normalize the request - remove .js extension if present
|
||||
var request_name = file
|
||||
if (file.endsWith('.js')) {
|
||||
request_name = file.substring(0, file.length - 3)
|
||||
}
|
||||
|
||||
// Check cache first - both 'transform' and 'transform.js' should return same cached value
|
||||
for (var cached_key in use_cache) {
|
||||
if (cached_key === file || cached_key === request_name ||
|
||||
cached_key === request_name + '.js' ||
|
||||
(cached_key.endsWith('.js') && cached_key.substring(0, cached_key.length - 3) === request_name)) {
|
||||
return use_cache[cached_key]
|
||||
}
|
||||
}
|
||||
|
||||
// Check for circular dependencies
|
||||
if (loadingStack.includes(file)) {
|
||||
// Find where in the stack this file first appeared
|
||||
let cycleIndex = loadingStack.indexOf(file)
|
||||
// Extract just the modules in the cycle
|
||||
let cyclePath = loadingStack.slice(cycleIndex).concat(file)
|
||||
|
||||
throw new Error(
|
||||
@@ -314,292 +202,81 @@ globalThis.use = function use(file, ...args) {
|
||||
`Cycle specifically: ${cyclePath.join(" -> ")}`
|
||||
)
|
||||
}
|
||||
|
||||
// Already fully loaded? Return it
|
||||
if (use_cache[file]) {
|
||||
return use_cache[file]
|
||||
|
||||
// Try to find the script file
|
||||
var path = resources.find_script(request_name)
|
||||
|
||||
// Check if there's an embedded module
|
||||
var embed_mod = use_embed(request_name)
|
||||
|
||||
// If no script and no embedded module, error
|
||||
if (!path && !embed_mod) {
|
||||
throw new Error(`Module ${file} could not be found`)
|
||||
}
|
||||
|
||||
// If it's loading (but not on the stack), mark it as a new chain entry
|
||||
// (This is optional if you just rely on loadingStack.
|
||||
// But if you'd like a simple “already loading” check, keep 'inProgress'.)
|
||||
if (inProgress[file]) {
|
||||
|
||||
// If only embedded module exists, return it
|
||||
if (!path && embed_mod) {
|
||||
use_cache[file] = embed_mod
|
||||
use_cache[request_name] = embed_mod
|
||||
if (file !== request_name) {
|
||||
use_cache[request_name + '.js'] = embed_mod
|
||||
}
|
||||
return embed_mod
|
||||
}
|
||||
|
||||
// If we have a script path, check for circular dependency
|
||||
if (inProgress[path]) {
|
||||
throw new Error(`Circular dependency detected while loading "${file}"`)
|
||||
}
|
||||
inProgress[file] = true
|
||||
|
||||
// Push onto loading stack for chain tracking
|
||||
|
||||
inProgress[path] = true
|
||||
loadingStack.push(file)
|
||||
|
||||
// Actually load the module with arguments
|
||||
var mod = script_fn(file, args)
|
||||
|
||||
// Done loading, remove from the chain and mark as loaded
|
||||
|
||||
// Load and execute the script
|
||||
var script = io.slurp(path)
|
||||
var mod_name = path.name()
|
||||
|
||||
// Create context - if embedded module exists, script extends it
|
||||
var context = {}
|
||||
if (embed_mod)
|
||||
context.__proto__ = embed_mod
|
||||
|
||||
var mod_script = `(function setup_${mod_name}_module(arg){${script};})`
|
||||
var fn = js.eval(path, mod_script)
|
||||
|
||||
// Call the script - pass embedded module as 'this' if it exists
|
||||
var ret = fn.call(context, args)
|
||||
|
||||
// If script doesn't return anything, check if we have embedded module
|
||||
if (!ret && embed_mod) {
|
||||
ret = embed_mod
|
||||
} else if (!ret) {
|
||||
throw new Error(`Use must be used with a module, but ${path} doesn't return a value`)
|
||||
}
|
||||
|
||||
loadingStack.pop()
|
||||
delete inProgress[file]
|
||||
|
||||
// Cache and return
|
||||
use_cache[file] = mod.module_ret
|
||||
return use_cache[file]
|
||||
delete inProgress[path]
|
||||
|
||||
// Cache under all possible keys
|
||||
use_cache[path] = ret
|
||||
use_cache[file] = ret
|
||||
use_cache[request_name] = ret
|
||||
if (file !== request_name && !file.endsWith('.js')) {
|
||||
use_cache[request_name + '.js'] = ret
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
globalThis.json = use('json')
|
||||
var time = use('time')
|
||||
|
||||
function parse_file(content, file) {
|
||||
if (!content) return {}
|
||||
if (!/^\s*---\s*$/m.test(content)) {
|
||||
var part = content.trim()
|
||||
if (part.match(/return\s+[^;]+;?\s*$/)) {
|
||||
return { module: part }
|
||||
}
|
||||
return { program: part }
|
||||
}
|
||||
var parts = content.split(/\n\s*---\s*\n/)
|
||||
var module = parts[0]
|
||||
if (!/\breturn\b/.test(module))
|
||||
throw new Error(`Malformed file: ${file}. Module section must end with a return statement.`)
|
||||
|
||||
try {
|
||||
new Function(module)()
|
||||
} catch (e) {
|
||||
throw new Error(`Malformed file: ${file}. Module section must end with a return statement.\n` + e.message)
|
||||
}
|
||||
|
||||
var pad = '\n'.repeat(module.split('\n').length + 4)
|
||||
return {
|
||||
module,
|
||||
program: pad + parts[1]
|
||||
}
|
||||
}
|
||||
|
||||
globalThis.Register = {
|
||||
registries: [],
|
||||
|
||||
add_cb(name) {
|
||||
var n = {}
|
||||
var fns = []
|
||||
|
||||
n.register = function (fn, oname) {
|
||||
if (typeof fn !== '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) {
|
||||
fns.forEach(fn => {
|
||||
fn(...args)
|
||||
})
|
||||
}
|
||||
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
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
|
||||
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, actor_context) {
|
||||
if (this[DEAD]) throw new 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()
|
||||
|
||||
// Make $_ available to the actor (either passed context or the engine's $_)
|
||||
var actor_dollar = actor_context || $_
|
||||
Object.defineProperty(underling, '$_', {
|
||||
value: actor_dollar,
|
||||
writable:false,
|
||||
enumerable:false,
|
||||
configurable:false
|
||||
})
|
||||
|
||||
Object.defineProperty(underling, 'overling', {
|
||||
get() { return this[OVERLING] },
|
||||
enumerable:true,
|
||||
configurable:false
|
||||
})
|
||||
|
||||
Object.defineProperty(underling, 'underlings', {
|
||||
get() { return new Set(this[UNDERLINGS]) },
|
||||
enumerable:true,
|
||||
configurable:false
|
||||
})
|
||||
|
||||
Object.defineProperty(underling, 'spawn', {
|
||||
value: function(script, config) {
|
||||
return actor.spawn.call(this, script, config, actor_dollar)
|
||||
},
|
||||
writable:false,
|
||||
enumerable:true,
|
||||
configurable:false
|
||||
})
|
||||
|
||||
Object.defineProperty(underling, 'kill', {
|
||||
value: actor.kill,
|
||||
writable:false,
|
||||
enumerable:true,
|
||||
configurable:false
|
||||
})
|
||||
|
||||
Object.defineProperty(underling, 'delay', {
|
||||
value: actor.delay,
|
||||
writable:false,
|
||||
enumerable:true,
|
||||
configurable:false
|
||||
})
|
||||
|
||||
try {
|
||||
// Pass $_ as a parameter to actor scripts
|
||||
prog.prog_fn.call(underling, actor_dollar)
|
||||
} 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) act.tag_add(underling.tag, underling)
|
||||
|
||||
underling[GARBAGE] = underling.garbage
|
||||
return underling
|
||||
}
|
||||
|
||||
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())
|
||||
delete this[TIMERS]
|
||||
input.do_uncontrol(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()
|
||||
|
||||
act.tag_clear_guid(this)
|
||||
}
|
||||
actor.kill.doc = `Remove this actor and all its underlings from existence.`
|
||||
|
||||
actor.delay = function(fn, seconds) {
|
||||
if (this[DEAD]) return
|
||||
add_timer(this, fn, seconds)
|
||||
}
|
||||
actor.delay.doc = `Call 'fn' after 'seconds' with 'this' set to the actor.`
|
||||
|
||||
var act = use('actor')
|
||||
actor[UNDERLINGS] = new Set()
|
||||
|
||||
globalThis.mixin("color")
|
||||
|
||||
var DOCPATH = 'scripts/core/doc.js'
|
||||
var script = io.slurp(DOCPATH)
|
||||
var fnname = "doc"
|
||||
script = `(function ${fnname}() { ${script}; })`
|
||||
//js.eval(DOCPATH, script)()
|
||||
|
||||
/*
|
||||
When handling a message, the message appears like this:
|
||||
@@ -937,10 +614,7 @@ if (!prosperon.args.program)
|
||||
os.exit(1)
|
||||
|
||||
if (typeof prosperon.args.program !== 'string')
|
||||
prosperon.args.program = 'main.js';
|
||||
|
||||
// Spawn the root actor with access to $_
|
||||
actor.spawn.call({[UNDERLINGS]: new Set()}, prosperon.args.program, undefined)
|
||||
prosperon.args.program = 'main.js';
|
||||
|
||||
function destroyself() {
|
||||
console.log(`Got the message to destroy self.`)
|
||||
@@ -1023,4 +697,11 @@ function enet_check()
|
||||
send_messages();
|
||||
enet_check();
|
||||
|
||||
// Finally, run the program
|
||||
var prog = io.slurp(prosperon.args.program)
|
||||
var prog_script = `(function ${prosperon.args.program.name()}($_) { ${prog} })`
|
||||
var val = js.eval(prosperon.args.program, prog_script)($_)
|
||||
if (val)
|
||||
throw new Error('Program must not return anything');
|
||||
|
||||
})()
|
||||
@@ -73,9 +73,6 @@ Cmdline.register_order(
|
||||
else
|
||||
app = actor.spawn("nogame.js")
|
||||
|
||||
// rm actor so it can't be tampered
|
||||
globalThis.actor = undefined
|
||||
|
||||
var loop = use('loop')
|
||||
while(1) loop.step();
|
||||
},
|
||||
|
||||
@@ -459,7 +459,6 @@ var sysfont = graphics.get_font('fonts/c64.ttf', 8)
|
||||
draw.text = function text(text, rect, font = sysfont, size = 0, color = Color.white, wrap = 0, pipeline) {
|
||||
if (typeof font === 'string') font = graphics.get_font(font)
|
||||
var mesh = graphics.make_text_buffer(text, rect, 0, color, wrap, font)
|
||||
console.log(json.encode(mesh))
|
||||
render.geometry(font, mesh)
|
||||
}
|
||||
draw.text[prosperon.DOC] = `
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
return {
|
||||
get pos() {
|
||||
if (!this.transform) return
|
||||
return this.transform.pos;
|
||||
},
|
||||
set pos(x) {
|
||||
this.transform.pos = x;
|
||||
},
|
||||
get angle() {
|
||||
return this.transform.angle;
|
||||
},
|
||||
set angle(x) {
|
||||
this.transform.angle = x;
|
||||
},
|
||||
get scale() {
|
||||
return this.transform.scale;
|
||||
},
|
||||
set scale(x) {
|
||||
this.transform.scale = x;
|
||||
},
|
||||
move(vec) {
|
||||
this.pos = this.pos.add(vec);
|
||||
},
|
||||
rotate(x) {
|
||||
this.transform.rotate([0, 0, -1],x);
|
||||
},
|
||||
grow(vec) {
|
||||
if (typeof vec === "number") vec = [vec, vec];
|
||||
this.scale = this.scale.map((x, i) => x * vec[i]);
|
||||
},
|
||||
}
|
||||
@@ -19,6 +19,7 @@ var io = use('io');
|
||||
var render = use('render');
|
||||
var actor = use('actor');
|
||||
var transform = use('transform');
|
||||
|
||||
var gameConfig = {};
|
||||
var gameDir = "";
|
||||
|
||||
|
||||
@@ -2,6 +2,19 @@ var io = use_embed('io');
|
||||
var miniz = use_embed('miniz');
|
||||
var os = use_embed('os');
|
||||
|
||||
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
|
||||
},
|
||||
})
|
||||
|
||||
// Merge of the old resources.js and packer.js functionalities
|
||||
var Resources = {}
|
||||
|
||||
|
||||
@@ -1,4 +1,259 @@
|
||||
var ex = this
|
||||
|
||||
var input = use('input')
|
||||
|
||||
var DEAD = Symbol()
|
||||
var GARBAGE = Symbol()
|
||||
var FILE = Symbol()
|
||||
var TIMERS = Symbol()
|
||||
var REGGIES = Symbol()
|
||||
var UNDERLINGS = Symbol()
|
||||
var OVERLING = Symbol()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
globalThis.Register = {
|
||||
registries: [],
|
||||
|
||||
add_cb(name) {
|
||||
var n = {}
|
||||
var fns = []
|
||||
|
||||
n.register = function (fn, oname) {
|
||||
if (typeof fn !== '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) {
|
||||
fns.forEach(fn => {
|
||||
fn(...args)
|
||||
})
|
||||
}
|
||||
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
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
|
||||
|
||||
var actor = {}
|
||||
|
||||
actor.toString = function() { return this[FILE] }
|
||||
|
||||
actor.spawn = function spawn(script, config, actor_context) {
|
||||
if (this[DEAD]) throw new 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()
|
||||
|
||||
// Make $_ available to the actor (either passed context or the engine's $_)
|
||||
var actor_dollar = actor_context || $_
|
||||
Object.defineProperty(underling, '$_', {
|
||||
value: actor_dollar,
|
||||
writable:false,
|
||||
enumerable:false,
|
||||
configurable:false
|
||||
})
|
||||
|
||||
Object.defineProperty(underling, 'overling', {
|
||||
get() { return this[OVERLING] },
|
||||
enumerable:true,
|
||||
configurable:false
|
||||
})
|
||||
|
||||
Object.defineProperty(underling, 'underlings', {
|
||||
get() { return new Set(this[UNDERLINGS]) },
|
||||
enumerable:true,
|
||||
configurable:false
|
||||
})
|
||||
|
||||
Object.defineProperty(underling, 'spawn', {
|
||||
value: function(script, config) {
|
||||
return actor.spawn.call(this, script, config, actor_dollar)
|
||||
},
|
||||
writable:false,
|
||||
enumerable:true,
|
||||
configurable:false
|
||||
})
|
||||
|
||||
Object.defineProperty(underling, 'kill', {
|
||||
value: actor.kill,
|
||||
writable:false,
|
||||
enumerable:true,
|
||||
configurable:false
|
||||
})
|
||||
|
||||
Object.defineProperty(underling, 'delay', {
|
||||
value: actor.delay,
|
||||
writable:false,
|
||||
enumerable:true,
|
||||
configurable:false
|
||||
})
|
||||
|
||||
try {
|
||||
// Pass $_ as a parameter to actor scripts
|
||||
prog.prog_fn.call(underling, actor_dollar)
|
||||
} 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) act.tag_add(underling.tag, underling)
|
||||
|
||||
underling[GARBAGE] = underling.garbage
|
||||
return underling
|
||||
}
|
||||
|
||||
actor.clear = function actor_clear() {
|
||||
this[UNDERLINGS].forEach(p => {
|
||||
p.kill()
|
||||
})
|
||||
this[UNDERLINGS].clear()
|
||||
}
|
||||
|
||||
actor.kill = function kill() {
|
||||
if (this[DEAD]) return
|
||||
this[DEAD] = true
|
||||
this[TIMERS].slice().forEach(t => t())
|
||||
delete this[TIMERS]
|
||||
input.do_uncontrol(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()
|
||||
|
||||
act.tag_clear_guid(this)
|
||||
}
|
||||
actor.kill.doc = `Remove this actor and all its underlings from existence.`
|
||||
|
||||
actor.delay = function(fn, seconds) {
|
||||
if (this[DEAD]) return
|
||||
add_timer(this, fn, seconds)
|
||||
}
|
||||
actor.delay.doc = `Call 'fn' after 'seconds' with 'this' set to the actor.`
|
||||
|
||||
actor[UNDERLINGS] = new Set()
|
||||
|
||||
|
||||
ex[prosperon.DOC] = `
|
||||
A set of utilities for iterating over a hierarchy of actor-like objects, as well
|
||||
as managing tag-based lookups. Objects are assumed to have a "objects" property,
|
||||
@@ -75,4 +330,68 @@ ex.objects_with_tag[prosperon.DOC] = `
|
||||
Retrieve all objects currently tagged with the specified tag.
|
||||
`
|
||||
|
||||
function parse_file(content, file) {
|
||||
if (!content) return {}
|
||||
if (content.match()
|
||||
if (!/^\s*---\s*$/m.test(content)) {
|
||||
var part = content.trim()
|
||||
if (part.match(/return\s+[^;]+;?\s*$/)) {
|
||||
return { module: part }
|
||||
}
|
||||
return { program: part }
|
||||
}
|
||||
var parts = content.split(/\n\s*---\s*\n/)
|
||||
var module = parts[0]
|
||||
if (!/\breturn\b/.test(module))
|
||||
throw new Error(`Malformed file: ${file}. Module section must end with a return statement.`)
|
||||
|
||||
try {
|
||||
new Function(module)()
|
||||
} catch (e) {
|
||||
throw new Error(`Malformed file: ${file}. Module section must end with a return statement.\n` + e.message)
|
||||
}
|
||||
|
||||
var pad = '\n'.repeat(module.split('\n').length + 4)
|
||||
return {
|
||||
module,
|
||||
program: pad + parts[1]
|
||||
}
|
||||
}
|
||||
|
||||
// path is the path of a module or script to resolve
|
||||
var script_fn = function script_fn(path, args) {
|
||||
var parsed = {}
|
||||
var file = resources.find_script(path)
|
||||
|
||||
if (!file) {
|
||||
parsed.module_ret = bare_load(path)
|
||||
if (!parsed.module_ret) throw new Error(`Module ${path} could not be created`)
|
||||
return parsed
|
||||
}
|
||||
|
||||
var content = io.slurp(file)
|
||||
var parsed = parse_file(content, file)
|
||||
var module_name = file.name()
|
||||
parsed.module_ret = bare_load(path)
|
||||
parsed.module_ret ??= {}
|
||||
|
||||
if (parsed.module) {
|
||||
// Create a context object with args
|
||||
var context = Object.create(parsed.module_ret)
|
||||
context.__args__ = args || []
|
||||
var mod_script = `(function setup_${module_name}_module(){ var self = this; var $ = this; var exports = {}; var module = {exports: exports}; var define = undefined; var arg = this.__args__; ${parsed.module}})`
|
||||
var module_fn = js.eval(file, mod_script)
|
||||
parsed.module_ret = module_fn.call(context)
|
||||
if (parsed.module_ret === undefined || parsed.module_ret === null)
|
||||
throw new Error(`Module ${module_name} must return a value`)
|
||||
parsed.module_fn = module_fn
|
||||
}
|
||||
|
||||
parsed.program ??= ""
|
||||
var prog_script = `(function use_${module_name}($_) { var self = this; var $ = this.__proto__; ${parsed.program}})`
|
||||
parsed.prog_fn = js.eval(file, prog_script)
|
||||
|
||||
return parsed
|
||||
}.hashify()
|
||||
|
||||
return ex
|
||||
|
||||
@@ -1906,9 +1906,7 @@ JSC_CCALL(os_guid,
|
||||
return JS_NewString(js,guid_str);
|
||||
)
|
||||
|
||||
JSC_SCALL(console_print,
|
||||
printf("%s", str);
|
||||
)
|
||||
JSC_SCALL(console_print, printf("%s", str); )
|
||||
|
||||
static const JSCFunctionListEntry js_console_funcs[] = {
|
||||
MIST_FUNC_DEF(console,print,1),
|
||||
@@ -2762,9 +2760,6 @@ JSC_SCALL(os_use_embed,
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (JS_IsUndefined(ret))
|
||||
ret = JS_ThrowReferenceError(js,"Library %s could not be found embedded", str);
|
||||
)
|
||||
|
||||
JSC_SCALL(os_use_dyn,
|
||||
|
||||
Reference in New Issue
Block a user