fix makefile; prettify javascript

This commit is contained in:
John Alanbrook 2024-09-26 11:36:09 -05:00
parent f95249f147
commit f35c77c4a9
34 changed files with 4384 additions and 4203 deletions

4
.prettierignore Normal file
View file

@ -0,0 +1,4 @@
**/*.html
**/*.json
**/*.css
**/*.md

21
.prettierrc Normal file
View file

@ -0,0 +1,21 @@
{
"arrowParens": "avoid",
"bracketSameLine": false,
"bracketSpacing": true,
"semi": true,
"experimentalTernaries": false,
"singleQuote": false,
"jsxSingleQuote": false,
"quoteProps": "as-needed",
"trailingComma": "all",
"singleAttributePerLine": false,
"htmlWhitespaceSensitivity": "css",
"vueIndentScriptAndStyle": false,
"proseWrap": "preserve",
"insertPragma": false,
"printWidth": 1000,
"requirePragma": false,
"tabWidth": 2,
"useTabs": false,
"embeddedLanguageFormatting": "auto"
}

View file

@ -16,5 +16,5 @@ achievement.doc = {
hidden: "True if the player shouldn't see this achievement.", hidden: "True if the player shouldn't see this achievement.",
icon: "Path to an unlocked icon.", icon: "Path to an unlocked icon.",
licon: "Path to icon for not achieved.", licon: "Path to icon for not achieved.",
max: "Value needed to reach to unlock the achievement." max: "Value needed to reach to unlock the achievement.",
}; };

View file

@ -5,8 +5,7 @@ var script_times = {};
var actor_spawns = {}; var actor_spawns = {};
globalThis.class_use = function(script, config, base, callback) globalThis.class_use = function (script, config, base, callback) {
{
var file = Resources.find_script(script); var file = Resources.find_script(script);
if (!file) { if (!file) {
@ -35,20 +34,17 @@ globalThis.class_use = function(script, config, base, callback)
var fn = os.eval(file, script); var fn = os.eval(file, script);
fn.call(padawan); fn.call(padawan);
if (typeof config === 'object') if (typeof config === "object") Object.merge(padawan, config);
Object.merge(padawan,config);
return padawan; return padawan;
} };
globalThis.rmactor = function(e) globalThis.rmactor = function (e) {
{
if (!actor_spawns[e._file]) return; if (!actor_spawns[e._file]) return;
actor_spawns[e._file].remove(e); actor_spawns[e._file].remove(e);
} };
actor.__stats = function() actor.__stats = function () {
{
var total = 0; var total = 0;
var stats = {}; var stats = {};
for (var i in actor_spawns) { for (var i in actor_spawns) {
@ -57,10 +53,9 @@ actor.__stats = function()
} }
stats.total = total; stats.total = total;
return stats; return stats;
} };
actor.hotreload = function() actor.hotreload = function () {
{
profile.cache("hotreload", "check"); profile.cache("hotreload", "check");
for (var i in script_times) { for (var i in script_times) {
if (io.mod(i) > script_times[i]) { if (io.mod(i) > script_times[i]) {
@ -76,8 +71,7 @@ actor.hotreload = function()
for (var obj of actor_spawns[i]) { for (var obj of actor_spawns[i]) {
var a = obj; var a = obj;
for (var t of a.timers) for (var t of a.timers) t();
t();
a.timers = []; a.timers = [];
var save = json.decode(json.encode(a)); var save = json.decode(json.encode(a));
fn.call(a); fn.call(a);
@ -87,10 +81,10 @@ actor.hotreload = function()
} }
} }
profile.endcache(); profile.endcache();
} };
actor.spawn = function (script, config) { actor.spawn = function (script, config) {
if (typeof script !== 'string') return undefined; if (typeof script !== "string") return undefined;
var padawan = class_use(script, config, actor); var padawan = class_use(script, config, actor);
@ -98,7 +92,9 @@ actor.spawn = function(script, config){
padawan.timers = []; padawan.timers = [];
padawan.master = this; padawan.master = this;
Object.hide(padawan, "master", "padawans"); Object.hide(padawan, "master", "padawans");
padawan.toString = function() { return script; } padawan.toString = function () {
return script;
};
check_registers(padawan); check_registers(padawan);
this.padawans.push(padawan); this.padawans.push(padawan);
if (padawan.awake) padawan.awake(); if (padawan.awake) padawan.awake();
@ -109,14 +105,13 @@ actor.tween = function(from,to,time,fn) {
var stop = tween(from, to, time, fn); var stop = tween(from, to, time, fn);
this.timers.push(stop); this.timers.push(stop);
return 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.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) actor.rm_pawn = function (pawn) {
{
this.padawans.remove(pawn); this.padawans.remove(pawn);
} };
actor.timers = []; actor.timers = [];
actor.kill = function () { actor.kill = function () {
@ -129,10 +124,10 @@ actor.kill = function(){
this.padawans = []; this.padawans = [];
this.__dead__ = true; this.__dead__ = true;
actor_spawns[this._file].remove(this); actor_spawns[this._file].remove(this);
if (typeof this.die === 'function') this.die(); if (typeof this.die === "function") this.die();
if (typeof this.stop === 'function') this.stop(); if (typeof this.stop === "function") this.stop();
if (typeof this.garbage === 'function') this.garbage(); if (typeof this.garbage === "function") this.garbage();
if (typeof this.then === 'function') this.then(); if (typeof this.then === "function") this.then();
}; };
actor.kill.doc = `Remove this actor and all its padawans from existence.`; actor.kill.doc = `Remove this actor and all its padawans from existence.`;
@ -143,7 +138,7 @@ actor.delay = function(fn, seconds) {
timers.remove(stop); timers.remove(stop);
stop = undefined; stop = undefined;
rm(); rm();
} };
function execute() { function execute() {
if (fn) fn(); if (fn) fn();
@ -153,7 +148,9 @@ actor.delay = function(fn, seconds) {
stop.remain = seconds; stop.remain = seconds;
stop.seconds = seconds; stop.seconds = seconds;
stop.pct = function() { return 1-(stop.remain / stop.seconds); }; stop.pct = function () {
return 1 - stop.remain / stop.seconds;
};
function update(dt) { function update(dt) {
profile.frame("timer"); profile.frame("timer");
@ -169,22 +166,23 @@ actor.delay = function(fn, seconds) {
}; };
actor.delay.doc = `Call 'fn' after 'seconds' with 'this' set to the actor.`; actor.delay.doc = `Call 'fn' after 'seconds' with 'this' set to the actor.`;
actor.interval = function(fn, seconds) actor.interval = function (fn, seconds) {
{
var self = this; var self = this;
var stop; var stop;
var usefn = function () { var usefn = function () {
fn(); fn();
stop = self.delay(usefn, seconds); stop = self.delay(usefn, seconds);
} };
stop = self.delay(usefn, seconds); stop = self.delay(usefn, seconds);
return stop; return stop;
} };
actor.padawans = []; actor.padawans = [];
global.app = Object.create(actor); global.app = Object.create(actor);
app.die = function() { os.quit(); } app.die = function () {
os.quit();
};
return { actor, app }; return { actor, app };

View file

@ -2,9 +2,7 @@ var ai = {
race(list) { race(list) {
return function (dt) { return function (dt) {
var good = false; var good = false;
for (var i = 0; i < list.length; i++) for (var i = 0; i < list.length; i++) if (list[i].call(this, dt)) good = true;
if (list[i].call(this,dt)) good=true;
return good; return good;
}; };
}, },
@ -13,32 +11,31 @@ var ai = {
var i = 0; var i = 0;
var fn = function (dt) { var fn = function (dt) {
while (i !== list.length) { while (i !== list.length) {
if (list[i].call(this,dt)) if (list[i].call(this, dt)) i++;
i++; else return false;
else
return false;
} }
if (fn.done) fn.done(); if (fn.done) fn.done();
return true; return true;
}; };
fn.restart = function() { i = 0; }; fn.restart = function () {
i = 0;
};
return fn; return fn;
}, },
parallel(list) { parallel(list) {
return function (dt) { return function (dt) {
var good = true; var good = true;
list.forEach(function(x){ if (!x.call(this,dt)) good = false; },this); list.forEach(function (x) {
if (!x.call(this, dt)) good = false;
}, this);
return good; return good;
}; };
}, },
dofor(secs, fn) { dofor(secs, fn) {
return ai.race([ return ai.race([ai.wait(secs), fn]);
ai.wait(secs),
fn
]);
}, },
wait(secs = 1) { wait(secs = 1) {

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,7 @@
this.phys = physics.kinematic; this.phys = physics.kinematic;
this.dir_view2world = function(dir) { return dir.scale(this.zoom); }; this.dir_view2world = function (dir) {
return dir.scale(this.zoom);
};
this.view2world = function (pos) { this.view2world = function (pos) {
var useren = window.rendersize.scale(this.zoom); var useren = window.rendersize.scale(this.zoom);
if (window.mode === window.modetypes.stretch) { if (window.mode === window.modetypes.stretch) {
@ -31,13 +33,16 @@ this.world2view = function(pos) {
pos = pos.add(window.size.scale(0.5)); pos = pos.add(window.size.scale(0.5));
} }
if (window.mode === window.modetypes.expand) { if (window.mode === window.modetypes.expand) {
} }
return pos; return pos;
}; };
this.screenright = function() { return this.view2world(window.size).x; } this.screenright = function () {
this.screenleft = function() { return this.view2world([0,0]).x; } return this.view2world(window.size).x;
};
this.screenleft = function () {
return this.view2world([0, 0]).x;
};
var zoom = 1; var zoom = 1;
@ -46,6 +51,7 @@ Object.mixin(self, {
zoom = x; zoom = x;
if (zoom <= 0.1) zoom = 0.1; if (zoom <= 0.1) zoom = 0.1;
}, },
get zoom() { return zoom; } get zoom() {
} return zoom;
); },
});

View file

@ -15,19 +15,22 @@ var Color = {
Color.editor = {}; Color.editor = {};
Color.editor.ur = Color.green; Color.editor.ur = Color.green;
Color.tohtml = function(v) Color.tohtml = function (v) {
{ var html = v.map(function (n) {
var html = v.map(function(n) { return Number.hex(n*255); }); return Number.hex(n * 255);
return "#" + html.join(''); });
} return "#" + html.join("");
};
var esc = {}; var esc = {};
esc.reset = "\x1b[0"; esc.reset = "\x1b[0";
esc.color = function (v) { esc.color = function (v) {
var c = v.map(function(n) { return Math.floor(n*255); }); var c = v.map(function (n) {
var truecolor = "\x1b[38;2;" + c.join(';') + ';'; return Math.floor(n * 255);
});
var truecolor = "\x1b[38;2;" + c.join(";") + ";";
return truecolor; return truecolor;
} };
esc.doc = "Functions and constants for ANSI escape sequences."; esc.doc = "Functions and constants for ANSI escape sequences.";
@ -44,13 +47,13 @@ Color.Arkanoid = {
}; };
Color.Arkanoid.Powerups = { Color.Arkanoid.Powerups = {
red: [174,0,0], /* laser */ red: [174, 0, 0] /* laser */,
blue: [0,0,174], /* enlarge */ blue: [0, 0, 174] /* enlarge */,
green: [0,174,0], /* catch */ green: [0, 174, 0] /* catch */,
orange: [224,143,0], /* slow */ orange: [224, 143, 0] /* slow */,
purple: [210,0,210], /* break */ purple: [210, 0, 210] /* break */,
cyan: [0,174,255], /* disruption */ cyan: [0, 174, 255] /* disruption */,
gray: [143,143,143] /* 1up */ gray: [143, 143, 143] /* 1up */,
}; };
Color.Gameboy = { Color.Gameboy = {
@ -66,7 +69,7 @@ Color.Apple = {
orange: [247, 130, 0], orange: [247, 130, 0],
red: [226, 56, 56], red: [226, 56, 56],
purple: [151, 57, 153], purple: [151, 57, 153],
blue: [0,156,223] blue: [0, 156, 223],
}; };
Color.Debug = { Color.Debug = {
@ -90,7 +93,7 @@ Color.normalize = function(c) {
for (var p of Object.keys(c)) { for (var p of Object.keys(c)) {
var fmt = "nrm"; var fmt = "nrm";
if (typeof c[p] !== 'object') continue; if (typeof c[p] !== "object") continue;
if (!Array.isArray(c[p])) { if (!Array.isArray(c[p])) {
Color.normalize(c[p]); Color.normalize(c[p]);
continue; continue;
@ -107,7 +110,9 @@ Color.normalize = function(c) {
switch (fmt) { switch (fmt) {
case "8b": case "8b":
c[p] = c[p].map(function(x) { return x/255; }); c[p] = c[p].map(function (x) {
return x / 255;
});
} }
c[p].alpha = add_a; c[p].alpha = add_a;
} }
@ -118,24 +123,23 @@ Color.normalize(Color);
Object.deepfreeze(Color); Object.deepfreeze(Color);
var ColorMap = {}; var ColorMap = {};
ColorMap.makemap = function(map) ColorMap.makemap = function (map) {
{
var newmap = Object.create(ColorMap); var newmap = Object.create(ColorMap);
Object.assign(newmap, map); Object.assign(newmap, map);
return newmap; return newmap;
} };
ColorMap.Jet = ColorMap.makemap({ ColorMap.Jet = ColorMap.makemap({
0: [0, 0, 131], 0: [0, 0, 131],
0.125: [0, 60, 170], 0.125: [0, 60, 170],
0.375: [5, 255, 255], 0.375: [5, 255, 255],
0.625: [255, 255, 0], 0.625: [255, 255, 0],
0.875: [250, 0, 0], 0.875: [250, 0, 0],
1: [128,0,0] 1: [128, 0, 0],
}); });
ColorMap.BlueRed = ColorMap.makemap({ ColorMap.BlueRed = ColorMap.makemap({
0: [0, 0, 255], 0: [0, 0, 255],
1: [255,0,0] 1: [255, 0, 0],
}); });
ColorMap.Inferno = ColorMap.makemap({ ColorMap.Inferno = ColorMap.makemap({
@ -147,7 +151,7 @@ ColorMap.Inferno = ColorMap.makemap({
0.63: [227, 89, 51], 0.63: [227, 89, 51],
0.75: [249, 140, 10], 0.75: [249, 140, 10],
0.88: [249, 201, 50], 0.88: [249, 201, 50],
1: [252,255,164] 1: [252, 255, 164],
}); });
ColorMap.Bathymetry = ColorMap.makemap({ ColorMap.Bathymetry = ColorMap.makemap({
@ -159,7 +163,7 @@ ColorMap.Bathymetry = ColorMap.makemap({
0.63: [85, 174, 163], 0.63: [85, 174, 163],
0.75: [120, 206, 163], 0.75: [120, 206, 163],
0.88: [187, 230, 172], 0.88: [187, 230, 172],
1: [253,254,204] 1: [253, 254, 204],
}); });
ColorMap.Viridis = ColorMap.makemap({ ColorMap.Viridis = ColorMap.makemap({
@ -171,13 +175,12 @@ ColorMap.Viridis = ColorMap.makemap({
0.63: [39, 173, 129], 0.63: [39, 173, 129],
0.75: [92, 200, 99], 0.75: [92, 200, 99],
0.88: [170, 220, 50], 0.88: [170, 220, 50],
1: [253,231,37] 1: [253, 231, 37],
}); });
Color.normalize(ColorMap); Color.normalize(ColorMap);
ColorMap.sample = function(t, map = this) ColorMap.sample = function (t, map = this) {
{
if (t < 0) return map[0]; if (t < 0) return map[0];
if (t > 1) return map[1]; if (t > 1) return map[1];
@ -192,7 +195,7 @@ ColorMap.sample = function(t, map = this)
lastkey = key; lastkey = key;
} }
return map[1]; return map[1];
} };
ColorMap.doc = { ColorMap.doc = {
sample: "Sample a given colormap at the given percentage (0 to 1).", sample: "Sample a given colormap at the given percentage (0 to 1).",
@ -203,5 +206,5 @@ Object.freeze(ColorMap);
return { return {
Color, Color,
esc, esc,
ColorMap ColorMap,
} };

View file

@ -1,7 +1,6 @@
var component = {}; var component = {};
var make_point_obj = function(o, p) var make_point_obj = function (o, p) {
{
return { return {
pos: p, pos: p,
move(d) { move(d) {
@ -9,26 +8,22 @@ var make_point_obj = function(o, p)
p.x += d.x; p.x += d.x;
p.y += d.y; p.y += d.y;
}, },
sync: o.sync.bind(o) sync: o.sync.bind(o),
} };
} };
var fullrect = [0, 0, 1, 1]; var fullrect = [0, 0, 1, 1];
var sprite_addbucket = function(sprite) var sprite_addbucket = function (sprite) {
{
var layer = sprite.gameobject.drawlayer; var layer = sprite.gameobject.drawlayer;
sprite_buckets[layer] ??= {}; sprite_buckets[layer] ??= {};
sprite_buckets[layer][sprite.path] ??= {}; sprite_buckets[layer][sprite.path] ??= {};
sprite_buckets[layer][sprite.path][sprite.guid] = sprite; sprite_buckets[layer][sprite.path][sprite.guid] = sprite;
} };
var sprite_rmbucket = function(sprite) var sprite_rmbucket = function (sprite) {
{ for (var layer of Object.values(sprite_buckets)) for (var path of Object.values(layer)) delete path[sprite.guid];
for (var layer of Object.values(sprite_buckets)) };
for (var path of Object.values(layer))
delete path[sprite.guid];
}
var sprite = { var sprite = {
loop: true, loop: true,
@ -45,12 +40,11 @@ var sprite = {
self = undefined; self = undefined;
advance = undefined; advance = undefined;
stop?.(); stop?.();
} };
var playing = self.anim[str]; var playing = self.anim[str];
if (!playing) return; if (!playing) return;
var f = 0; var f = 0;
if (reverse) if (reverse) f = playing.frames.length - 1;
f = playing.frames.length-1;
self.path = playing.path; self.path = playing.path;
@ -64,20 +58,21 @@ var sprite = {
if (reverse) { if (reverse) {
f = (((f - 1) % playing.frames.length) + playing.frames.length) % playing.frames.length; f = (((f - 1) % playing.frames.length) + playing.frames.length) % playing.frames.length;
if (f === playing.frames.length - 1) done = true; if (f === playing.frames.length - 1) done = true;
} } else {
else {
f = (f + 1) % playing.frames.length; f = (f + 1) % playing.frames.length;
if (f === 0) done = true; if (f === 0) done = true;
} }
if (done) { if (done) {
fn?.(); fn?.();
if (!loop) { self?.stop(); return; } if (!loop) {
self?.stop();
return;
}
// self?.anim_done?.(); // self?.anim_done?.();
// if (!self.loop) { self.stop(); return; } // if (!self.loop) { self.stop(); return; }
} }
if (self) if (self) stop = self.gameobject.delay(advance, playing.frames[f].time / self.anim_speed);
stop = self.gameobject.delay(advance, playing.frames[f].time/self.anim_speed);
} }
advance(); advance();
@ -126,7 +121,9 @@ var sprite = {
this.anim_done = undefined; this.anim_done = undefined;
delete allsprites[this.guid]; delete allsprites[this.guid];
}, },
move(d) { this.pos = this.pos.add(d); }, move(d) {
this.pos = this.pos.add(d);
},
grow(x) { grow(x) {
this.scale = this.scale.scale(x); this.scale = this.scale.scale(x);
this.pos = this.pos.scale(x); this.pos = this.pos.scale(x);
@ -136,7 +133,9 @@ var sprite = {
sprite_rmbucket(this); sprite_rmbucket(this);
sprite_addbucket(this); sprite_addbucket(this);
}, },
pick() { return this; }, pick() {
return this;
},
boundingbox() { boundingbox() {
var dim = this.dimensions(); var dim = this.dimensions();
dim = dim.scale(this.gameobject.scale); dim = dim.scale(this.gameobject.scale);
@ -152,47 +151,88 @@ var sprite = {
dimensions() { dimensions() {
return this._dimensions; return this._dimensions;
}, },
width() { return this.dimensions().x; }, width() {
height() { return this.dimensions().y; }, return this.dimensions().x;
},
height() {
return this.dimensions().y;
},
}; };
globalThis.allsprites = {}; globalThis.allsprites = {};
var sprite_buckets = {}; var sprite_buckets = {};
component.sprite_buckets = function() { return sprite_buckets; } component.sprite_buckets = function () {
return sprite_buckets;
};
sprite.doc = { sprite.doc = {
path: "Path to the texture.", path: "Path to the texture.",
color: "Color to mix with the sprite.", color: "Color to mix with the sprite.",
pos: "The offset position of the sprite, relative to its entity." pos: "The offset position of the sprite, relative to its entity.",
}; };
sprite.setanchor = function (anch) { sprite.setanchor = function (anch) {
var off = [0, 0]; var off = [0, 0];
switch (anch) { switch (anch) {
case "ll": break; case "ll":
case "lm": off = [-0.5,0]; break; break;
case "lr": off = [-1,0]; break; case "lm":
case "ml": off = [0,-0.5]; break; off = [-0.5, 0];
case "mm": off = [-0.5,-0.5]; break; break;
case "mr": off = [-1,-0.5]; break; case "lr":
case "ul": off = [0,-1]; break; off = [-1, 0];
case "um": off = [-0.5,-1]; break; break;
case "ur": off = [-1,-1]; break; case "ml":
off = [0, -0.5];
break;
case "mm":
off = [-0.5, -0.5];
break;
case "mr":
off = [-1, -0.5];
break;
case "ul":
off = [0, -1];
break;
case "um":
off = [-0.5, -1];
break;
case "ur":
off = [-1, -1];
break;
} }
this.anchor = off; this.anchor = off;
this.pos = this.dimensions().scale(off); this.pos = this.dimensions().scale(off);
} };
sprite.inputs = {}; sprite.inputs = {};
sprite.inputs.kp9 = function() { this.setanchor("ll"); } sprite.inputs.kp9 = function () {
sprite.inputs.kp8 = function() { this.setanchor("lm"); } this.setanchor("ll");
sprite.inputs.kp7 = function() { this.setanchor("lr"); } };
sprite.inputs.kp6 = function() { this.setanchor("ml"); } sprite.inputs.kp8 = function () {
sprite.inputs.kp5 = function() { this.setanchor("mm"); } this.setanchor("lm");
sprite.inputs.kp4 = function() { this.setanchor("mr"); } };
sprite.inputs.kp3 = function() { this.setanchor("ur"); } sprite.inputs.kp7 = function () {
sprite.inputs.kp2 = function() { this.setanchor("um"); } this.setanchor("lr");
sprite.inputs.kp1 = function() { this.setanchor("ul"); } };
sprite.inputs.kp6 = function () {
this.setanchor("ml");
};
sprite.inputs.kp5 = function () {
this.setanchor("mm");
};
sprite.inputs.kp4 = function () {
this.setanchor("mr");
};
sprite.inputs.kp3 = function () {
this.setanchor("ur");
};
sprite.inputs.kp2 = function () {
this.setanchor("um");
};
sprite.inputs.kp1 = function () {
this.setanchor("ul");
};
component.sprite = function (obj) { component.sprite = function (obj) {
var sp = Object.create(sprite); var sp = Object.create(sprite);
@ -203,12 +243,14 @@ component.sprite = function(obj) {
if (component.sprite.make_hook) component.sprite.make_hook(sp); if (component.sprite.make_hook) component.sprite.make_hook(sp);
sprite_addbucket(sp); sprite_addbucket(sp);
return sp; return sp;
} };
sprite.shade = [1, 1, 1, 1]; sprite.shade = [1, 1, 1, 1];
Object.mixin(os.make_seg2d(), { Object.mixin(os.make_seg2d(), {
sync() { this.set_endpoints(this.points[0], this.points[1]); } sync() {
this.set_endpoints(this.points[0], this.points[1]);
},
}); });
/* sprite anim returns a data structure for the given file path /* sprite anim returns a data structure for the given file path
@ -221,16 +263,11 @@ var animcache = {};
var SpriteAnim = {}; var SpriteAnim = {};
SpriteAnim.make = function (path) { SpriteAnim.make = function (path) {
var anim; var anim;
if (io.exists(path.set_ext(".ase"))) if (io.exists(path.set_ext(".ase"))) anim = SpriteAnim.aseprite(path.set_ext(".ase"));
anim = SpriteAnim.aseprite(path.set_ext(".ase")); else if (io.exists(path.set_ext(".json"))) anim = SpriteAnim.aseprite(path.set_ext(".json"));
else if (io.exists(path.set_ext(".json"))) else if (path.ext() === "ase") anim = SpriteAnim.aseprite(path);
anim = SpriteAnim.aseprite(path.set_ext(".json")); else if (path.ext() === "gif") anim = SpriteAnim.gif(path);
else if (path.ext() === 'ase') else anim = undefined;
anim = SpriteAnim.aseprite(path);
else if (path.ext() === 'gif')
anim = SpriteAnim.gif(path);
else
anim = undefined;
animcache[path] = anim; animcache[path] = anim;
return animcache[path]; return animcache[path];
@ -249,14 +286,13 @@ SpriteAnim.gif = function(path) {
x: 0, x: 0,
w: 1, w: 1,
y: yslice * f, y: yslice * f,
h: yslice h: yslice,
}; };
frame.time = 0.05; frame.time = 0.05;
anim.frames.push(frame); anim.frames.push(frame);
} }
var times = tex.delays; var times = tex.delays;
for (var i = 0; i < frames; i++) for (var i = 0; i < frames; i++) anim.frames[i].time = times[i] / 1000;
anim.frames[i].time = times[i]/1000;
anim.loop = true; anim.loop = true;
var dim = [tex.width, tex.height]; var dim = [tex.width, tex.height];
dim.y /= frames; dim.y /= frames;
@ -294,7 +330,7 @@ SpriteAnim.aseprite = function(path) {
x: f.x / dim.w, x: f.x / dim.w,
w: f.w / dim.w, w: f.w / dim.w,
y: f.y / dim.h, y: f.y / dim.h,
h: f.h/dim.h h: f.h / dim.h,
}; };
frame.time = ase_frame.duration / 1000; frame.time = ase_frame.duration / 1000;
anim.frames.push(frame); anim.frames.push(frame);
@ -304,7 +340,7 @@ SpriteAnim.aseprite = function(path) {
anim.dim = frameset[0].sourceSize; anim.dim = frameset[0].sourceSize;
anim.loop = true; anim.loop = true;
return anim; return anim;
}; }
var data = json.decode(io.slurp(path)); var data = json.decode(io.slurp(path));
if (!data?.meta?.app.includes("aseprite")) return; if (!data?.meta?.app.includes("aseprite")) return;
@ -326,8 +362,8 @@ SpriteAnim.aseprite = function(path) {
SpriteAnim.validate = function (anim) { SpriteAnim.validate = function (anim) {
if (!Object.isObject(anim)) return false; if (!Object.isObject(anim)) return false;
if (typeof anim.path !== 'string') return false; if (typeof anim.path !== "string") return false;
if (typeof anim.dim !== 'object') return false; if (typeof anim.dim !== "object") return false;
return true; return true;
}; };
@ -336,19 +372,23 @@ SpriteAnim.find = function(path) {
var asset = JSON.parse(io.slurp(path + ".asset")); var asset = JSON.parse(io.slurp(path + ".asset"));
}; };
SpriteAnim.doc = 'Functions to create Primum animations from varying sources.'; SpriteAnim.doc = "Functions to create Primum animations from varying sources.";
SpriteAnim.gif.doc = 'Convert a gif.'; SpriteAnim.gif.doc = "Convert a gif.";
SpriteAnim.strip.doc = 'Given a path and number of frames, converts a horizontal strip animation, where each cell is the same width.' SpriteAnim.strip.doc = "Given a path and number of frames, converts a horizontal strip animation, where each cell is the same width.";
SpriteAnim.aseprite.doc = 'Given an aseprite json metadata, returns an object of animations defined in the aseprite file.'; SpriteAnim.aseprite.doc = "Given an aseprite json metadata, returns an object of animations defined in the aseprite file.";
SpriteAnim.find.doc = 'Given a path, find the relevant animation for the file.'; SpriteAnim.find.doc = "Given a path, find the relevant animation for the file.";
var collider2d = {}; var collider2d = {};
collider2d.inputs = {}; collider2d.inputs = {};
collider2d.inputs['M-s'] = function() { this.sensor = !this.sensor; } collider2d.inputs["M-s"] = function () {
collider2d.inputs['M-s'].doc = "Toggle if this collider is a sensor."; this.sensor = !this.sensor;
};
collider2d.inputs["M-s"].doc = "Toggle if this collider is a sensor.";
collider2d.inputs['M-t'] = function() { this.enabled = !this.enabled; } collider2d.inputs["M-t"] = function () {
collider2d.inputs['M-t'].doc = "Toggle if this collider is enabled."; this.enabled = !this.enabled;
};
collider2d.inputs["M-t"].doc = "Toggle if this collider is enabled.";
Object.mix(os.make_poly2d(), { Object.mix(os.make_poly2d(), {
boundingbox() { boundingbox() {
@ -383,20 +423,18 @@ Object.mix(os.make_poly2d(), {
}, },
pick(pos) { pick(pos) {
if (!Object.hasOwn(this,'points')) if (!Object.hasOwn(this, "points")) this.points = deep_copy(this.__proto__.points);
this.points = deep_copy(this.__proto__.points);
var i = Gizmos.pick_gameobject_points(pos, this.gameobject, this.points); var i = Gizmos.pick_gameobject_points(pos, this.gameobject, this.points);
var p = this.points[i]; var p = this.points[i];
if (p) if (p) return make_point_obj(this, p);
return make_point_obj(this, p);
return undefined; return undefined;
}, },
}); });
function pointscaler(x) { function pointscaler(x) {
if (typeof x === 'number') return; if (typeof x === "number") return;
this.points = this.points.map(p => p.mult(x)); this.points = this.points.map(p => p.mult(x));
} }
@ -416,35 +454,39 @@ polyinputs.f10 = function() {
}; };
polyinputs.f10.doc = "Sort all points to be CCW order."; polyinputs.f10.doc = "Sort all points to be CCW order.";
polyinputs['C-lm'] = function() { polyinputs["C-lm"] = function () {
this.points.push(this.gameobject.world2this(input.mouse.worldpos())); this.points.push(this.gameobject.world2this(input.mouse.worldpos()));
}; };
polyinputs['C-lm'].doc = "Add a point to location of mouse."; polyinputs["C-lm"].doc = "Add a point to location of mouse.";
polyinputs.lm = function () {}; polyinputs.lm = function () {};
polyinputs.lm.released = function () {}; polyinputs.lm.released = function () {};
polyinputs['C-M-lm'] = function() { polyinputs["C-M-lm"] = function () {
var idx = Math.grab_from_points(input.mouse.worldpos(), this.points.map(p => this.gameobject.this2world(p)), 25); var idx = Math.grab_from_points(
input.mouse.worldpos(),
this.points.map(p => this.gameobject.this2world(p)),
25,
);
if (idx === -1) return; if (idx === -1) return;
this.points.splice(idx, 1); this.points.splice(idx, 1);
}; };
polyinputs['C-M-lm'].doc = "Remove point under mouse."; polyinputs["C-M-lm"].doc = "Remove point under mouse.";
polyinputs['C-b'] = function() { polyinputs["C-b"] = function () {
this.points = this.spoints; this.points = this.spoints;
this.flipx = false; this.flipx = false;
this.flipy = false; this.flipy = false;
}; };
polyinputs['C-b'].doc = "Freeze mirroring in place."; polyinputs["C-b"].doc = "Freeze mirroring in place.";
var edge2d = { var edge2d = {
dimensions: 2, dimensions: 2,
thickness: 1, thickness: 1,
/* if type === -1, point to point */ /* if type === -1, point to point */
type: Spline.type.catmull, type: Spline.type.catmull,
C: 1, /* when in bezier, continuity required. 0, 1 or 2. */ C: 1 /* when in bezier, continuity required. 0, 1 or 2. */,
looped: false, looped: false,
angle: 0.5, /* smaller for smoother bezier */ angle: 0.5 /* smaller for smoother bezier */,
elasticity: 0, elasticity: 0,
friction: 0, friction: 0,
sync() { sync() {
@ -474,8 +516,7 @@ var edge2d = {
var spoints = this.points.slice(); var spoints = this.points.slice();
if (this.flipx) { if (this.flipx) {
if (Spline.is_bezier(this.type)) if (Spline.is_bezier(this.type)) spoints.push(Vector.reflect_point(spoints.at(-2), spoints.at(-1)));
spoints.push(Vector.reflect_point(spoints.at(-2), spoints.at(-1)));
for (var i = spoints.length - 1; i >= 0; i--) { for (var i = spoints.length - 1; i >= 0; i--) {
var newpoint = spoints[i].slice(); var newpoint = spoints[i].slice();
@ -485,8 +526,7 @@ var edge2d = {
} }
if (this.flipy) { if (this.flipy) {
if (Spline.is_bezier(this.type)) if (Spline.is_bezier(this.type)) spoints.push(Vector.reflect(point(spoints.at(-2), spoints.at(-1))));
spoints.push(Vector.reflect(point(spoints.at(-2),spoints.at(-1))));
for (var i = spoints.length - 1; i >= 0; i--) { for (var i = spoints.length - 1; i >= 0; i--) {
var newpoint = spoints[i].slice(); var newpoint = spoints[i].slice();
@ -498,13 +538,16 @@ var edge2d = {
if (this.hollow) { if (this.hollow) {
var hpoints = vector.inflate(spoints, this.hollowt); var hpoints = vector.inflate(spoints, this.hollowt);
if (hpoints.length === spoints.length) return spoints; if (hpoints.length === spoints.length) return spoints;
var arr1 = hpoints.filter(function(x,i) { return i % 2 === 0; }); var arr1 = hpoints.filter(function (x, i) {
var arr2 = hpoints.filter(function(x,i) { return i % 2 !== 0; }); return i % 2 === 0;
});
var arr2 = hpoints.filter(function (x, i) {
return i % 2 !== 0;
});
return arr1.concat(arr2.reverse()); return arr1.concat(arr2.reverse());
} }
if (this.looped) if (this.looped) spoints = spoints.wrapped(1);
spoints = spoints.wrapped(1);
return spoints; return spoints;
}, },
@ -523,21 +566,20 @@ var edge2d = {
} }
if (this.type === Spline.type.catmull) { if (this.type === Spline.type.catmull) {
if (this.looped) if (this.looped) spoints = Spline.catmull_loop(spoints);
spoints = Spline.catmull_loop(spoints); else spoints = Spline.catmull_caps(spoints);
else
spoints = Spline.catmull_caps(spoints);
return Spline.sample_angle(this.type, spoints, this.angle); return Spline.sample_angle(this.type, spoints, this.angle);
} }
if (this.looped && Spline.is_bezier(this.type)) if (this.looped && Spline.is_bezier(this.type)) spoints = Spline.bezier_loop(spoints);
spoints = Spline.bezier_loop(spoints);
return Spline.sample_angle(this.type, spoints, this.angle); return Spline.sample_angle(this.type, spoints, this.angle);
}, },
boundingbox() { return bbox.frompoints(this.points.map(x => x.scale(this.gameobject.scale))); }, boundingbox() {
return bbox.frompoints(this.points.map(x => x.scale(this.gameobject.scale)));
},
/* EDITOR */ /* EDITOR */
gizmo() { gizmo() {
@ -545,8 +587,7 @@ var edge2d = {
this.spoints().forEach(x => render.point(this.gameobject.this2screen(x), 3, Color.teal)); this.spoints().forEach(x => render.point(this.gameobject.this2screen(x), 3, Color.teal));
this.points.forEach((x, i) => render.coordinate(this.gameobject.this2screen(x))); this.points.forEach((x, i) => render.coordinate(this.gameobject.this2screen(x)));
} else { } else {
for (var i = 0; i < this.points.length; i += 3) for (var i = 0; i < this.points.length; i += 3) render.coordinate(this.gameobject.this2screen(this.points[i]), 1, Color.teal);
render.coordinate(this.gameobject.this2screen(this.points[i]), 1, Color.teal);
for (var i = 1; i < this.points.length; i += 3) { for (var i = 1; i < this.points.length; i += 3) {
render.coordinate(this.gameobject.this2screen(this.points[i]), 1, Color.green); render.coordinate(this.gameobject.this2screen(this.points[i]), 1, Color.green);
@ -557,22 +598,25 @@ var edge2d = {
} }
}, },
finish_center(change) { this.points = this.points.map(function(x) { return x.sub(change); }); }, finish_center(change) {
this.points = this.points.map(function (x) {
return x.sub(change);
});
},
pick(pos) { pick(pos) {
var i = Gizmos.pick_gameobject_points(pos, this.gameobject, this.points); var i = Gizmos.pick_gameobject_points(pos, this.gameobject, this.points);
var p = this.points[i]; var p = this.points[i];
if (!p) return undefined; if (!p) return undefined;
if (Spline.is_catmull(this.type) || this.type === -1) if (Spline.is_catmull(this.type) || this.type === -1) return make_point_obj(this, p);
return make_point_obj(this,p);
var that = this.gameobject; var that = this.gameobject;
var me = this; var me = this;
if (p) { if (p) {
var o = { var o = {
pos: p, pos: p,
sync: me.sync.bind(me) sync: me.sync.bind(me),
}; };
if (Spline.bezier_is_handle(this.points, i)) if (Spline.bezier_is_handle(this.points, i))
o.move = function (d) { o.move = function (d) {
@ -587,25 +631,21 @@ var edge2d = {
p.x += d.x; p.x += d.x;
p.y += d.y; p.y += d.y;
var pp = Spline.bezier_point_handles(me.points, i); var pp = Spline.bezier_point_handles(me.points, i);
pp.forEach(ph => me.points[ph] = me.points[ph].add(d)); pp.forEach(ph => (me.points[ph] = me.points[ph].add(d)));
} };
return o; return o;
} }
}, },
rm_node(idx) { rm_node(idx) {
if (idx < 0 || idx >= this.points.length) return; if (idx < 0 || idx >= this.points.length) return;
if (Spline.is_catmull(this.type)) if (Spline.is_catmull(this.type)) this.points.splice(idx, 1);
this.points.splice(idx,1);
if (Spline.is_bezier(this.type)) { if (Spline.is_bezier(this.type)) {
assert(Spline.bezier_is_node(this.points, idx), 'Attempted to delete a bezier handle.'); assert(Spline.bezier_is_node(this.points, idx), "Attempted to delete a bezier handle.");
if (idx === 0) if (idx === 0) this.points.splice(idx, 2);
this.points.splice(idx,2); else if (idx === this.points.length - 1) this.points.splice(this.points.length - 2, 2);
else if (idx === this.points.length-1) else this.points.splice(idx - 1, 3);
this.points.splice(this.points.length-2,2);
else
this.points.splice(idx-1,3);
} }
}, },
@ -613,13 +653,10 @@ var edge2d = {
pos = this.gameobject.world2this(pos); pos = this.gameobject.world2this(pos);
var idx = 0; var idx = 0;
if (Spline.is_catmull(this.type) || this.type === -1) { if (Spline.is_catmull(this.type) || this.type === -1) {
if (this.points.length >= 2) if (this.points.length >= 2) idx = physics.closest_point(pos, this.points, 400);
idx = physics.closest_point(pos, this.points, 400);
if (idx === this.points.length) if (idx === this.points.length) this.points.push(pos);
this.points.push(pos); else this.points.splice(idx, 0, pos);
else
this.points.splice(idx, 0, pos);
} }
if (Spline.is_bezier(this.type)) { if (Spline.is_bezier(this.type)) {
@ -653,35 +690,45 @@ component.edge2d = function(obj) {
var edge = Object.create(edge2d); var edge = Object.create(edge2d);
edge.body = obj.body; edge.body = obj.body;
return edge; return edge;
} };
edge2d.spoints.doc = "Returns the controls points after modifiers are applied, such as it being hollow or mirrored on its axises."; edge2d.spoints.doc = "Returns the controls points after modifiers are applied, such as it being hollow or mirrored on its axises.";
edge2d.inputs = {}; edge2d.inputs = {};
edge2d.inputs.h = function() { this.hollow = !this.hollow; }; edge2d.inputs.h = function () {
this.hollow = !this.hollow;
};
edge2d.inputs.h.doc = "Toggle hollow."; edge2d.inputs.h.doc = "Toggle hollow.";
edge2d.inputs['C-g'] = function() { if (this.hollowt > 0) this.hollowt--; }; edge2d.inputs["C-g"] = function () {
edge2d.inputs['C-g'].doc = "Thin the hollow thickness."; if (this.hollowt > 0) this.hollowt--;
edge2d.inputs['C-g'].rep = true; };
edge2d.inputs["C-g"].doc = "Thin the hollow thickness.";
edge2d.inputs["C-g"].rep = true;
edge2d.inputs['C-f'] = function() { this.hollowt++; }; edge2d.inputs["C-f"] = function () {
edge2d.inputs['C-f'].doc = "Increase the hollow thickness."; this.hollowt++;
edge2d.inputs['C-f'].rep = true; };
edge2d.inputs["C-f"].doc = "Increase the hollow thickness.";
edge2d.inputs["C-f"].rep = true;
edge2d.inputs['M-v'] = function() { if (this.thickness > 0) this.thickness--; }; edge2d.inputs["M-v"] = function () {
edge2d.inputs['M-v'].doc = "Decrease spline thickness."; if (this.thickness > 0) this.thickness--;
edge2d.inputs['M-v'].rep = true; };
edge2d.inputs["M-v"].doc = "Decrease spline thickness.";
edge2d.inputs["M-v"].rep = true;
edge2d.inputs['C-y'] = function() { edge2d.inputs["C-y"] = function () {
this.points = this.spoints(); this.points = this.spoints();
this.flipx = false; this.flipx = false;
this.flipy = false; this.flipy = false;
this.hollow = false; this.hollow = false;
}; };
edge2d.inputs['C-y'].doc = "Freeze mirroring,"; edge2d.inputs["C-y"].doc = "Freeze mirroring,";
edge2d.inputs['M-b'] = function() { this.thickness++; }; edge2d.inputs["M-b"] = function () {
edge2d.inputs['M-b'].doc = "Increase spline thickness."; this.thickness++;
edge2d.inputs['M-b'].rep = true; };
edge2d.inputs["M-b"].doc = "Increase spline thickness.";
edge2d.inputs["M-b"].rep = true;
edge2d.inputs.plus = function () { edge2d.inputs.plus = function () {
if (this.angle <= 1) { if (this.angle <= 1) {
@ -693,17 +740,23 @@ edge2d.inputs.plus = function() {
edge2d.inputs.plus.doc = "Increase the number of samples of this spline."; edge2d.inputs.plus.doc = "Increase the number of samples of this spline.";
edge2d.inputs.plus.rep = true; edge2d.inputs.plus.rep = true;
edge2d.inputs.minus = function() { this.angle *= 1.1; }; edge2d.inputs.minus = function () {
this.angle *= 1.1;
};
edge2d.inputs.minus.doc = "Decrease the number of samples on this spline."; edge2d.inputs.minus.doc = "Decrease the number of samples on this spline.";
edge2d.inputs.minus.rep = true; edge2d.inputs.minus.rep = true;
edge2d.inputs['C-r'] = function() { this.points = this.points.reverse(); }; edge2d.inputs["C-r"] = function () {
edge2d.inputs['C-r'].doc = "Reverse the order of the spline's points."; this.points = this.points.reverse();
};
edge2d.inputs["C-r"].doc = "Reverse the order of the spline's points.";
edge2d.inputs['C-l'] = function() { this.looped = !this.looped}; edge2d.inputs["C-l"] = function () {
edge2d.inputs['C-l'].doc = "Toggle spline being looped."; this.looped = !this.looped;
};
edge2d.inputs["C-l"].doc = "Toggle spline being looped.";
edge2d.inputs['C-c'] = function() { edge2d.inputs["C-c"] = function () {
switch (this.type) { switch (this.type) {
case Spline.type.bezier: case Spline.type.bezier:
this.points = Spline.bezier2catmull(this.points); this.points = Spline.bezier2catmull(this.points);
@ -712,9 +765,9 @@ edge2d.inputs['C-c'] = function() {
this.type = Spline.type.catmull; this.type = Spline.type.catmull;
}; };
edge2d.inputs['C-c'].doc = "Set type of spline to catmull-rom."; edge2d.inputs["C-c"].doc = "Set type of spline to catmull-rom.";
edge2d.inputs['C-b'] = function() { edge2d.inputs["C-b"] = function () {
switch (this.type) { switch (this.type) {
case Spline.type.catmull: case Spline.type.catmull:
this.points = Spline.catmull2bezier(Spline.catmull_caps(this.points)); this.points = Spline.catmull2bezier(Spline.catmull_caps(this.points));
@ -723,37 +776,52 @@ edge2d.inputs['C-b'] = function() {
this.type = Spline.type.bezier; this.type = Spline.type.bezier;
}; };
edge2d.inputs['C-o'] = function() { this.type = -1; }; edge2d.inputs["C-o"] = function () {
edge2d.inputs['C-o'].doc = "Set spline to linear."; this.type = -1;
};
edge2d.inputs["C-o"].doc = "Set spline to linear.";
edge2d.inputs['C-M-lm'] = function() { edge2d.inputs["C-M-lm"] = function () {
if (Spline.is_catmull(this.type)) { if (Spline.is_catmull(this.type)) {
var idx = Math.grab_from_points(input.mouse.worldpos(), this.points.map(p => this.gameobject.this2world(p)), 25); var idx = Math.grab_from_points(
input.mouse.worldpos(),
this.points.map(p => this.gameobject.this2world(p)),
25,
);
if (idx === -1) return; if (idx === -1) return;
} else { } else {
} }
this.points = this.points.newfirst(idx); this.points = this.points.newfirst(idx);
}; };
edge2d.inputs['C-M-lm'].doc = "Select the given point as the '0' of this spline."; edge2d.inputs["C-M-lm"].doc = "Select the given point as the '0' of this spline.";
edge2d.inputs['C-lm'] = function() { this.add_node(input.mouse.worldpos()); } edge2d.inputs["C-lm"] = function () {
edge2d.inputs['C-lm'].doc = "Add a point to the spline at the mouse position."; this.add_node(input.mouse.worldpos());
};
edge2d.inputs["C-lm"].doc = "Add a point to the spline at the mouse position.";
edge2d.inputs['C-M-lm'] = function() { edge2d.inputs["C-M-lm"] = function () {
var idx = -1; var idx = -1;
if (Spline.is_catmull(this.type)) if (Spline.is_catmull(this.type))
idx = Math.grab_from_points(input.mouse.worldpos(), this.points.map(p => this.gameobject.this2world(p)), 25); idx = Math.grab_from_points(
input.mouse.worldpos(),
this.points.map(p => this.gameobject.this2world(p)),
25,
);
else { else {
var nodes = Spline.bezier_nodes(this.points); var nodes = Spline.bezier_nodes(this.points);
idx = Math.grab_from_points(input.mouse.worldpos(), nodes.map(p => this.gameobject.this2world(p)), 25); idx = Math.grab_from_points(
input.mouse.worldpos(),
nodes.map(p => this.gameobject.this2world(p)),
25,
);
idx *= 3; idx *= 3;
} }
this.rm_node(idx); this.rm_node(idx);
}; };
edge2d.inputs['C-M-lm'].doc = "Remove point from the spline."; edge2d.inputs["C-M-lm"].doc = "Remove point from the spline.";
edge2d.inputs.lm = function () {}; edge2d.inputs.lm = function () {};
edge2d.inputs.lm.released = function () {}; edge2d.inputs.lm.released = function () {};
@ -788,24 +856,34 @@ function shape_maker(maker) {
return function (obj) { return function (obj) {
if (!obj.body) obj.rigidify(); if (!obj.body) obj.rigidify();
return maker(obj.body); return maker(obj.body);
} };
} }
component.circle2d = shape_maker(os.make_circle2d); component.circle2d = shape_maker(os.make_circle2d);
component.poly2d = shape_maker(os.make_poly2d); component.poly2d = shape_maker(os.make_poly2d);
component.seg2d = shape_maker(os.make_seg2d); component.seg2d = shape_maker(os.make_seg2d);
Object.mix(os.make_circle2d(), { Object.mix(os.make_circle2d(), {
boundingbox() { return bbox.fromcwh(this.offset, [this.radius,this.radius]); }, boundingbox() {
return bbox.fromcwh(this.offset, [this.radius, this.radius]);
},
set scale(x) { this.radius = x; }, set scale(x) {
get scale() { return this.radius; }, this.radius = x;
},
get scale() {
return this.radius;
},
get pos() { return this.offset; }, get pos() {
set pos(x) { this.offset = x; }, return this.offset;
},
set pos(x) {
this.offset = x;
},
grow(x) { grow(x) {
if (typeof x === 'number') this.scale *= x; if (typeof x === "number") this.scale *= x;
else if (typeof x === 'object') this.scale *= x[0]; else if (typeof x === "object") this.scale *= x[0];
}, },
}); });

View file

@ -1,4 +1,7 @@
debug.build = function(fn) { if (!debug.show) return; fn(); } debug.build = function (fn) {
if (!debug.show) return;
fn();
};
debug.show = true; debug.show = true;
debug.urnames = false; debug.urnames = false;
debug.termout = true; debug.termout = true;
@ -8,14 +11,14 @@ debug.meta = false;
debug.showprofiler = false; debug.showprofiler = false;
debug.fn_break = function (fn, obj = globalThis) { debug.fn_break = function (fn, obj = globalThis) {
if (typeof fn !== 'function') return; if (typeof fn !== "function") return;
var newfn = function () { var newfn = function () {
console.log("broke"); console.log("broke");
fn(); fn();
}; };
obj[fn.name] = newfn; obj[fn.name] = newfn;
} };
debug.draw_phys = false; debug.draw_phys = false;
debug.draw_bb = false; debug.draw_bb = false;
@ -23,10 +26,15 @@ debug.draw_gizmos = false;
debug.draw_names = false; debug.draw_names = false;
debug.sprite_nums = false; debug.sprite_nums = false;
debug.draw = function () { debug.draw = function () {
if (this.draw_phys) game.all_objects(function(x) { debug.draw_gameobject(x); }); if (this.draw_phys)
game.all_objects(function (x) {
debug.draw_gameobject(x);
});
if (this.draw_bb) if (this.draw_bb)
game.all_objects(function(x) { debug.boundingbox(x.boundingbox(), Color.debug.boundingbox.alpha(0.05)); }); game.all_objects(function (x) {
debug.boundingbox(x.boundingbox(), Color.debug.boundingbox.alpha(0.05));
});
if (this.draw_gizmos) if (this.draw_gizmos)
game.all_objects(function (x) { game.all_objects(function (x) {
@ -48,19 +56,12 @@ debug.draw = function() {
if (sim.paused()) render.text("PAUSED", [0, 0], 1); if (sim.paused()) render.text("PAUSED", [0, 0], 1);
render.text(sim.playing() ? "PLAYING" render.text(sim.playing() ? "PLAYING" : sim.stepping() ? "STEP" : sim.paused() ? "PAUSED; EDITING" : "EDIT", [0, 0], 1);
: sim.stepping() ? };
"STEP" :
sim.paused() ?
"PAUSED; EDITING" :
"EDIT", [0, 0], 1);
}
var assert = function(op, str = `assertion failed [value '${op}']`) var assert = function (op, str = `assertion failed [value '${op}']`) {
{ if (!op) console.panic(str);
if (!op) };
console.panic(str);
}
var Gizmos = { var Gizmos = {
pick_gameobject_points(worldpos, gameobject, points) { pick_gameobject_points(worldpos, gameobject, points) {
@ -72,9 +73,13 @@ var Gizmos = {
/* These controls are available during editing, and during play of debug builds */ /* These controls are available during editing, and during play of debug builds */
debug.inputs = {}; debug.inputs = {};
debug.inputs.f1 = function () { debug.draw_phys = !debug.draw_phys; }; debug.inputs.f1 = function () {
debug.draw_phys = !debug.draw_phys;
};
debug.inputs.f1.doc = "Draw physics debugging aids."; debug.inputs.f1.doc = "Draw physics debugging aids.";
debug.inputs.f3 = function() { debug.draw_bb = !debug.draw_bb; }; debug.inputs.f3 = function () {
debug.draw_bb = !debug.draw_bb;
};
debug.inputs.f3.doc = "Toggle drawing bounding boxes."; debug.inputs.f3.doc = "Toggle drawing bounding boxes.";
debug.inputs.f4 = function () { debug.inputs.f4 = function () {
debug.draw_names = !debug.draw_names; debug.draw_names = !debug.draw_names;
@ -83,9 +88,9 @@ debug.inputs.f4 = function() {
debug.inputs.f4.doc = "Toggle drawing gizmos and names of objects."; debug.inputs.f4.doc = "Toggle drawing gizmos and names of objects.";
var gif = { var gif = {
w: 640, /* Max width */ w: 640 /* Max width */,
h: 480, /* Max height */ h: 480 /* Max height */,
stretch: false, /* True if you want to stretch */ stretch: false /* True if you want to stretch */,
cpf: 4, cpf: 4,
depth: 16, depth: 16,
file: "out.gif", file: "out.gif",
@ -99,10 +104,8 @@ var gif = {
if (!this.stretch) { if (!this.stretch) {
var win = window.height / window.width; var win = window.height / window.width;
var gif = h / w; var gif = h / w;
if (gif > win) if (gif > win) h = w * win;
h = w * win; else w = h / win;
else
w = h / win;
} }
// cmd(131, w, h, this.cpf, this.depth); // cmd(131, w, h, this.cpf, this.depth);
@ -127,23 +130,29 @@ debug.inputs.f8 = function() {
}; };
debug.inputs.f9 = function () { debug.inputs.f9 = function () {
debug.gif.stop(); debug.gif.stop();
} };
debug.inputs.f10 = function() { time.timescale = 0.1; }; debug.inputs.f10 = function () {
time.timescale = 0.1;
};
debug.inputs.f10.doc = "Toggle timescale to 1/10."; debug.inputs.f10.doc = "Toggle timescale to 1/10.";
debug.inputs.f10.released = function () { time.timescale = 1.0; }; debug.inputs.f10.released = function () {
debug.inputs.f12 = function() { gui.defaults.debug = !gui.defaults.debug; console.warn("gui toggle debug");}; time.timescale = 1.0;
};
debug.inputs.f12 = function () {
gui.defaults.debug = !gui.defaults.debug;
console.warn("gui toggle debug");
};
debug.inputs.f12.doc = "Toggle drawing gui debugging aids."; debug.inputs.f12.doc = "Toggle drawing gui debugging aids.";
debug.inputs['M-1'] = render.normal; debug.inputs["M-1"] = render.normal;
debug.inputs['M-2'] = render.wireframe; debug.inputs["M-2"] = render.wireframe;
debug.inputs['C-M-f'] = function() {}; debug.inputs["C-M-f"] = function () {};
debug.inputs['C-M-f'].doc = "Enter camera fly mode."; debug.inputs["C-M-f"].doc = "Enter camera fly mode.";
debug.api = {}; debug.api = {};
debug.api.doc_entry = function(obj, key) debug.api.doc_entry = function (obj, key) {
{ if (typeof key !== "string") {
if (typeof key !== 'string') {
console.warn("Cannot print a key that isn't a string."); console.warn("Cannot print a key that isn't a string.");
return undefined; return undefined;
} }
@ -151,18 +160,17 @@ debug.api.doc_entry = function(obj, key)
var title = key; var title = key;
var o = obj[key]; var o = obj[key];
if (typeof o === 'undefined' && obj.impl && typeof obj.impl[key] !== 'undefined') if (typeof o === "undefined" && obj.impl && typeof obj.impl[key] !== "undefined") o = obj.impl[key];
o = obj.impl[key];
var t = typeof o; var t = typeof o;
if (Array.isArray(o)) t = "array"; if (Array.isArray(o)) t = "array";
else if (t === 'function') { else if (t === "function") {
title = o.toString().tofirst(')') + ")"; title = o.toString().tofirst(")") + ")";
title = title.fromfirst('('); title = title.fromfirst("(");
title = key + "(" + title; title = key + "(" + title;
if (o.doc) doc = o.doc; if (o.doc) doc = o.doc;
t = ""; t = "";
} else if (t === 'undefined') t = ""; } else if (t === "undefined") t = "";
if (t) t = "**" + t + "**\n"; if (t) t = "**" + t + "**\n";
@ -175,12 +183,11 @@ debug.api.doc_entry = function(obj, key)
${t} ${t}
${doc} ${doc}
`; `;
} };
debug.api.print_doc = function(name) debug.api.print_doc = function (name) {
{
var obj = name; var obj = name;
if (typeof name === 'string') { if (typeof name === "string") {
obj = eval(name); obj = eval(name);
if (!obj) { if (!obj) {
console.warn(`Cannot print the API of '${name}', as it was not found.`); console.warn(`Cannot print the API of '${name}', as it was not found.`);
@ -204,21 +211,21 @@ debug.api.print_doc = function(name)
var mdoc = "# " + name + "\n"; var mdoc = "# " + name + "\n";
if (obj.doc?.doc) mdoc += obj.doc.doc + "\n"; if (obj.doc?.doc) mdoc += obj.doc.doc + "\n";
else if (typeof obj.doc === 'string') mdoc += obj.doc + "\n"; else if (typeof obj.doc === "string") mdoc += obj.doc + "\n";
var keys = Object.keys(obj); var keys = Object.keys(obj);
for (var key of keys) { for (var key of keys) {
if (key === 'doc') continue; if (key === "doc") continue;
if (key === 'toString') continue; if (key === "toString") continue;
mdoc += debug.api.doc_entry(obj, key) + "\n"; mdoc += debug.api.doc_entry(obj, key) + "\n";
} }
return mdoc; return mdoc;
} };
return { return {
debug, debug,
Gizmos, Gizmos,
assert assert,
} };

View file

@ -1,27 +1,26 @@
function deep_copy(from) { return json.decode(json.encode(from)); } function deep_copy(from) {
return json.decode(json.encode(from));
}
function valdiff(from,to) function valdiff(from, to) {
{
if (typeof from !== typeof to) return from; if (typeof from !== typeof to) return from;
if (typeof from === 'function') return undefined; if (typeof from === "function") return undefined;
if (typeof from === 'undefined') return undefined; if (typeof from === "undefined") return undefined;
if (typeof from === 'number') { if (typeof from === "number") {
return to; return to;
return undefined; return undefined;
} }
if (typeof from === 'object') if (typeof from === "object") return ediff(from, to);
return ediff(from,to);
if (from !== to) return to; if (from !== to) return to;
return undefined; return undefined;
} }
function ediff(from,to) function ediff(from, to) {
{
var ret = {}; var ret = {};
if (!to) if (!to)
@ -29,9 +28,8 @@ function ediff(from,to)
return deep_copy(from); return deep_copy(from);
Object.entries(from).forEach(function ([key, v]) { Object.entries(from).forEach(function ([key, v]) {
if (typeof v === 'function') return; if (typeof v === "function") return;
if (typeof v === 'undefined') return; if (typeof v === "undefined") return;
if (Array.isArray(v)) { if (Array.isArray(v)) {
if (!Array.isArray(to[key]) || v.length !== to[key].length) { if (!Array.isArray(to[key]) || v.length !== to[key].length) {
@ -41,28 +39,24 @@ function ediff(from,to)
} }
var diff = ediff(from[key], to[key]); var diff = ediff(from[key], to[key]);
if (diff && !Object.empty(diff)) if (diff && !Object.empty(diff)) ret[key] = Object.values(ediff(v, []));
ret[key] = Object.values(ediff(v,[]));
return; return;
} }
if (typeof v === 'object' && v !== null) { if (typeof v === "object" && v !== null) {
var diff = ediff(v, to[key]); var diff = ediff(v, to[key]);
if (diff && !Object.empty(diff)) if (diff && !Object.empty(diff)) ret[key] = diff;
ret[key] = diff;
return; return;
} }
if (typeof v === 'number' || v === null) { if (typeof v === "number" || v === null) {
if (!isFinite(v)) v = null; // Squash infinity to null if (!isFinite(v)) v = null; // Squash infinity to null
if (v !== to[key]) if (v !== to[key]) ret[key] = v;
ret[key] = v;
return; return;
} }
if (!to || v !== to[key]) if (!to || v !== to[key]) ret[key] = v;
ret[key] = v;
}); });
if (Object.empty(ret)) return undefined; if (Object.empty(ret)) return undefined;
@ -71,11 +65,10 @@ function ediff(from,to)
ediff.doc = "Given a from and to object, returns an object that, if applied to from, will make it the same as to. Does not include deletion; it is only additive. If one element in an array is different, the entire array is copied. Squashes infinite numbers to null for use in JSON."; ediff.doc = "Given a from and to object, returns an object that, if applied to from, will make it the same as to. Does not include deletion; it is only additive. If one element in an array is different, the entire array is copied. Squashes infinite numbers to null for use in JSON.";
function samediff(from, to) function samediff(from, to) {
{
var same = []; var same = [];
if (!to) return same; if (!to) return same;
if (typeof to !== 'object') { if (typeof to !== "object") {
console.warn("'To' must be an object. Got " + to); console.warn("'To' must be an object. Got " + to);
return same; return same;
} }
@ -91,8 +84,7 @@ function samediff(from, to)
// } // }
var d = valdiff(from[k], to[k]); var d = valdiff(from[k], to[k]);
if (!d) if (!d) delete from[k];
delete from[k];
}); });
return same; return same;
@ -104,4 +96,4 @@ return {
deep_copy, deep_copy,
ediff, ediff,
samediff, samediff,
} };

File diff suppressed because it is too large Load diff

View file

@ -4,8 +4,10 @@ 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.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."; os.max_stacksize.doc = "Set the max stack size in bytes.";
Object.defineProperty(String.prototype, 'rm', { Object.defineProperty(String.prototype, "rm", {
value: function(index, endidx = index+1) { return this.slice(0,index) + this.slice(endidx); } value: function (index, endidx = index + 1) {
return this.slice(0, index) + this.slice(endidx);
},
}); });
Object.defineProperty(String.prototype, "tolast", { Object.defineProperty(String.prototype, "tolast", {
@ -33,23 +35,22 @@ Object.defineProperty(String.prototype, "folder", {
globalThis.Resources = {}; globalThis.Resources = {};
Resources.rm_fn = function rm_fn(fn, text) Resources.rm_fn = function rm_fn(fn, text) {
{
var reg = new RegExp(fn.source + "\\s*\\("); var reg = new RegExp(fn.source + "\\s*\\(");
var match; var match;
while (match = text.match(reg)) { while ((match = text.match(reg))) {
var last = match.index + match[0].length; var last = match.index + match[0].length;
var par = 1; var par = 1;
while (par !== 0) { while (par !== 0) {
if (text[last] === '(') par++; if (text[last] === "(") par++;
if (text[last] === ')') par--; if (text[last] === ")") par--;
last++; last++;
} }
text = text.rm(match.index, last); text = text.rm(match.index, last);
} }
return text; return text;
} };
Resources.rm_fn.doc = "Remove calls to a given function from a given text script."; Resources.rm_fn.doc = "Remove calls to a given function from a given text script.";
Resources.replpath = function replpath(str, path) { Resources.replpath = function replpath(str, path) {
@ -76,11 +77,9 @@ Resources.replstrs = function replstrs(path) {
var stem = path.dir(); var stem = path.dir();
if (!console.enabled) if (!console.enabled) script = Resources.rm_fn(/console\.(spam|info|warn|error)/, script);
script = Resources.rm_fn(/console\.(spam|info|warn|error)/, script);
if (!profile.enabled) if (!profile.enabled) script = Resources.rm_fn(/profile\.(cache|frame|endcache|endframe)/, script);
script = Resources.rm_fn(/profile\.(cache|frame|endcache|endframe)/, script);
if (!debug.enabled) { if (!debug.enabled) {
script = Resources.rm_fn(/assert/, script); script = Resources.rm_fn(/assert/, script);
@ -98,20 +97,18 @@ Resources.replstrs = function replstrs(path) {
Resources.is_sound = function (path) { Resources.is_sound = function (path) {
var ext = path.ext(); var ext = path.ext();
return Resources.sounds.any(x => x === ext); return Resources.sounds.any(x => x === ext);
} };
Resources.is_animation = function(path) Resources.is_animation = function (path) {
{ if (path.ext() === "gif" && Resources.gif.frames(path) > 1) return true;
if (path.ext() === 'gif' && Resources.gif.frames(path) > 1) return true; if (path.ext() === "ase") return true;
if (path.ext() === 'ase') return true;
return false; return false;
} };
Resources.is_path = function(str) Resources.is_path = function (str) {
{
return !/[\\\/:*?"<>|]/.test(str); return !/[\\\/:*?"<>|]/.test(str);
} };
globalThis.json = {}; globalThis.json = {};
json.encode = function (value, replacer, space = 1) { json.encode = function (value, replacer, space = 1) {
@ -154,16 +151,14 @@ function find_ext(file, ext, root = "") {
if (ext.some(x => x === file_ext)) return file; if (ext.some(x => x === file_ext)) return file;
for (var e of ext) { for (var e of ext) {
var nf = `${file}.${e}`; var nf = `${file}.${e}`;
if (io.exists(nf)) if (io.exists(nf)) return nf;
return nf;
} }
var all_files = io.glob(`**/${file}.*`); var all_files = io.glob(`**/${file}.*`);
var find = undefined; var find = undefined;
for (var e of ext) { for (var e of ext) {
var finds = all_files.filter(x => x.ext() === e); var finds = all_files.filter(x => x.ext() === e);
if (finds.length > 1) if (finds.length > 1) console.warn(`Found conflicting files when searching for '${file}': ${json.encode(finds)}. Returning the first one.`);
console.warn(`Found conflicting files when searching for '${file}': ${json.encode(finds)}. Returning the first one.`);
if (finds.length > 0) { if (finds.length > 0) {
find = finds[0]; find = finds[0];
break; break;
@ -176,18 +171,17 @@ function find_ext(file, ext, root = "") {
var hashhit = 0; var hashhit = 0;
var hashmiss = 0; var hashmiss = 0;
Object.defineProperty(Function.prototype, 'hashify', { Object.defineProperty(Function.prototype, "hashify", {
value: function () { value: function () {
var hash = new Map(); var hash = new Map();
var fn = this; var fn = this;
function ret() { function ret() {
if (!hash.has(arguments[0])) if (!hash.has(arguments[0])) hash.set(arguments[0], fn(...arguments));
hash.set(arguments[0], fn(...arguments));
return hash.get(arguments[0]); return hash.get(arguments[0]);
} }
return ret; return ret;
} },
}); });
Resources.find_image = function (file, root = "") { Resources.find_image = function (file, root = "") {
@ -301,7 +295,7 @@ globalThis.use = function use(file) {
profile.endcache(); profile.endcache();
return ret; return ret;
} };
function stripped_use(file, script) { function stripped_use(file, script) {
file = Resources.find_script(file); file = Resources.find_script(file);
@ -320,8 +314,7 @@ function stripped_use (file, script) {
return ret; return ret;
} }
function bare_use(file) function bare_use(file) {
{
var script = io.slurp(file); var script = io.slurp(file);
if (!script) return; if (!script) return;
script = `(function() { var self = this; ${script}; })`; script = `(function() { var self = this; ${script}; })`;
@ -337,17 +330,15 @@ debug.enabled = true;
bare_use("scripts/base.js"); bare_use("scripts/base.js");
bare_use("scripts/profile.js"); bare_use("scripts/profile.js");
prosperon.release = function() prosperon.release = function () {
{
profile.enabled = false; profile.enabled = false;
console.enabled = false; console.enabled = false;
debug.enabled = false; debug.enabled = false;
} };
bare_use("preconfig.js"); bare_use("preconfig.js");
if (!profile.enabled) if (!profile.enabled) use = stripped_use;
use = stripped_use;
Object.assign(globalThis, use("scripts/prosperon.js")); Object.assign(globalThis, use("scripts/prosperon.js"));
@ -359,4 +350,3 @@ app.interval(_ => {
repl.hotreload(); repl.hotreload();
profile.endframe(); profile.endframe();
}, 1); }, 1);

View file

@ -1,12 +1,11 @@
globalThis.entityreport = {}; globalThis.entityreport = {};
var timer_update = function(dt) var timer_update = function (dt) {
{
this.fn(); this.fn();
} };
function obj_unique_name(name, obj) { function obj_unique_name(name, obj) {
name = name.replaceAll('.', '_'); name = name.replaceAll(".", "_");
if (!(name in obj)) return name; if (!(name in obj)) return name;
var t = 1; var t = 1;
var n = name + t; var n = name + t;
@ -18,7 +17,7 @@ function obj_unique_name(name, obj) {
} }
function unique_name(list, name = "new_object") { function unique_name(list, name = "new_object") {
var str = name.replaceAll('.', '_'); var str = name.replaceAll(".", "_");
var n = 1; var n = 1;
var t = str; var t = str;
while (list.indexOf(t) !== -1) { while (list.indexOf(t) !== -1) {
@ -26,14 +25,13 @@ function unique_name(list, name = "new_object") {
n++; n++;
} }
return t; return t;
}; }
var entity = { var entity = {
drawlayer: -1, drawlayer: -1,
get_comp_by_name(name) { get_comp_by_name(name) {
var comps = []; var comps = [];
for (var c of Object.values(this.components)) for (var c of Object.values(this.components)) if (c.comp === name) comps.push(c);
if (c.comp === name) comps.push(c);
if (comps.length) return comps; if (comps.length) return comps;
return undefined; return undefined;
@ -56,12 +54,14 @@ var entity = {
drawlayer: 0, drawlayer: 0,
full_path() { return this.path_from(world); }, full_path() {
return this.path_from(world);
},
clear() { clear() {
for (var k in this.objects) { for (var k in this.objects) {
this.objects[k].kill(); this.objects[k].kill();
}; }
this.objects = {}; this.objects = {};
}, },
@ -79,7 +79,7 @@ var entity = {
rm?.(); rm?.();
rm = undefined; rm = undefined;
update = undefined; update = undefined;
} };
function execute() { function execute() {
fn(); fn();
@ -88,11 +88,14 @@ var entity = {
stop.remain = seconds; stop.remain = seconds;
stop.seconds = seconds; stop.seconds = seconds;
stop.pct = function() { return 1 - (stop.remain/stop.seconds); }; stop.pct = function () {
return 1 - stop.remain / stop.seconds;
};
function update(dt) { function update(dt) {
profile.frame("timer"); profile.frame("timer");
if (stop) { // TODO: This seems broken if (stop) {
// TODO: This seems broken
stop.remain -= dt; stop.remain -= dt;
if (stop.remain <= 0) execute(); if (stop.remain <= 0) execute();
} }
@ -104,19 +107,37 @@ var entity = {
return stop; return stop;
}, },
cry(file) { return audio.cry(file); }, cry(file) {
return audio.cry(file);
},
get pos() { return this.transform.pos; }, get pos() {
set pos(x) { this.transform.pos = x; }, return this.transform.pos;
get angle() { return this.transform.angle; }, },
set angle(x) { this.transform.angle = x; }, set pos(x) {
get scale() { return this.transform.scale; }, this.transform.pos = x;
set scale(x) { this.transform.scale = 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); }, move(vec) {
rotate(x) { this.transform.rotate(x, [0,0,-1]); }, this.pos = this.pos.add(vec);
},
rotate(x) {
this.transform.rotate(x, [0, 0, -1]);
},
grow(vec) { grow(vec) {
if (typeof vec === 'number') vec = [vec,vec]; if (typeof vec === "number") vec = [vec, vec];
this.scale = this.scale.map((x, i) => x * vec[i]); this.scale = this.scale.map((x, i) => x * vec[i]);
}, },
@ -139,10 +160,8 @@ var entity = {
}, },
remove_obj(obj) { remove_obj(obj) {
if (this.objects) if (this.objects) delete this.objects[obj.guid];
delete this.objects[obj.guid]; else console.warn(`Object ${this.guid} has no objects file.`);
else
console.warn(`Object ${this.guid} has no objects file.`);
delete this[obj.name]; delete this[obj.name];
Object.unhide(this, obj.name); Object.unhide(this, obj.name);
}, },
@ -195,12 +214,12 @@ var entity = {
for (var [prop, p] of Object.entries(ent)) { for (var [prop, p] of Object.entries(ent)) {
if (!p) continue; if (!p) continue;
if (typeof p !== 'object') continue; if (typeof p !== "object") continue;
if (!p.comp) continue; if (!p.comp) continue;
ent[prop] = component[p.comp](ent); ent[prop] = component[p.comp](ent);
Object.merge(ent[prop], p); Object.merge(ent[prop], p);
ent.components[prop] = ent[prop]; ent.components[prop] = ent[prop];
}; }
check_registers(ent); check_registers(ent);
@ -210,16 +229,16 @@ var entity = {
if (ent.start instanceof Function) ent.start(); if (ent.start instanceof Function) ent.start();
} }
Object.hide(ent, 'ur', 'components', 'objects', 'timers', 'guid', 'master', 'guid'); Object.hide(ent, "ur", "components", "objects", "timers", "guid", "master", "guid");
ent._ed = { ent._ed = {
selectable: true, selectable: true,
dirty: false, dirty: false,
inst: false, inst: false,
urdiff: {} urdiff: {},
}; };
Object.hide(ent, '_ed'); Object.hide(ent, "_ed");
ent.sync(); ent.sync();
@ -242,25 +261,36 @@ var entity = {
ent.ur.fresh ??= json.decode(json.encode(ent)); ent.ur.fresh ??= json.decode(json.encode(ent));
ent.ur.fresh.objects = {}; ent.ur.fresh.objects = {};
for (var i in ent.objects) for (var i in ent.objects) ent.ur.fresh.objects[i] = ent.objects[i].instance_obj();
ent.ur.fresh.objects[i] = ent.objects[i].instance_obj();
profile.endcache(); profile.endcache();
return ent; return ent;
}, },
disable() { for (var x of this.components) x.disable(); }, disable() {
enable() { for (var x of this.components) x.enable(); }, for (var x of this.components) x.disable();
},
enable() {
for (var x of this.components) x.enable();
},
this2screen(pos) { return game.camera.world2view(this.this2world(pos)); }, this2screen(pos) {
screen2this(pos) { return this.world2this(game.camera.view2world(pos)); }, return game.camera.world2view(this.this2world(pos));
},
screen2this(pos) {
return this.world2this(game.camera.view2world(pos));
},
/* Make a unique object the same as its prototype */ /* Make a unique object the same as its prototype */
revert() { Object.merge(this, this.ur.fresh); }, revert() {
Object.merge(this, this.ur.fresh);
},
name: "new_object", name: "new_object",
toString() { return this.name; }, toString() {
return this.name;
},
width() { width() {
var bb = this.boundingbox(); var bb = this.boundingbox();
return bb.r - bb.l; return bb.r - bb.l;
@ -271,10 +301,16 @@ var entity = {
return bb.t - bb.b; return bb.t - bb.b;
}, },
flipx() { return this.scale.x < 0; }, flipx() {
flipy() { return this.scale.y < 0; }, return this.scale.x < 0;
},
flipy() {
return this.scale.y < 0;
},
mirror(plane) { this.scale = Vector.reflect(this.scale, plane); }, mirror(plane) {
this.scale = Vector.reflect(this.scale, plane);
},
/* Bounding box of the object in world dimensions */ /* Bounding box of the object in world dimensions */
boundingbox() { boundingbox() {
@ -283,15 +319,13 @@ var entity = {
t: 0, t: 0,
r: 0, r: 0,
b: 0, b: 0,
l: 0 l: 0,
}); });
for (var key in this.components) { for (var key in this.components) {
if ('boundingbox' in this.components[key]) if ("boundingbox" in this.components[key]) boxes.push(this.components[key].boundingbox());
boxes.push(this.components[key].boundingbox());
} }
for (var key in this.objects) for (var key in this.objects) boxes.push(this.objects[key].boundingbox());
boxes.push(this.objects[key].boundingbox());
var bb = boxes.shift(); var bb = boxes.shift();
@ -316,12 +350,10 @@ var entity = {
fresh.objects ??= {}; fresh.objects ??= {};
var curobjs = {}; var curobjs = {};
for (var o in this.objects) for (var o in this.objects) curobjs[o] = this.objects[o].instance_obj();
curobjs[o] = this.objects[o].instance_obj();
var odiff = ediff(curobjs, fresh.objects); var odiff = ediff(curobjs, fresh.objects);
if (odiff) if (odiff) d.objects = curobjs;
d.objects = curobjs;
delete d.pos; delete d.pos;
delete d.angle; delete d.angle;
@ -343,7 +375,7 @@ var entity = {
var t = {}; var t = {};
t.pos = this.get_pos(this.master).map(x => Math.places(x, 0)); t.pos = this.get_pos(this.master).map(x => Math.places(x, 0));
t.angle = Math.places(this.get_angle(this.master), 4); t.angle = Math.places(this.get_angle(this.master), 4);
t.scale = this.get_scale(this.master).map(x => Math.places(x, 2));; t.scale = this.get_scale(this.master).map(x => Math.places(x, 2));
return t; return t;
}, },
@ -378,8 +410,8 @@ dup(diff) {
this.clear(); this.clear();
if (this.stop instanceof Function) this.stop(); if (this.stop instanceof Function) this.stop();
if (typeof this.garbage === 'function') this.garbage(); if (typeof this.garbage === "function") this.garbage();
if (typeof this.then === 'function') this.then(); if (typeof this.then === "function") this.then();
game.tag_clear_guid(this.guid); game.tag_clear_guid(this.guid);
@ -406,12 +438,13 @@ dup(diff) {
Object.hide(this, name); Object.hide(this, name);
return; return;
} }
if (this.objects[newname]) if (this.objects[newname]) return;
return;
this.objects[newname] = this.objects[name]; this.objects[newname] = this.objects[name];
this[newname] = this[name]; this[newname] = this[name];
this[newname].toString = function() { return newname; }; this[newname].toString = function () {
return newname;
};
Object.hide(this, newname); Object.hide(this, newname);
delete this.objects[name]; delete this.objects[name];
delete this[name]; delete this[name];
@ -438,10 +471,8 @@ var gameobject = {
if (!lur) return; if (!lur) return;
var lur = lur.objects[this.toString()]; var lur = lur.objects[this.toString()];
var d = ediff(this._ed.urdiff, lur); var d = ediff(this._ed.urdiff, lur);
if (!d || Object.empty(d)) if (!d || Object.empty(d)) this._ed.inst = true;
this._ed.inst = true; else this._ed.inst = false;
else
this._ed.inst = false;
}, },
namestr() { namestr() {
@ -518,14 +549,17 @@ var gameobject = {
}, },
set_scale(x, relative = world) { set_scale(x, relative = world) {
if (typeof x === 'number') x = [x,x,x]; if (typeof x === "number") x = [x, x, x];
var newscale = relative.scale.map((s, i) => x[i] * s); var newscale = relative.scale.map((s, i) => x[i] * s);
var pct = this.scale.map((s, i) => newscale[i] / s); var pct = this.scale.map((s, i) => newscale[i] / s);
this.rscale = newscale; this.rscale = newscale;
for (var obj of this.objects) { for (var obj of this.objects) {
obj.grow(pct); obj.grow(pct);
obj.set_pos(obj.get_pos(obj.master).map((x,i) => x*pct[i]), obj.master); obj.set_pos(
}; obj.get_pos(obj.master).map((x, i) => x * pct[i]),
obj.master,
);
}
}, },
get_pos(relative = world) { get_pos(relative = world) {
@ -545,7 +579,9 @@ var gameobject = {
return this.scale.map((x, i) => x / masterscale[i]); return this.scale.map((x, i) => x / masterscale[i]);
}, },
in_air() { return this.in_air(); }, in_air() {
return this.in_air();
},
/* Velocity and angular velocity of the object */ /* Velocity and angular velocity of the object */
phys_obj() { phys_obj() {
@ -560,7 +596,7 @@ var gameobject = {
this.categories = n; this.categories = n;
return; return;
} }
var cat = (1 << (n-1)); var cat = 1 << (n - 1);
this.categories = cat; this.categories = cat;
}, },
get category() { get category() {
@ -576,8 +612,8 @@ var gameobject = {
} }
return pos + 1; return pos + 1;
} },
} };
entity.spawn.doc = `Spawn an entity of type 'ur' on this entity. Returns the spawned entity.`; entity.spawn.doc = `Spawn an entity of type 'ur' on this entity. Returns the spawned entity.`;
@ -598,12 +634,12 @@ gameobject.doc = {
set_pos: `Function to set the position of the object in world coordinates.`, set_pos: `Function to set the position of the object in world coordinates.`,
worldangle: `Function to get the angle of the entity in the world.`, worldangle: `Function to get the angle of the entity in the world.`,
rotate: `Function to rotate this object by x degrees.`, rotate: `Function to rotate this object by x degrees.`,
move: 'Move an object by x,y,z. If the first parameter is an array, uses up to the first three array values.', move: "Move an object by x,y,z. If the first parameter is an array, uses up to the first three array values.",
pulse: `Apply an impulse to this body in world coordinates. Impulse is a short force.`, pulse: `Apply an impulse to this body in world coordinates. Impulse is a short force.`,
shove: `Apply a force to this body in world coordinates. Should be used over many frames.`, shove: `Apply a force to this body in world coordinates. Should be used over many frames.`,
shove_at: 'Apply a force to this body, at a position relative to itself.', shove_at: "Apply a force to this body, at a position relative to itself.",
max_velocity: 'The max linear velocity this object can travel.', max_velocity: "The max linear velocity this object can travel.",
max_angularvelocity: 'The max angular velocity this object can rotate.', max_angularvelocity: "The max angular velocity this object can rotate.",
on_ground: `Return true if the object is on the ground.`, on_ground: `Return true if the object is on the ground.`,
spawn: `Create an instance of a supplied ur-type on this object. Optionally provide a data object to modify the created entity.`, spawn: `Create an instance of a supplied ur-type on this object. Optionally provide a data object to modify the created entity.`,
hide: `Make this object invisible.`, hide: `Make this object invisible.`,
@ -616,28 +652,27 @@ gameobject.doc = {
transform: `Return an object representing the transform state of this object.`, transform: `Return an object representing the transform state of this object.`,
kill: `Remove this object from the world.`, kill: `Remove this object from the world.`,
master: "The entity this entity belongs to.", master: "The entity this entity belongs to.",
delay: 'Run the given function after the given number of seconds has elapsed.', delay: "Run the given function after the given number of seconds has elapsed.",
cry: 'Make a sound. Can only make one at a time.', cry: "Make a sound. Can only make one at a time.",
add_component: 'Add a component to the object by name.', add_component: "Add a component to the object by name.",
pin: 'Pin joint to another object. Acts as if a rigid rod is between the two objects.', pin: "Pin joint to another object. Acts as if a rigid rod is between the two objects.",
slide: 'Slide joint, similar to a pin but with min and max allowed distances.', slide: "Slide joint, similar to a pin but with min and max allowed distances.",
pivot: 'Pivot joint to an object, with the pivot given in world coordinates.', pivot: "Pivot joint to an object, with the pivot given in world coordinates.",
groove: 'Groove joint. The groove is on to, from to local coordinates a and b, with this object anchored at anchor.', groove: "Groove joint. The groove is on to, from to local coordinates a and b, with this object anchored at anchor.",
damped_spring: 'Damped spring to another object. Length is the distance it wants to be, stiffness is the spring constant, and damping is the damping ratio. 1 is critical, < 1 is underdamped, > 1 is overdamped.', damped_spring: "Damped spring to another object. Length is the distance it wants to be, stiffness is the spring constant, and damping is the damping ratio. 1 is critical, < 1 is underdamped, > 1 is overdamped.",
damped_rotary_spring: 'Similar to damped spring but for rotation. Rest angle is the attempted angle.', damped_rotary_spring: "Similar to damped spring but for rotation. Rest angle is the attempted angle.",
rotary_limit: 'Limit the angle relative to the to body between min and max.', rotary_limit: "Limit the angle relative to the to body between min and max.",
ratchet: 'Like a socket wrench, relative to to. ratch is the distance between clicks.', ratchet: "Like a socket wrench, relative to to. ratch is the distance between clicks.",
gear: 'Keeps the angular velocity ratio of this body and to constant. Ratio is the gear ratio.', gear: "Keeps the angular velocity ratio of this body and to constant. Ratio is the gear ratio.",
motor: 'Keeps the relative angular velocity of this body to to at a constant rate. The most simple idea is for one of the bodies to be static, to the other is kept at rate.', motor: "Keeps the relative angular velocity of this body to to at a constant rate. The most simple idea is for one of the bodies to be static, to the other is kept at rate.",
layer: 'Bitmask for collision layers.', layer: "Bitmask for collision layers.",
drawlayer: 'Layer for drawing. Higher numbers draw above lower ones.', drawlayer: "Layer for drawing. Higher numbers draw above lower ones.",
warp_filter: 'Bitmask for selecting what warps should affect this entity.', warp_filter: "Bitmask for selecting what warps should affect this entity.",
}; };
global.ur = {}; global.ur = {};
if (io.exists(`${io.dumpfolder}/ur.json`)) if (io.exists(`${io.dumpfolder}/ur.json`)) ur = json.decode(io.slurp(`${io.dumpfolder}/ur.json`));
ur = json.decode(io.slurp(`${io.dumpfolder}/ur.json`));
else { else {
ur = {}; ur = {};
ur._list = []; ur._list = [];
@ -655,12 +690,12 @@ ur {
/* Apply an ur u to an entity e */ /* Apply an ur u to an entity e */
/* u is given as */ /* u is given as */
function apply_ur(u, ent) { function apply_ur(u, ent) {
if (typeof u !== 'string') { if (typeof u !== "string") {
console.warn("Must give u as a string."); console.warn("Must give u as a string.");
return; return;
} }
var urs = u.split('.'); var urs = u.split(".");
if (!urs.every(u => ur[u])) { if (!urs.every(u => ur[u])) {
console.error(`Attempted to make ur combo ${u} but not every ur in the chain exists.`); console.error(`Attempted to make ur combo ${u} but not every ur in the chain exists.`);
return; return;
@ -669,49 +704,42 @@ function apply_ur(u, ent) {
for (var u of urs) { for (var u of urs) {
var text = u.text; var text = u.text;
var data = u.data; var data = u.data;
if (typeof text === 'string') if (typeof text === "string") use(text, ent);
use(text, ent); else if (Array.isArray(text)) for (var path of text) use(path, ent);
else if (Array.isArray(text))
for (var path of text) use(path,ent);
if (typeof data === 'string') if (typeof data === "string") Object.merge(ent, json.decode(Resources.replstrs(data)));
Object.merge(ent, json.decode(Resources.replstrs(data)));
else if (Array.isArray(data)) { else if (Array.isArray(data)) {
for (var path of data) { for (var path of data) {
if (typeof path === 'string') if (typeof path === "string") Object.merge(ent, json.decode(Resources.replstrs(data)));
Object.merge(ent, json.decode(Resources.replstrs(data))); else if (path instanceof Object) Object.merge(ent, path);
else if (path instanceof Object) }
Object.merge(ent,path);
};
} }
} }
} }
var emptyur = { var emptyur = {
name: "empty" name: "empty",
} };
var getur = function(text, data) var getur = function (text, data) {
{
if (!text && !data) { if (!text && !data) {
console.info('empty ur'); console.info("empty ur");
return { return {
name: "empty" name: "empty",
}; };
} }
var urstr = text; var urstr = text;
if (data) if (data) urstr += "+" + data;
urstr += "+" + data;
if (!ur[urstr]) { if (!ur[urstr]) {
ur[urstr] = { ur[urstr] = {
name: urstr, name: urstr,
text: text, text: text,
data: data data: data,
} };
} }
return ur[urstr]; return ur[urstr];
} };
var ur_from_file = function (file) { var ur_from_file = function (file) {
var urname = file.name(); var urname = file.name();
@ -720,12 +748,12 @@ var ur_from_file = function(file) {
return undefined; return undefined;
} }
var newur = { var newur = {
name: urname name: urname,
}; };
ur[urname] = newur; ur[urname] = newur;
ur._list.push(urname); ur._list.push(urname);
return newur; return newur;
} };
game.loadurs = function () { game.loadurs = function () {
return; return;
@ -741,7 +769,7 @@ game.loadurs = function() {
} }
for (var file of io.glob("**.jso").filter(f => !ur[f.name()])) { for (var file of io.glob("**.jso").filter(f => !ur[f.name()])) {
if (file[0] === '.' || file[0] === '_') continue; if (file[0] === "." || file[0] === "_") continue;
var newur = ur_from_file(file); var newur = ur_from_file(file);
if (!newur) continue; if (!newur) continue;
newur.text = file; newur.text = file;
@ -755,9 +783,8 @@ game.loadurs = function() {
}; };
game.ur = {}; game.ur = {};
game.ur.load = function(str) {} game.ur.load = function (str) {};
game.ur.add_data = function(str, data) game.ur.add_data = function (str, data) {
{
var nur = ur[str]; var nur = ur[str];
if (!nur) { if (!nur) {
console.warn(`Cannot add data to the ur ${str}.`); console.warn(`Cannot add data to the ur ${str}.`);
@ -770,15 +797,14 @@ game.ur.add_data = function(str, data)
} }
ur.data.push(data); ur.data.push(data);
} };
game.ur.save = function(str) game.ur.save = function (str) {
{
var nur = ur[str]; var nur = ur[str];
if (!nur) { if (!nur) {
console.warn(`Cannot save ur ${str}.`); console.warn(`Cannot save ur ${str}.`);
return; return;
} }
} };
return { entity } return { entity };

View file

@ -1,33 +1,30 @@
var shape = {}; var shape = {};
shape.box = {}; shape.box = {};
shape.box.points = function(ll, ur) shape.box.points = function (ll, ur) {
{
return [ll, ll.add([ur.x - ll.x, 0]), ur, ll.add([0, ur.y - ll.y])]; return [ll, ll.add([ur.x - ll.x, 0]), ur, ll.add([0, ur.y - ll.y])];
} };
shape.sphere = {}; shape.sphere = {};
shape.circle = {}; shape.circle = {};
shape.sphere.volume = function(r) { return Math.pi*r*r*r*4/3; }; shape.sphere.volume = function (r) {
shape.sphere.random = function(r,theta = [0,1], phi = [-0.5,0.5]) return (Math.pi * r * r * r * 4) / 3;
{ };
if (typeof r === 'number') r = [r,r]; shape.sphere.random = function (r, theta = [0, 1], phi = [-0.5, 0.5]) {
if (typeof theta === 'number') theta = [theta,theta]; if (typeof r === "number") r = [r, r];
if (typeof phi === 'number') phi = [phi,phi]; if (typeof theta === "number") theta = [theta, theta];
if (typeof phi === "number") phi = [phi, phi];
var ra = Math.random_range(r[0], r[1]); var ra = Math.random_range(r[0], r[1]);
var ta = Math.turn2rad(Math.random_range(theta[0], theta[1])); var ta = Math.turn2rad(Math.random_range(theta[0], theta[1]));
var pa = Math.turn2rad(Math.random_range(phi[0], phi[1])); var pa = Math.turn2rad(Math.random_range(phi[0], phi[1]));
return [ return [ra * Math.sin(ta) * Math.cos(pa), ra * Math.sin(ta) * Math.sin(pa), ra * Math.cos(ta)];
ra*Math.sin(ta)*Math.cos(pa), };
ra*Math.sin(ta)*Math.sin(pa),
ra*Math.cos(ta)
];
}
shape.circle.area = function(r) { return Math.pi*r*r; }; shape.circle.area = function (r) {
shape.circle.random = function(r,theta) return Math.pi * r * r;
{ };
shape.circle.random = function (r, theta) {
return shape.sphere.random(r, theta).xz; return shape.sphere.random(r, theta).xz;
} };
shape.box = function (w, h) { shape.box = function (w, h) {
w /= 2; w /= 2;
@ -37,7 +34,7 @@ shape.box = function(w,h) {
[w, h], [w, h],
[-w, h], [-w, h],
[-w, -h], [-w, -h],
[w,-h] [w, -h],
]; ];
return points; return points;
@ -49,16 +46,14 @@ shape.ngon = function(radius, n) {
shape.arc = function (radius, angle, n, start = 0) { shape.arc = function (radius, angle, n, start = 0) {
start = Math.deg2rad(start); start = Math.deg2rad(start);
if (angle >= 360) if (angle >= 360) angle = 360;
angle = 360;
if (n <= 1) return []; if (n <= 1) return [];
var points = []; var points = [];
angle = Math.deg2rad(angle); angle = Math.deg2rad(angle);
var arclen = angle / n; var arclen = angle / n;
for (var i = 0; i < n; i++) for (var i = 0; i < n; i++) points.push(Vector.rotate([radius, 0], start + arclen * i));
points.push(Vector.rotate([radius,0], start + (arclen*i)));
return points; return points;
}; };
@ -68,14 +63,8 @@ shape.circle.points = function(radius, n) {
return shape.arc(radius, 360, n); return shape.arc(radius, 360, n);
}; };
shape.corners2points = function(ll, ur) shape.corners2points = function (ll, ur) {
{ return [ll, ll.add([ur.x, 0]), ur, ll.add([0, ur.y])];
return [ };
ll,
ll.add([ur.x,0]),
ur,
ll.add([0,ur.y]),
];
}
return { shape }; return { shape };

View file

@ -18,14 +18,13 @@ input.keycodes = {
}; };
input.codekeys = {}; input.codekeys = {};
for (var code in input.keycodes) for (var code in input.keycodes) input.codekeys[input.keycodes[code]] = code;
input.codekeys[input.keycodes[code]] = code;
var mod = { var mod = {
shift: 0, shift: 0,
ctrl: 0, ctrl: 0,
alt: 0, alt: 0,
super: 0 super: 0,
}; };
/* /*
@ -35,10 +34,11 @@ pressed
down down
*/ */
function keycode(name) { return charCodeAt(name); } function keycode(name) {
return charCodeAt(name);
}
function keyname_extd(key) function keyname_extd(key) {
{
if (!parseInt(key)) return key; if (!parseInt(key)) return key;
if (key > 289 && key < 302) { if (key > 289 && key < 302) {
@ -59,8 +59,7 @@ function keyname_extd(key)
var downkeys = {}; var downkeys = {};
function modstr() function modstr() {
{
var s = ""; var s = "";
if (mod.ctrl) s += "C-"; if (mod.ctrl) s += "C-";
if (mod.alt) s += "M-"; if (mod.alt) s += "M-";
@ -68,50 +67,36 @@ function modstr()
return s; return s;
} }
prosperon.keydown = function(key, repeat) prosperon.keydown = function (key, repeat) {
{
downkeys[key] = true; downkeys[key] = true;
if (key == 341 || key == 345) if (key == 341 || key == 345) mod.ctrl = 1;
mod.ctrl = 1; else if (key == 342 || key == 346) mod.alt = 1;
else if (key == 342 || key == 346) else if (key == 343 || key == 347) mod.super = 1;
mod.alt = 1; else if (key == 340 || key == 344) mod.shift = 1;
else if (key == 343 || key == 347)
mod.super = 1;
else if (key == 340 || key == 344)
mod.shift = 1;
else { else {
var emacs = modstr() + keyname_extd(key); var emacs = modstr() + keyname_extd(key);
if (repeat) if (repeat) player[0].raw_input(emacs, "rep");
player[0].raw_input(emacs, "rep"); else player[0].raw_input(emacs, "pressed");
else
player[0].raw_input(emacs, "pressed");
}
} }
};
prosperon.keyup = function(key) prosperon.keyup = function (key) {
{
delete downkeys[key]; delete downkeys[key];
if (key == 341 || key == 345) if (key == 341 || key == 345) mod.ctrl = 0;
mod.ctrl = 0; else if (key == 342 || key == 346) mod.alt = 0;
else if (key == 343 || key == 347) mod.super = 0;
else if (key == 342 || key == 346) else if (key == 340 || key == 344) mod.shift = 0;
mod.alt = 0;
else if (key == 343 || key == 347)
mod.super = 0;
else if (key == 340 || key == 344)
mod.shift = 0;
else { else {
var emacs = modstr() + keyname_extd(key); var emacs = modstr() + keyname_extd(key);
player[0].raw_input(emacs, "released"); player[0].raw_input(emacs, "released");
} }
} };
prosperon.droppedfile = function(path) prosperon.droppedfile = function (path) {
{
player[0].raw_input("drop", "pressed", path); player[0].raw_input("drop", "pressed", path);
} };
var mousepos = [0, 0]; var mousepos = [0, 0];
@ -136,30 +121,35 @@ prosperon.mouseup = function(b){
}; };
input.mouse = {}; input.mouse = {};
input.mouse.screenpos = function() { return mousepos.slice(); }; input.mouse.screenpos = function () {
input.mouse.worldpos = function() { return game.camera.view2world(mousepos); }; return mousepos.slice();
input.mouse.disabled = function() { input.mouse_mode(1); }; };
input.mouse.normal = function() { input.mouse_mode(0); }; input.mouse.worldpos = function () {
return game.camera.view2world(mousepos);
};
input.mouse.disabled = function () {
input.mouse_mode(1);
};
input.mouse.normal = function () {
input.mouse_mode(0);
};
input.mouse.mode = function (m) { input.mouse.mode = function (m) {
if (input.mouse.custom[m]) if (input.mouse.custom[m]) input.cursor_img(input.mouse.custom[m]);
input.cursor_img(input.mouse.custom[m]); else input.mouse_cursor(m);
else
input.mouse_cursor(m);
}; };
input.mouse.set_custom_cursor = function (img, mode = input.mouse.cursor.default) { input.mouse.set_custom_cursor = function (img, mode = input.mouse.cursor.default) {
if (!img) if (!img) delete input.mouse.custom[mode];
delete input.mouse.custom[mode];
else { else {
input.cursor_img(img); input.cursor_img(img);
input.mouse.custom[mode] = img; input.mouse.custom[mode] = img;
} }
}; };
input.mouse.button = { /* left, right, middle mouse */ input.mouse.button = {
0: "lm", /* left, right, middle mouse */ 0: "lm",
1: "rm", 1: "rm",
2: "mm" 2: "mm",
}; };
input.mouse.custom = []; input.mouse.custom = [];
input.mouse.cursor = { input.mouse.cursor = {
@ -173,7 +163,7 @@ input.mouse.cursor = {
nwse: 7, nwse: 7,
nesw: 8, nesw: 8,
resize: 9, resize: 9,
no: 10 no: 10,
}; };
input.mouse.doc = {}; input.mouse.doc = {};
@ -184,13 +174,13 @@ input.mouse.normal.doc = "Set the mouse to show again after hiding.";
input.keyboard = {}; input.keyboard = {};
input.keyboard.down = function (code) { input.keyboard.down = function (code) {
if (typeof code === 'number') return downkeys[code]; if (typeof code === "number") return downkeys[code];
if (typeof code === 'string') return (downkeys[code.uc().charCodeAt()] || downkeys[code.lc().charCodeAt()]); if (typeof code === "string") return downkeys[code.uc().charCodeAt()] || downkeys[code.lc().charCodeAt()];
return undefined; return undefined;
} };
input.state2str = function (state) { input.state2str = function (state) {
if (typeof state === 'string') return state; if (typeof state === "string") return state;
switch (state) { switch (state) {
case 0: case 0:
return "down"; return "down";
@ -199,10 +189,10 @@ input.state2str = function(state) {
case 2: case 2:
return "released"; return "released";
} }
} };
input.print_pawn_kbm = function (pawn) { input.print_pawn_kbm = function (pawn) {
if (!('inputs' in pawn)) return; if (!("inputs" in pawn)) return;
var str = ""; var str = "";
for (var key in pawn.inputs) { for (var key in pawn.inputs) {
if (!pawn.inputs[key].doc) continue; if (!pawn.inputs[key].doc) continue;
@ -217,13 +207,11 @@ joysticks["wasd"] = {
uy: "w", uy: "w",
dy: "s", dy: "s",
ux: "d", ux: "d",
dx: "a" dx: "a",
}; };
input.procdown = function() input.procdown = function () {
{ for (var k in downkeys) player[0].raw_input(keyname_extd(k), "down");
for (var k in downkeys)
player[0].raw_input(keyname_extd(k), "down");
for (var i in joysticks) { for (var i in joysticks) {
var joy = joysticks[i]; var joy = joysticks[i];
@ -231,10 +219,10 @@ input.procdown = function()
var y = joy.uy - joy.dy; var y = joy.uy - joy.dy;
player[0].joy_input(i, joysticks[i]); player[0].joy_input(i, joysticks[i]);
} }
} };
input.print_md_kbm = function (pawn) { input.print_md_kbm = function (pawn) {
if (!('inputs' in pawn)) return; if (!("inputs" in pawn)) return;
var str = ""; var str = "";
str += "|control|description|\n|---|---|\n"; str += "|control|description|\n|---|---|\n";
@ -248,7 +236,7 @@ input.print_md_kbm = function(pawn) {
}; };
input.has_bind = function (pawn, bind) { input.has_bind = function (pawn, bind) {
return (typeof pawn.inputs?.[bind] === 'function'); return typeof pawn.inputs?.[bind] === "function";
}; };
input.action = { input.action = {
@ -265,7 +253,9 @@ input.action = {
input.tabcomplete = function (val, list) { input.tabcomplete = function (val, list) {
if (!val) return val; if (!val) return val;
list.dofilter(function(x) { return x.startsWith(val); }); list.dofilter(function (x) {
return x.startsWith(val);
});
if (list.length === 1) { if (list.length === 1) {
return list[0]; return list[0];
@ -275,16 +265,22 @@ input.tabcomplete = function(val, list) {
var i = val.length; var i = val.length;
while (!ret && !Object.empty(list)) { while (!ret && !Object.empty(list)) {
var char = list[0][i]; var char = list[0][i];
if (!list.every(function(x) { return x[i] === char; })) if (
!list.every(function (x) {
return x[i] === char;
})
)
ret = list[0].slice(0, i); ret = list[0].slice(0, i);
else { else {
i++; i++;
list.dofilter(function(x) { return x.length-1 > i; }); list.dofilter(function (x) {
return x.length - 1 > i;
});
} }
} }
return ret ? ret : val; return ret ? ret : val;
} };
/* May be a human player; may be an AI player */ /* May be a human player; may be an AI player */
@ -300,24 +296,22 @@ var Player = {
mouse_input(type, ...args) { mouse_input(type, ...args) {
for (var pawn of this.pawns.reversed()) { for (var pawn of this.pawns.reversed()) {
if (typeof pawn.inputs?.mouse?.[type] === 'function') { if (typeof pawn.inputs?.mouse?.[type] === "function") {
pawn.inputs.mouse[type].call(pawn, ...args); pawn.inputs.mouse[type].call(pawn, ...args);
pawn.inputs.post?.call(pawn); pawn.inputs.post?.call(pawn);
if (!pawn.inputs.fallthru) if (!pawn.inputs.fallthru) return;
return;
} }
} }
}, },
char_input(c) { char_input(c) {
for (var pawn of this.pawns.reversed()) { for (var pawn of this.pawns.reversed()) {
if (typeof pawn.inputs?.char === 'function') { if (typeof pawn.inputs?.char === "function") {
pawn.inputs.char.call(pawn, c); pawn.inputs.char.call(pawn, c);
pawn.inputs.post?.call(pawn); pawn.inputs.post?.call(pawn);
if (!pawn.inputs.fallthru) if (!pawn.inputs.fallthru) return;
return; }
} }
};
}, },
joy_input(name, joystick) { joy_input(name, joystick) {
@ -354,31 +348,27 @@ var Player = {
var fn = null; var fn = null;
switch (state) { switch (state) {
case 'pressed': case "pressed":
fn = pawn.inputs[cmd]; fn = pawn.inputs[cmd];
break; break;
case 'rep': case "rep":
fn = pawn.inputs[cmd].rep ? pawn.inputs[cmd] : null; fn = pawn.inputs[cmd].rep ? pawn.inputs[cmd] : null;
break; break;
case 'released': case "released":
fn = pawn.inputs[cmd].released; fn = pawn.inputs[cmd].released;
break; break;
case 'down': case "down":
if (typeof pawn.inputs[cmd].down === 'function') if (typeof pawn.inputs[cmd].down === "function") fn = pawn.inputs[cmd].down;
fn = pawn.inputs[cmd].down; else if (pawn.inputs[cmd].down) fn = pawn.inputs[cmd];
else if (pawn.inputs[cmd].down)
fn = pawn.inputs[cmd];
} }
if (typeof fn === 'function') if (typeof fn === "function") fn.call(pawn, ...args);
fn.call(pawn, ... args);
if (!pawn.inputs) if (!pawn.inputs)
if (block) return; if (block) return;
else continue; else continue;
if (state === 'released') if (state === "released") pawn.inputs.release_post?.call(pawn);
pawn.inputs.release_post?.call(pawn);
if (!pawn.inputs.fallthru) return; if (!pawn.inputs.fallthru) return;
if (pawn.inputs.block) return; if (pawn.inputs.block) return;
@ -387,16 +377,14 @@ var Player = {
obj_controlled(obj) { obj_controlled(obj) {
for (var p in Player.players) { for (var p in Player.players) {
if (p.pawns.contains(obj)) if (p.pawns.contains(obj)) return true;
return true;
} }
return false; return false;
}, },
print_pawns() { print_pawns() {
for (var pawn of this.pawns.reversed()) for (var pawn of this.pawns.reversed()) say(pawn.toString());
say(pawn.toString());
}, },
create() { create() {
@ -423,12 +411,11 @@ var Player = {
}, },
}; };
input.do_uncontrol = function(pawn) input.do_uncontrol = function (pawn) {
{
Player.players.forEach(function (p) { Player.players.forEach(function (p) {
p.pawns = p.pawns.filter(x => x !== pawn); p.pawns = p.pawns.filter(x => x !== pawn);
}); });
} };
for (var i = 0; i < 4; i++) { for (var i = 0; i < 4; i++) {
Player.create(); Player.create();
@ -443,5 +430,5 @@ Player.doc.players = "A list of current players.";
var player = Player; var player = Player;
return { return {
player player,
}; };

View file

@ -5,12 +5,11 @@ var panel;
var selected = undefined; var selected = undefined;
mum.inputs = {}; mum.inputs = {};
mum.inputs.lm = function() mum.inputs.lm = function () {
{
if (!selected) return; if (!selected) return;
if (!selected.action) return; if (!selected.action) return;
selected.action(); selected.action();
} };
mum.base = { mum.base = {
pos: null, // If set, puts the cursor to this position before drawing the element pos: null, // If set, puts the cursor to this position before drawing the element
@ -36,7 +35,7 @@ mum.base = {
border: 0, // Draw a border around the element. For text, an outline. border: 0, // Draw a border around the element. For text, an outline.
overflow: "wrap", // how to deal with overflow from parent element overflow: "wrap", // how to deal with overflow from parent element
wrap: -1, wrap: -1,
text_align: "left", /* left, center, right */ text_align: "left" /* left, center, right */,
shader: null, // Use this shader, instead of the engine provided one shader: null, // Use this shader, instead of the engine provided one
color: Color.white, color: Color.white,
opacity: 1, opacity: 1,
@ -46,10 +45,10 @@ mum.base = {
max_height: Infinity, max_height: Infinity,
image_repeat: false, image_repeat: false,
image_repeat_offset: [0, 0], image_repeat_offset: [0, 0],
debug: false, /* set to true to draw debug boxes */ debug: false /* set to true to draw debug boxes */,
hide: false, hide: false,
tooltip: null, tooltip: null,
} };
// data is passed into each function, and various stats are generated // data is passed into each function, and various stats are generated
// drawpos: the point to start the drawing from // drawpos: the point to start the drawing from
@ -57,7 +56,9 @@ mum.base = {
// bound: a boundingbox around the drawn UI element // bound: a boundingbox around the drawn UI element
// extent: a boundingbox around the total extents of the element (ie before padding) // extent: a boundingbox around the total extents of the element (ie before padding)
function show_debug() { return prosperon.debug && mum.debug; } function show_debug() {
return prosperon.debug && mum.debug;
}
mum.debug = false; mum.debug = false;
@ -68,8 +69,7 @@ mum.style = mum.base;
var cursor = [0, 0]; var cursor = [0, 0];
var pre = function(data) var pre = function (data) {
{
if (data.hide) return true; if (data.hide) return true;
data.__proto__ = mum.style; data.__proto__ = mum.style;
@ -82,22 +82,19 @@ var pre = function(data)
} }
data.wh = [data.width, data.height]; data.wh = [data.width, data.height];
} };
var anchor_calc = function(data) var anchor_calc = function (data) {
{
var aa = [0, 1].sub(data.anchor); var aa = [0, 1].sub(data.anchor);
data.drawpos = data.drawpos.add([data.width, data.height]).scale(aa); data.drawpos = data.drawpos.add([data.width, data.height]).scale(aa);
} };
var end = function(data) var end = function (data) {
{
cursor = cursor.add(data.padding); cursor = cursor.add(data.padding);
post(data); post(data);
} };
mum.list = function(fn, data = {}) mum.list = function (fn, data = {}) {
{
if (pre(data)) return; if (pre(data)) return;
var aa = [0, 1].sub(data.anchor); var aa = [0, 1].sub(data.anchor);
cursor = cursor.add([data.width, data.height].scale(aa)).add(data.offset).add(data.padding); cursor = cursor.add([data.width, data.height].scale(aa)).add(data.offset).add(data.padding);
@ -110,7 +107,7 @@ mum.list = function(fn, data = {})
t: cursor.y, t: cursor.y,
b: cursor.y - data.height, b: cursor.y - data.height,
l: cursor.x, l: cursor.x,
r:cursor.x+data.width r: cursor.x + data.width,
}); });
//if (data.background_image) mum.image(null, Object.create(data)) //if (data.background_image) mum.image(null, Object.create(data))
@ -119,10 +116,8 @@ mum.list = function(fn, data = {})
imgpos.y -= data.height / 2; imgpos.y -= data.height / 2;
imgpos.x -= data.width / 2; imgpos.x -= data.width / 2;
var imgscale = [data.width, data.height]; var imgscale = [data.width, data.height];
if (data.slice) if (data.slice) render.slice9(game.texture(data.background_image), imgpos, data.slice, imgscale);
render.slice9(game.texture(data.background_image), imgpos, data.slice, imgscale); else render.image(game.texture(data.background_image), imgpos, [data.width, data.height]);
else
render.image(game.texture(data.background_image), imgpos, [data.width,data.height]);
} }
fn(); fn();
@ -132,26 +127,21 @@ mum.list = function(fn, data = {})
data.bb.t += data.padding.y; data.bb.t += data.padding.y;
data.bb.b -= data.padding.y; data.bb.b -= data.padding.y;
if (show_debug()) if (show_debug()) render.boundingbox(data.bb);
render.boundingbox(data.bb);
post = posts.pop(); post = posts.pop();
end(data); end(data);
} };
mum.list.post = function(e) mum.list.post = function (e) {
{ cursor.y -= e.bb.t - e.bb.b;
cursor.y -= (e.bb.t - e.bb.b);
cursor.y -= e.padding.y; cursor.y -= e.padding.y;
if (this.bb) if (this.bb) this.bb = bbox.expand(this.bb, e.bb);
this.bb = bbox.expand(this.bb,e.bb) else this.bb = e.bb;
else };
this.bb = e.bb;
}
mum.label = function(str, data = {}) mum.label = function (str, data = {}) {
{
if (pre(data)) return; if (pre(data)) return;
render.set_font(data.font, data.font_size); render.set_font(data.font, data.font_size);
@ -161,7 +151,7 @@ mum.label = function(str, data = {})
var aa = [0, 1].sub(data.anchor); var aa = [0, 1].sub(data.anchor);
data.drawpos.y -= (data.bb.t-cursor.y); data.drawpos.y -= data.bb.t - cursor.y;
data.drawpos = data.drawpos.add(data.wh.scale(aa)).add(data.offset); data.drawpos = data.drawpos.add(data.wh.scale(aa)).add(data.offset);
data.bb = render.text_bb(str, data.scale, data.wrap, data.drawpos); data.bb = render.text_bb(str, data.scale, data.wrap, data.drawpos);
@ -176,31 +166,24 @@ mum.label = function(str, data = {})
data.bb = render.text(str, data.drawpos, data.scale, data.color, data.wrap); data.bb = render.text(str, data.drawpos, data.scale, data.color, data.wrap);
if (show_debug()) if (show_debug()) render.boundingbox(data.bb);
render.boundingbox(data.bb);
end(data); end(data);
} };
mum.image = function(path, data = {}) mum.image = function (path, data = {}) {
{
if (pre(data)) return; if (pre(data)) return;
path ??= data.background_image; path ??= data.background_image;
var tex = path; var tex = path;
if (typeof path === 'string') if (typeof path === "string") tex = game.texture(path);
tex = game.texture(path);
if (!data.height) if (!data.height)
if (data.width) if (data.width) data.height = tex.height * (data.width / tex.width);
data.height = tex.height * (data.width/tex.width); else data.height = tex.height;
else
data.height = tex.height;
if (!data.width) if (!data.width)
if (data.height) if (data.height) data.width = tex.width * (data.height / tex.height);
data.width = tex.width * (data.height/tex.height); else data.height = tex.height;
else
data.height = tex.height;
if (!data.width) data.width = tex.width; if (!data.width) data.width = tex.width;
if (!data.height) data.height = tex.height; if (!data.height) data.height = tex.height;
@ -208,16 +191,13 @@ mum.image = function(path, data = {})
var aa = [0, 1].sub(data.anchor); var aa = [0, 1].sub(data.anchor);
data.drawpos = data.drawpos.add(aa.scale([data.width, data.height])); data.drawpos = data.drawpos.add(aa.scale([data.width, data.height]));
if (data.slice) if (data.slice) render.slice9(tex, data.drawpos, data.slice, [data.width, data.height]);
render.slice9(tex, data.drawpos, data.slice, [data.width,data.height]); else data.bb = render.image(tex, data.drawpos, [data.width, data.height]);
else
data.bb = render.image(tex, data.drawpos, [data.width, data.height]);
end(data); end(data);
} };
mum.rectangle = function(data = {}) mum.rectangle = function (data = {}) {
{
if (pre(data)) return; if (pre(data)) return;
var aa = [0, 0].sub(data.anchor); var aa = [0, 0].sub(data.anchor);
data.drawpos = data.drawpos.add(aa.scale([data.width, data.height])); data.drawpos = data.drawpos.add(aa.scale([data.width, data.height]));
@ -225,23 +205,19 @@ mum.rectangle = function(data = {})
render.rectangle(data.drawpos, data.drawpos.add([data.width, data.height]), data.color); render.rectangle(data.drawpos, data.drawpos.add([data.width, data.height]), data.color);
end(data); end(data);
} };
var btnbb; var btnbb;
var btnpost = function() var btnpost = function () {
{
btnbb = data.bb; btnbb = data.bb;
} };
mum.button = function(str, data = {padding:[4,4], color:Color.black}) mum.button = function (str, data = { padding: [4, 4], color: Color.black }) {
{
if (pre(data)) return; if (pre(data)) return;
posts.push(post); posts.push(post);
post = btnpost; post = btnpost;
if (typeof str === 'string') if (typeof str === "string") render.text(str, cursor.add(data.padding), data.scale, data.color);
render.text(str, cursor.add(data.padding), data.scale, data.color); else str();
else
str();
if (data.action && data.hover && bbox.pointin(btnbb, input.mouse.screenpos())) { if (data.action && data.hover && bbox.pointin(btnbb, input.mouse.screenpos())) {
data.hover.__proto__ = data; data.hover.__proto__ = data;
@ -252,10 +228,9 @@ mum.button = function(str, data = {padding:[4,4], color:Color.black})
post = posts.pop(); post = posts.pop();
end(data); end(data);
} };
mum.window = function(fn, data = {}) mum.window = function (fn, data = {}) {
{
if (pre(data)) return; if (pre(data)) return;
render.rectangle(cursor, cursor.add(data.size), data.color); render.rectangle(cursor, cursor.add(data.size), data.color);
@ -263,28 +238,34 @@ mum.window = function(fn, data = {})
cursor = cursor.add(data.padding); cursor = cursor.add(data.padding);
fn(); fn();
end(data); end(data);
} };
mum.ex_hud = function() mum.ex_hud = function () {
{
mum.label("TOP LEFT", { pos: [0, game.size.y], anchor: [0, 1] }); mum.label("TOP LEFT", { pos: [0, game.size.y], anchor: [0, 1] });
mum.label("BOTTOM LEFT", { pos: [0, 0], anchor: [0, 0] }); mum.label("BOTTOM LEFT", { pos: [0, 0], anchor: [0, 0] });
mum.label("TOP RIGHT", { pos: game.size, anchor: [1, 1] }); mum.label("TOP RIGHT", { pos: game.size, anchor: [1, 1] });
mum.label("BOTTOM RIGHT", { pos: [game.size.x, 0], anchor: [1, 0] }); mum.label("BOTTOM RIGHT", { pos: [game.size.x, 0], anchor: [1, 0] });
} };
mum.drawinput = undefined; mum.drawinput = undefined;
var ptext = ""; var ptext = "";
var panpan = { var panpan = {
draw() { draw() {
mum.rectangle({pos:[0,0], anchor:[0,0], height:20, width: window.size.x, padding:[10,16], color:Color.black}); mum.rectangle({
pos: [0, 0],
anchor: [0, 0],
height: 20,
width: window.size.x,
padding: [10, 16],
color: Color.black,
});
mum.label("input level: "); mum.label("input level: ");
mum.label(ptext, { offset: [50, 0], color: Color.red }); mum.label(ptext, { offset: [50, 0], color: Color.red });
}, },
inputs: { inputs: {
block: true, block: true,
char(c) { char(c) {
ptext += c ptext += c;
}, },
enter() { enter() {
delete mum.drawinput; delete mum.drawinput;
@ -296,9 +277,9 @@ var panpan = {
}, },
backspace() { backspace() {
ptext = ptext.slice(0, ptext.length - 1); ptext = ptext.slice(0, ptext.length - 1);
}
}, },
} },
};
mum.textinput = function (fn, str = "") { mum.textinput = function (fn, str = "") {
mum.drawinput = panpan.draw; mum.drawinput = panpan.draw;
@ -308,5 +289,5 @@ mum.textinput = function (fn, str = "") {
fn(ptext); fn(ptext);
delete mum.drawinput; delete mum.drawinput;
player[0].uncontrol(panpan); player[0].uncontrol(panpan);
} };
} };

View file

@ -1,4 +1,6 @@
this.hud = function() this.hud = function () {
{ mum.label("No game yet! Make game.js to get started!", {
mum.label("No game yet! Make game.js to get started!", {pos:game.size.scale(0.5), anchor:[0.5,0.5]}); pos: game.size.scale(0.5),
} anchor: [0.5, 0.5],
});
};

View file

@ -7,38 +7,32 @@ emitter.spawn_timer = 0;
emitter.pps = 0; emitter.pps = 0;
emitter.color = Color.white; emitter.color = Color.white;
emitter.draw = function() emitter.draw = function () {
{
var pars = Object.values(this.particles); var pars = Object.values(this.particles);
if (pars.length === 0) return; if (pars.length === 0) return;
render.use_shader(this.shader); render.use_shader(this.shader);
render.use_mat(this); render.use_mat(this);
render.make_particle_ssbo(pars, this.ssbo); render.make_particle_ssbo(pars, this.ssbo);
render.draw(this.shape, this.ssbo, pars.length); render.draw(this.shape, this.ssbo, pars.length);
} };
emitter.kill = function() emitter.kill = function () {
{
emitters.remove(this); emitters.remove(this);
} };
var std_step = function(p) var std_step = function (p) {
{
if (p.time < this.grow_for) { if (p.time < this.grow_for) {
var s = Math.lerp(0, this.scale, p.time / this.grow_for); var s = Math.lerp(0, this.scale, p.time / this.grow_for);
p.transform.scale = [s, s, s]; p.transform.scale = [s, s, s];
} } else if (p.time > p.life - this.shrink_for) {
else if (p.time > (p.life - this.shrink_for)) {
var s = Math.lerp(0, this.scale, (p.life - p.time) / this.shrink_for); var s = Math.lerp(0, this.scale, (p.life - p.time) / this.shrink_for);
p.transform.scale = [s, s, s]; p.transform.scale = [s, s, s];
} else } else p.transform.scale = [this.scale, this.scale, this.scale];
p.transform.scale = [this.scale,this.scale,this.scale]; };
}
emitter.step_hook = std_step; emitter.step_hook = std_step;
emitter.spawn = function(t) emitter.spawn = function (t) {
{
t ??= this.transform; t ??= this.transform;
var par = this.dead.shift(); var par = this.dead.shift();
@ -56,7 +50,7 @@ emitter.spawn = function(t)
transform: os.make_transform(), transform: os.make_transform(),
life: this.life, life: this.life,
time: 0, time: 0,
color: this.color color: this.color,
}; };
par.body = os.make_body(par.transform); par.body = os.make_body(par.transform);
@ -67,10 +61,9 @@ emitter.spawn = function(t)
this.particles[par.id] = par; this.particles[par.id] = par;
this.spawn_hook(par); this.spawn_hook(par);
} };
emitter.step = function(dt) emitter.step = function (dt) {
{
// update spawning particles // update spawning particles
if (this.on && this.pps > 0) { if (this.on && this.pps > 0) {
this.spawn_timer += dt; this.spawn_timer += dt;
@ -92,16 +85,15 @@ emitter.step = function(dt)
delete this.particles[p.id]; delete this.particles[p.id];
} }
} }
} };
emitter.burst = function (count, t) { emitter.burst = function (count, t) {
for (var i = 0; i < count; i++) this.spawn(t); for (var i = 0; i < count; i++) this.spawn(t);
} };
var emitters = []; var emitters = [];
var make_emitter = function() var make_emitter = function () {
{
var e = Object.create(emitter); var e = Object.create(emitter);
e.ssbo = render.make_textssbo(); e.ssbo = render.make_textssbo();
e.shape = shape.centered_quad; e.shape = shape.centered_quad;
@ -109,16 +101,13 @@ var make_emitter = function()
e.dead = []; e.dead = [];
emitters.push(e); emitters.push(e);
return e; return e;
};
function update_emitters(dt) {
for (var e of emitters) e.step(dt);
} }
function update_emitters(dt) function draw_emitters() {
{
for (var e of emitters)
e.step(dt);
}
function draw_emitters()
{
for (var e of emitters) e.draw(); for (var e of emitters) e.draw();
} }

View file

@ -13,23 +13,21 @@ physics.pos_query = function(pos, start = world, give = 10) {
var ret; var ret;
ret = physics.point_query_nearest(pos, 0); ret = physics.point_query_nearest(pos, 0);
if (ret) if (ret) return ret.entity;
return ret.entity;
return game.all_objects(function (o) { return game.all_objects(function (o) {
var dist = Vector.length(o.pos.sub(pos)); var dist = Vector.length(o.pos.sub(pos));
if (dist <= give) return o; if (dist <= give) return o;
}); });
} };
physics.box_point_query = function (box, points) { physics.box_point_query = function (box, points) {
if (!box || !points) return []; if (!box || !points) return [];
var bbox = bbox.fromcwh(box.pos, box.wh); var bbox = bbox.fromcwh(box.pos, box.wh);
var inside = []; var inside = [];
for (var i in points) for (var i in points) if (bbox.pointin(bbox, points[i])) inside.push[i];
if (bbox.pointin(bbox,points[i])) inside.push[i];
return inside; return inside;
} };
Object.assign(physics, { Object.assign(physics, {
dynamic: 0, dynamic: 0,
@ -56,5 +54,5 @@ physics.damp.mask = ~1;
physics.delta = 1 / 240; physics.delta = 1 / 240;
return { return {
physics physics,
} };

View file

@ -40,11 +40,12 @@ profile.cpu = function profile_cpu(fn, times = 1, q = "unnamed") {
say(`profile [${q}]: ${avgt} ± ${profile.best_t(Math.ci(series))} [${totalt} for ${times} loops]`); say(`profile [${q}]: ${avgt} ± ${profile.best_t(Math.ci(series))} [${totalt} for ${times} loops]`);
say(`result of function is ${fn()}`); say(`result of function is ${fn()}`);
if (retgather) if (retgather) profile.start_prof_gather();
profile.start_prof_gather(); };
}
profile.ms = function(t) { return profile.secs(t)*1000; } profile.ms = function (t) {
return profile.secs(t) * 1000;
};
var callgraph = {}; var callgraph = {};
profile.rawstacks = {}; profile.rawstacks = {};
@ -70,13 +71,12 @@ var start_gather = profile.now();
profile.cpu_start = undefined; profile.cpu_start = undefined;
profile.clear_cpu = function() profile.clear_cpu = function () {
{
callgraph = {}; callgraph = {};
} };
profile.start_cpu_gather = function(gathertime = 5) // gather cpu frames for 'time' seconds profile.start_cpu_gather = function (gathertime = 5) {
{ // gather cpu frames for 'time' seconds
if (profile.cpu_start) return; if (profile.cpu_start) return;
profile.cpu_start = profile.now(); profile.cpu_start = profile.now();
var st = profile.cpu_start; var st = profile.cpu_start;
@ -86,26 +86,24 @@ profile.start_cpu_gather = function(gathertime = 5) // gather cpu frames for 'ti
var err = new Error(); var err = new Error();
var stack = err.stack.split("\n").slice(1); var stack = err.stack.split("\n").slice(1);
var rawstack = stack.join('\n'); var rawstack = stack.join("\n");
profile.rawstacks[rawstack] ??= { profile.rawstacks[rawstack] ??= {
time: 0, time: 0,
hits: 0 hits: 0,
}; };
profile.rawstacks[rawstack].hits++; profile.rawstacks[rawstack].hits++;
profile.rawstacks[rawstack].time += time; profile.rawstacks[rawstack].time += time;
stack = stack.map(x => x.slice(7).split(' ')); stack = stack.map(x => x.slice(7).split(" "));
var fns = stack.map(x => x[0]).filter(x => x); var fns = stack.map(x => x[0]).filter(x => x);
var lines = stack.map(x => x[1]).filter(x => x); var lines = stack.map(x => x[1]).filter(x => x);
lines = lines.map(x => x.slice(1, x.length - 1)); lines = lines.map(x => x.slice(1, x.length - 1));
for (var i = 0; i < fns.length; i++) for (var i = 0; i < fns.length; i++) add_callgraph(fns[i], lines[i], time);
add_callgraph(fns[i], lines[i], time);
st = profile.now(); st = profile.now();
if (profile.secs(st-profile.cpu_start) < gathertime) if (profile.secs(st - profile.cpu_start) < gathertime) profile.gather_rate(Math.variate(hittar, hitpct));
profile.gather_rate(Math.variate(hittar,hitpct));
else { else {
profile.gather_stop(); profile.gather_stop();
profile.cpu_start = undefined; profile.cpu_start = undefined;
@ -116,9 +114,9 @@ profile.start_cpu_gather = function(gathertime = 5) // gather cpu frames for 'ti
}); });
for (var x of e) { for (var x of e) {
var ffs = x.line.split(':'); var ffs = x.line.split(":");
x.timestr = profile.best_t(x.time); x.timestr = profile.best_t(x.time);
var pct = profile.secs(x.time)/gathertime*100; var pct = (profile.secs(x.time) / gathertime) * 100;
x.timeper = x.time / x.hits; x.timeper = x.time / x.hits;
x.timeperstr = profile.best_t(x.timeper); x.timeperstr = profile.best_t(x.timeper);
x.fncall = get_line(ffs[0], ffs[1]); x.fncall = get_line(ffs[0], ffs[1]);
@ -128,26 +126,24 @@ profile.start_cpu_gather = function(gathertime = 5) // gather cpu frames for 'ti
profile.cpu_instr = e; profile.cpu_instr = e;
} }
}); });
} };
function push_time(arr, ob, max) function push_time(arr, ob, max) {
{
arr.push({ arr.push({
time: profile.now(), time: profile.now(),
ob ob,
}); });
} }
profile.cpu_frames = []; profile.cpu_frames = [];
profile.last_cpu_frame = undefined; profile.last_cpu_frame = undefined;
profile.cpu_frame = function() profile.cpu_frame = function () {
{
profile.gather(Math.random_range(300, 600), function () { profile.gather(Math.random_range(300, 600), function () {
var err = new Error(); var err = new Error();
profile.last_cpu_frame = err.stack; //.split('\n').slicconsole.stack(2); profile.last_cpu_frame = err.stack; //.split('\n').slicconsole.stack(2);
profile.gather_stop(); profile.gather_stop();
}); });
} };
var filecache = {}; var filecache = {};
function get_line(file, line) { function get_line(file, line) {
@ -158,17 +154,19 @@ function get_line (file, line) {
filecache[file] = "undefined"; filecache[file] = "undefined";
return filecache[file]; return filecache[file];
} }
filecache[file] = io.slurp(file).split('\n'); filecache[file] = io.slurp(file).split("\n");
text = filecache[file]; text = filecache[file];
} }
if (typeof text === 'string') return text; if (typeof text === "string") return text;
text = text[Number(line) - 1]; text = text[Number(line) - 1];
if (!text) return "NULL"; if (!text) return "NULL";
return text.trim(); return text.trim();
} }
profile.stop_cpu_instr = function () { return; } profile.stop_cpu_instr = function () {
return;
};
profile.best_t = function (t) { profile.best_t = function (t) {
var qq = 0; var qq = 0;
@ -193,26 +191,23 @@ profile.report = function (start, msg = "[undefined report]") {
var frame_avg = false; var frame_avg = false;
profile.frame_avg_t = 72000; profile.frame_avg_t = 72000;
profile.start_frame_avg = function() profile.start_frame_avg = function () {
{
if (frame_avg) return; if (frame_avg) return;
profile_frames = {}; profile_frames = {};
profile_frame_ts = []; profile_frame_ts = [];
profile_cframe = profile_frames; profile_cframe = profile_frames;
pframe = 0; pframe = 0;
frame_avg = true; frame_avg = true;
} };
profile.stop_frame_avg = function() profile.stop_frame_avg = function () {
{
frame_avg = false; frame_avg = false;
} };
profile.toggle_frame_avg = function() profile.toggle_frame_avg = function () {
{
if (frame_avg) profile.stop_frame_avg(); if (frame_avg) profile.stop_frame_avg();
else profile.start_frame_avg(); else profile.start_frame_avg();
} };
var profile_framer = { var profile_framer = {
series: [], series: [],
@ -223,8 +218,7 @@ var profile_cframe = undefined;
var pframe = 0; var pframe = 0;
var profile_stack = []; var profile_stack = [];
profile.frame = function profile_frame(title) profile.frame = function profile_frame(title) {
{
if (profile.cpu_start) return; if (profile.cpu_start) return;
if (!frame_avg) return; if (!frame_avg) return;
@ -232,43 +226,38 @@ profile.frame = function profile_frame(title)
profile_cframe = {}; profile_cframe = {};
profile_framer.series.push({ profile_framer.series.push({
time: profile.now(), time: profile.now(),
data:profile_cframe data: profile_cframe,
}); });
} else } else profile_stack.push(profile_cframe);
profile_stack.push(profile_cframe);
profile_cframe[title] ??= {}; profile_cframe[title] ??= {};
profile_cframe = profile_cframe[title]; profile_cframe = profile_cframe[title];
profile_cframe.time = profile.now(); profile_cframe.time = profile.now();
} };
profile.endframe = function profile_endframe() profile.endframe = function profile_endframe() {
{
if (!frame_avg) return; if (!frame_avg) return;
profile_cframe.time = profile.now() - profile_cframe.time; profile_cframe.time = profile.now() - profile_cframe.time;
profile_cframe = profile_frame_ts.pop(); profile_cframe = profile_frame_ts.pop();
} };
var print_frame = function(frame, indent, title) var print_frame = function (frame, indent, title) {
{
say(indent + `${title} ::::: ${profile.best_t(Math.mean(frame._times))} ± ${profile.best_t(Math.ci(frame._times))} (${frame._times.length} hits)`); say(indent + `${title} ::::: ${profile.best_t(Math.mean(frame._times))} ± ${profile.best_t(Math.ci(frame._times))} (${frame._times.length} hits)`);
for (var i in frame) { for (var i in frame) {
if (i === '_times') continue; if (i === "_times") continue;
print_frame(frame[i], indent + " ", i); print_frame(frame[i], indent + " ", i);
} }
} };
profile.print_frame_avg = function() profile.print_frame_avg = function () {
{
say("===FRAME AVERAGES===\n"); say("===FRAME AVERAGES===\n");
var indent = ""; var indent = "";
for (var i in profile_frames) for (var i in profile_frames) print_frame(profile_frames[i], "", "frame");
print_frame(profile_frames[i], "", 'frame');
say("\n"); say("\n");
} };
/* /*
Cache reporting is to measure how long specific events take, that are NOT every frame Cache reporting is to measure how long specific events take, that are NOT every frame
@ -283,38 +272,39 @@ var cachest = 0;
var cachegroup; var cachegroup;
var cachetitle; var cachetitle;
profile.cache_reporting = function() { return cache_reporting; } profile.cache_reporting = function () {
profile.cache_toggle = function() { cache_reporting = !cache_reporting; } return cache_reporting;
};
profile.cache_toggle = function () {
cache_reporting = !cache_reporting;
};
profile.cache_dump = function () { profile.cache_dump = function () {
report_cache = {}; report_cache = {};
} };
profile.cache = function profile_cache(group, title) profile.cache = function profile_cache(group, title) {
{
if (!cache_reporting) return; if (!cache_reporting) return;
cachest = profile.now(); cachest = profile.now();
cachegroup = group; cachegroup = group;
cachetitle = title; cachetitle = title;
} };
profile.endcache = function profile_endcache(tag = "") profile.endcache = function profile_endcache(tag = "") {
{
addreport(cachegroup, cachetitle + tag, cachest); addreport(cachegroup, cachetitle + tag, cachest);
} };
function addreport(group, line, start) { function addreport(group, line, start) {
if (typeof group !== 'string') group = 'UNGROUPED'; if (typeof group !== "string") group = "UNGROUPED";
report_cache[group] ??= {}; report_cache[group] ??= {};
var cache = report_cache[group]; var cache = report_cache[group];
cache[line] ??= []; cache[line] ??= [];
var t = profile.now(); var t = profile.now();
cache[line].push(t - start); cache[line].push(t - start);
return t; return t;
}; }
function printreport(cache, name) { function printreport(cache, name) {
var report = `==${name}==` + var report = `==${name}==` + "\n";
"\n";
var reports = []; var reports = [];
for (var i in cache) { for (var i in cache) {
@ -323,7 +313,7 @@ function printreport (cache, name) {
time: time, time: time,
name: i, name: i,
hits: cache[i].length, hits: cache[i].length,
avg : time / cache[i].length avg: time / cache[i].length,
}); });
} }
reports = reports.sort((a, b) => { reports = reports.sort((a, b) => {
@ -331,12 +321,10 @@ function printreport (cache, name) {
return -1; return -1;
}); });
for (var rep of reports) for (var rep of reports) report += `${rep.name} ${profile.best_t(rep.avg)} (${rep.hits} hits) (total ${profile.best_t(rep.time)})\n`;
report += `${rep.name} ${profile.best_t(rep.avg)} (${
rep.hits} hits) (total ${profile.best_t(rep.time)})\n`;
return report; return report;
}; }
profile.data = {}; profile.data = {};
profile.curframe = 0; profile.curframe = 0;
@ -348,30 +336,25 @@ function prof_add_stats (obj, stat) {
} }
} }
profile.pushdata = function(arr, val) profile.pushdata = function (arr, val) {
{ if (arr.last() !== val) arr[profile.curframe] = val;
if (arr.last() !== val) };
arr[profile.curframe] = val;
}
profile.capture_data = function() profile.capture_data = function () {
{
prof_add_stats(profile.data.memory, os.mem()); prof_add_stats(profile.data.memory, os.mem());
prof_add_stats(profile.data.gfx, imgui.framestats()); prof_add_stats(profile.data.gfx, imgui.framestats());
prof_add_stats(profile.data.actors, actor.__stats()); prof_add_stats(profile.data.actors, actor.__stats());
profile.curframe++; profile.curframe++;
} };
profile.best_mem = function(bytes) profile.best_mem = function (bytes) {
{ var sizes = ["Bytes", "KB", "MB", "GB", "TB"];
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; if (bytes == 0) return "0 Bytes";
if (bytes == 0) return '0 Bytes';
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return (bytes / Math.pow(1024, i)).toPrecision(3) + ' ' + sizes[i]; return (bytes / Math.pow(1024, i)).toPrecision(3) + " " + sizes[i];
} };
profile.cleardata = function() profile.cleardata = function () {
{
profile.data.gpu = {}; profile.data.gpu = {};
profile.data.physics = {}; profile.data.physics = {};
profile.data.script = {}; profile.data.script = {};
@ -383,19 +366,18 @@ profile.cleardata = function()
render: [], render: [],
physics: [], physics: [],
}; };
} };
profile.cleardata(); profile.cleardata();
profile.last_mem = undefined; profile.last_mem = undefined;
profile.mems = []; profile.mems = [];
profile.gcs = []; profile.gcs = [];
profile.print_gc = function() profile.print_gc = function () {
{
var gc = os.check_gc(); var gc = os.check_gc();
if (!gc) return; if (!gc) return;
profile.data.gc ??= []; profile.data.gc ??= [];
profile.data.gc[profile.curframe] = gc; profile.data.gc[profile.curframe] = gc;
} };
return { profile }; return { profile };

View file

@ -2,7 +2,7 @@ globalThis.gamestate = {};
global.check_registers = function (obj) { global.check_registers = function (obj) {
for (var reg in Register.registries) { for (var reg in Register.registries) {
if (typeof obj[reg] === 'function') { if (typeof obj[reg] === "function") {
var fn = obj[reg].bind(obj); var fn = obj[reg].bind(obj);
fn.layer = obj[reg].layer; fn.layer = obj[reg].layer;
var name = obj.ur ? obj.ur.name : obj.toString(); var name = obj.ur ? obj.ur.name : obj.toString();
@ -115,7 +115,7 @@ game.engine_start = function (s) {
verts: 4, verts: 4,
uv: os.make_buffer([0, 1, 1, 1, 0, 0, 1, 0], 2), uv: os.make_buffer([0, 1, 1, 1, 0, 0, 1, 0], 2),
index: os.make_buffer([0, 1, 2, 2, 1, 3], 1), index: os.make_buffer([0, 1, 2, 2, 1, 3], 1),
count: 6 count: 6,
}; };
render.init(); render.init();
@ -128,12 +128,11 @@ game.engine_start = function (s) {
game.startengine = 0; game.startengine = 0;
prosperon.release_mode = function() prosperon.release_mode = function () {
{
prosperon.debug = false; prosperon.debug = false;
mum.debug = false; mum.debug = false;
debug.kill(); debug.kill();
} };
prosperon.debug = true; prosperon.debug = true;
game.timescale = 1; game.timescale = 1;
@ -142,8 +141,7 @@ var eachobj = function (obj, fn) {
var val = fn(obj); var val = fn(obj);
if (val) return val; if (val) return val;
for (var o in obj.objects) { for (var o in obj.objects) {
if (obj.objects[o] === obj) if (obj.objects[o] === obj) console.error(`Object ${obj.toString()} is referenced by itself.`);
console.error(`Object ${obj.toString()} is referenced by itself.`);
val = eachobj(obj.objects[o], fn); val = eachobj(obj.objects[o], fn);
if (val) return val; if (val) return val;
} }
@ -179,8 +177,7 @@ game.doc.pause = "Pause game simulation.";
game.doc.play = "Resume or start game simulation."; game.doc.play = "Resume or start game simulation.";
game.doc.camera = "Current camera."; game.doc.camera = "Current camera.";
game.tex_hotreload = function() game.tex_hotreload = function () {
{
for (var path in game.texture.cache) { for (var path in game.texture.cache) {
if (io.mod(path) > game.texture.time_cache[path]) { if (io.mod(path) > game.texture.time_cache[path]) {
var tex = game.texture.cache[path]; var tex = game.texture.cache[path];
@ -193,7 +190,7 @@ game.tex_hotreload = function()
} }
} }
} }
} };
game.texture = function (path) { game.texture = function (path) {
if (!path) return game.texture("icons/no_text.gif"); if (!path) return game.texture("icons/no_text.gif");
@ -229,8 +226,7 @@ prosperon.semver.valid = function (v, range) {
if (range[0] === "~") { if (range[0] === "~") {
range[0] = range[0].slice(1); range[0] = range[0].slice(1);
for (var i = 0; i < 2; i++) for (var i = 0; i < 2; i++) if (parseInt(v[i]) < parseInt(range[i])) return false;
if (parseInt(v[i]) < parseInt(range[i])) return false;
return true; return true;
} }
@ -251,8 +247,7 @@ prosperon.semver.cmp = function (v1, v2) {
return 0; return 0;
}; };
prosperon.semver.cmp.doc = prosperon.semver.cmp.doc = "Compare two semantic version numbers, given like X.X.X.";
"Compare two semantic version numbers, given like X.X.X.";
prosperon.semver.valid.doc = `Test if semantic version v is valid, given a range. prosperon.semver.valid.doc = `Test if semantic version v is valid, given a range.
Range is given by a semantic versioning number, prefixed with nothing, a ~, or a ^. Range is given by a semantic versioning number, prefixed with nothing, a ~, or a ^.
~ means that MAJOR and MINOR must match exactly, but any PATCH greater or equal is valid. ~ means that MAJOR and MINOR must match exactly, but any PATCH greater or equal is valid.
@ -277,7 +272,9 @@ prosperon.quit = function () {
window.size = [640, 480]; window.size = [640, 480];
window.mode = "keep"; window.mode = "keep";
window.toggle_fullscreen = function() { window.fullscreen = !window.fullscreen; } window.toggle_fullscreen = function () {
window.fullscreen = !window.fullscreen;
};
window.set_icon.doc = "Set the icon of the window using the PNG image at path."; window.set_icon.doc = "Set the icon of the window using the PNG image at path.";
@ -285,14 +282,13 @@ window.doc = {};
window.doc.dimensions = "Window width and height packaged in an array [width,height]"; window.doc.dimensions = "Window width and height packaged in an array [width,height]";
window.doc.title = "Name in the title bar of the window."; window.doc.title = "Name in the title bar of the window.";
window.doc.boundingbox = "Boundingbox of the window, with top and right being its height and width."; window.doc.boundingbox = "Boundingbox of the window, with top and right being its height and width.";
window.__proto__.toJSON = function() window.__proto__.toJSON = function () {
{
return { return {
size: this.size, size: this.size,
fullscreen: this.fullscreen, fullscreen: this.fullscreen,
title: this.title title: this.title,
};
}; };
}
global.mixin("scripts/input"); global.mixin("scripts/input");
global.mixin("scripts/std"); global.mixin("scripts/std");
@ -302,7 +298,7 @@ global.mixin("scripts/tween");
global.mixin("scripts/ai"); global.mixin("scripts/ai");
global.mixin("scripts/particle"); global.mixin("scripts/particle");
global.mixin("scripts/physics"); global.mixin("scripts/physics");
global.mixin("scripts/geometry") global.mixin("scripts/geometry");
/* /*
Factory for creating registries. Register one with 'X.register', Factory for creating registries. Register one with 'X.register',
@ -325,7 +321,7 @@ var Register = {
var st = profile.now(); var st = profile.now();
fn(...args); fn(...args);
profile.endcache(); profile.endcache();
} };
fns.push(dofn); fns.push(dofn);
dofn.layer = fn.layer; dofn.layer = fn.layer;
@ -341,9 +337,8 @@ var Register = {
if (!flush) { if (!flush) {
prosperon[name] = function (...args) { prosperon[name] = function (...args) {
fns.forEach(fn => fn(...args)); fns.forEach(fn => fn(...args));
} };
} } else
else
prosperon[name] = function (...args) { prosperon[name] = function (...args) {
var layer = undefined; var layer = undefined;
for (var fn of fns) { for (var fn of fns) {
@ -353,7 +348,7 @@ var Register = {
} }
fn(); fn();
} }
} };
prosperon[name].fns = fns; prosperon[name].fns = fns;
n.clear = function () { n.clear = function () {
@ -384,11 +379,11 @@ var Event = {
}, },
unobserve(name, obj) { unobserve(name, obj) {
this.events[name] = this.events[name].filter((x) => x[0] !== obj); this.events[name] = this.events[name].filter(x => x[0] !== obj);
}, },
rm_obj(obj) { rm_obj(obj) {
Object.keys(this.events).forEach((name) => Event.unobserve(name, obj)); Object.keys(this.events).forEach(name => Event.unobserve(name, obj));
}, },
notify(name, ...args) { notify(name, ...args) {
@ -435,5 +430,5 @@ return {
sim, sim,
frame_t, frame_t,
physlag, physlag,
Event Event,
} };

View file

@ -1,25 +1,22 @@
render.doc = { render.doc = {
doc: "Functions for rendering modes.", doc: "Functions for rendering modes.",
normal: "Final render with all lighting.", normal: "Final render with all lighting.",
wireframe: "Show only wireframes of models." wireframe: "Show only wireframes of models.",
}; };
var cur = {}; var cur = {};
render.use_shader = function use_shader(shader) render.use_shader = function use_shader(shader) {
{ if (typeof shader === "string") shader = make_shader(shader);
if (typeof shader === 'string')
shader = make_shader(shader);
if (cur.shader === shader) return; if (cur.shader === shader) return;
cur.shader = shader; cur.shader = shader;
cur.bind = undefined; cur.bind = undefined;
cur.mesh = undefined; cur.mesh = undefined;
render.setpipeline(shader.pipe); render.setpipeline(shader.pipe);
shader_globals(cur.shader); shader_globals(cur.shader);
} };
render.use_mat = function use_mat(mat) render.use_mat = function use_mat(mat) {
{
if (!cur.shader) return; if (!cur.shader) return;
if (cur.mat === mat) return; if (cur.mat === mat) return;
@ -30,18 +27,14 @@ render.use_mat = function use_mat(mat)
cur.images = []; cur.images = [];
if (!cur.shader.fs.images) return; if (!cur.shader.fs.images) return;
for (var img of cur.shader.fs.images) for (var img of cur.shader.fs.images)
if (mat[img.name]) if (mat[img.name]) cur.images.push(mat[img.name]);
cur.images.push(mat[img.name]); else cur.images.push(game.texture("icons/no_tex.gif"));
else };
cur.images.push(game.texture("icons/no_tex.gif"));
}
var models_array = []; var models_array = [];
function set_model(t) function set_model(t) {
{ if (cur.shader.vs.unimap.model) render.setunim4(0, cur.shader.vs.unimap.model.slot, t);
if (cur.shader.vs.unimap.model)
render.setunim4(0, cur.shader.vs.unimap.model.slot, t);
} }
render.set_model = set_model; render.set_model = set_model;
@ -52,7 +45,7 @@ var shaderlang = {
linux: "glsl430", linux: "glsl430",
web: "wgsl", web: "wgsl",
ios: "metal_ios", ios: "metal_ios",
} };
var attr_map = { var attr_map = {
a_pos: 0, a_pos: 0,
@ -66,45 +59,43 @@ var attr_map = {
a_wh: 8, a_wh: 8,
a_st: 9, a_st: 9,
a_ppos: 10, a_ppos: 10,
a_scale: 11 a_scale: 11,
} };
var blend_map = { var blend_map = {
mix: true, mix: true,
none: false none: false,
} };
var primitive_map = { var primitive_map = {
point: 1, point: 1,
line: 2, line: 2,
linestrip: 3, linestrip: 3,
triangle: 4, triangle: 4,
trianglestrip: 5 trianglestrip: 5,
} };
var cull_map = { var cull_map = {
none: 1, none: 1,
front: 2, front: 2,
back: 3 back: 3,
} };
var depth_map = { var depth_map = {
off: false, off: false,
on: true on: true,
} };
var face_map = { var face_map = {
cw: 2, cw: 2,
ccw: 1 ccw: 1,
} };
render.poly_prim = function poly_prim(verts) render.poly_prim = function poly_prim(verts) {
{
var index = []; var index = [];
if (verts.length < 1) return undefined; if (verts.length < 1) return undefined;
for (var i = 0; i < verts.length; i++) for (var i = 0; i < verts.length; i++) verts[i][2] = 0;
verts[i][2] = 0;
for (var i = 2; i < verts.length; i++) { for (var i = 2; i < verts.length; i++) {
index.push(0); index.push(0);
@ -116,56 +107,59 @@ render.poly_prim = function poly_prim(verts)
pos: os.make_buffer(verts.flat()), pos: os.make_buffer(verts.flat()),
verts: verts.length, verts: verts.length,
index: os.make_buffer(index, 1), index: os.make_buffer(index, 1),
count: index.length count: index.length,
};
}; };
}
function shader_directive(shader, name, map) function shader_directive(shader, name, map) {
{ var reg = new RegExp(`#${name}.*`, "g");
var reg = new RegExp(`#${name}.*`, 'g');
var mat = shader.match(reg); var mat = shader.match(reg);
if (!mat) return undefined; if (!mat) return undefined;
reg = new RegExp(`#${name}\s*`, 'g'); reg = new RegExp(`#${name}\s*`, "g");
var ff = mat.map(d=>d.replace(reg,''))[0].trim(); var ff = mat.map(d => d.replace(reg, ""))[0].trim();
if (map) return map[ff]; if (map) return map[ff];
return ff; return ff;
} }
var uni_globals = { var uni_globals = {
time(stage, slot) { render.setuniv(stage, slot, profile.secs(profile.now())); }, time(stage, slot) {
projection(stage,slot) { render.setuniproj(stage, slot); }, render.setuniv(stage, slot, profile.secs(profile.now()));
view(stage,slot) { render.setuniview(stage, slot); }, },
vp(stage,slot) { render.setunivp(stage,slot); }, projection(stage, slot) {
} render.setuniproj(stage, slot);
},
view(stage, slot) {
render.setuniview(stage, slot);
},
vp(stage, slot) {
render.setunivp(stage, slot);
},
};
function set_global_uni(uni, stage) { function set_global_uni(uni, stage) {
uni_globals[uni.name]?.(stage, uni.slot); uni_globals[uni.name]?.(stage, uni.slot);
} }
var setcam = render.set_camera; var setcam = render.set_camera;
render.set_camera = function(cam) render.set_camera = function (cam) {
{
if (nextflush) { if (nextflush) {
nextflush(); nextflush();
nextflush = undefined; nextflush = undefined;
} }
delete cur.shader; delete cur.shader;
setcam(cam); setcam(cam);
} };
var shader_cache = {}; var shader_cache = {};
var shader_times = {}; var shader_times = {};
function strip_shader_inputs(shader) function strip_shader_inputs(shader) {
{ for (var a of shader.vs.inputs) a.name = a.name.slice(2);
for (var a of shader.vs.inputs)
a.name = a.name.slice(2);
} }
render.hotreload = function() render.hotreload = function () {
{
for (var i in shader_times) { for (var i in shader_times) {
if (io.mod(i) <= shader_times[i]) continue; if (io.mod(i) <= shader_times[i]) continue;
say(`HOT RELOADING SHADER ${i}`); say(`HOT RELOADING SHADER ${i}`);
@ -178,10 +172,9 @@ render.hotreload = function()
cur.bind = undefined; cur.bind = undefined;
cur.mesh = undefined; cur.mesh = undefined;
} }
} };
function create_shader_obj(file) function create_shader_obj(file) {
{
var files = [file]; var files = [file];
var out = ".prosperon/tmp.shader"; var out = ".prosperon/tmp.shader";
var shader = io.slurp(file); var shader = io.slurp(file);
@ -199,15 +192,15 @@ function create_shader_obj(file)
files.push(filez); files.push(filez);
} }
var blend = shader_directive(shader, 'blend', blend_map); var blend = shader_directive(shader, "blend", blend_map);
var primitive = shader_directive(shader, 'primitive', primitive_map); var primitive = shader_directive(shader, "primitive", primitive_map);
var cull = shader_directive(shader, 'cull', cull_map); var cull = shader_directive(shader, "cull", cull_map);
var depth = shader_directive(shader, 'depth', depth_map); var depth = shader_directive(shader, "depth", depth_map);
var face = shader_directive(shader, 'face', face_map); var face = shader_directive(shader, "face", face_map);
var indexed = shader_directive(shader, 'indexed'); var indexed = shader_directive(shader, "indexed");
if (typeof indexed == 'undefined') indexed = true; if (typeof indexed == "undefined") indexed = true;
if (indexed === 'false') indexed = false; if (indexed === "false") indexed = false;
shader = shader.replace(/uniform\s+(\w+)\s+(\w+);/g, "uniform _$2 { $1 $2; };"); shader = shader.replace(/uniform\s+(\w+)\s+(\w+);/g, "uniform _$2 { $1 $2; };");
shader = shader.replace(/(texture2D|sampler) /g, "uniform $1 "); shader = shader.replace(/(texture2D|sampler) /g, "uniform $1 ");
@ -256,10 +249,8 @@ function create_shader_obj(file)
if (obj.vs.inputs) if (obj.vs.inputs)
for (var i of obj.vs.inputs) { for (var i of obj.vs.inputs) {
if (!(i.name in attr_map)) if (!(i.name in attr_map)) i.mat = -1;
i.mat = -1; else i.mat = attr_map[i.name];
else
i.mat = attr_map[i.name];
} }
function make_unimap(stage) { function make_unimap(stage) {
@ -271,7 +262,7 @@ function create_shader_obj(file)
unimap[uniname] = { unimap[uniname] = {
name: uniname, name: uniname,
slot: Number(uni.slot), slot: Number(uni.slot),
size: Number(uni.size) size: Number(uni.size),
}; };
} }
@ -294,8 +285,7 @@ function create_shader_obj(file)
return compiled; return compiled;
} }
function make_shader(shader) function make_shader(shader) {
{
if (shader_cache[shader]) return shader_cache[shader]; if (shader_cache[shader]) return shader_cache[shader];
var file = shader; var file = shader;
@ -342,27 +332,23 @@ var shader_unisize = {
4: render.setuniv, 4: render.setuniv,
8: render.setuniv2, 8: render.setuniv2,
12: render.setuniv3, 12: render.setuniv3,
16: render.setuniv4 16: render.setuniv4,
}; };
function shader_globals(shader) function shader_globals(shader) {
{ for (var p in shader.vs.unimap) set_global_uni(shader.vs.unimap[p], 0);
for (var p in shader.vs.unimap)
set_global_uni(shader.vs.unimap[p], 0);
for (var p in shader.fs.unimap) for (var p in shader.fs.unimap) set_global_uni(shader.fs.unimap[p], 1);
set_global_uni(shader.fs.unimap[p], 1);
} }
function shader_apply_material(shader, material = {}, old = {}) function shader_apply_material(shader, material = {}, old = {}) {
{
for (var p in shader.vs.unimap) { for (var p in shader.vs.unimap) {
if (!(p in material)) continue; if (!(p in material)) continue;
if (material[p] === old[p]) continue; if (material[p] === old[p]) continue;
assert(p in material, `shader ${shader.name} has no uniform for ${p}`); assert(p in material, `shader ${shader.name} has no uniform for ${p}`);
var s = shader.vs.unimap[p]; var s = shader.vs.unimap[p];
if (p === 'bones') { if (p === "bones") {
render.setunibones(0, s.slot, material[p]); render.setunibones(0, s.slot, material[p]);
continue; continue;
} }
@ -381,15 +367,12 @@ function shader_apply_material(shader, material = {}, old = {})
if (!material.diffuse) return; if (!material.diffuse) return;
if (material.diffuse === old.diffuse) return; if (material.diffuse === old.diffuse) return;
if ("diffuse_size" in shader.fs.unimap) if ("diffuse_size" in shader.fs.unimap) render.setuniv2(1, shader.fs.unimap.diffuse_size.slot, [material.diffuse.width, material.diffuse.height]);
render.setuniv2(1, shader.fs.unimap.diffuse_size.slot, [material.diffuse.width, material.diffuse.height]);
if ("diffuse_size" in shader.vs.unimap) if ("diffuse_size" in shader.vs.unimap) render.setuniv2(0, shader.vs.unimap.diffuse_size.slot, [material.diffuse.width, material.diffuse.height]);
render.setuniv2(0, shader.vs.unimap.diffuse_size.slot, [material.diffuse.width, material.diffuse.height]);
} }
function sg_bind(mesh, ssbo) function sg_bind(mesh, ssbo) {
{
cur.mesh = mesh; cur.mesh = mesh;
var bind = {}; var bind = {};
@ -399,20 +382,16 @@ function sg_bind(mesh, ssbo)
if (!(a.name in mesh)) { if (!(a.name in mesh)) {
console.error(`cannot draw shader ${cur.shader.name}; there is no attrib ${a.name} in the given mesh. ${json.encode(mesh)}`); console.error(`cannot draw shader ${cur.shader.name}; there is no attrib ${a.name} in the given mesh. ${json.encode(mesh)}`);
return undefined; return undefined;
} else } else bind.attrib.push(mesh[a.name]);
bind.attrib.push(mesh[a.name]);
} }
if (cur.shader.indexed) { if (cur.shader.indexed) {
bind.index = mesh.index; bind.index = mesh.index;
bind.count = mesh.count; bind.count = mesh.count;
} else } else bind.count = mesh.verts;
bind.count = mesh.verts;
bind.ssbo = []; bind.ssbo = [];
if (cur.shader.vs.storage_buffers) if (cur.shader.vs.storage_buffers) for (var b of cur.shader.vs.storage_buffers) bind.ssbo.push(ssbo);
for (var b of cur.shader.vs.storage_buffers)
bind.ssbo.push(ssbo);
bind.inst = 1; bind.inst = 1;
bind.images = cur.images; bind.images = cur.images;
@ -487,7 +466,7 @@ render.init = function() {
os.make_circle2d().draw = function () { os.make_circle2d().draw = function () {
render.circle(this.body().transform().pos, this.radius, [1, 1, 0, 1]); render.circle(this.body().transform().pos, this.radius, [1, 1, 0, 1]);
} };
var disabled = [148 / 255, 148 / 255, 148 / 255, 1]; var disabled = [148 / 255, 148 / 255, 148 / 255, 1];
var sleep = [1, 140 / 255, 228 / 255, 1]; var sleep = [1, 140 / 255, 228 / 255, 1];
@ -502,18 +481,18 @@ render.init = function() {
render.poly(this.points, color, body.transform()); render.poly(this.points, color, body.transform());
color.a = 1; color.a = 1;
render.line(this.points.wrapped(1), color, 1, body.transform()); render.line(this.points.wrapped(1), color, 1, body.transform());
} };
os.make_seg2d().draw = function () { os.make_seg2d().draw = function () {
render.line([this.a(), this.b()], [1, 0, 1, 1], Math.max(this.radius / 2, 1), this.body().transform()); render.line([this.a(), this.b()], [1, 0, 1, 1], Math.max(this.radius / 2, 1), this.body().transform());
} };
joint.pin().draw = function () { joint.pin().draw = function () {
var a = this.bodyA(); var a = this.bodyA();
var b = this.bodyB(); var b = this.bodyB();
render.line([a.transform().pos.xy, b.transform().pos.xy], [0, 1, 1, 1], 1); render.line([a.transform().pos.xy, b.transform().pos.xy], [0, 1, 1, 1], 1);
} };
} };
render.draw_sprites = true; render.draw_sprites = true;
render.draw_particles = true; render.draw_particles = true;
@ -521,8 +500,7 @@ render.draw_hud = true;
render.draw_gui = true; render.draw_gui = true;
render.draw_gizmos = true; render.draw_gizmos = true;
render.sprites = function render_sprites(gridsize = 1) render.sprites = function render_sprites(gridsize = 1) {
{
// When y sorting, draw layer is firstly important followed by the gameobject position // When y sorting, draw layer is firstly important followed by the gameobject position
var buckets; var buckets;
if (render.y_sort) { if (render.y_sort) {
@ -531,12 +509,10 @@ render.sprites = function render_sprites(gridsize = 1)
buckets = {}; buckets = {};
for (var sprite of sps) { for (var sprite of sps) {
var layer = (sprite.gameobject.drawlayer*10000)-sprite.gameobject.pos.y; var layer = sprite.gameobject.drawlayer * 10000 - sprite.gameobject.pos.y;
buckets[layer] ??= {}; buckets[layer] ??= {};
if (buckets[layer][sprite.path]) if (buckets[layer][sprite.path]) buckets[layer][sprite.path].push(sprite);
buckets[layer][sprite.path].push(sprite); else buckets[layer][sprite.path] = [sprite];
else
buckets[layer][sprite.path] = [sprite];
} }
profile.endframe(); profile.endframe();
@ -594,15 +570,13 @@ render.sprites = function render_sprites(gridsize = 1)
} }
} }
profile.endframe(); profile.endframe();
} };
render.circle = function render_circle(pos, radius, color, inner_radius = 1) { render.circle = function render_circle(pos, radius, color, inner_radius = 1) {
check_flush(undefined); check_flush(undefined);
if (inner_radius >= 1) if (inner_radius >= 1) inner_radius = inner_radius / radius;
inner_radius = inner_radius/radius; else if (inner_radius < 0) inner_radius = 1.0;
else if (inner_radius < 0)
inner_radius = 1.0;
var mat = { var mat = {
radius: radius, radius: radius,
@ -613,7 +587,7 @@ render.circle = function render_circle(pos, radius, color, inner_radius = 1) {
render.use_shader(circleshader); render.use_shader(circleshader);
render.use_mat(mat); render.use_mat(mat);
render.draw(shape.quad); render.draw(shape.quad);
} };
render.circle.doc = "Draw a circle at pos, with a given radius and color. If inner_radius is between 0 and 1, it acts as a percentage of radius. If it is above 1, is acts as a unit (usually a pixel)."; render.circle.doc = "Draw a circle at pos, with a given radius and color. If inner_radius is between 0 and 1, it acts as a percentage of radius. If it is above 1, is acts as a unit (usually a pixel).";
render.poly = function render_poly(points, color, transform) { render.poly = function render_poly(points, color, transform) {
@ -623,20 +597,17 @@ render.poly = function render_poly(points, color, transform) {
set_model(transform); set_model(transform);
render.use_mat(mat); render.use_mat(mat);
render.draw(buffer); render.draw(buffer);
} };
var nextflush = undefined; var nextflush = undefined;
function flush() function flush() {
{
nextflush?.(); nextflush?.();
nextflush = undefined; nextflush = undefined;
} }
// If flush_fn was already on deck, it does not flush. Otherwise, flushes and then sets the flush fn // If flush_fn was already on deck, it does not flush. Otherwise, flushes and then sets the flush fn
function check_flush(flush_fn) function check_flush(flush_fn) {
{ if (!nextflush) nextflush = flush_fn;
if (!nextflush)
nextflush = flush_fn;
else if (nextflush !== flush_fn) { else if (nextflush !== flush_fn) {
nextflush(); nextflush();
nextflush = flush_fn; nextflush = flush_fn;
@ -649,14 +620,13 @@ var poly_cache = [];
var poly_idx = 0; var poly_idx = 0;
var poly_ssbo; var poly_ssbo;
function poly_e() function poly_e() {
{
var e; var e;
poly_idx++; poly_idx++;
if (poly_idx > poly_cache.length) { if (poly_idx > poly_cache.length) {
e = { e = {
transform: os.make_transform(), transform: os.make_transform(),
color: Color.white color: Color.white,
}; };
poly_cache.push(e); poly_cache.push(e);
return e; return e;
@ -666,8 +636,7 @@ function poly_e()
return e; return e;
} }
function flush_poly() function flush_poly() {
{
if (poly_idx === 0) return; if (poly_idx === 0) return;
render.use_shader(polyssboshader); render.use_shader(polyssboshader);
render.use_mat({}); render.use_mat({});
@ -676,10 +645,7 @@ function flush_poly()
poly_idx = 0; poly_idx = 0;
} }
function flush_image() function flush_image() {}
{
}
render.line = function render_line(points, color = Color.white, thickness = 1) { render.line = function render_line(points, color = Color.white, thickness = 1) {
for (var i = 0; i < points.length - 1; i++) { for (var i = 0; i < points.length - 1; i++) {
@ -693,7 +659,7 @@ render.line = function render_line(points, color = Color.white, thickness = 1) {
poly.color = color; poly.color = color;
} }
check_flush(flush_poly); check_flush(flush_poly);
} };
/* All draw in screen space */ /* All draw in screen space */
render.point = function (pos, size, color = Color.blue) { render.point = function (pos, size, color = Color.blue) {
@ -701,28 +667,16 @@ render.point = function(pos,size,color = Color.blue) {
}; };
render.cross = function render_cross(pos, size, color = Color.red, thickness = 1) { render.cross = function render_cross(pos, size, color = Color.red, thickness = 1) {
var a = [ var a = [pos.add([0, size]), pos.add([0, -size])];
pos.add([0,size]), var b = [pos.add([size, 0]), pos.add([-size, 0])];
pos.add([0,-size])
];
var b = [
pos.add([size,0]),
pos.add([-size,0])
];
render.line(a, color, thickness); render.line(a, color, thickness);
render.line(b, color, thickness); render.line(b, color, thickness);
}; };
render.arrow = function render_arrow(start, end, color = Color.red, wingspan = 4, wingangle = 10) { render.arrow = function render_arrow(start, end, color = Color.red, wingspan = 4, wingangle = 10) {
var dir = end.sub(start).normalized(); var dir = end.sub(start).normalized();
var wing1 = [ var wing1 = [Vector.rotate(dir, wingangle).scale(wingspan).add(end), end];
Vector.rotate(dir, wingangle).scale(wingspan).add(end), var wing2 = [Vector.rotate(dir, -wingangle).scale(wingspan).add(end), end];
end
];
var wing2 = [
Vector.rotate(dir,-wingangle).scale(wingspan).add(end),
end
];
render.line([start, end], color); render.line([start, end], color);
render.line(wing1, color); render.line(wing1, color);
render.line(wing2, color); render.line(wing2, color);
@ -731,11 +685,11 @@ render.arrow = function render_arrow(start, end, color = Color.red, wingspan = 4
render.coordinate = function render_coordinate(pos, size, color) { render.coordinate = function render_coordinate(pos, size, color) {
render.text(JSON.stringify(pos.map(p => Math.round(p))), pos, size, color); render.text(JSON.stringify(pos.map(p => Math.round(p))), pos, size, color);
render.point(pos, 2, color); render.point(pos, 2, color);
} };
render.boundingbox = function render_boundingbox(bb, color = Color.white) { render.boundingbox = function render_boundingbox(bb, color = Color.white) {
render.line(bbox.topoints(bb).wrapped(1), color); render.line(bbox.topoints(bb).wrapped(1), color);
} };
render.rectangle = function render_rectangle(lowerleft, upperright, color) { render.rectangle = function render_rectangle(lowerleft, upperright, color) {
var transform = os.make_transform(); var transform = os.make_transform();
@ -755,20 +709,21 @@ render.box = function render_box(pos, wh, color = Color.white) {
check_flush(flush_poly); check_flush(flush_poly);
}; };
render.window = function render_window(pos, wh, color) { render.box(pos.add(wh.scale(0.5)),wh,color); }; render.window = function render_window(pos, wh, color) {
render.box(pos.add(wh.scale(0.5)), wh, color);
};
render.text_bb = function(str, size = 1, wrap = -1, pos = [0,0]) render.text_bb = function (str, size = 1, wrap = -1, pos = [0, 0]) {
{
var bb = render.text_size(str, size, wrap); var bb = render.text_size(str, size, wrap);
var w = (bb.r - bb.l); var w = bb.r - bb.l;
var h = (bb.t - bb.b); var h = bb.t - bb.b;
bb.r += pos.x; bb.r += pos.x;
bb.l += pos.x; bb.l += pos.x;
bb.t += pos.y; bb.t += pos.y;
bb.b += pos.y; bb.b += pos.y;
return bb; return bb;
} };
render.text = function (str, pos, size = 1, color = Color.white, wrap = -1, anchor = [0, 1], cursor = -1) { render.text = function (str, pos, size = 1, color = Color.white, wrap = -1, anchor = [0, 1], cursor = -1) {
var bb = render.text_bb(str, size, wrap, pos); var bb = render.text_bb(str, size, wrap, pos);
@ -777,8 +732,8 @@ render.text = function(str, pos, size = 1, color = Color.white, wrap = -1, ancho
return bb; return bb;
p.x -= w * anchor.x; p.x -= w * anchor.x;
bb.r += (w*anchor.x); bb.r += w * anchor.x;
bb.l += (w*anchor.x); bb.l += w * anchor.x;
p.y += h * (1 - anchor.y); p.y += h * (1 - anchor.y);
bb.t += h * (1 - anchor.y); bb.t += h * (1 - anchor.y);
bb.b += h * (1 - anchor.y); bb.b += h * (1 - anchor.y);
@ -790,8 +745,7 @@ var lasttex = undefined;
var img_cache = []; var img_cache = [];
var img_idx = 0; var img_idx = 0;
function flush_img() function flush_img() {
{
if (img_idx === 0) return; if (img_idx === 0) return;
render.use_shader(spritessboshader); render.use_shader(spritessboshader);
render.use_mat({ diffuse: lasttex }); render.use_mat({ diffuse: lasttex });
@ -801,14 +755,13 @@ function flush_img()
img_idx = 0; img_idx = 0;
} }
function img_e() function img_e() {
{
img_idx++; img_idx++;
if (img_idx > img_cache.length) { if (img_idx > img_cache.length) {
e = { e = {
transform: os.make_transform(), transform: os.make_transform(),
shade: Color.white, shade: Color.white,
rect: [0,0,1,1] rect: [0, 0, 1, 1],
}; };
img_cache.push(e); img_cache.push(e);
return e; return e;
@ -819,7 +772,7 @@ function img_e()
} }
render.image = function (tex, pos, scale, rotation = 0, color = Color.white) { render.image = function (tex, pos, scale, rotation = 0, color = Color.white) {
if (typeof tex === 'string') { if (typeof tex === "string") {
tex = game.texture(tex); tex = game.texture(tex);
scale.x ??= tex.width; scale.x ??= tex.width;
scale.y ??= tex.height; scale.y ??= tex.height;
@ -838,8 +791,7 @@ render.image = function(tex, pos, scale, rotation = 0, color = Color.white) {
var e = img_e(); var e = img_e();
e.transform.move(pos); e.transform.move(pos);
if (scale) if (scale) e.transform.scale = scale.div([tex.width, tex.height]);
e.transform.scale = scale.div([tex.width, tex.height]);
e.shade = color; e.shade = color;
return; return;
@ -849,20 +801,16 @@ render.image = function(tex, pos, scale, rotation = 0, color = Color.white) {
bb.t = pos.y + tex.height * scale; bb.t = pos.y + tex.height * scale;
bb.r = pos.x + tex.width * scale; bb.r = pos.x + tex.width * scale;
return bb; return bb;
} };
// pos is the lower left corner, scale is the width and height // pos is the lower left corner, scale is the width and height
render.slice9 = function(tex, pos, bb, scale = [tex.width,tex.height], color = Color.white) render.slice9 = function (tex, pos, bb, scale = [tex.width, tex.height], color = Color.white) {
{
var t = os.make_transform(); var t = os.make_transform();
t.pos = pos; t.pos = pos;
t.scale = [scale.x / tex.width, scale.y / tex.height, 1]; t.scale = [scale.x / tex.width, scale.y / tex.height, 1];
var border; var border;
if (typeof bb === 'number') if (typeof bb === "number") border = [bb / tex.width, bb / tex.height, bb / tex.width, bb / tex.height];
border = [bb/tex.width,bb/tex.height,bb/tex.width,bb/tex.height]; else border = [bb.l / tex.width, bb.b / tex.height, bb.r / tex.width, bb.t / tex.height];
else
border = [bb.l/tex.width, bb.b/tex.height, bb.r/tex.width, bb.t/tex.height];
render.use_shader(slice9shader); render.use_shader(slice9shader);
set_model(t); set_model(t);
@ -871,26 +819,23 @@ render.slice9 = function(tex, pos, bb, scale = [tex.width,tex.height], color = C
diffuse: tex, diffuse: tex,
rect: [0, 0, 1, 1], rect: [0, 0, 1, 1],
border: border, border: border,
scale: [scale.x/tex.width,scale.y/tex.height] scale: [scale.x / tex.width, scale.y / tex.height],
}); });
render.draw(shape.quad); render.draw(shape.quad);
} };
function endframe() function endframe() {
{
tdraw = 0; tdraw = 0;
} }
var textssbos = []; var textssbos = [];
var tdraw = 0; var tdraw = 0;
render.flush_text = function() render.flush_text = function () {
{
if (!render.textshader) return; if (!render.textshader) return;
tdraw++; tdraw++;
if (textssbos.length < tdraw) if (textssbos.length < tdraw) textssbos.push(render.make_textssbo());
textssbos.push(render.make_textssbo());
var textssbo = textssbos[tdraw - 1]; var textssbo = textssbos[tdraw - 1];
var amt = render.flushtext(textssbo); // load from buffer into ssbo var amt = render.flushtext(textssbo); // load from buffer into ssbo
@ -904,7 +849,7 @@ render.flush_text = function()
render.use_mat({ text: render.font.texture }); render.use_mat({ text: render.font.texture });
render.draw(shape.quad, textssbo, amt); render.draw(shape.quad, textssbo, amt);
} };
var fontcache = {}; var fontcache = {};
render.set_font = function (path, size) { render.set_font = function (path, size) {
@ -916,26 +861,24 @@ render.set_font = function(path, size) {
gui.font_set(fontcache[fontstr]); gui.font_set(fontcache[fontstr]);
render.font = fontcache[fontstr]; render.font = fontcache[fontstr];
} };
render.doc = "Draw shapes in screen space."; render.doc = "Draw shapes in screen space.";
render.cross.doc = "Draw a cross centered at pos, with arm length size."; render.cross.doc = "Draw a cross centered at pos, with arm length size.";
render.arrow.doc = "Draw an arrow from start to end, with wings of length wingspan at angle wingangle."; render.arrow.doc = "Draw an arrow from start to end, with wings of length wingspan at angle wingangle.";
render.rectangle.doc = "Draw a rectangle, with its corners at lowerleft and upperright."; render.rectangle.doc = "Draw a rectangle, with its corners at lowerleft and upperright.";
render.draw = function render_draw(mesh, ssbo, inst = 1, e_start = 0) render.draw = function render_draw(mesh, ssbo, inst = 1, e_start = 0) {
{
sg_bind(mesh, ssbo); sg_bind(mesh, ssbo);
profile.frame("gpu"); profile.frame("gpu");
render.spdraw(e_start, cur.bind.count, inst); render.spdraw(e_start, cur.bind.count, inst);
profile.endframe(); profile.endframe();
} };
// Returns an array in the form of [left, bottom, right, top] in pixels of the camera to render to // Returns an array in the form of [left, bottom, right, top] in pixels of the camera to render to
// Camera viewport is [left,bottom,width,height] in relative values // Camera viewport is [left,bottom,width,height] in relative values
function camviewport() function camviewport() {
{ var aspect = (((this.viewport[2] - this.viewport[0]) / (this.viewport[3] - this.viewport[1])) * window.size.x) / window.size.y;
var aspect = (this.viewport[2]-this.viewport[0])/(this.viewport[3]-this.viewport[1])*window.size.x/window.size.y;
var raspect = this.size.x / this.size.y; var raspect = this.size.x / this.size.y;
var left = this.viewport[0] * window.size.x; var left = this.viewport[0] * window.size.x;
@ -943,8 +886,7 @@ function camviewport()
var usemode = this.mode; var usemode = this.mode;
if (this.break && this.size.x > window.size.x && this.size.y > window.size.y) if (this.break && this.size.x > window.size.x && this.size.y > window.size.y) usemode = this.break;
usemode = this.break;
if (usemode === "fit") if (usemode === "fit")
if (raspect < aspect) usemode = "height"; if (raspect < aspect) usemode = "height";
@ -970,8 +912,7 @@ function camviewport()
} }
// pos is pixels on the screen, lower left[0,0] // pos is pixels on the screen, lower left[0,0]
function camscreen2world(pos) function camscreen2world(pos) {
{
var view = this.screen2cam(pos); var view = this.screen2cam(pos);
view.x *= this.size.x; view.x *= this.size.x;
view.y *= this.size.y; view.y *= this.size.y;
@ -985,11 +926,10 @@ function camscreen2world(pos)
// camera coordinates, normalized from 0 to 1 inside of a camera's viewport, bottom left is 0,0, top right is 1,1 // camera coordinates, normalized from 0 to 1 inside of a camera's viewport, bottom left is 0,0, top right is 1,1
// screen coordinates, pixels, 0,0 at the bottom left of the window and [w,h] at the top right of the screen // screen coordinates, pixels, 0,0 at the bottom left of the window and [w,h] at the top right of the screen
camscreen2world.doc = "Convert a view position for a camera to world." camscreen2world.doc = "Convert a view position for a camera to world.";
// return camera coordinates given a screen position // return camera coordinates given a screen position
function screen2cam(pos) function screen2cam(pos) {
{
var viewport = this.view(); var viewport = this.view();
var width = viewport[2]; var width = viewport[2];
var height = viewport[3]; var height = viewport[3];
@ -997,28 +937,25 @@ function screen2cam(pos)
return viewpos.div([width, height]); return viewpos.div([width, height]);
} }
function camextents() function camextents() {
{
var half = this.size; //.scale(0.5); var half = this.size; //.scale(0.5);
return { return {
l: this.pos.x - half.x, l: this.pos.x - half.x,
r: this.pos.x + half.x, r: this.pos.x + half.x,
t: this.pos.y + half.y, t: this.pos.y + half.y,
b:this.pos.y-half.y b: this.pos.y - half.y,
}; };
} }
screen2cam.doc = "Convert a screen space position in pixels to a normalized viewport position in a camera." screen2cam.doc = "Convert a screen space position in pixels to a normalized viewport position in a camera.";
prosperon.gizmos = function() prosperon.gizmos = function () {
{
game.all_objects(o => { game.all_objects(o => {
if (o.gizmo) render.image(game.texture(o.gizmo), o.pos); if (o.gizmo) render.image(game.texture(o.gizmo), o.pos);
}); });
} };
prosperon.make_camera = function() prosperon.make_camera = function () {
{
var cam = world.spawn(); var cam = world.spawn();
cam.near = 0.1; cam.near = 0.1;
cam.far = 1000; cam.far = 1000;
@ -1030,32 +967,42 @@ prosperon.make_camera = function()
cam.screen2world = camscreen2world; cam.screen2world = camscreen2world;
cam.screen2cam = screen2cam; cam.screen2cam = screen2cam;
cam.extents = camextents; cam.extents = camextents;
cam.mousepos = function() { return this.screen2world(input.mouse.screenpos()); } cam.mousepos = function () {
return this.screen2world(input.mouse.screenpos());
};
cam.view = camviewport; cam.view = camviewport;
cam.offscreen = false; cam.offscreen = false;
return cam; return cam;
} };
var screencolor; var screencolor;
globalThis.imtoggle = function(name, obj, field) { obj[field] = imgui.checkbox(name, obj[field]); } globalThis.imtoggle = function (name, obj, field) {
obj[field] = imgui.checkbox(name, obj[field]);
};
var replstr = ""; var replstr = "";
var imdebug = function() var imdebug = function () {
{ imtoggle("Physics", debug, "draw_phys");
imtoggle("Physics", debug, 'draw_phys'); imtoggle("Bouning boxes", debug, "draw_bb");
imtoggle("Bouning boxes", debug, 'draw_bb'); imtoggle("Gizmos", debug, "draw_gizmos");
imtoggle("Gizmos", debug, 'draw_gizmos'); imtoggle("Names", debug, "draw_names");
imtoggle("Names", debug, 'draw_names'); imtoggle("Sprite nums", debug, "sprite_nums");
imtoggle("Sprite nums", debug, 'sprite_nums'); imtoggle("Debug overlay", debug, "show");
imtoggle("Debug overlay", debug, 'show'); imtoggle("Show ur names", debug, "urnames");
imtoggle("Show ur names", debug, 'urnames'); };
}
var imgui_fn = function() var imgui_fn = function () {
{
render.imgui_new(window.size.x, window.size.y, 0.01); render.imgui_new(window.size.x, window.size.y, 0.01);
if (debug.console) debug.console = imgui.window("console", _ => { imgui.text(console.transcript); replstr = imgui.textinput(undefined, replstr); imgui.button("submit", _ => { eval(replstr); replstr = ""; }); }); if (debug.console)
debug.console = imgui.window("console", _ => {
imgui.text(console.transcript);
replstr = imgui.textinput(undefined, replstr);
imgui.button("submit", _ => {
eval(replstr);
replstr = "";
});
});
imgui.mainmenubar(_ => { imgui.mainmenubar(_ => {
imgui.menu("File", _ => { imgui.menu("File", _ => {
@ -1070,34 +1017,31 @@ var imgui_fn = function()
}); });
imgui.menu("Debug", imdebug); imgui.menu("Debug", imdebug);
imgui.menu("View", _ => { imgui.menu("View", _ => {
imtoggle("Profiler", debug, 'showprofiler'); imtoggle("Profiler", debug, "showprofiler");
imtoggle("Terminal out", debug, 'termout'); imtoggle("Terminal out", debug, "termout");
imtoggle("Meta [f7]", debug, 'meta'); imtoggle("Meta [f7]", debug, "meta");
imtoggle("Cheats [f8]", debug, 'cheat'); imtoggle("Cheats [f8]", debug, "cheat");
imtoggle("Console [f9]", debug, 'console'); imtoggle("Console [f9]", debug, "console");
}); });
imgui.sokol_gfx(); imgui.sokol_gfx();
imgui.menu("Graphics", _ => { imgui.menu("Graphics", _ => {
imtoggle("Draw sprites", render, 'draw_sprites'); imtoggle("Draw sprites", render, "draw_sprites");
imtoggle("Draw particles", render, 'draw_particles'); imtoggle("Draw particles", render, "draw_particles");
imtoggle("Draw HUD", render, 'draw_hud'); imtoggle("Draw HUD", render, "draw_hud");
imtoggle("Draw GUI", render, 'draw_gui'); imtoggle("Draw GUI", render, "draw_gui");
imtoggle("Draw gizmos", render, 'draw_gizmos'); imtoggle("Draw gizmos", render, "draw_gizmos");
imgui.menu("Window", _ => { imgui.menu("Window", _ => {
window.fullscreen = imgui.checkbox("fullscreen", window.fullscreen); window.fullscreen = imgui.checkbox("fullscreen", window.fullscreen);
// window.vsync = imgui.checkbox("vsync", window.vsync); // window.vsync = imgui.checkbox("vsync", window.vsync);
imgui.menu("MSAA", _ => { imgui.menu("MSAA", _ => {
for (var msaa of gamestate.msaa) for (var msaa of gamestate.msaa) imgui.button(msaa + "x", _ => (window.sample_count = msaa));
imgui.button(msaa + "x", _ => window.sample_count = msaa);
}); });
imgui.menu("Resolution", _ => { imgui.menu("Resolution", _ => {
for (var res of gamestate.resolutions) for (var res of gamestate.resolutions) imgui.button(res, _ => (window.resolution = res));
imgui.button(res, _ => window.resolution = res);
}); });
}); });
}); });
@ -1106,10 +1050,9 @@ var imgui_fn = function()
prosperon.imgui(); prosperon.imgui();
render.imgui_end(); render.imgui_end();
} };
prosperon.render = function() prosperon.render = function () {
{
profile.frame("world"); profile.frame("world");
render.set_camera(prosperon.camera); render.set_camera(prosperon.camera);
profile.frame("sprites"); profile.frame("sprites");
@ -1154,8 +1097,7 @@ prosperon.render = function()
// Flush & render // Flush & render
prosperon.appcam.transform.pos = [window.size.x / 2, window.size.y / 2, -100]; prosperon.appcam.transform.pos = [window.size.x / 2, window.size.y / 2, -100];
prosperon.appcam.size = window.size.slice(); prosperon.appcam.size = window.size.slice();
if (os.sys() !== 'macos') if (os.sys() !== "macos") prosperon.appcam.size.y *= -1;
prosperon.appcam.size.y *= -1;
render.set_camera(prosperon.appcam); render.set_camera(prosperon.appcam);
render.viewport(...prosperon.appcam.view()); render.viewport(...prosperon.appcam.view());
@ -1173,8 +1115,7 @@ prosperon.render = function()
profile.frame("imgui"); profile.frame("imgui");
if (debug.show) if (debug.show) imgui_fn();
imgui_fn();
profile.endframe(); profile.endframe();
@ -1182,7 +1123,7 @@ prosperon.render = function()
render.commit(); render.commit();
endframe(); endframe();
} };
prosperon.process = function process() { prosperon.process = function process() {
profile.frame("frame"); profile.frame("frame");
@ -1197,8 +1138,6 @@ prosperon.process = function process() {
var cycles = os.check_cycles(); var cycles = os.check_cycles();
if (cycles) say(cycles); if (cycles) say(cycles);
profile.frame("app update"); profile.frame("app update");
prosperon.appupdate(dt); prosperon.appupdate(dt);
profile.endframe(); profile.endframe();
@ -1240,6 +1179,6 @@ prosperon.process = function process() {
profile.endframe(); profile.endframe();
profile.capture_data(); profile.capture_data();
} };
return { render }; return { render };

View file

@ -10,6 +10,6 @@ repl.hotreload = function() {
var script = io.slurp(file); var script = io.slurp(file);
eval(script); eval(script);
} }
} };
return { repl: repl }; return { repl: repl };

View file

@ -1,8 +1,8 @@
/* This file runs after the audio system is initiated */ /* This file runs after the audio system is initiated */
Object.readonly(audio, 'samplerate'); Object.readonly(audio, "samplerate");
Object.readonly(audio, 'channels'); Object.readonly(audio, "channels");
Object.readonly(audio, 'buffer_frames'); Object.readonly(audio, "buffer_frames");
var sources = []; var sources = [];
@ -20,7 +20,7 @@ audio.play = function(file,bus = audio.bus.master) {
src.type = "source"; src.type = "source";
sources.push(src); sources.push(src);
return src; return src;
} };
audio.bus = {}; audio.bus = {};
audio.bus.master = dspsound.master(); audio.bus.master = dspsound.master();
audio.dsp = {}; audio.dsp = {};
@ -30,44 +30,42 @@ audio.bus.master.__proto__.type = "bus";
audio.bus.master.name = "master"; audio.bus.master.name = "master";
var plugin_node = audio.bus.master.plugin; var plugin_node = audio.bus.master.plugin;
audio.bus.master.__proto__.plugin = function(to) audio.bus.master.__proto__.plugin = function (to) {
{
this.tos ??= []; this.tos ??= [];
this.tos.push(to); this.tos.push(to);
to.ins ??= []; to.ins ??= [];
to.ins.push(this); to.ins.push(this);
plugin_node.call(this, to); plugin_node.call(this, to);
} };
var unplug_node = audio.bus.master.unplug; var unplug_node = audio.bus.master.unplug;
audio.bus.master.__proto__.unplug = function() audio.bus.master.__proto__.unplug = function () {
{
if (this.tos) { if (this.tos) {
for (var node of this.tos) for (var node of this.tos) node.ins.remove(this);
node.ins.remove(this);
this.tos = []; this.tos = [];
} }
unplug_node.call(this); unplug_node.call(this);
} };
audio.dsp.mix().__proto__.imgui = function() audio.dsp.mix().__proto__.imgui = function () {
{
imgui.pushid(this.memid()); imgui.pushid(this.memid());
this.volume = imgui.slider("Volume", this.volume); this.volume = imgui.slider("Volume", this.volume);
this.off = imgui.checkbox("Mute", this.off); this.off = imgui.checkbox("Mute", this.off);
imgui.popid(); imgui.popid();
} };
audio.cry = function(file, bus = audio.bus.sfx) audio.cry = function (file, bus = audio.bus.sfx) {
{
file = Resources.find_sound(file); file = Resources.find_sound(file);
var player = audio.play(file, bus); var player = audio.play(file, bus);
if (!player) return; if (!player) return;
player.ended = function() { player.unplug(); player = undefined; } player.ended = function () {
player.unplug();
player = undefined;
};
return player.ended; return player.ended;
} };
// This function is called when every audio source is finished // This function is called when every audio source is finished
var killer = Register.appupdate.register(function () { var killer = Register.appupdate.register(function () {
@ -85,8 +83,7 @@ var song;
// Play 'file' for new song, cross fade for seconds // Play 'file' for new song, cross fade for seconds
audio.music = function (file, fade = 0.5) { audio.music = function (file, fade = 0.5) {
if (!file) { if (!file) {
if (song) if (song) song.volume = 0;
song.volume = 0;
return; return;
} }
file = Resources.find_sound(file); file = Resources.find_sound(file);
@ -112,7 +109,7 @@ audio.music = function(file, fade = 0.5) {
// tween(temp2, 'volume', 0, fade); // tween(temp2, 'volume', 0, fade);
song = temp; song = temp;
song.loop = true; song.loop = true;
} };
audio.bus.music = audio.dsp.mix(); audio.bus.music = audio.dsp.mix();
audio.bus.music.plugin(audio.bus.master); audio.bus.music.plugin(audio.bus.master);
@ -131,7 +128,7 @@ audio.dsp.allpass = function(secs, decay) {
composite.unplug = dsp_node.unplug.bind(fbk); composite.unplug = dsp_node.unplug.bind(fbk);
fwd.plugin(fbk); fwd.plugin(fbk);
return composite; return composite;
} };
audio.dsp.doc = { audio.dsp.doc = {
delay: "Delays the input by secs, multiplied by decay", delay: "Delays the input by secs, multiplied by decay",
@ -146,27 +143,35 @@ audio.dsp.doc = {
pitchshift: "Shift sound by octaves", pitchshift: "Shift sound by octaves",
noise: "Plain randon noise", noise: "Plain randon noise",
pink: "Pink noise", pink: "Pink noise",
red: "Red noise" red: "Red noise",
}; };
audio.dsp.obscure('doc'); audio.dsp.obscure("doc");
Object.mixin(audio.bus.master.__proto__, { Object.mixin(audio.bus.master.__proto__, {
get db() { return 20*Math.log10(Math.abs(this.volume)); }, get db() {
set db(x) { x = Math.clamp(x,-100,0); this.volume = Math.pow(10, x/20); }, return 20 * Math.log10(Math.abs(this.volume));
get volume() { return this.gain; }, },
set volume(x) { this.gain = x; }, set db(x) {
x = Math.clamp(x, -100, 0);
this.volume = Math.pow(10, x / 20);
},
get volume() {
return this.gain;
},
set volume(x) {
this.gain = x;
},
}); });
audio.bus.master.__proto__.toJSON = function() audio.bus.master.__proto__.toJSON = function () {
{
return { return {
volume: this.volume, volume: this.volume,
off: this.off, off: this.off,
pan: this.pan, pan: this.pan,
pass: this.pass pass: this.pass,
};
}; };
}
/*Object.mixin(audio.dsp.source().__proto__, { /*Object.mixin(audio.dsp.source().__proto__, {
length() { return this.frames()/audio.samplerate(); }, length() { return this.frames()/audio.samplerate(); },

View file

@ -3,115 +3,119 @@ Spline.sample_angle = function(type, points, angle) {
if (type === 0) return spline.catmull(points, angle); if (type === 0) return spline.catmull(points, angle);
else if (type === 1) return spline.bezier(points, angle); else if (type === 1) return spline.bezier(points, angle);
return undefined; return undefined;
} };
Spline.bezier_loop = function(cp) Spline.bezier_loop = function (cp) {
{
cp.push(Vector.reflect_point(cp.at(-2), cp.at(-1))); cp.push(Vector.reflect_point(cp.at(-2), cp.at(-1)));
cp.push(Vector.reflect_point(cp[1], cp[0])); cp.push(Vector.reflect_point(cp[1], cp[0]));
cp.push(cp[0].slice()); cp.push(cp[0].slice());
return cp; return cp;
} };
Spline.bezier_node_count = function(cp) Spline.bezier_node_count = function (cp) {
{
if (cp.length === 4) return 2; if (cp.length === 4) return 2;
return 2 + (cp.length - 4) / 3; return 2 + (cp.length - 4) / 3;
} };
Spline.is_bezier = function(t) { return t === Spline.type.bezier; } Spline.is_bezier = function (t) {
Spline.is_catmull = function(t) { return t === Spline.type.catmull; } return t === Spline.type.bezier;
};
Spline.is_catmull = function (t) {
return t === Spline.type.catmull;
};
Spline.bezier2catmull = function(b) Spline.bezier2catmull = function (b) {
{
var c = []; var c = [];
for (var i = 0; i < b.length; i += 3) for (var i = 0; i < b.length; i += 3) c.push(b[i]);
c.push(b[i]);
return c; return c;
} };
Spline.catmull2bezier = function(c) Spline.catmull2bezier = function (c) {
{
var b = []; var b = [];
for (var i = 1; i < c.length - 2; i++) { for (var i = 1; i < c.length - 2; i++) {
b.push(c[i].slice()); b.push(c[i].slice());
b.push(c[i+1].sub(c[i-1]).scale(0.25).add(c[i])); b.push(
b.push(c[i].sub(c[i+2]).scale(0.25).add(c[i+1])); c[i + 1]
.sub(c[i - 1])
.scale(0.25)
.add(c[i]),
);
b.push(
c[i]
.sub(c[i + 2])
.scale(0.25)
.add(c[i + 1]),
);
} }
b.push(c[c.length - 2]); b.push(c[c.length - 2]);
return b; return b;
} };
Spline.catmull_loop = function(cp) Spline.catmull_loop = function (cp) {
{
cp = cp.slice(); cp = cp.slice();
cp.unshift(cp.last()); cp.unshift(cp.last());
cp.push(cp[1]); cp.push(cp[1]);
cp.push(cp[2]); cp.push(cp[2]);
return cp; return cp;
} };
Spline.catmull_caps = function(cp) Spline.catmull_caps = function (cp) {
{
if (cp.length < 2) return; if (cp.length < 2) return;
cp = cp.slice(); cp = cp.slice();
cp.unshift(cp[0].sub(cp[1]).add(cp[0])); cp.unshift(cp[0].sub(cp[1]).add(cp[0]));
cp.push(cp.last().sub(cp.at(-2).add(cp.last()))); cp.push(cp.last().sub(cp.at(-2).add(cp.last())));
return cp; return cp;
} };
Spline.catmull_caps.doc = "Given a set of control points cp, return the necessary caps added to the spline."; Spline.catmull_caps.doc = "Given a set of control points cp, return the necessary caps added to the spline.";
Spline.catmull2bezier.doc = "Given a set of control points C for a camtull-rom type curve, return a set of cubic bezier points to give the same curve." Spline.catmull2bezier.doc = "Given a set of control points C for a camtull-rom type curve, return a set of cubic bezier points to give the same curve.";
Spline.type = { Spline.type = {
catmull: 0, catmull: 0,
bezier: 1, bezier: 1,
bspline: 2, bspline: 2,
cubichermite: 3 cubichermite: 3,
}; };
Spline.bezier_tan_partner = function(points, i) Spline.bezier_tan_partner = function (points, i) {
{
if (i % 3 === 0) return undefined; if (i % 3 === 0) return undefined;
var partner_i = (i%3) === 2 ? i-1 : i+1; var partner_i = i % 3 === 2 ? i - 1 : i + 1;
return points[i]; return points[i];
} };
Spline.bezier_cp_mirror = function(points, i) Spline.bezier_cp_mirror = function (points, i) {
{
if (i % 3 === 0) return undefined; if (i % 3 === 0) return undefined;
var partner_i = (i%3) === 2 ? i+2 : i-2; var partner_i = i % 3 === 2 ? i + 2 : i - 2;
var node_i = (i%3) === 2 ? i+1 : i-1; var node_i = i % 3 === 2 ? i + 1 : i - 1;
if (partner_i >= points.length || node_i >= points.length) return; if (partner_i >= points.length || node_i >= points.length) return;
points[partner_i] = points[node_i].sub(points[i]).add(points[node_i]); points[partner_i] = points[node_i].sub(points[i]).add(points[node_i]);
} };
Spline.bezier_point_handles = function(points, i) Spline.bezier_point_handles = function (points, i) {
{
if (!Spline.bezier_is_node(points, i)) return []; if (!Spline.bezier_is_node(points, i)) return [];
var a = i - 1; var a = i - 1;
var b = i + 1; var b = i + 1;
var c = []
if (a > 0)
c.push(a);
if (b < points.length)
c.push(b);
return c;
}
Spline.bezier_nodes = function(points)
{
var c = []; var c = [];
for (var i = 0; i < points.length; i+=3) if (a > 0) c.push(a);
c.push(points[i].slice());
if (b < points.length) c.push(b);
return c; return c;
} };
Spline.bezier_is_node = function(points, i) { return i%3 === 0; } Spline.bezier_nodes = function (points) {
Spline.bezier_is_handle = function(points, i) { return !Spline.bezier_is_node(points,i); } var c = [];
for (var i = 0; i < points.length; i += 3) c.push(points[i].slice());
return c;
};
Spline.bezier_is_node = function (points, i) {
return i % 3 === 0;
};
Spline.bezier_is_handle = function (points, i) {
return !Spline.bezier_is_node(points, i);
};
return { Spline }; return { Spline };

View file

@ -1,25 +1,31 @@
os.cwd.doc = "Get the absolute path of the current working directory."; os.cwd.doc = "Get the absolute path of the current working directory.";
os.env.doc = "Return the value of the environment variable v."; os.env.doc = "Return the value of the environment variable v.";
os.platform = "steam"; os.platform = "steam";
if (os.sys() === 'windows') if (os.sys() === "windows") os.user = os.env("USERNAME");
os.user = os.env("USERNAME"); else os.user = os.env("USER");
else
os.user = os.env("USER");
var appy = {}; var appy = {};
appy.inputs = {}; appy.inputs = {};
if (os.sys() === 'macos') { if (os.sys() === "macos") {
appy.inputs['S-q'] = os.quit; appy.inputs["S-q"] = os.quit;
} }
appy.inputs.f7 = function() { debug.meta = !debug.meta; } appy.inputs.f7 = function () {
appy.inputs.f8 = function() { debug.cheat = !debug.cheat; } debug.meta = !debug.meta;
appy.inputs.f9 = function() { debug.console = !debug.console; } };
appy.inputs.f10 = function() { debug.show = !debug.show; } appy.inputs.f8 = function () {
debug.cheat = !debug.cheat;
};
appy.inputs.f9 = function () {
debug.console = !debug.console;
};
appy.inputs.f10 = function () {
debug.show = !debug.show;
};
appy.inputs.f11 = window.toggle_fullscreen; appy.inputs.f11 = window.toggle_fullscreen;
appy.inputs.f11.doc = "Toggle window fullscreen."; appy.inputs.f11.doc = "Toggle window fullscreen.";
appy.inputs.f11.title = "Toggle Fullscreen"; appy.inputs.f11.title = "Toggle Fullscreen";
appy.inputs['M-f4'] = os.quit; appy.inputs["M-f4"] = os.quit;
player[0].control(appy); player[0].control(appy);
@ -37,28 +43,30 @@ os.home = os.env("HOME");
var otherpath = { var otherpath = {
windows: `C:/Users/${os.user}/Saved Games`, windows: `C:/Users/${os.user}/Saved Games`,
macos: `${os.home}/Library/Application Support`, macos: `${os.home}/Library/Application Support`,
linux: `${os.home}/.local/share` linux: `${os.home}/.local/share`,
} };
os.prefpath = function () { os.prefpath = function () {
return otherpath[os.sys()] + "/" + (game.title ? game.title : "Untitled Prosperon Game"); return otherpath[os.sys()] + "/" + (game.title ? game.title : "Untitled Prosperon Game");
} };
os.openurl = function (url) { os.openurl = function (url) {
if (os.sys() === 'windows') if (os.sys() === "windows") os.system(`start ${url}`);
os.system(`start ${url}`); else os.system(`open ${url}`);
else };
os.system(`open ${url}`);
}
var projectfile = "project.prosperon"; var projectfile = "project.prosperon";
io.dumpfolder = '.prosperon'; io.dumpfolder = ".prosperon";
Resources.texture = {}; Resources.texture = {};
Resources.texture.dimensions = function(path) { texture.dimensions(path); } Resources.texture.dimensions = function (path) {
texture.dimensions(path);
};
Resources.gif = {}; Resources.gif = {};
Resources.gif.frames = function(path) { return render.gif_frames(path); } Resources.gif.frames = function (path) {
return render.gif_frames(path);
};
/* /*
io path rules. Starts with, meaning: io path rules. Starts with, meaning:
@ -70,28 +78,25 @@ Resources.gif.frames = function(path) { return render.gif_frames(path); }
var tmpchm = io.chmod; var tmpchm = io.chmod;
io.chmod = function (file, mode) { io.chmod = function (file, mode) {
return tmpchm(file, parseInt(mode, 8)); return tmpchm(file, parseInt(mode, 8));
} };
var tmpslurp = io.slurp; var tmpslurp = io.slurp;
io.slurp = function(path) io.slurp = function (path) {
{
path = Resources.replpath(path); path = Resources.replpath(path);
return tmpslurp(path); return tmpslurp(path);
} };
var tmpslurpb = io.slurpbytes; var tmpslurpb = io.slurpbytes;
io.slurpbytes = function(path) io.slurpbytes = function (path) {
{
path = Resources.replpath(path); path = Resources.replpath(path);
return tmpslurpb(path); return tmpslurpb(path);
} };
io.mkpath = function(dir) io.mkpath = function (dir) {
{
if (!dir) return; if (!dir) return;
var mkstack = []; var mkstack = [];
while (!io.exists(dir)) { while (!io.exists(dir)) {
mkstack.push(dir.fromlast('/')); mkstack.push(dir.fromlast("/"));
dir = dir.dir(); dir = dir.dir();
} }
for (var d of mkstack) { for (var d of mkstack) {
@ -99,55 +104,54 @@ io.mkpath = function(dir)
say(`making ${dir}`); say(`making ${dir}`);
io.mkdir(dir); io.mkdir(dir);
} }
} };
var tmpslurpw = io.slurpwrite; var tmpslurpw = io.slurpwrite;
io.slurpwrite = function(path, c) io.slurpwrite = function (path, c) {
{
path = Resources.replpath(path); path = Resources.replpath(path);
io.mkpath(path.dir()); io.mkpath(path.dir());
return tmpslurpw(path, c); return tmpslurpw(path, c);
} };
var tmpcp = io.cp; var tmpcp = io.cp;
io.cp = function(f1,f2) io.cp = function (f1, f2) {
{
io.mkpath(f2.dir()); io.mkpath(f2.dir());
tmpcp(f1, f2); tmpcp(f1, f2);
} };
var tmprm = io.rm; var tmprm = io.rm;
io.rm = function(f) io.rm = function (f) {
{
tmprm(Resources.replpath(f)); tmprm(Resources.replpath(f));
} };
io.globToRegex = function (glob) { io.globToRegex = function (glob) {
// Escape special regex characters // Escape special regex characters
// Replace glob characters with regex equivalents // Replace glob characters with regex equivalents
let regexStr = glob let regexStr = glob
.replace(/[\.\\]/g, '\\$&') // Escape literal backslashes and dots .replace(/[\.\\]/g, "\\$&") // Escape literal backslashes and dots
.replace(/([^\*])\*/g, '$1[^/]*') // * matches any number of characters except / .replace(/([^\*])\*/g, "$1[^/]*") // * matches any number of characters except /
.replace(/\*\*/g, '.*') // ** matches any number of characters, including none .replace(/\*\*/g, ".*") // ** matches any number of characters, including none
.replace(/\[(.*?)\]/g, '[$1]') // Character sets .replace(/\[(.*?)\]/g, "[$1]") // Character sets
.replace(/\?/g, '.'); // ? matches any single character .replace(/\?/g, "."); // ? matches any single character
// Ensure the regex matches the whole string // Ensure the regex matches the whole string
regexStr = '^' + regexStr + '$'; regexStr = "^" + regexStr + "$";
// Create and return the regex object // Create and return the regex object
return new RegExp(regexStr); return new RegExp(regexStr);
} };
io.mixin({ io.mixin({
extensions(ext) { extensions(ext) {
var paths = io.ls(); var paths = io.ls();
paths = paths.filter(function(str) { return str.ext() === ext; }); paths = paths.filter(function (str) {
return str.ext() === ext;
});
return paths; return paths;
}, },
glob(pat) { glob(pat) {
var paths = io.ls('.'); var paths = io.ls(".");
var regex = io.globToRegex(pat); var regex = io.globToRegex(pat);
paths = paths.filter(str => str.match(regex)).sort(); paths = paths.filter(str => str.match(regex)).sort();
return paths; return paths;
@ -176,7 +180,7 @@ Cmdline.register_cmd = function(flag, fn, doc) {
Cmdline.cmds.push({ Cmdline.cmds.push({
flag: flag, flag: flag,
fn: fn, fn: fn,
doc: doc doc: doc,
}); });
}; };
@ -184,9 +188,11 @@ Cmdline.register_order = function(order, fn, doc, usage = "") {
Cmdline.orders[order] = fn; Cmdline.orders[order] = fn;
fn.doc = doc; fn.doc = doc;
fn.usage = `${order} ${usage}`; fn.usage = `${order} ${usage}`;
} };
Cmdline.register_order("edit", function() { Cmdline.register_order(
"edit",
function () {
if (!io.exists(projectfile)) { if (!io.exists(projectfile)) {
say("No game to edit. Try making one with 'prosperon init'."); say("No game to edit. Try making one with 'prosperon init'.");
return; return;
@ -203,9 +209,14 @@ Cmdline.register_order("edit", function() {
render.set_font("fonts/c64.ttf", 8); render.set_font("fonts/c64.ttf", 8);
editor.enter_editor(); editor.enter_editor();
}); });
}, "Edit the project in this folder. Give it the name of an UR to edit that specific object.", "?UR?"); },
"Edit the project in this folder. Give it the name of an UR to edit that specific object.",
"?UR?",
);
Cmdline.register_order("init", function() { Cmdline.register_order(
"init",
function () {
if (io.exists(projectfile)) { if (io.exists(projectfile)) {
say("Already a game here."); say("Already a game here.");
return; return;
@ -216,19 +227,30 @@ Cmdline.register_order("init", function() {
project.version = prosperon.version; project.version = prosperon.version;
project.revision = prosperon.revision; project.revision = prosperon.revision;
io.slurpwrite(projectfile, json.encode(project)); io.slurpwrite(projectfile, json.encode(project));
}, "Turn the directory into a Prosperon game."); },
"Turn the directory into a Prosperon game.",
);
Cmdline.register_order("debug", function() { Cmdline.register_order(
"debug",
function () {
Cmdline.orders.play([]); Cmdline.orders.play([]);
}, "Play the game with debugging enabled."); },
"Play the game with debugging enabled.",
);
Cmdline.register_order("web", function() { Cmdline.register_order(
"web",
function () {
Cmdline.orders.play([]); Cmdline.orders.play([]);
}, "Play the game in a web browser."); },
"Play the game in a web browser.",
);
Cmdline.register_order("play", function(argv) { Cmdline.register_order(
if (argv[0]) "play",
io.chdir(argv[0]); function (argv) {
if (argv[0]) io.chdir(argv[0]);
// game.loadurs(); // game.loadurs();
@ -241,50 +263,52 @@ Cmdline.register_order("play", function(argv) {
game.title = project.title; game.title = project.title;
game.size = [1280, 720]; game.size = [1280, 720];
window.size = game.size; window.size = game.size;
if (io.exists("config.js")) if (io.exists("config.js")) global.mixin("config.js");
global.mixin("config.js"); else console.warn("No config.js file found. Starting with default parameters.");
else
console.warn('No config.js file found. Starting with default parameters.');
if (project.title) window.title = project.title; if (project.title) window.title = project.title;
game.engine_start(function () { game.engine_start(function () {
render.set_font("fonts/c64.ttf", 8); render.set_font("fonts/c64.ttf", 8);
if (io.exists("game.js")) if (io.exists("game.js")) global.app = actor.spawn("game.js");
global.app = actor.spawn("game.js"); else global.app = actor.spawn("scripts/nogame.js");
else
global.app = actor.spawn("scripts/nogame.js");
if (project.icon) window.set_icon(game.texture(project.icon)); if (project.icon) window.set_icon(game.texture(project.icon));
game.camera = world.spawn("scripts/camera2d"); game.camera = world.spawn("scripts/camera2d");
}); });
}, "Play the game present in this folder."); },
"Play the game present in this folder.",
);
Cmdline.register_order("pack", function(str) { Cmdline.register_order(
"pack",
function (str) {
var packname; var packname;
if (str.length === 0) if (str.length === 0) packname = "game.zip";
packname = "game.zip";
else if (str.length > 1) { else if (str.length > 1) {
console.warn("Give me a single filename for the pack."); console.warn("Give me a single filename for the pack.");
return; return;
} else } else packname = str[0];
packname = str[0];
say(`Packing into ${packname}`); say(`Packing into ${packname}`);
io.pack_start(packname); io.pack_start(packname);
var files = io.ls('.'); var files = io.ls(".");
files = files.filter(f => !f.startsWith('.git')); files = files.filter(f => !f.startsWith(".git"));
files = files.filter(f => !f.startsWith('.nova')); files = files.filter(f => !f.startsWith(".nova"));
files = files.filter(f => !f.includes('.DS_Store')); files = files.filter(f => !f.includes(".DS_Store"));
files = files.filter(f => !f.startsWith('.gitignore')); files = files.filter(f => !f.startsWith(".gitignore"));
say(files); say(files);
for (var f of files) for (var f of files) io.pack_add(f);
io.pack_add(f);
io.pack_end(); io.pack_end();
}, "Pack the game into the given name.", "NAME"); },
"Pack the game into the given name.",
"NAME",
);
Cmdline.register_order("cdb", function(argv) { Cmdline.register_order(
"cdb",
function (argv) {
var cdb = "game.zip"; var cdb = "game.zip";
if (!io.exists(cdb)) { if (!io.exists(cdb)) {
say(`No 'game.zip' present.`); say(`No 'game.zip' present.`);
@ -292,23 +316,29 @@ Cmdline.register_order("cdb", function(argv) {
} }
if (argv.length === 0) { if (argv.length === 0) {
say(`cdb name: ${cdb}`); say(`cdb name: ${cdb}`);
} }
}, "CDB commands."); },
"CDB commands.",
);
Cmdline.register_order("qoa", function(argv) { Cmdline.register_order(
"qoa",
function (argv) {
var sounds = Resources.sounds.filter(x => x !== "qoa"); var sounds = Resources.sounds.filter(x => x !== "qoa");
for (var file of argv) { for (var file of argv) {
if (!sounds.includes(file.ext())) continue; if (!sounds.includes(file.ext())) continue;
say(`converting ${file}`); say(`converting ${file}`);
io.save_qoa(file); io.save_qoa(file);
} }
}, "Convert file(s) to qoa."); },
"Convert file(s) to qoa.",
Cmdline.register_order("about", function(argv) { );
Cmdline.register_order(
"about",
function (argv) {
if (!argv[0]) { if (!argv[0]) {
say('About your game'); say("About your game");
say(`Prosperon version ${prosperon.version}`); say(`Prosperon version ${prosperon.version}`);
say(`Total entities ${ur._list.length}`); say(`Total entities ${ur._list.length}`);
} }
@ -317,14 +347,22 @@ Cmdline.register_order("about", function(argv) {
for (var i of ur._list) say(i); for (var i of ur._list) say(i);
break; break;
} }
}, "Get information about this game."); },
"Get information about this game.",
);
Cmdline.register_order("ur", function(argv) { Cmdline.register_order(
"ur",
function (argv) {
// game.loadurs(); // game.loadurs();
for (var i of ur._list.sort()) say(i); for (var i of ur._list.sort()) say(i);
}, "Get information about the ur types in your game."); },
"Get information about the ur types in your game.",
);
Cmdline.register_order("env", function(argv) { Cmdline.register_order(
"env",
function (argv) {
if (argv.length > 2) return; if (argv.length > 2) return;
var gg = json.decode(io.slurp(projectfile)); var gg = json.decode(io.slurp(projectfile));
if (argv.length === 0) { if (argv.length === 0) {
@ -345,17 +383,29 @@ Cmdline.register_order("env", function(argv) {
say(json.encode(gg, null, 1)); say(json.encode(gg, null, 1));
io.slurpwrite(projectfile, json.encode(gg)); io.slurpwrite(projectfile, json.encode(gg));
} }
}, "Get or set game variables."); },
"Get or set game variables.",
);
Cmdline.register_order("unpack", function() { Cmdline.register_order(
"unpack",
function () {
say("Unpacking not implemented."); say("Unpacking not implemented.");
}, "Unpack this binary's contents into this folder for editing."); },
"Unpack this binary's contents into this folder for editing.",
);
Cmdline.register_order("build", function() { Cmdline.register_order(
"build",
function () {
say("Building not implemented."); say("Building not implemented.");
}, "Build static assets for this project."); },
"Build static assets for this project.",
);
Cmdline.register_order("nota", function(argv) { Cmdline.register_order(
"nota",
function (argv) {
for (var file of argv) { for (var file of argv) {
if (!io.exists(file)) { if (!io.exists(file)) {
say(`File ${file} does not exist.`); say(`File ${file} does not exist.`);
@ -366,9 +416,13 @@ Cmdline.register_order("nota", function(argv) {
var nn = nota.encode(obj); var nn = nota.encode(obj);
io.slurpwrite(file.set_ext(".nota"), nn); io.slurpwrite(file.set_ext(".nota"), nn);
} }
}, "Create a nota file from a json."); },
"Create a nota file from a json.",
);
Cmdline.register_order("json", function(argv) { Cmdline.register_order(
"json",
function (argv) {
for (var file of argv) { for (var file of argv) {
if (!io.exists(file)) { if (!io.exists(file)) {
say(`File ${file} does not exist.`); say(`File ${file} does not exist.`);
@ -379,9 +433,13 @@ Cmdline.register_order("json", function(argv) {
var nn = json.encode(obj); var nn = json.encode(obj);
io.slurpwrite(file.set_ext(".json", nn)); io.slurpwrite(file.set_ext(".json", nn));
} }
}, "Create a JSON from a nota."); },
"Create a JSON from a nota.",
);
Cmdline.register_order("api", function(obj) { Cmdline.register_order(
"api",
function (obj) {
if (!obj[0]) { if (!obj[0]) {
Cmdline.print_order("api"); Cmdline.print_order("api");
return; return;
@ -389,19 +447,28 @@ Cmdline.register_order("api", function(obj) {
use("scripts/editor.js"); use("scripts/editor.js");
var api = debug.api.print_doc(obj[0]); var api = debug.api.print_doc(obj[0]);
if (!api) if (!api) return;
return;
say(api); say(api);
}, "Print the API for an object as markdown. Give it a file to save the output to.", "OBJECT"); },
"Print the API for an object as markdown. Give it a file to save the output to.",
"OBJECT",
);
Cmdline.register_order("input", function(pawn) { Cmdline.register_order(
"input",
function (pawn) {
use("scripts/editor.js"); use("scripts/editor.js");
say(`## Input for ${pawn}`); say(`## Input for ${pawn}`);
eval(`say(input.print_md_kbm(${pawn}));`); eval(`say(input.print_md_kbm(${pawn}));`);
}, "Print input documentation for a given object as markdown. Give it a file to save the output to", "OBJECT ?FILE?"); },
"Print input documentation for a given object as markdown. Give it a file to save the output to",
"OBJECT ?FILE?",
);
Cmdline.register_order("run", function(script) { Cmdline.register_order(
"run",
function (script) {
script = script.join(" "); script = script.join(" ");
if (!script) { if (!script) {
say("Need something to run."); say("Need something to run.");
@ -409,22 +476,24 @@ Cmdline.register_order("run", function(script) {
} }
say(eval(script)); say(eval(script));
}, "Run a given script. SCRIPT can be the script itself, or a file containing the script", "SCRIPT"); },
"Run a given script. SCRIPT can be the script itself, or a file containing the script",
"SCRIPT",
);
Cmdline.orders.script = Cmdline.orders.run; Cmdline.orders.script = Cmdline.orders.run;
Cmdline.print_order = function(fn) Cmdline.print_order = function (fn) {
{ if (typeof fn === "string") fn = Cmdline.orders[fn];
if (typeof fn === 'string')
fn = Cmdline.orders[fn];
if (!fn) return; if (!fn) return;
say(`Usage: prosperon ${fn.usage}`); say(`Usage: prosperon ${fn.usage}`);
say(fn.doc); say(fn.doc);
} };
Cmdline.register_order("help", function(order) {
Cmdline.register_order(
"help",
function (order) {
if (!Object.empty(order)) { if (!Object.empty(order)) {
var orfn = Cmdline.orders[order]; var orfn = Cmdline.orders[order];
@ -439,61 +508,77 @@ Cmdline.register_order("help", function(order) {
Cmdline.print_order("help"); Cmdline.print_order("help");
for (var cmd of Object.keys(Cmdline.orders).sort()) for (var cmd of Object.keys(Cmdline.orders).sort()) say(cmd);
say(cmd);
Cmdline.orders.version(); Cmdline.orders.version();
}, "Give help with a specific command.", "TOPIC"); },
"Give help with a specific command.",
"TOPIC",
);
Cmdline.register_order("version", function() { Cmdline.register_order(
"version",
function () {
say(`Prosperon version ${prosperon.version} [${prosperon.revision}]`); say(`Prosperon version ${prosperon.version} [${prosperon.revision}]`);
}, "Display Prosperon info."); },
"Display Prosperon info.",
);
function cmd_args(cmdargs) function cmd_args(cmdargs) {
{
var play = false; var play = false;
var cmds = cmdargs.split(/\s+/).slice(1); var cmds = cmdargs.split(/\s+/).slice(1);
if (cmds.length === 0) if (cmds.length === 0) cmds[0] = "play";
cmds[0] = "play";
else if (!Cmdline.orders[cmds[0]]) { else if (!Cmdline.orders[cmds[0]]) {
console.warn(`Command ${cmds[0]} not found. Playing instead.`); console.warn(`Command ${cmds[0]} not found. Playing instead.`);
cmds[0] = "play"; cmds[0] = "play";
} }
Cmdline.orders[cmds[0]](cmds.slice(1)); Cmdline.orders[cmds[0]](cmds.slice(1));
if (!game.startengine) if (!game.startengine) os.exit(0);
os.exit(0);
} }
Cmdline.register_order("clean", function(argv) { Cmdline.register_order(
"clean",
function (argv) {
say("Cleaning not implemented."); say("Cleaning not implemented.");
}, "Clean up a given object file.", "JSON ..."); },
"Clean up a given object file.",
"JSON ...",
);
Cmdline.register_order("test", function(argv) { Cmdline.register_order(
"test",
function (argv) {
use("scripts/test.js"); use("scripts/test.js");
}, "Run tests."); },
"Run tests.",
);
Cmdline.register_cmd("l", function(n) { Cmdline.register_cmd(
"l",
function (n) {
console.level = n; console.level = n;
}, "Set log level."); },
"Set log level.",
);
function convertYAMLtoJSON(yamlString) { function convertYAMLtoJSON(yamlString) {
const lines = yamlString.split('\n'); const lines = yamlString.split("\n");
const jsonObj = {}; const jsonObj = {};
let currentKey = ''; let currentKey = "";
let currentValue = ''; let currentValue = "";
let currentDepth = 0; let currentDepth = 0;
for (let i = 0; i < lines.length; i++) { for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim(); const line = lines[i].trim();
if (!line || line.startsWith('#')) { if (!line || line.startsWith("#")) {
continue; continue;
} }
const depth = (line.match(/^\s+/g) || [''])[0].length; const depth = (line.match(/^\s+/g) || [""])[0].length;
const keyValue = line.split(':'); const keyValue = line.split(":");
const key = keyValue[0].trim(); const key = keyValue[0].trim();
const value = keyValue[1].trim(); const value = keyValue[1].trim();
@ -507,8 +592,8 @@ function convertYAMLtoJSON(yamlString) {
currentValue = value; currentValue = value;
} else { } else {
jsonObj[currentKey] = convertYAMLtoJSON(currentValue); jsonObj[currentKey] = convertYAMLtoJSON(currentValue);
currentKey = ''; currentKey = "";
currentValue = ''; currentValue = "";
i--; // To reprocess the current line with updated values i--; // To reprocess the current line with updated values
} }
@ -526,6 +611,5 @@ return {
Resources, Resources,
Cmdline, Cmdline,
cmd_args, cmd_args,
convertYAMLtoJSON convertYAMLtoJSON,
}; };

View file

@ -2,9 +2,5 @@ test.run("set transform scale", _ => {
var t = os.make_transform(); var t = os.make_transform();
var s1 = t.scale.slice(); var s1 = t.scale.slice();
t.scale = s1; t.scale = s1;
return (t.scale.equal(s1)); return t.scale.equal(s1);
}); });

View file

@ -7,18 +7,14 @@ var pass = 0;
var fail = 0; var fail = 0;
var failed = []; var failed = [];
test.run_suite = function(file) test.run_suite = function (file) {
{
test = []; test = [];
pass = 0; pass = 0;
fail = 0; fail = 0;
failed = []; failed = [];
};
test.run = function (name, fn) {
}
test.run = function(name, fn)
{
var func = function () { var func = function () {
print(`${pass + fail + 1}/${tests.length}: ${name} ... `); print(`${pass + fail + 1}/${tests.length}: ${name} ... `);
var p = profile.now(); var p = profile.now();
@ -29,12 +25,11 @@ test.run = function(name, fn)
}; };
func.testname = name; func.testname = name;
tests.push(func); tests.push(func);
} };
say(`Testing ${tests.length} tests.`); say(`Testing ${tests.length} tests.`);
for (var t of tests) { for (var t of tests) {
if (t()) if (t()) pass++;
pass++;
else { else {
fail++; fail++;
failed.push(t.testname); failed.push(t.testname);
@ -42,10 +37,9 @@ for (var t of tests) {
print("\n"); print("\n");
} }
say(`Passed ${pass} tests and failed ${fail} [${(pass*100/(pass+fail)).toPrecision(4)}%].`); say(`Passed ${pass} tests and failed ${fail} [${((pass * 100) / (pass + fail)).toPrecision(4)}%].`);
say(`Failed tests are:`); say(`Failed tests are:`);
for (var f of failed) for (var f of failed) say(f);
say(f);
os.quit(); os.quit();

View file

@ -1,13 +1,13 @@
var texteditor = Object.copy(inputpanel, { var texteditor = Object.copy(inputpanel, {
title: "text editor", title: "text editor",
wh: [700, 500], wh: [700, 500],
_cursor:0, /* Text cursor: [char,line] */ _cursor: 0 /* Text cursor: [char,line] */,
get cursor() { return this._cursor; }, get cursor() {
return this._cursor;
},
set cursor(x) { set cursor(x) {
if (x > this.value.length) if (x > this.value.length) x = this.value.length;
x = this.value.length; if (x < 0) x = 0;
if (x < 0)
x = 0;
this._cursor = x; this._cursor = x;
this.line = this.get_line(); this.line = this.get_line();
@ -45,17 +45,13 @@ var texteditor = Object.copy(inputpanel, {
var ls = this.line_start(p); var ls = this.line_start(p);
var le = this.line_end(p); var le = this.line_end(p);
var line = this.value.slice(ls, le); var line = this.value.slice(ls, le);
if (line.search(/[^\s]/g) === -1) if (line.search(/[^\s]/g) === -1) return true;
return true; else return false;
else
return false;
}, },
get_line() { get_line() {
var line = 0; var line = 0;
for (var i = 0; i < this.cursor; i++) for (var i = 0; i < this.cursor; i++) if (this.value[i] === "\n") line++;
if (this.value[i] === "\n")
line++;
return line; return line;
}, },
@ -72,10 +68,7 @@ var texteditor = Object.copy(inputpanel, {
src: "NEW FILE", src: "NEW FILE",
guibody() { guibody() {
return [ return [Mum.text({ str: `EDITING ${this.src}` }), Mum.text({ str: this.value, caret: this.cursor, offset: [0, -16] })];
Mum.text({str:`EDITING ${this.src}`}),
Mum.text({str:this.value, caret:this.cursor, offset:[0,-16]}),
];
}, },
insert_char(char) { insert_char(char) {
@ -100,8 +93,7 @@ var texteditor = Object.copy(inputpanel, {
this.savestate(); this.savestate();
var ret = this.value.slice(start, end); var ret = this.value.slice(start, end);
this.value = this.value.slice(0, start) + this.value.slice(end); this.value = this.value.slice(0, start) + this.value.slice(end);
if (start > this.cursor) if (start > this.cursor) return ret;
return ret;
this.cursor -= ret.length; this.cursor -= ret.length;
return ret; return ret;
@ -114,8 +106,7 @@ var texteditor = Object.copy(inputpanel, {
}, },
prev_word(pos) { prev_word(pos) {
while (this.value.slice(pos,pos+2).search(/[^\w]\w/g) === -1 && pos > 0) while (this.value.slice(pos, pos + 2).search(/[^\w]\w/g) === -1 && pos > 0) pos--;
pos--;
return pos + 1; return pos + 1;
}, },
@ -126,67 +117,66 @@ var texteditor = Object.copy(inputpanel, {
}, },
get inset() { get inset() {
return this.cursor - this.value.prev('\n', this.cursor) - 1; return this.cursor - this.value.prev("\n", this.cursor) - 1;
}, },
line_start(p) { line_start(p) {
return this.value.prev('\n', p)+1; return this.value.prev("\n", p) + 1;
}, },
line_end(p) { line_end(p) {
return this.value.next('\n', p); return this.value.next("\n", p);
}, },
next_line(p) { next_line(p) {
return this.value.next('\n',p)+1; return this.value.next("\n", p) + 1;
}, },
prev_line(p) { prev_line(p) {
return this.line_start(this.value.prev('\n', p)); return this.line_start(this.value.prev("\n", p));
}, },
to_line_start() { to_line_start() {
this.cursor = this.value.prev('\n', this.cursor)+1; this.cursor = this.value.prev("\n", this.cursor) + 1;
}, },
to_line_end() { to_line_end() {
var p = this.value.next('\n', this.cursor); var p = this.value.next("\n", this.cursor);
if (p === -1) if (p === -1) this.to_file_end();
this.to_file_end(); else this.cursor = p;
else
this.cursor = p;
}, },
line_width(pos) { line_width(pos) {
var start = this.line_start(pos); var start = this.line_start(pos);
var end = this.line_end(pos); var end = this.line_end(pos);
if (end === -1) if (end === -1) end = this.value.length;
end = this.value.length;
return end - start; return end - start;
}, },
to_file_end() { this.cursor = this.value.length; }, to_file_end() {
this.cursor = this.value.length;
},
to_file_start() { this.cursor = 0; }, to_file_start() {
this.cursor = 0;
},
desired_inset: 0, desired_inset: 0,
}); });
texteditor.inputs = {}; texteditor.inputs = {};
texteditor.inputs.char = function(char) { (texteditor.inputs.char = function (char) {
this.insert_char(char); this.insert_char(char);
this.keycb(); this.keycb();
}, }),
(texteditor.inputs.enter = function () {
texteditor.inputs.enter = function(){
var white = this.line_starting_whitespace(this.cursor); var white = this.line_starting_whitespace(this.cursor);
this.insert_char('\n'); this.insert_char("\n");
for (var i = 0; i < white; i++) for (var i = 0; i < white; i++) this.insert_char(" ");
this.insert_char(" "); });
};
texteditor.inputs.enter.rep = true; texteditor.inputs.enter.rep = true;
texteditor.inputs.backspace = function () { texteditor.inputs.backspace = function () {
@ -195,18 +185,19 @@ texteditor.inputs.backspace = function(){
}; };
texteditor.inputs.backspace.rep = true; texteditor.inputs.backspace.rep = true;
texteditor.inputs["C-s"] = function () {
texteditor.inputs['C-s'] = function() { if (this.srctype === "function") {
if (this.srctype === 'function') {
eval(`${this.src} = ${this.value}`); eval(`${this.src} = ${this.value}`);
} }
}; };
texteditor.inputs['C-s'].doc = "Save edited text."; texteditor.inputs["C-s"].doc = "Save edited text.";
texteditor.inputs['C-u'] = function() { this.popstate(); }; texteditor.inputs["C-u"] = function () {
texteditor.inputs['C-u'].doc = "Undo."; this.popstate();
};
texteditor.inputs["C-u"].doc = "Undo.";
texteditor.inputs['C-q'] = function() { texteditor.inputs["C-q"] = function () {
var ws = this.prev_word(this.cursor); var ws = this.prev_word(this.cursor);
var we = this.end_of_word(this.cursor) + 1; var we = this.end_of_word(this.cursor) + 1;
var find = this.copy(ws, we); var find = this.copy(ws, we);
@ -217,109 +208,109 @@ texteditor.inputs['C-q'] = function() {
editor.selectlist.push(obj); editor.selectlist.push(obj);
} }
}; };
texteditor.inputs['C-q'].doc = "Select object of selected word."; texteditor.inputs["C-q"].doc = "Select object of selected word.";
texteditor.inputs['C-o'] = function() { texteditor.inputs["C-o"] = function () {
this.insert_char('\n'); this.insert_char("\n");
this.cursor--; this.cursor--;
}; };
texteditor.inputs['C-o'].doc = "Insert newline."; texteditor.inputs["C-o"].doc = "Insert newline.";
texteditor.inputs['C-o'].rep = true; texteditor.inputs["C-o"].rep = true;
texteditor.inputs['M-o'] = function() { texteditor.inputs["M-o"] = function () {
while (this.line_blank(this.next_line(this.cursor))) while (this.line_blank(this.next_line(this.cursor))) this.delete_line(this.next_line(this.cursor));
this.delete_line(this.next_line(this.cursor));
while (this.line_blank(this.prev_line(this.cursor))) while (this.line_blank(this.prev_line(this.cursor))) this.delete_line(this.prev_line(this.cursor));
this.delete_line(this.prev_line(this.cursor));
}; };
texteditor.inputs['M-o'].doc = "Delete surround blank lines."; texteditor.inputs["M-o"].doc = "Delete surround blank lines.";
texteditor.inputs['C-d'] = function () { this.value = this.value.slice(0,this.cursor) + this.value.slice(this.cursor+1); }; texteditor.inputs["C-d"] = function () {
texteditor.inputs['C-d'].doc = "Delete character."; this.value = this.value.slice(0, this.cursor) + this.value.slice(this.cursor + 1);
};
texteditor.inputs["C-d"].doc = "Delete character.";
texteditor.inputs['M-d'] = function() { this.cut_span(this.cursor, this.end_of_word(this.cursor)+1); }; texteditor.inputs["M-d"] = function () {
texteditor.inputs['M-d'].doc = "Delete word."; this.cut_span(this.cursor, this.end_of_word(this.cursor) + 1);
};
texteditor.inputs["M-d"].doc = "Delete word.";
texteditor.inputs['C-a'] = function() { texteditor.inputs["C-a"] = function () {
this.to_line_start(); this.to_line_start();
this.desired_inset = this.inset; this.desired_inset = this.inset;
}; };
texteditor.inputs['C-a'].doc = "To start of line."; texteditor.inputs["C-a"].doc = "To start of line.";
texteditor.inputs['C-y'] = function() { texteditor.inputs["C-y"] = function () {
if (this.killring.length === 0) return; if (this.killring.length === 0) return;
this.insert_char(this.killring.pop()); this.insert_char(this.killring.pop());
}; };
texteditor.inputs['C-y'].doc = "Insert from killring."; texteditor.inputs["C-y"].doc = "Insert from killring.";
texteditor.inputs['C-e'] = function() { texteditor.inputs["C-e"] = function () {
this.to_line_end(); this.to_line_end();
this.desired_inset = this.inset; this.desired_inset = this.inset;
}; };
texteditor.inputs['C-e'].doc = "To line end."; texteditor.inputs["C-e"].doc = "To line end.";
texteditor.inputs['C-k'] = function() { texteditor.inputs["C-k"] = function () {
if (this.cursor === this.value.length - 1) return; if (this.cursor === this.value.length - 1) return;
var killamt = this.value.next('\n', this.cursor) - this.cursor; var killamt = this.value.next("\n", this.cursor) - this.cursor;
var killed = this.cut_span(this.cursor - 1, this.cursor + killamt); var killed = this.cut_span(this.cursor - 1, this.cursor + killamt);
this.killring.push(killed); this.killring.push(killed);
}; };
texteditor.inputs['C-k'].doc = "Kill from cursor to end of line."; texteditor.inputs["C-k"].doc = "Kill from cursor to end of line.";
texteditor.inputs['M-k'] = function() { texteditor.inputs["M-k"] = function () {
var prevn = this.value.prev('\n', this.cursor); var prevn = this.value.prev("\n", this.cursor);
var killamt = this.cursor - prevn; var killamt = this.cursor - prevn;
var killed = this.cut_span(prevn + 1, prevn + killamt); var killed = this.cut_span(prevn + 1, prevn + killamt);
this.killring.push(killed); this.killring.push(killed);
this.to_line_start(); this.to_line_start();
}; };
texteditor.inputs['M-k'].doc = "Kill entire line the cursor is on."; texteditor.inputs["M-k"].doc = "Kill entire line the cursor is on.";
texteditor.inputs['C-b'] = function() { texteditor.inputs["C-b"] = function () {
this.cursor--; this.cursor--;
this.desired_inset = this.inset; this.desired_inset = this.inset;
}; };
texteditor.inputs['C-b'].rep = true; texteditor.inputs["C-b"].rep = true;
texteditor.inputs['M-b'] = function() { texteditor.inputs["M-b"] = function () {
this.cursor = this.prev_word(this.cursor - 2); this.cursor = this.prev_word(this.cursor - 2);
this.desired_inset = this.inset; this.desired_inset = this.inset;
}; };
texteditor.inputs['M-b'].rep = true; texteditor.inputs["M-b"].rep = true;
texteditor.inputs['C-f'] = function() { texteditor.inputs["C-f"] = function () {
this.cursor++; this.cursor++;
this.desired_inset = this.inset; this.desired_inset = this.inset;
}; };
texteditor.inputs['C-f'].rep = true; texteditor.inputs["C-f"].rep = true;
texteditor.inputs['M-f'] = function() { texteditor.inputs["M-f"] = function () {
this.cursor = this.next_word(this.cursor); this.cursor = this.next_word(this.cursor);
this.desired_inset = this.inset; this.desired_inset = this.inset;
}; };
texteditor.inputs['M-f'].rep = true; texteditor.inputs["M-f"].rep = true;
texteditor.inputs['C-p'] = function() { texteditor.inputs["C-p"] = function () {
if (this.cursor === 0) return; if (this.cursor === 0) return;
this.desired_inset = Math.max(this.desired_inset, this.inset); this.desired_inset = Math.max(this.desired_inset, this.inset);
this.cursor = this.prev_line(this.cursor); this.cursor = this.prev_line(this.cursor);
var newlinew = this.line_width(this.cursor); var newlinew = this.line_width(this.cursor);
this.cursor += Math.min(this.desired_inset, newlinew); this.cursor += Math.min(this.desired_inset, newlinew);
}; };
texteditor.inputs['C-p'].rep = true; texteditor.inputs["C-p"].rep = true;
texteditor.inputs['M-p'] = function() { texteditor.inputs["M-p"] = function () {
while (this.line_blank(this.cursor)) while (this.line_blank(this.cursor)) this.cursor = this.prev_line(this.cursor);
this.cursor = this.prev_line(this.cursor);
while (!this.line_blank(this.cursor)) while (!this.line_blank(this.cursor)) this.cursor = this.prev_line(this.cursor);
this.cursor = this.prev_line(this.cursor);
}; };
texteditor.inputs['M-p'].doc = "Go up to next line with text on it."; texteditor.inputs["M-p"].doc = "Go up to next line with text on it.";
texteditor.inputs['M-p'].rep = true; texteditor.inputs["M-p"].rep = true;
texteditor.inputs['C-n'] = function() { texteditor.inputs["C-n"] = function () {
if (this.cursor === this.value.length - 1) return; if (this.cursor === this.value.length - 1) return;
if (this.value.next('\n', this.cursor) === -1) { if (this.value.next("\n", this.cursor) === -1) {
this.to_file_end(); this.to_file_end();
return; return;
} }
@ -329,20 +320,16 @@ texteditor.inputs['C-n'] = function() {
var newlinew = this.line_width(this.cursor); var newlinew = this.line_width(this.cursor);
this.cursor += Math.min(this.desired_inset, newlinew); this.cursor += Math.min(this.desired_inset, newlinew);
}; };
texteditor.inputs['C-n'].rep = true; texteditor.inputs["C-n"].rep = true;
texteditor.inputs['M-n'] = function() { texteditor.inputs["M-n"] = function () {
while (this.line_blank(this.cursor)) while (this.line_blank(this.cursor)) this.cursor = this.next_line(this.cursor);
this.cursor = this.next_line(this.cursor); while (!this.line_blank(this.cursor)) this.cursor = this.next_line(this.cursor);
while (!this.line_blank(this.cursor))
this.cursor = this.next_line(this.cursor);
}; };
texteditor.inputs['M-n'].doc = "Go down to next line with text on it."; texteditor.inputs["M-n"].doc = "Go down to next line with text on it.";
texteditor.inputs['M-n'].rep = true; texteditor.inputs["M-n"].rep = true;
texteditor.open_fn = function(fnstr) texteditor.open_fn = function (fnstr) {
{
var fn = eval(fnstr); var fn = eval(fnstr);
if (!fn) { if (!fn) {
console.warn(`${fnstr} is not a function.`); console.warn(`${fnstr} is not a function.`);
@ -353,4 +340,4 @@ texteditor.open_fn = function(fnstr)
editor.openpanel(this); editor.openpanel(this);
this.value = fn; this.value = fn;
this.cursor = 0; this.cursor = 0;
} };

View file

@ -1,12 +1,16 @@
/* Take numbers from 0 to 1 and remap them to easing functions */ /* Take numbers from 0 to 1 and remap them to easing functions */
var Ease = { var Ease = {
linear(t) { return t; }, linear(t) {
return t;
},
in(t) { return t*t; }, in(t) {
return t * t;
},
out(t) { out(t) {
var d = 1 - t; var d = 1 - t;
return 1 - d*d return 1 - d * d;
}, },
inout(t) { inout(t) {
@ -15,7 +19,6 @@ var Ease = {
}, },
}; };
function make_easing_fns(num) { function make_easing_fns(num) {
var obj = {}; var obj = {};
@ -34,7 +37,7 @@ function make_easing_fns(num) {
}; };
return obj; return obj;
}; }
Ease.quad = make_easing_fns(2); Ease.quad = make_easing_fns(2);
Ease.cubic = make_easing_fns(3); Ease.cubic = make_easing_fns(3);
@ -52,7 +55,7 @@ Ease.expo = {
inout(t) { inout(t) {
return t === 0 ? 0 : t === 1 ? 1 : t < 0.5 ? Math.pow(2, 20 * t - 10) / 2 : (2 - Math.pow(2, -20 * t + 10)) / 2; return t === 0 ? 0 : t === 1 ? 1 : t < 0.5 ? Math.pow(2, 20 * t - 10) / 2 : (2 - Math.pow(2, -20 * t + 10)) / 2;
} },
}; };
Ease.bounce = { Ease.bounce = {
@ -64,24 +67,32 @@ Ease.bounce = {
var n1 = 7.5625; var n1 = 7.5625;
var d1 = 2.75; var d1 = 2.75;
if (t < 1 / d1) { return n1 * t * t; } if (t < 1 / d1) {
else if (t < 2 / d1) { return n1 * (t -= 1.5 / d1) * t + 0.75; } return n1 * t * t;
else if (t < 2.5 / d1) { return n1 * (t -= 2.25 / d1) * t + 0.9375; } } else if (t < 2 / d1) {
else return n1 * (t -= 1.5 / d1) * t + 0.75;
return n1 * (t -= 2.625 / d1) * t + 0.984375; } else if (t < 2.5 / d1) {
return n1 * (t -= 2.25 / d1) * t + 0.9375;
} else return n1 * (t -= 2.625 / d1) * t + 0.984375;
}, },
inout(t) { inout(t) {
return t < 0.5 ? (1 - this.out(1 - 2 * t)) / 2 : (1 + this.out(2 * t - 1)) / 2; return t < 0.5 ? (1 - this.out(1 - 2 * t)) / 2 : (1 + this.out(2 * t - 1)) / 2;
} },
}; };
Ease.sine = { Ease.sine = {
in(t) { return 1 - Math.cos((t * Math.PI)/2); }, in(t) {
return 1 - Math.cos((t * Math.PI) / 2);
},
out(t) { return Math.sin((t*Math.PI)/2); }, out(t) {
return Math.sin((t * Math.PI) / 2);
},
inout(t) { return -(Math.cos(Math.PI*t) - 1) / 2; } inout(t) {
return -(Math.cos(Math.PI * t) - 1) / 2;
},
}; };
Ease.elastic = { Ease.elastic = {
@ -94,17 +105,14 @@ Ease.elastic = {
}, },
inout(t) { inout(t) {
t === 0 ? 0 : t === 1 ? 1 : t < 0.5 ? t === 0 ? 0 : t === 1 ? 1 : t < 0.5 ? -(Math.pow(2, 20 * t - 10) * Math.sin((20 * t - 11.125) * this.c5)) / 2 : (Math.pow(2, -20 * t + 10) * Math.sin((20 * t - 11.125) * this.c5)) / 2 + 1;
-(Math.pow(2, 20 * t - 10) * Math.sin((20 * t - 11.125) * this.c5)) / 2
: (Math.pow(2, -20 * t + 10) * Math.sin((20 * t - 11.125) * this.c5)) / 2 + 1;
}, },
}; };
Ease.elastic.c4 = 2*Math.PI/3; Ease.elastic.c4 = (2 * Math.PI) / 3;
Ease.elastic.c5 = 2*Math.PI / 4.5; Ease.elastic.c5 = (2 * Math.PI) / 4.5;
var tween = function(from, to, time, fn, endfn) var tween = function (from, to, time, fn, endfn) {
{
var start = profile.secs(profile.now()); var start = profile.secs(profile.now());
var update = function (dt) { var update = function (dt) {
profile.frame("tween"); profile.frame("tween");
@ -120,7 +128,7 @@ var tween = function(from, to, time, fn, endfn)
}; };
var stop = Register.update.register(update); var stop = Register.update.register(update);
return stop; return stop;
} };
var Tween = { var Tween = {
default: { default: {
@ -133,22 +141,19 @@ var Tween = {
yoyo: go up and then back down yoyo: go up and then back down
circle: go up and back down, looped circle: go up and back down, looped
*/ */
time: 1, /* seconds to do */ time: 1 /* seconds to do */,
ease: Ease.linear, ease: Ease.linear,
whole: true, /* True if time is for the entire tween, false if each stage */ whole: true /* True if time is for the entire tween, false if each stage */,
cb: function () {}, cb: function () {},
}, },
start(obj, target, tvals, options) start(obj, target, tvals, options) {
{
var defn = Object.create(this.default); var defn = Object.create(this.default);
Object.assign(defn, options); Object.assign(defn, options);
if (defn.loop === 'circle') if (defn.loop === "circle") tvals.push(tvals[0]);
tvals.push(tvals[0]); else if (defn.loop === "yoyo") {
else if (defn.loop === 'yoyo') { for (var i = tvals.length - 2; i >= 0; i--) tvals.push(tvals[i]);
for (var i = tvals.length-2; i >= 0; i--)
tvals.push(tvals[i]);
} }
defn.accum = 0; defn.accum = 0;
@ -158,11 +163,9 @@ var Tween = {
defn.fn = function (dt) { defn.fn = function (dt) {
defn.accum += dt; defn.accum += dt;
if (defn.accum >= defn.time && defn.loop === 'hold') { if (defn.accum >= defn.time && defn.loop === "hold") {
if (typeof target === 'string') if (typeof target === "string") obj[target] = tvals[tvals.length - 1];
obj[target] = tvals[tvals.length-1]; else target(tvals[tvals.length - 1]);
else
target(tvals[tvals.length-1]);
defn.pause(); defn.pause();
defn.cb.call(obj); defn.cb.call(obj);
@ -170,8 +173,7 @@ var Tween = {
} }
defn.pct = (defn.accum % defn.time) / defn.time; defn.pct = (defn.accum % defn.time) / defn.time;
if (defn.loop === 'none' && defn.accum >= defn.time) if (defn.loop === "none" && defn.accum >= defn.time) defn.stop();
defn.stop();
var t = defn.whole ? defn.ease(defn.pct) : defn.pct; var t = defn.whole ? defn.ease(defn.pct) : defn.pct;
@ -179,13 +181,10 @@ var Tween = {
var i = Math.trunc(nval); var i = Math.trunc(nval);
nval -= i; nval -= i;
if (!defn.whole) if (!defn.whole) nval = defn.ease(nval);
nval = defn.ease(nval);
if (typeof target === 'string') if (typeof target === "string") obj[target] = tvals[i].lerp(tvals[i + 1], nval);
obj[target] = tvals[i].lerp(tvals[i+1], nval); else target(tvals[i].lerp(tvals[i + 1], nval));
else
target(tvals[i].lerp(tvals[i+1],nval));
}; };
var playing = false; var playing = false;
@ -197,12 +196,14 @@ var Tween = {
}; };
defn.restart = function () { defn.restart = function () {
defn.accum = 0; defn.accum = 0;
if (typeof target === 'string') if (typeof target === "string") obj[target] = tvals[0];
obj[target] = tvals[0]; else target(tvals[0]);
else };
target(tvals[0]); defn.stop = function () {
if (!playing) return;
defn.pause();
defn.restart();
}; };
defn.stop = function() { if (!playing) return; defn.pause(); defn.restart(); };
defn.pause = function () { defn.pause = function () {
defn._end(); defn._end();
if (!playing) return; if (!playing) return;

View file

@ -1,6 +1,8 @@
var inputpanel = { var inputpanel = {
title: "untitled", title: "untitled",
toString() { return this.title; }, toString() {
return this.title;
},
value: "", value: "",
on: false, on: false,
pos: [20, window.size.y - 20], pos: [20, window.size.y - 20],
@ -9,24 +11,23 @@ var inputpanel = {
padding: [5, -15], padding: [5, -15],
gui() { gui() {
this.win ??= Mum.window({width:this.wh.x,height:this.wh.y, color:Color.black.alpha(0.1), anchor:this.anchor, padding:this.padding}); this.win ??= Mum.window({
width: this.wh.x,
height: this.wh.y,
color: Color.black.alpha(0.1),
anchor: this.anchor,
padding: this.padding,
});
var itms = this.guibody(); var itms = this.guibody();
if (!Array.isArray(itms)) itms = [itms]; if (!Array.isArray(itms)) itms = [itms];
if (this.title) if (this.title) this.win.items = [Mum.column({ items: [Mum.text({ str: this.title }), ...itms] })];
this.win.items = [ else this.win.items = itms;
Mum.column({items: [Mum.text({str:this.title}), ...itms ]})
];
else
this.win.items = itms;
this.win.draw([100, window.size.y - 50]); this.win.draw([100, window.size.y - 50]);
}, },
guibody() { guibody() {
return [ return [Mum.text({ str: this.value, color: Color.green }), Mum.button({ str: "SUBMIT", action: this.submit.bind(this) })];
Mum.text({str:this.value, color:Color.green}),
Mum.button({str:"SUBMIT", action:this.submit.bind(this)})
];
}, },
open() { open() {
@ -41,23 +42,21 @@ var inputpanel = {
close() { close() {
player[0].uncontrol(this); player[0].uncontrol(this);
this.on = false; this.on = false;
if ('on_close' in this) if ("on_close" in this) this.on_close();
this.on_close();
}, },
action() { action() {},
},
closeonsubmit: true, closeonsubmit: true,
submit() { submit() {
if (!this.submit_check()) return; if (!this.submit_check()) return;
this.action(); this.action();
if (this.closeonsubmit) if (this.closeonsubmit) this.close();
this.close();
}, },
submit_check() { return true; }, submit_check() {
return true;
},
keycb() {}, keycb() {},
@ -77,49 +76,62 @@ var inputpanel = {
inputpanel.inputs = {}; inputpanel.inputs = {};
inputpanel.inputs.block = true; inputpanel.inputs.block = true;
inputpanel.inputs.post = function() { this.keycb(); } inputpanel.inputs.post = function () {
this.keycb();
};
inputpanel.inputs.char = function (c) { inputpanel.inputs.char = function (c) {
this.value = this.value.slice(0, this.caret) + c + this.value.slice(this.caret); this.value = this.value.slice(0, this.caret) + c + this.value.slice(this.caret);
this.caret++; this.caret++;
} };
inputpanel.inputs['C-d'] = function() { this.value = this.value.slice(0,this.caret) + this.value.slice(this.caret+1); }; inputpanel.inputs["C-d"] = function () {
inputpanel.inputs['C-d'].rep = true; this.value = this.value.slice(0, this.caret) + this.value.slice(this.caret + 1);
};
inputpanel.inputs["C-d"].rep = true;
inputpanel.inputs.tab = function () { inputpanel.inputs.tab = function () {
this.value = input.tabcomplete(this.value, this.assets); this.value = input.tabcomplete(this.value, this.assets);
this.caret = this.value.length; this.caret = this.value.length;
} };
inputpanel.inputs.escape = function() { this.close(); } inputpanel.inputs.escape = function () {
inputpanel.inputs['C-b'] = function() { this.close();
};
inputpanel.inputs["C-b"] = function () {
if (this.caret === 0) return; if (this.caret === 0) return;
this.caret--; this.caret--;
}; };
inputpanel.inputs['C-b'].rep = true; inputpanel.inputs["C-b"].rep = true;
inputpanel.inputs['C-u'] = function() inputpanel.inputs["C-u"] = function () {
{
this.value = this.value.slice(this.caret); this.value = this.value.slice(this.caret);
this.caret = 0; this.caret = 0;
} };
inputpanel.inputs['C-f'] = function() { inputpanel.inputs["C-f"] = function () {
if (this.caret === this.value.length) return; if (this.caret === this.value.length) return;
this.caret++; this.caret++;
}; };
inputpanel.inputs['C-f'].rep = true; inputpanel.inputs["C-f"].rep = true;
inputpanel.inputs['C-a'] = function() { this.caret = 0; }; inputpanel.inputs["C-a"] = function () {
inputpanel.inputs['C-e'] = function() { this.caret = this.value.length; }; this.caret = 0;
};
inputpanel.inputs["C-e"] = function () {
this.caret = this.value.length;
};
inputpanel.inputs.backspace = function () { inputpanel.inputs.backspace = function () {
if (this.caret === 0) return; if (this.caret === 0) return;
this.value = this.value.slice(0, this.caret - 1) + this.value.slice(this.caret); this.value = this.value.slice(0, this.caret - 1) + this.value.slice(this.caret);
this.caret--; this.caret--;
}; };
inputpanel.inputs.backspace.rep = true; inputpanel.inputs.backspace.rep = true;
inputpanel.inputs.enter = function() { this.submit(); } inputpanel.inputs.enter = function () {
this.submit();
};
inputpanel.inputs['C-k'] = function() { inputpanel.inputs["C-k"] = function () {
this.value = this.value.slice(0, this.caret); this.value = this.value.slice(0, this.caret);
}; };
inputpanel.inputs.lm = function() { gui.controls.check_submit(); } inputpanel.inputs.lm = function () {
gui.controls.check_submit();
};
var notifypanel = Object.copy(inputpanel, { var notifypanel = Object.copy(inputpanel, {
title: "notification", title: "notification",
@ -129,10 +141,9 @@ var notifypanel = Object.copy(inputpanel, {
}, },
guibody() { guibody() {
return Mum.column({items: [ return Mum.column({
Mum.text({str:this.msg}), items: [Mum.text({ str: this.msg }), Mum.button({ str: "OK", action: this.close.bind(this) })],
Mum.button({str:"OK", action:this.close.bind(this)}) });
]});
}, },
}); });
@ -141,9 +152,14 @@ var gen_notify = function(val, fn) {
panel.msg = val; panel.msg = val;
panel.yes = fn; panel.yes = fn;
panel.inputs = {}; panel.inputs = {};
panel.inputs.y = function() { panel.yes(); panel.close(); }; panel.inputs.y = function () {
panel.yes();
panel.close();
};
panel.inputs.y.doc = "Confirm yes."; panel.inputs.y.doc = "Confirm yes.";
panel.inputs.enter = function() { panel.close(); }; panel.inputs.enter = function () {
panel.close();
};
panel.inputs.enter.doc = "Close."; panel.inputs.enter.doc = "Close.";
return panel; return panel;
}; };
@ -165,17 +181,20 @@ var listpanel = Object.copy(inputpanel, {
this.caret = 0; this.caret = 0;
this.mumlist = []; this.mumlist = [];
this.assets.forEach(function (x) { this.assets.forEach(function (x) {
this.mumlist[x] = Mum.text({str:x, action:this.action, color: Color.blue, hovered: {color:Color.red}, selectable:true}); this.mumlist[x] = Mum.text({
str: x,
action: this.action,
color: Color.blue,
hovered: { color: Color.red },
selectable: true,
});
}, this); }, this);
}, },
keycb() { keycb() {
if(this.value) if (this.value) this.assets = this.allassets.filter(x => x.startsWith(this.value));
this.assets = this.allassets.filter(x => x.startsWith(this.value)); else this.assets = this.allassets.slice();
else for (var m in this.mumlist) this.mumlist[m].hide = true;
this.assets = this.allassets.slice();
for (var m in this.mumlist)
this.mumlist[m].hide = true;
this.assets.forEach(function (x) { this.assets.forEach(function (x) {
this.mumlist[x].hide = false; this.mumlist[x].hide = false;
}, this); }, this);
@ -188,4 +207,4 @@ var listpanel = Object.copy(inputpanel, {
}, },
}); });
return {inputpanel, gen_notify, notifypanel, listpanel} return { inputpanel, gen_notify, notifypanel, listpanel };