This commit is contained in:
2025-01-21 16:46:18 -06:00
parent e628256f44
commit 3d7ea7d358
32 changed files with 1209 additions and 3067 deletions

View File

@@ -71,7 +71,7 @@ endif
deps += dependency('qjs-layout',static:true) deps += dependency('qjs-layout',static:true)
deps += dependency('qjs-nota',static:true) deps += dependency('qjs-nota',static:true)
deps += dependency('qjs-miniz',static:true) deps += dependency('qjs-miniz')
deps += dependency('qjs-soloud',static:true) deps += dependency('qjs-soloud',static:true)
deps += dependency('physfs') deps += dependency('physfs')
@@ -81,15 +81,15 @@ deps += dependency('physfs')
deps += dependency('threads') deps += dependency('threads')
if get_option('chipmunk') if get_option('chipmunk')
deps += dependency('qjs-chipmunk',static:true) deps += dependency('qjs-chipmunk', static:false)
endif endif
if get_option('enet') if get_option('enet')
deps += dependency('qjs-enet',static:true) deps += dependency('qjs-enet', static:false)
endif endif
sources = [] sources = []
src += ['anim.c', 'config.c', 'datastream.c','font.c','gameobject.c','HandmadeMath.c','jsffi.c','model.c','render.c','script.c','simplex.c','spline.c', 'timer.c', 'transform.c','warp.c','yugine.c', 'wildmatch.c', 'sprite.c', 'quadtree.c', 'aabb.c', 'rtree.c'] src += ['anim.c', 'config.c', 'datastream.c','font.c','gameobject.c','HandmadeMath.c','jsffi.c','model.c','render.c','script.c','simplex.c','spline.c', 'timer.c', 'transform.c','yugine.c', 'wildmatch.c', 'sprite.c', 'quadtree.c', 'aabb.c', 'rtree.c']
imsrc = ['GraphEditor.cpp','ImCurveEdit.cpp','ImGradient.cpp','imgui_draw.cpp','imgui_tables.cpp','imgui_widgets.cpp','imgui.cpp','ImGuizmo.cpp','imnodes.cpp','implot_items.cpp','implot.cpp', 'imgui_impl_sdlrenderer3.cpp', 'imgui_impl_sdl3.cpp', 'imgui_impl_sdlgpu3.cpp'] imsrc = ['GraphEditor.cpp','ImCurveEdit.cpp','ImGradient.cpp','imgui_draw.cpp','imgui_tables.cpp','imgui_widgets.cpp','imgui.cpp','ImGuizmo.cpp','imnodes.cpp','implot_items.cpp','implot.cpp', 'imgui_impl_sdlrenderer3.cpp', 'imgui_impl_sdl3.cpp', 'imgui_impl_sdlgpu3.cpp']
@@ -108,7 +108,7 @@ if get_option('editor')
foreach imgui : imsrc foreach imgui : imsrc
sources += tp / 'imgui' / imgui sources += tp / 'imgui' / imgui
endforeach endforeach
deps += dependency('qjs-dmon',static:true) deps += dependency('qjs-dmon')
endif endif
includers = [] includers = []
@@ -130,7 +130,8 @@ core = custom_target('core.zip',
prosperon = executable('prosperon', sources, prosperon = executable('prosperon', sources,
dependencies: deps, dependencies: deps,
include_directories: includers, include_directories: includers,
link_args: link link_args: link,
build_rpath: '$ORIGIN'
) )
prosperon_dep = declare_dependency( prosperon_dep = declare_dependency(

View File

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

View File

@@ -50,6 +50,8 @@ convert.buf2hex = function (buffer) {
return [...new Uint8Array(buffer)].map(x => x.toString(16).padStart(2, "0")).join(" "); return [...new Uint8Array(buffer)].map(x => x.toString(16).padStart(2, "0")).join(" ");
}; };
var time = os.use('time')
/* Time values are always expressed in terms of real earth-seconds */ /* Time values are always expressed in terms of real earth-seconds */
Object.assign(time, { Object.assign(time, {
hour2minute() { hour2minute() {
@@ -490,7 +492,7 @@ Object.defineProperty(Object.prototype, "obscure", {
}); });
Object.defineProperty(Object.prototype, "mixin", { Object.defineProperty(Object.prototype, "mixin", {
value: function (obj) { value: function mixin(obj) {
if (typeof obj === "string") obj = use(obj); if (typeof obj === "string") obj = use(obj);
if (obj) Object.mixin(this, obj); if (obj) Object.mixin(this, obj);
@@ -1058,6 +1060,18 @@ Object.defineProperty(Array.prototype, "remove", {
}, },
}); });
Object.defineProperty(Array.prototype, "delete", {
value: function remove(b) {
var idx = this.indexOf(b);
if (idx === -1) return false;
this.splice(idx, 1);
return true;
},
});
Object.defineProperty(Array.prototype, "set", { Object.defineProperty(Array.prototype, "set", {
value: function set(b) { value: function set(b) {
if (this.length !== b.length) return; if (this.length !== b.length) return;
@@ -1195,6 +1209,8 @@ Object.defineProperty(Array.prototype, "forEachRight", {
} }
}); });
var vector = os.use('vector')
Math.lerp = vector.lerp; Math.lerp = vector.lerp;
Math.gcd = vector.gcd; Math.gcd = vector.gcd;
Math.lcm = vector.lcm; Math.lcm = vector.lcm;
@@ -1314,122 +1330,6 @@ Math.randomint = function (max) {
}; };
Math.variate = vector.variate; Math.variate = vector.variate;
/* BOUNDINGBOXES */
var bbox = {};
bbox.overlap = function (box1, box2) {
return box1.l > box2.l && box1.r < box2.r && box1.t < box2.t && box1.b > box2.b;
return box1.l > box2.r || box1.r < box2.l || box1.t > box2.b || box1.b < box2.t;
};
bbox.fromcwh = function (c, wh) {
return {
t: c.y + wh.y / 2,
b: c.y - wh.y / 2,
l: c.x - wh.x / 2,
r: c.x + wh.x / 2,
};
};
bbox.frompoints = function (points) {
var b = { t: 0, b: 0, l: 0, r: 0 };
points.forEach(function (x) {
if (x.y > b.t) b.t = x.y;
if (x.y < b.b) b.b = x.y;
if (x.x > b.r) b.r = x.x;
if (x.x < b.l) b.l = x.x;
});
return b;
};
bbox.topoints = function (bb) {
return [
[bb.l, bb.t],
[bb.r, bb.t],
[bb.r, bb.b],
[bb.l, bb.b],
];
};
bbox.tocwh = function (bb) {
if (!bb) return undefined;
var cwh = {};
var w = bb.r - bb.l;
var h = bb.t - bb.b;
cwh.wh = [w, h];
cwh.c = [bb.l + w / 2, bb.b + h / 2];
return cwh;
};
bbox.towh = function (bb) {
return [bb.r - bb.l, bb.t - bb.b];
};
bbox.pointin = function (bb, p) {
if (bb.t < p.y || bb.b > p.y || bb.l > p.x || bb.r < p.x) return false;
return true;
};
bbox.zero = function (bb) {
var newbb = Object.assign({}, bb);
newbb.r -= newbb.l;
newbb.t -= newbb.b;
newbb.b = 0;
newbb.l = 0;
return newbb;
};
bbox.move = function (bb, pos) {
var newbb = Object.assign({}, bb);
newbb.t += pos.y;
newbb.b += pos.y;
newbb.l += pos.x;
newbb.r += pos.x;
return newbb;
};
bbox.moveto = function (bb, pos) {
bb = bbox.zero(bb);
return bbox.move(bb, pos);
};
bbox.expand = function (oldbb, x) {
if (!oldbb || !x) return;
var bb = {};
Object.assign(bb, oldbb);
if (bb.t < x.t) bb.t = x.t;
if (bb.r < x.r) bb.r = x.r;
if (bb.b > x.b) bb.b = x.b;
if (bb.l > x.l) bb.l = x.l;
return bb;
};
bbox.blwh = function (bl, wh) {
return {
b: bl.y,
l: bl.x,
r: bl.x + wh.x,
t: bl.y + wh.y,
};
};
bbox.blwh.doc = "Bounding box from (bottom left, width height)";
bbox.fromobjs = function (objs) {
var bb = objs[0].boundingbox;
objs.forEach(function (obj) {
bb = bbox.expand(bb, obj.boundingbox);
});
return bb;
};
/* VECTORS */ /* VECTORS */
var Vector = {}; var Vector = {};
Vector.length = vector.length; Vector.length = vector.length;
@@ -1563,11 +1463,104 @@ lodash.get = function (obj, path, defValue) {
return result === undefined ? defValue : result; return result === undefined ? defValue : result;
}; };
function deep_copy(from) {
return json.decode(json.encode(from));
}
function valdiff(from, to) {
if (typeof from !== typeof to) return from;
if (typeof from === "function") return undefined;
if (typeof from === "undefined") return undefined;
if (typeof from === "number") {
return to;
return undefined;
}
if (typeof from === "object") return ediff(from, to);
if (from !== to) return to;
return undefined;
}
function ediff(from, to) {
var ret = {};
if (!to)
// return ediff(from, {});
return deep_copy(from);
Object.entries(from).forEach(function ([key, v]) {
if (typeof v === "function") return;
if (typeof v === "undefined") return;
if (Array.isArray(v)) {
if (!Array.isArray(to[key]) || v.length !== to[key].length) {
var r = ediff(v, []);
if (r) ret[key] = Object.values(r);
return;
}
var diff = ediff(from[key], to[key]);
if (diff && !Object.empty(diff)) ret[key] = Object.values(ediff(v, []));
return;
}
if (typeof v === "object" && v !== null) {
var diff = ediff(v, to[key]);
if (diff && !Object.empty(diff)) ret[key] = diff;
return;
}
if (typeof v === "number" || v === null) {
if (!isFinite(v)) v = null; // Squash infinity to null
if (v !== to[key]) ret[key] = v;
return;
}
if (!to || v !== to[key]) ret[key] = v;
});
if (Object.empty(ret)) return undefined;
return ret;
}
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) {
var same = [];
if (!to) return same;
if (typeof to !== "object") {
console.warn("'To' must be an object. Got " + to);
return same;
}
Object.keys(from).forEach(function (k) {
if (Object.isObject(from[k])) {
samediff(from[k], to[k]);
return;
}
// if (Array.isArray(from[k])) {
// var d = valdiff(from[k], to[k]);
// if (!d)
// }
var d = valdiff(from[k], to[k]);
if (!d) delete from[k];
});
return same;
}
samediff.doc = "Given a from and to object, returns an array of keys that are the same on from as on to.";
return { return {
convert, convert,
time, time,
Vector, Vector,
bbox,
yaml, yaml,
lodash, lodash,
}; };

View File

@@ -1,780 +0,0 @@
var component = {};
function make_point_obj(o, p) {
return {
pos: p,
move(d) {
d = o.gameobject.dir_world2this(d);
p.x += d.x;
p.y += d.y;
},
sync: o.sync.bind(o),
};
};
/* an anim is simply an array of images */
/* an anim set is like this
frog = {
walk: [],
hop: [],
...etc
}
*/
if (!globalThis.sprite_qt) globalThis.sprite_qt = os.make_rtree();
//globalThis.sprite_qt = os.make_quadtree({x:-2000,y:2000,w:4000,h:4000});
var sprite = {
image: undefined,
get diffuse() { return this.image; },
set diffuse(x) {},
set color(x) {
this._sprite.color = x;
},
get color() { return this._sprite.color; },
anim_speed: 1,
play(str, loop = true, reverse = false, fn) {
if (!this.animset) {
fn?.();
return;
}
if (typeof str === 'string') {
if (!this.animset[str]) {
fn?.();
return;
}
this.anim = this.animset[str];
}
var playing = this.anim;
var self = this;
var stop;
this.del_anim?.();
self.del_anim = function () {
self.del_anim = undefined;
self = undefined;
advance = undefined;
stop?.();
};
var f = 0;
if (reverse) f = playing.frames.length - 1;
function advance(time) {
if (!self) return;
if (!self.gameobject) return;
var done = false;
if (reverse) {
f = (((f - 1) % playing.frames.length) + playing.frames.length) % playing.frames.length;
if (f === playing.frames.length - 1) done = true;
} else {
f = (f + 1) % playing.frames.length;
if (f === 0) done = true;
}
self.image = playing.frames[f];
if (done) {
// notify requestor
fn?.();
if (!loop) {
self?.stop();
return;
}
}
return playing.frames[f].time/self.anim_speed;
}
stop = self.gameobject.delay(advance, playing.frames[f].time/self.anim_speed);
advance();
},
stop() {
this.del_anim?.();
},
set path(p) {
var image = game.texture(p);
if (!image) {
console.warn(`Could not find image ${p}.`);
return;
}
this._p = p;
this.stop();
if (image.texture)
this.image = image;
else if (image.frames) {
// It's an animation
this.anim = image;
this.image = image.frames[0];
this.animset = [this.anim]
this.play();
} else {
// Maybe an animset; try to grab the first one
for (var anim in image) {
if (image[anim].frames) {
this.anim = image[anim];
this.image = image[anim].frames[0];
this.animset = image;
this.play();
break;
}
}
}
this.transform.scale = [this.image.texture.width, this.image.texture.height];
this._sprite.set_image(this.image);
},
get path() {
return this._p;
},
kill: function kill() {
this.del_anim?.();
this.anim = undefined;
this.gameobject = undefined;
sprite_qt.remove(this._sprite)
},
anchor: [0, 0],
set layer(v) { this._sprite.layer = v; },
get layer() { return this._sprite.layer; },
pick() {
return this;
},
boundingbox() {
var dim = this.dimensions();
dim = dim.scale(this.gameobject.scale);
var realpos = dim.scale(0.5).add(this.pos);
return bbox.fromcwh(realpos, dim);
},
};
sprite.doc = {
path: "Path to the texture.",
color: "Color to mix with the sprite.",
pos: "The offset position of the sprite, relative to its entity.",
};
sprite.setanchor = function (anch) {
var off = [0, 0];
switch (anch) {
case "ll":
break;
case "lm":
off = [-0.5, 0];
break;
case "lr":
off = [-1, 0];
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.pos = this.dimensions().scale(off);
};
sprite.inputs = {};
sprite.inputs.kp9 = function () {
this.setanchor("ll");
};
sprite.inputs.kp8 = function () {
this.setanchor("lm");
};
sprite.inputs.kp7 = function () {
this.setanchor("lr");
};
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");
};
var changed_ts = [];
function bulkupdate()
{
for (var t of changed_ts) {
var sprite = t.sprite;
sprite_qt.remove(sprite);
sprite.rect = t.torect();
sprite.set_affine(t)
sprite_qt.insert(sprite)
}
// changed_ts.clear();
changed_ts = [];
}
Register.registries.prerender.register(bulkupdate)
function sprite_t_hook()
{
changed_ts.push(this);
}
component.sprite = function (obj) {
var sp = Object.create(sprite);
sp.gameobject = obj;
sp.transform = os.make_transform();
sp.transform.parent = obj.transform;
sp.transform.change_hook = sprite_t_hook;
var msp = os.make_sprite();
sp._sprite = msp;
msp.color = Color.white;
sp.transform.sprite = msp; // or msp
return sp;
};
return {component};
Object.mixin(os.make_seg2d(), {
sync() {
this.set_endpoints(this.points[0], this.points[1]);
},
});
var collider2d = {};
collider2d.inputs = {};
collider2d.inputs["M-s"] = function () {
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"].doc = "Toggle if this collider is enabled.";
Object.mix(os.make_poly2d(), {
boundingbox() {
return bbox.frompoints(this.spoints());
},
/* EDITOR */
spoints() {
var spoints = this.points.slice();
if (this.flipx) {
spoints.forEach(function (x) {
var newpoint = x.slice();
newpoint.x = -newpoint.x;
spoints.push(newpoint);
});
}
if (this.flipy) {
spoints.forEach(function (x) {
var newpoint = x.slice();
newpoint.y = -newpoint.y;
spoints.push(newpoint);
});
}
return spoints;
},
gizmo() {
this.spoints().forEach(x => render.point(this.gameobject.this2screen(x), 3, Color.green));
this.points.forEach((x, i) => render.coordinate(this.gameobject.this2screen(x)));
},
pick(pos) {
if (!Object.hasOwn(this, "points")) this.points = deep_copy(this.__proto__.points);
var i = Gizmos.pick_gameobject_points(pos, this.gameobject, this.points);
var p = this.points[i];
if (p) return make_point_obj(this, p);
return undefined;
},
});
function pointscaler(x) {
if (typeof x === "number") return;
this.points = this.points.map(p => p.mult(x));
}
Object.mixin(os.make_poly2d(), {
sync() {
this.setverts(this.points);
},
grow: pointscaler,
});
var polyinputs = Object.create(collider2d.inputs);
os.make_poly2d().inputs = polyinputs;
polyinputs = {};
polyinputs.f10 = function () {
this.points = Math.sortpointsccw(this.points);
};
polyinputs.f10.doc = "Sort all points to be CCW order.";
polyinputs["C-lm"] = function () {
this.points.push(this.gameobject.world2this(input.mouse.worldpos()));
};
polyinputs["C-lm"].doc = "Add a point to location of mouse.";
polyinputs.lm = function () {};
polyinputs.lm.released = function () {};
polyinputs["C-M-lm"] = function () {
var idx = Math.grab_from_points(
input.mouse.worldpos(),
this.points.map(p => this.gameobject.this2world(p)),
25,
);
if (idx === -1) return;
this.points.splice(idx, 1);
};
polyinputs["C-M-lm"].doc = "Remove point under mouse.";
polyinputs["C-b"] = function () {
this.points = this.spoints;
this.flipx = false;
this.flipy = false;
};
polyinputs["C-b"].doc = "Freeze mirroring in place.";
var edge2d = {
dimensions: 2,
thickness: 1,
/* if type === -1, point to point */
type: Spline.type.catmull,
C: 1 /* when in bezier, continuity required. 0, 1 or 2. */,
looped: false,
angle: 0.5 /* smaller for smoother bezier */,
elasticity: 0,
friction: 0,
sync() {
var ppp = this.sample();
this.segs ??= [];
var count = ppp.length - 1;
this.segs.length = count;
for (var i = 0; i < count; i++) {
this.segs[i] ??= os.make_seg2d(this.body);
this.segs[i].set_endpoints(ppp[i], ppp[i + 1]);
this.segs[i].set_neighbors(ppp[i], ppp[i + 1]);
this.segs[i].radius = this.thickness;
this.segs[i].elasticity = this.elasticity;
this.segs[i].friction = this.friction;
this.segs[i].collide = this.collide;
}
},
flipx: false,
flipy: false,
hollow: false,
hollowt: 0,
spoints() {
if (!this.points) return [];
var spoints = this.points.slice();
if (this.flipx) {
if (Spline.is_bezier(this.type)) spoints.push(Vector.reflect_point(spoints.at(-2), spoints.at(-1)));
for (var i = spoints.length - 1; i >= 0; i--) {
var newpoint = spoints[i].slice();
newpoint.x = -newpoint.x;
spoints.push(newpoint);
}
}
if (this.flipy) {
if (Spline.is_bezier(this.type)) spoints.push(Vector.reflect(point(spoints.at(-2), spoints.at(-1))));
for (var i = spoints.length - 1; i >= 0; i--) {
var newpoint = spoints[i].slice();
newpoint.y = -newpoint.y;
spoints.push(newpoint);
}
}
if (this.hollow) {
var hpoints = vector.inflate(spoints, this.hollowt);
if (hpoints.length === spoints.length) return spoints;
var arr1 = hpoints.filter(function (x, i) {
return i % 2 === 0;
});
var arr2 = hpoints.filter(function (x, i) {
return i % 2 !== 0;
});
return arr1.concat(arr2.reverse());
}
if (this.looped) spoints = spoints.wrapped(1);
return spoints;
},
post() {
this.points = [];
},
sample() {
var spoints = this.spoints();
if (spoints.length === 0) return [];
if (this.type === -1) {
if (this.looped) spoints.push(spoints[0]);
return spoints;
}
if (this.type === Spline.type.catmull) {
if (this.looped) spoints = Spline.catmull_loop(spoints);
else spoints = Spline.catmull_caps(spoints);
return Spline.sample_angle(this.type, spoints, this.angle);
}
if (this.looped && Spline.is_bezier(this.type)) spoints = Spline.bezier_loop(spoints);
return Spline.sample_angle(this.type, spoints, this.angle);
},
boundingbox() {
return bbox.frompoints(this.points.map(x => x.scale(this.gameobject.scale)));
},
/* EDITOR */
gizmo() {
if (this.type === Spline.type.catmull || this.type === -1) {
this.spoints().forEach(x => render.point(this.gameobject.this2screen(x), 3, Color.teal));
this.points.forEach((x, i) => render.coordinate(this.gameobject.this2screen(x)));
} else {
for (var i = 0; i < this.points.length; i += 3) render.coordinate(this.gameobject.this2screen(this.points[i]), 1, Color.teal);
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]), 1, Color.green);
render.line([this.gameobject.this2screen(this.points[i - 1]), this.gameobject.this2screen(this.points[i])], Color.yellow);
render.line([this.gameobject.this2screen(this.points[i + 1]), this.gameobject.this2screen(this.points[i + 2])], Color.yellow);
}
}
},
finish_center(change) {
this.points = this.points.map(function (x) {
return x.sub(change);
});
},
pick(pos) {
var i = Gizmos.pick_gameobject_points(pos, this.gameobject, this.points);
var p = this.points[i];
if (!p) return undefined;
if (Spline.is_catmull(this.type) || this.type === -1) return make_point_obj(this, p);
var that = this.gameobject;
var me = this;
if (p) {
var o = {
pos: p,
sync: me.sync.bind(me),
};
if (Spline.bezier_is_handle(this.points, i))
o.move = function (d) {
d = that.dir_world2this(d);
p.x += d.x;
p.y += d.y;
Spline.bezier_cp_mirror(me.points, i);
};
else
o.move = function (d) {
d = that.dir_world2this(d);
p.x += d.x;
p.y += d.y;
var pp = Spline.bezier_point_handles(me.points, i);
pp.forEach(ph => (me.points[ph] = me.points[ph].add(d)));
};
return o;
}
},
rm_node(idx) {
if (idx < 0 || idx >= this.points.length) return;
if (Spline.is_catmull(this.type)) this.points.splice(idx, 1);
if (Spline.is_bezier(this.type)) {
assert(Spline.bezier_is_node(this.points, idx), "Attempted to delete a bezier handle.");
if (idx === 0) this.points.splice(idx, 2);
else if (idx === this.points.length - 1) this.points.splice(this.points.length - 2, 2);
else this.points.splice(idx - 1, 3);
}
},
add_node(pos) {
pos = this.gameobject.world2this(pos);
var idx = 0;
if (Spline.is_catmull(this.type) || this.type === -1) {
if (this.points.length >= 2) idx = physics.closest_point(pos, this.points, 400);
if (idx === this.points.length) this.points.push(pos);
else this.points.splice(idx, 0, pos);
}
if (Spline.is_bezier(this.type)) {
idx = physics.closest_point(pos, Spline.bezier_nodes(this.points), 400);
if (idx < 0) return;
if (idx === 0) {
this.points.unshift(pos.slice(), pos.add([-100, 0]), Vector.reflect_point(this.points[1], this.points[0]));
return;
}
if (idx === Spline.bezier_node_count(this.points)) {
this.points.push(Vector.reflect_point(this.points.at(-2), this.points.at(-1)), pos.add([-100, 0]), pos.slice());
return;
}
idx = 2 + (idx - 1) * 3;
var adds = [pos.add([100, 0]), pos.slice(), pos.add([-100, 0])];
this.points.splice(idx, 0, ...adds);
}
},
pick_all() {
var picks = [];
this.points.forEach(x => picks.push(make_point_obj(this, x)));
return picks;
},
};
component.edge2d = function (obj) {
// if (!obj.body) obj.rigidify();
var edge = Object.create(edge2d);
edge.body = obj.body;
return edge;
};
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.h = function () {
this.hollow = !this.hollow;
};
edge2d.inputs.h.doc = "Toggle hollow.";
edge2d.inputs["C-g"] = function () {
if (this.hollowt > 0) this.hollowt--;
};
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"].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"].doc = "Decrease spline thickness.";
edge2d.inputs["M-v"].rep = true;
edge2d.inputs["C-y"] = function () {
this.points = this.spoints();
this.flipx = false;
this.flipy = false;
this.hollow = false;
};
edge2d.inputs["C-y"].doc = "Freeze mirroring,";
edge2d.inputs["M-b"] = function () {
this.thickness++;
};
edge2d.inputs["M-b"].doc = "Increase spline thickness.";
edge2d.inputs["M-b"].rep = true;
edge2d.inputs.plus = function () {
if (this.angle <= 1) {
this.angle = 1;
return;
}
this.angle *= 0.9;
};
edge2d.inputs.plus.doc = "Increase the number of samples of this spline.";
edge2d.inputs.plus.rep = true;
edge2d.inputs.minus = function () {
this.angle *= 1.1;
};
edge2d.inputs.minus.doc = "Decrease the number of samples on this spline.";
edge2d.inputs.minus.rep = true;
edge2d.inputs["C-r"] = function () {
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"].doc = "Toggle spline being looped.";
edge2d.inputs["C-c"] = function () {
switch (this.type) {
case Spline.type.bezier:
this.points = Spline.bezier2catmull(this.points);
break;
}
this.type = Spline.type.catmull;
};
edge2d.inputs["C-c"].doc = "Set type of spline to catmull-rom.";
edge2d.inputs["C-b"] = function () {
switch (this.type) {
case Spline.type.catmull:
this.points = Spline.catmull2bezier(Spline.catmull_caps(this.points));
break;
}
this.type = Spline.type.bezier;
};
edge2d.inputs["C-o"] = function () {
this.type = -1;
};
edge2d.inputs["C-o"].doc = "Set spline to linear.";
edge2d.inputs["C-M-lm"] = function () {
if (Spline.is_catmull(this.type)) {
var idx = Math.grab_from_points(
input.mouse.worldpos(),
this.points.map(p => this.gameobject.this2world(p)),
25,
);
if (idx === -1) return;
} else {
}
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-lm"] = function () {
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 () {
var idx = -1;
if (Spline.is_catmull(this.type))
idx = Math.grab_from_points(
input.mouse.worldpos(),
this.points.map(p => this.gameobject.this2world(p)),
25,
);
else {
var nodes = Spline.bezier_nodes(this.points);
idx = Math.grab_from_points(
input.mouse.worldpos(),
nodes.map(p => this.gameobject.this2world(p)),
25,
);
idx *= 3;
}
this.rm_node(idx);
};
edge2d.inputs["C-M-lm"].doc = "Remove point from the spline.";
edge2d.inputs.lm = function () {};
edge2d.inputs.lm.released = function () {};
edge2d.inputs.lb = function () {
var np = [];
this.points.forEach(function (c) {
np.push(Vector.rotate(c, Math.deg2rad(-1)));
});
this.points = np;
};
edge2d.inputs.lb.doc = "Rotate the points CCW.";
edge2d.inputs.lb.rep = true;
edge2d.inputs.rb = function () {
var np = [];
this.points.forEach(function (c) {
np.push(Vector.rotate(c, Math.deg2rad(1)));
});
this.points = np;
};
edge2d.inputs.rb.doc = "Rotate the points CW.";
edge2d.inputs.rb.rep = true;
/* CIRCLE */
function shape_maker(maker) {
return function (obj) {
// if (!obj.body) obj.rigidify();
return maker(obj.body);
};
}
component.circle2d = shape_maker(os.make_circle2d);
component.poly2d = shape_maker(os.make_poly2d);
component.seg2d = shape_maker(os.make_seg2d);
Object.mix(os.make_circle2d(), {
boundingbox() {
return bbox.fromcwh(this.offset, [this.radius, this.radius]);
},
set scale(x) {
this.radius = x;
},
get scale() {
return this.radius;
},
get pos() {
return this.offset;
},
set pos(x) {
this.offset = x;
},
grow(x) {
if (typeof x === "number") this.scale *= x;
else if (typeof x === "object") this.scale *= x[0];
},
});
return { component };

View File

@@ -1,3 +1,6 @@
var render = use('render')
var debug = os.use('debug')
debug.build = function (fn) { debug.build = function (fn) {
if (!debug.show) return; if (!debug.show) return;
fn(); fn();
@@ -20,6 +23,8 @@ debug.fn_break = function (fn, obj = globalThis) {
obj[fn.name] = newfn; obj[fn.name] = newfn;
}; };
var sim = use('sim')
debug.draw_phys = false; debug.draw_phys = false;
debug.draw_bb = false; debug.draw_bb = false;
debug.draw_gizmos = false; debug.draw_gizmos = false;
@@ -59,18 +64,6 @@ debug.draw = function () {
render.text(sim.playing() ? "PLAYING" : sim.stepping() ? "STEP" : sim.paused() ? "PAUSED; EDITING" : "EDIT", [0, 0], 1); render.text(sim.playing() ? "PLAYING" : sim.stepping() ? "STEP" : sim.paused() ? "PAUSED; EDITING" : "EDIT", [0, 0], 1);
}; };
var assert = function (op, str = `assertion failed [value '${op}']`) {
if (!op) console.panic(str);
};
var Gizmos = {
pick_gameobject_points(worldpos, gameobject, points) {
var idx = Math.grab_from_points(worldpos, points.map(gameobject.this2world, gameobject), 25);
if (idx === -1) return undefined;
return idx;
},
};
/* 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.inputs.f1 = function () {
@@ -267,8 +260,5 @@ debug.try = function(fn)
} }
} }
return { return {debug}
debug,
Gizmos,
assert,
};

View File

@@ -1,99 +0,0 @@
function deep_copy(from) {
return json.decode(json.encode(from));
}
function valdiff(from, to) {
if (typeof from !== typeof to) return from;
if (typeof from === "function") return undefined;
if (typeof from === "undefined") return undefined;
if (typeof from === "number") {
return to;
return undefined;
}
if (typeof from === "object") return ediff(from, to);
if (from !== to) return to;
return undefined;
}
function ediff(from, to) {
var ret = {};
if (!to)
// return ediff(from, {});
return deep_copy(from);
Object.entries(from).forEach(function ([key, v]) {
if (typeof v === "function") return;
if (typeof v === "undefined") return;
if (Array.isArray(v)) {
if (!Array.isArray(to[key]) || v.length !== to[key].length) {
var r = ediff(v, []);
if (r) ret[key] = Object.values(r);
return;
}
var diff = ediff(from[key], to[key]);
if (diff && !Object.empty(diff)) ret[key] = Object.values(ediff(v, []));
return;
}
if (typeof v === "object" && v !== null) {
var diff = ediff(v, to[key]);
if (diff && !Object.empty(diff)) ret[key] = diff;
return;
}
if (typeof v === "number" || v === null) {
if (!isFinite(v)) v = null; // Squash infinity to null
if (v !== to[key]) ret[key] = v;
return;
}
if (!to || v !== to[key]) ret[key] = v;
});
if (Object.empty(ret)) return undefined;
return ret;
}
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) {
var same = [];
if (!to) return same;
if (typeof to !== "object") {
console.warn("'To' must be an object. Got " + to);
return same;
}
Object.keys(from).forEach(function (k) {
if (Object.isObject(from[k])) {
samediff(from[k], to[k]);
return;
}
// if (Array.isArray(from[k])) {
// var d = valdiff(from[k], to[k]);
// if (!d)
// }
var d = valdiff(from[k], to[k]);
if (!d) delete from[k];
});
return same;
}
samediff.doc = "Given a from and to object, returns an array of keys that are the same on from as on to.";
return {
deep_copy,
ediff,
samediff,
};

View File

@@ -85,13 +85,13 @@ Resources.replstrs = function replstrs(path) {
var stem = path.dir(); var stem = path.dir();
if (!console.enabled) script = Resources.rm_fn(/console\.(spam|info|warn|error)/, script); if (!console.enabled) script = Resources.rm_fn(/console\.(spam|info|warn|error)/, script);
if (!profile.enabled) script = Resources.rm_fn(/profile\.(cache|frame|endcache|endframe)/, script); /* if (!profile.enabled) 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);
script = Resources.rm_fn(/debug\.(build|fn_break)/, script); script = Resources.rm_fn(/debug\.(build|fn_break)/, script);
} }
*/
script = script.replace(regexp, function (str) { script = script.replace(regexp, function (str) {
var newstr = Resources.replpath(str.trimchr('"'), path); var newstr = Resources.replpath(str.trimchr('"'), path);
return `"${newstr}"`; return `"${newstr}"`;
@@ -188,15 +188,6 @@ function find_ext(file, ext) {
return find; return find;
} }
var hashhit = 0;
var hashmiss = 0;
globalThis.hashifier = {};
hashifier.stats = function()
{
}
Object.defineProperty(Function.prototype, "hashify", { Object.defineProperty(Function.prototype, "hashify", {
value: function () { value: function () {
var hash = new Map(); var hash = new Map();
@@ -227,6 +218,8 @@ Resources.find_font = function(file, root = "") {
return find_ext(file, Resources.fonts, root); return find_ext(file, Resources.fonts, root);
}.hashify(); }.hashify();
globalThis.console = os.use('console')
console.transcript = ""; console.transcript = "";
console.say = function (msg) { console.say = function (msg) {
console.print(msg); console.print(msg);
@@ -242,6 +235,8 @@ console.rec = function(category, priority, line, file, msg)
return `${file}:${line}: [${category} ${priority}]: ${msg}` + "\n"; return `${file}:${line}: [${category} ${priority}]: ${msg}` + "\n";
} }
var io = os.use('io')
var logfile = io.open('.prosperon/log.txt') var logfile = io.open('.prosperon/log.txt')
//logfile.buffer(1024*1024) // 1MB buffer //logfile.buffer(1024*1024) // 1MB buffer
@@ -304,6 +299,10 @@ console.panic = function (e) {
os.quit(); os.quit();
}; };
console.assert = function (op, str = `assertion failed [value '${op}']`) {
if (!op) console.panic(str);
};
os.on('uncaught_exception', function(e) { console.error(e); }); os.on('uncaught_exception', function(e) { console.error(e); });
console.stdout_lvl = 1; console.stdout_lvl = 1;
@@ -323,27 +322,6 @@ console.doc = {
globalThis.global = globalThis; globalThis.global = globalThis;
var use_cache = {};
globalThis.use = function use(file) {
file = Resources.find_script(file);
if (use_cache[file]) {
var ret = use_cache[file]();
return ret;
}
var script = Resources.replstrs(file);
var fnname = file.replace(/[^a-zA-Z0-9_$]/g, "_");
script = `(function ${fnname}() { var self = this; ${script}; } )`;
var fn = os.eval(file, script);
use_cache[file] = fn;
var ret = fn();
return ret;
};
var tmpslurp = io.slurp; var tmpslurp = io.slurp;
io.slurp = function slurp(path) io.slurp = function slurp(path)
{ {
@@ -360,6 +338,25 @@ io.slurpbytes = function(path)
return ret; return ret;
} }
function bare_use(file) {
try {
var script = io.slurp(file);
if (!script) return;
var fnname = file.replace(/[^a-zA-Z0-9_$]/g, "_");
script = `(function ${fnname}() { var self = this; ${script}; })`;
Object.assign(globalThis, os.eval(file, script)());
} catch(e) {
console.log(file)
console.error(e);
throw e
}
}
bare_use("core/scripts/base.js");
var game = os.use('game')
var ignore = io.slurp('.prosperonignore').split('\n'); var ignore = io.slurp('.prosperonignore').split('\n');
var allpaths; var allpaths;
var tmpglob = io.glob; var tmpglob = io.glob;
@@ -430,51 +427,387 @@ function matchPath(pathParts, patternParts) {
return patternIndex === patternParts.length; return patternIndex === patternParts.length;
} }
function stripped_use(file, script) { globalThis.prosperon = os.use('prosperon')
file = Resources.find_script(file);
if (use_cache[file]) { prosperon.SIGABRT = function()
var ret = use_cache[file](); {
return ret; console.error(new Error('SIGABRT'));
} os.exit(1);
script ??= Resources.replstrs(file);
script = `(function () { var self = this; ${script}; })`;
var fn = os.eval(file, script);
var ret = fn();
return ret;
} }
function bare_use(file) { prosperon.SIGSEGV = function()
{
console.error(new Error('SIGSEGV'));
os.exit(1);
}
function add_timer(obj, fn, seconds)
{
var timers = obj.timers;
var stop = function () {
timers.delete(stop);
timer.fn = undefined;
timer = undefined;
};
function execute() {
if (fn)
timer.remain = fn(stop.seconds);
if (!timer) return
if (!timer.remain)
stop();
else
stop.seconds = timer.remain;
}
var timer = os.make_timer(execute);
timer.remain = seconds;
stop.remain = seconds;
stop.seconds = seconds;
timers.push(stop);
return stop;
}
var actor = {};
var use = function use(file) {
try { try {
var script = io.slurp(file); var par = script_fn(file)
if (!script) return; if (par?.module_ret)
var fnname = file.replace(/[^a-zA-Z0-9_$]/g, "_"); return par.module_ret
script = `(function ${fnname}() { var self = this; ${script}; })`;
Object.assign(globalThis, os.eval(file, script)());
} catch(e) { } catch(e) {
console.log(file) console.error(e)
console.log(e); }
try {
return os.use('./lib' + file + '.so')
} catch(e) { console.error(e)}
return os.use(file)
}.hashify()
var script_fn = function script_fn(path) {
var file = Resources.find_script(path)
if (!file) throw new Error(`File ${path} could not be found`)
var content = Resources.replstrs(file);
var parsed = parse_file(content)
var module_name = file.name()
if (parsed.module) {
var mod_script = `(function setup_${module_name}_module(){ var self = this; var $ = this; ${parsed.module}})`;
var module_fn = os.eval(file, mod_script)
var module_ret = module_fn.call();
if (module_ret === undefined || module_ret === null)
throw new Error(`Module ${module_name} must return a value`);
parsed.module_fn = module_fn;
parsed.module_ret = module_ret;
} else
parsed.module_ret = {}
if (parsed.program) {
var prog_script = `(function use_${module_name}() { var self = this; var $ = this.__proto__; ${parsed.program}})`;
parsed.prog_fn = os.eval(file, prog_script);
}
return parsed;
}.hashify()
function parse_file(content) {
if (!content) return {}
var parts = content.split(/\n\s*---\s*\n/)
if (parts.length === 1) {
var part = parts[0]
if (part.match(/return\s+[^;]+;?\s*$/))
return { module: part }
return { program: part }
}
var module = parts[0]
if (!module.match(/return\s+[^;]+;?\s*$/))
throw new Error("Module section must end with a return statement")
var pad = '\n'.repeat(module.split('\n').length+2) // add 2 from the split search
return {
module,
program: pad+parts[1]
} }
} }
profile.enabled = true; actor.__stats = function () {
console.enabled = true; var total = 0;
debug.enabled = true; var stats = {};
search.all_objects(obj => {
bare_use("core/scripts/base.js"); if (!actor_spawns[obj._file]) return;
bare_use("core/scripts/profile.js"); stats[obj._file] ??= 0;
stats[obj._file]++;
prosperon.release = function () { total++;
profile.enabled = false; });
console.enabled = false; /* for (var i in actor_spawns) {
debug.enabled = false; stats[i] = actor_spawns[i].length;
total += stats[i];
}*/
// stats.total = total;
return stats;
}; };
//bare_use("core/scripts/preconfig.js"); actor.hotreload = function hotreload(file) {
var script = Resources.replstrs(file);
script = `(function() { var self = this; var $ = this.__proto__;${script};})`;
var fn = os.eval(file, script);
/*
for (var obj of actor_spawns[file]) {
var a = obj;
a.timers.forEachRight(t=>t());
a.timers = [];
var save = json.decode(json.encode(a));
fn.call(a);
Object.merge(a, save);
Register.check_registers(a);
}
*/
};
if (!profile.enabled) use = stripped_use; //////////
/// EVENT
/////////
var Event = {
events: {},
observe(name, obj, fn) {
this.events[name] ??= [];
this.events[name].push([obj, fn]);
},
unobserve(name, obj) {
this.events[name] = this.events[name].filter(x => x[0] !== obj);
},
rm_obj(obj) {
Object.keys(this.events).forEach(name => Event.unobserve(name, obj));
},
notify(name, ...args) {
if (!this.events[name]) return;
this.events[name].forEach(function (x) {
x[1].call(x[0], ...args);
});
},
};
//////////////////
///////REGISTRANT
/////////////////
/*
Factory for creating registries. Register one with 'X.register',
which returns a function that, when invoked, cancels the registry.
*/
var Register = {
registries: [],
add_cb(name) {
var n = {};
var fns = [];
n.register = function (fn, oname) {
if (!(fn instanceof Function)) return;
var dofn = function (...args) {
fn(...args);
};
Object.defineProperty(dofn, 'name', {value:`do_${oname}`});
var left = 0;
var right = fns.length - 1;
dofn.layer = fn.layer;
dofn.layer ??= 0;
while (left <= right) {
var mid = Math.floor((left + right) / 2);
if (fns[mid] === dofn.layer) {
left = mid;
break;
} else if (fns[mid].layer < dofn.layer) left = mid + 1;
else right = mid - 1;
}
fns.splice(left, 0, dofn);
return function () {
fns.remove(dofn);
};
};
prosperon[name] = function (...args) {
// tracy.fiber_enter(vector.fib);
fns.forEach(fn => {
fn(...args)
});
// tracy.fiber_leave(vector.fib);
};
Object.defineProperty(prosperon[name], 'name', {value:name});
prosperon[name].fns = fns;
n.clear = function () {
fns = [];
};
Register[name] = n;
Register.registries[name] = n;
return n;
},
};
Register.pull_registers = function pull_registers(obj)
{
var reggies = [];
for (var reg in Register.registries) {
if (typeof obj[reg] === "function")
reggies.push(reg);
}
return reggies;
}
Register.register_obj = function register_obj(obj, reg)
{
var fn = obj[reg].bind(obj);
fn.layer = obj[reg].layer;
var name = obj.ur ? obj.ur.name : obj.toString();
obj.timers.push(Register.registries[reg].register(fn, name));
if (!obj[reg].name) Object.defineProperty(obj[reg], 'name', {value:`${obj._file}_${reg}`});
}
Register.check_registers = function check_registers(obj) {
if (obj.__reggies) {
if (obj.__reggies.length == 0) return;
// fast path
for (var reg of obj.__reggies)
Register.register_obj(obj,reg);
return;
}
for (var reg in Register.registries) {
if (typeof obj[reg] === "function")
Register.register_obj(obj,reg);
}
/* for (var k in obj) {
if (!k.startsWith("on_")) continue;
var signal = k.fromfirst("on_");
Event.observe(signal, obj, obj[k]);
}*/
};
Register.add_cb("appupdate");
Register.add_cb("update").doc = "Called once per frame.";
Register.add_cb("physupdate");
Register.add_cb("gui");
Register.add_cb("hud");
Register.add_cb("draw");
Register.add_cb("imgui");
Register.add_cb("app");
Register.add_cb("prerender");
actor.spawn = function spawn(script, config, callback) {
var prog
if (!script) {
prog = Object.create(actor)
if (callback) callback(prog)
return prog
}
prog = script_fn(script);
if (!prog.prog_fn) throw new Error(`Script ${script} is not an actor script or has no actor component`)
var underling;
prog.module_ret.__proto__ = actor;
underling = Object.create(prog.module_ret);
underling.overling = this;
underling.timers = []
underling.underlings = new Set()
if (callback) callback(underling)
try{
prog.prog_fn.call(underling)
} catch(e) { console.error(e); throw e}
if (typeof config === 'object') Object.assign(underling,config);
if (!underling.__reggies)
underling.__proto__.__reggies = Register.pull_registers(underling)
Register.check_registers(underling);
if (underling.awake) underling.awake();
this.underlings.add(underling);
if (underling.tag)
search.tag_add(underling.tag, underling)
return underling;
};
actor.spawn.doc = `Create a new actor, using this actor as the overling, initializing it with 'script' and with data (as a JSON or Nota file) from 'config'.`;
actor.clear = function actor_clear()
{
this.underlings.forEach(p => p.kill())
this.underlings.clear()
}
var input = use('input')
actor.kill = function kill() {
if (this.__dead__) return;
this.__dead__ = true;
this.timers.forEachRight(t => t())
delete this.timers
input.do_uncontrol(this);
Event.rm_obj(this);
this.clear()
delete this.underlings
this.__dead__ = true;
if (typeof this.garbage === "function") this.garbage();
if (typeof this.then === "function") this.then();
};
actor.kill.doc = `Remove this actor and all its underlings from existence.`;
actor.delay = function (fn, seconds) { add_timer(this, fn, seconds) }
actor.delay.doc = `Call 'fn' after 'seconds' with 'this' set to the actor.`;
actor.interval = function interval(fn, seconds) {
var self = this;
var stop;
var usefn = function () {
fn();
stop = self.delay(usefn, seconds);
};
stop = self.delay(usefn, seconds);
return stop;
};
actor.underlings = new Set()
globalThis.app = actor.spawn()
app.garbage = function () {
os.exit(0);
};
globalThis.world = app;
var search = use('search')
global.mixin("color");
global.mixin("std")
Object.assign(globalThis, use("core/scripts/prosperon.js"));

View File

@@ -1,755 +0,0 @@
globalThis.entityreport = {};
function obj_unique_name(name, obj) {
name = name.replaceAll(".", "_");
if (!(name in obj)) return name;
var t = 1;
var n = name + t;
while (n in obj) {
t++;
n = name + t;
}
return n;
}
function unique_name(list, name = "new_object") {
var str = name.replaceAll(".", "_");
var n = 1;
var t = str;
while (list.indexOf(t) !== -1) {
t = str + n;
n++;
}
return t;
}
var entity = {
drawlayer: -1,
get_comp_by_name(name) {
var comps = [];
for (var c of Object.values(this.components)) if (c.comp === name) comps.push(c);
if (comps.length) return comps;
return undefined;
},
path_from(o) {
var p = this.toString();
var c = this.master;
while (c && c !== o && c !== world) {
p = c.toString() + "." + p;
c = c.master;
}
if (c === world) p = "world." + p;
return p;
},
drawlayer: 0,
full_path() {
return this.path_from(world);
},
clear() {
for (var k in this.objects)
this.objects[k].kill();
this.objects = {};
},
delay(fn, seconds) { prosperon.add_timer(this, fn, seconds); },
cry(file) {
return audio.cry(file);
},
get pos() {
return this.transform.pos;
},
set pos(x) {
this.transform.pos = x;
},
get angle() {
return this.transform.angle;
},
set angle(x) {
this.transform.angle = x;
},
get scale() {
return this.transform.scale;
},
set scale(x) {
this.transform.scale = x;
},
move(vec) {
this.pos = this.pos.add(vec);
},
rotate(x) {
this.transform.rotate([0, 0, -1],x);
},
grow(vec) {
if (typeof vec === "number") vec = [vec, vec];
this.scale = this.scale.map((x, i) => x * vec[i]);
},
/* Reparent 'this' to be 'parent's child */
reparent(parent) {
assert(parent, `Tried to reparent ${this.toString()} to nothing.`);
if (this.master === parent) {
console.warn(`not reparenting ... ${this.master} is the same as ${parent}`);
return;
}
var name = unique_name(Object.keys(parent), this.name);
this.name = name;
this.master?.remove_obj(this);
this.master = parent;
parent.objects[this.guid] = this;
parent[name] = this;
Object.hide(parent, name);
},
remove_obj(obj) {
if (this.objects) delete this.objects[obj.guid];
else console.warn(`Object ${this.guid} has no objects file.`);
delete this[obj.name];
Object.unhide(this, obj.name);
},
aspawn(text, config) {
var e = class_use(text,config, actor, undefined, this)
},
spawn(text, config, callback) {
var ent = class_use(text, config, entity, function (ent) {
ent.transform = os.make_transform();
// ent.guid = prosperon.guid();
ent.components = {};
ent.objects = {};
ent.timers = [];
// ent.ur = {};
// ent.urname = text;
}, this);
/*
if (!text)
ent.ur = emptyur;
else if (typeof text === 'object') {// assume it's an ur
ent.ur = text;
text = ent.ur.text;
config = [ent.ur.data, config].filter(x => x).flat();
}
else {
ent.ur = getur(text, config);
text = ent.ur.text;
config = [ent.ur.data, config];
}
if (typeof config === 'string')
Object.merge(ent, json.decode(Resources.replstrs(config)));
else if (Array.isArray(config))
for (var path of config) {
if (typeof path === 'string') {
console.info(`ingesting ${path} ...`);
Object.merge(ent, json.decode(Resources.replstrs(path)));
}
else if (path instanceof Object)
Object.merge(ent,path);
};
if (typeof text === 'string') {
class_use(
use(text, ent);
}
else if (Array.isArray(text))
for (var path of text) use(path,ent);
*/
// ent.reparent(this);
/* for (var [prop, p] of Object.entries(ent)) {
if (!p) continue;
if (typeof p !== "object") continue;
if (!p.comp) continue;
ent[prop] = component[p.comp](ent);
Object.merge(ent[prop], p);
ent.components[prop] = ent[prop];
}
*/
check_registers(ent);
if (typeof ent.awake === 'function') ent.awake();
if (sim.playing()) {
ent._started = true;
if (typeof ent.start === 'function') ent.start();
}
/* ent._ed = {
selectable: true,
dirty: false,
inst: false,
urdiff: {},
};
*/
// Object.hide(ent, "ur", "components", "objects", "timers", "guid", "master", "guid", "_ed");
/* if (!Object.empty(ent.objects)) {
var o = ent.objects;
delete ent.objects;
ent.objects = {};
for (var i in o) {
console.info(`creating ${i} on ${ent.toString()}`);
var newur = o[i].ur;
delete o[i].ur;
var n = ent.spawn(ur[newur], o[i]);
ent.rename_obj(n.toString(), i);
}
}
*/
if (ent.tag) game.tag_add(ent.tag, ent);
if (callback) callback(ent);
// ent.ur.fresh ??= json.decode(json.encode(ent));
// ent.ur.fresh.objects = {};
// for (var i in ent.objects) ent.ur.fresh.objects[i] = ent.objects[i].instance_obj();
return ent;
},
disable() {
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));
},
screen2this(pos) {
return this.world2this(game.camera.view2world(pos));
},
/* Make a unique object the same as its prototype */
revert() {
Object.merge(this, this.ur.fresh);
},
name: "new_object",
toString() {
return this.name;
},
width() {
var bb = this.boundingbox();
return bb.r - bb.l;
},
height() {
var bb = this.boundingbox();
return bb.t - bb.b;
},
flipx() {
return this.scale.x < 0;
},
flipy() {
return this.scale.y < 0;
},
mirror(plane) {
this.scale = Vector.reflect(this.scale, plane);
},
/* Bounding box of the object in world dimensions */
boundingbox() {
var boxes = [];
boxes.push({
t: 0,
r: 0,
b: 0,
l: 0,
});
for (var key in this.components) {
if ("boundingbox" in this.components[key]) boxes.push(this.components[key].boundingbox());
}
for (var key in this.objects) boxes.push(this.objects[key].boundingbox());
var bb = boxes.shift();
for (var x of boxes) bb = bbox.expand(bb, x);
bb = bbox.move(bb, this.pos);
return bb ? bb : bbox.fromcwh([0, 0], [0, 0]);
},
toJSON() {
return { guid: this.guid };
},
/* The unique components of this object. Its diff. */
json_obj(depth = 0) {
var fresh = this.ur.fresh;
var thiso = json.decode(json.encode(this)); // TODO: SLOW. Used to ignore properties in toJSON of components.
var d = ediff(thiso, fresh);
d ??= {};
fresh.objects ??= {};
var curobjs = {};
for (var o in this.objects) curobjs[o] = this.objects[o].instance_obj();
var odiff = ediff(curobjs, fresh.objects);
if (odiff) d.objects = curobjs;
delete d.pos;
delete d.angle;
delete d.scale;
delete d.velocity;
delete d.angularvelocity;
return d;
},
/* The object needed to store an object as an instance of a master */
instance_obj() {
var t = os.make_transform();
t = this.transform;
t.ur = this.ur.name;
return t;
},
transform() {
var t = {};
t.pos = this.get_pos(this.master).map(x => Math.places(x, 0));
t.angle = Math.places(this.get_angle(this.master), 4);
t.scale = this.get_scale(this.master).map(x => Math.places(x, 2));
return t;
},
dup(diff) {
var n = this.master.spawn(this.ur);
Object.totalmerge(n, this.transform());
return n;
},
kill() {
if (this.__kill) return;
this.__kill = true;
this.timers.forEachRight(x => x());
delete this.timers;
Event.rm_obj(this);
input.do_uncontrol(this);
if (this.master) {
this.master.remove_obj(this);
this.master = undefined;
}
for (var key in this.components) {
this.components[key].kill?.();
this.components[key].gameobject = undefined;
this.components[key].enabled = false;
delete this.components[key];
}
delete this.components;
this.clear();
if (typeof this.stop === 'function') this.stop();
if (typeof this.garbage === "function") this.garbage();
if (typeof this.then === "function") this.then();
game.tag_clear_guid(this.guid);
rmactor(this);
// for (var i in this)
// delete this[i]
},
make_objs(objs) {
for (var prop in objs) {
say(`spawning ${json.encode(objs[prop])}`);
var newobj = this.spawn(objs[prop]);
}
},
rename_obj(name, newname) {
if (!this.objects[name]) {
console.warn(`No object with name ${name}. Could not rename to ${newname}.`);
return;
}
if (name === newname) {
Object.hide(this, name);
return;
}
if (this.objects[newname]) return;
this.objects[newname] = this.objects[name];
this[newname] = this[name];
this[newname].toString = function () {
return newname;
};
Object.hide(this, newname);
delete this.objects[name];
delete this[name];
return this.objects[newname];
},
add_component(comp, data) {
var name = prosperon.guid();
this.components[name] = comp(this);
if (data)
Object.assign(this.components[name], data);
return this.components[name];
},
};
var gameobject = {
check_dirty() {
this._ed.urdiff = this.json_obj();
this._ed.dirty = !Object.empty(this._ed.urdiff);
return; // TODO: IMPLEMENT
var lur = this.master.ur;
if (!lur) return;
var lur = lur.objects[this.toString()];
var d = ediff(this._ed.urdiff, lur);
if (!d || Object.empty(d)) this._ed.inst = true;
else this._ed.inst = false;
},
namestr() {
var s = this.toString();
if (this._ed?.dirty)
if (this._ed.inst) s += "#";
else s += "*";
return s;
},
urstr() {
var str = this.ur.name;
if (this._ed.dirty) str = "*" + str;
return str;
},
/* pin this object to the to object */
pin(to) {
var p = joint.pin(this, to);
},
slide(to, a = [0, 0], b = [0, 0], min = 0, max = 50) {
var p = joint.slide(this, to, a, b, min, max);
p.max_force = 500;
p.break();
},
pivot(to, piv = this.pos) {
var p = joint.pivot(this, to, piv);
},
/* groove is on to, from local points a and b, anchored to this at local anchor */
groove(to, a, b, anchor = [0, 0]) {
var p = joint.groove(to, this, a, b, anchor);
},
damped_spring(to, length = Vector.length(this.pos, to.pos), stiffness = 1, damping = 1) {
var dc = 2 * Math.sqrt(stiffness * this.mass);
var p = joint.damped_spring(this, to, [0, 0], [0, 0], stiffness, damping * dc);
},
damped_rotary_spring(to, angle = 0, stiffness = 1, damping = 1) {
/* calculate actual damping value from the damping ratio */
/* damping = 1 is critical */
var dc = 2 * Math.sqrt(stiffness * this.get_moi()); /* critical damping number */
/* zeta = actual/critical */
var p = joint.damped_rotary(this, to, angle, stiffness, damping * dc);
},
rotary_limit(to, min, max) {
var p = joint.rotary(this, to, Math.turn2rad(min), Math.turn2rad(max));
},
ratchet(to, ratch) {
var phase = this.angle - to.angle;
var p = joint.ratchet(this, to, phase, Math.turn2rad(ratch));
},
gear(to, ratio = 1, phase = 0) {
var phase = this.angle - to.angle;
var p = joint.gear(this, to, phase, ratio);
},
motor(to, rate) {
var p = joint.motor(this, to, rate);
},
set_pos(x, relative = world) {
var newpos = relative.this2world(x);
var move = newpos.sub(this.pos);
this.rpos = newpos;
for (var o of this.objects) o.move(move);
},
set_angle(x, relative = world) {
var newangle = relative.angle + x;
var diff = newangle - this.angle;
this.rangle = newangle;
for (var obj of this.objects) {
obj.rotate(diff);
obj.set_pos(Vector.rotate(obj.get_pos(obj.master), diff), obj.master);
}
},
set_scale(x, relative = world) {
if (typeof x === "number") x = [x, x, x];
var newscale = relative.scale.map((s, i) => x[i] * s);
var pct = this.scale.map((s, i) => newscale[i] / s);
this.rscale = newscale;
for (var obj of this.objects) {
obj.grow(pct);
obj.set_pos(
obj.get_pos(obj.master).map((x, i) => x * pct[i]),
obj.master,
);
}
},
get_pos(relative = world) {
if (relative === world) return this.pos;
return relative.world2this(this.pos);
//return this.pos.sub(relative.pos);
},
get_angle(relative = world) {
if (relative === world) return this.angle;
return this.angle - relative.angle;
},
get_scale(relative = world) {
if (relative === world) return this.scale;
var masterscale = relative.scale;
return this.scale.map((x, i) => x / masterscale[i]);
},
in_air() {
return this.in_air();
},
/* Velocity and angular velocity of the object */
phys_obj() {
var phys = {};
phys.velocity = this.velocity;
phys.angularvelocity = this.angularvelocity;
return phys;
},
set category(n) {
if (n === 0) {
this.categories = n;
return;
}
var cat = 1 << (n - 1);
this.categories = cat;
},
get category() {
if (this.categories === 0) return 0;
var pos = 0;
var num = this.categories;
while (num > 0) {
if (num & 1) {
break;
}
pos++;
num >>>= 1;
}
return pos + 1;
},
};
entity.spawn.doc = `Spawn an entity of type 'ur' on this entity. Returns the spawned entity.`;
gameobject.doc = {
doc: "All objects in the game created through spawning have these attributes.",
pos: "Position of the object, relative to its master.",
angle: "Rotation of this object, relative to its master.",
velocity: "Velocity of the object, relative to world.",
angularvelocity: "Angular velocity of the object, relative to the world.",
scale: "Scale of the object, relative to its master.",
flipx: "Check if the object is flipped on its x axis.",
flipy: "Check if the object is flipped on its y axis.",
elasticity: `When two objects collide, their elasticities are multiplied together. Their velocities are then multiplied by this value to find √their resultant velocities.`,
friction: `When one object touches another, friction slows them down.`,
mass: `The higher the mass of the object, the less forces will affect it.`,
phys: `Set to 0, 1, or 2, representing dynamic, kinematic, and static.`,
worldpos: `Function returns the world position of the object.`,
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.`,
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.",
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_at: "Apply a force to this body, at a position relative to itself.",
max_velocity: "The max linear velocity this object can travel.",
max_angularvelocity: "The max angular velocity this object can rotate.",
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.`,
hide: `Make this object invisible.`,
show: `Make this object visible.`,
width: `The total width of the object and all its components.`,
height: `The total height of the object.`,
move: `Move this object the given amount.`,
boundingbox: `The boundingbox of the object.`,
dup: `Make an exact copy of this object.`,
transform: `Return an object representing the transform state of this object.`,
kill: `Remove this object from the world.`,
master: "The entity this entity belongs to.",
delay: "Run the given function after the given number of seconds has elapsed.",
cry: "Make a sound. Can only make one at a time.",
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.",
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.",
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_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.",
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.",
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.",
drawlayer: "Layer for drawing. Higher numbers draw above lower ones.",
warp_filter: "Bitmask for selecting what warps should affect this entity.",
};
global.ur = {};
if (io.exists(`${io.dumpfolder}/ur.json`)) ur = json.decode(io.slurp(`${io.dumpfolder}/ur.json`));
else {
ur = {};
ur._list = [];
}
/* UR OBJECT
ur {
name: fully qualified name of ur
text: file path to the script
data: file path to data
proto: resultant object of a freshly made entity
}
*/
/* Apply an ur u to an entity e */
/* u is given as */
function apply_ur(u, ent) {
if (typeof u !== "string") {
console.warn("Must give u as a string.");
return;
}
var urs = u.split(".");
if (!urs.every(u => ur[u])) {
console.error(`Attempted to make ur combo ${u} but not every ur in the chain exists.`);
return;
}
for (var u of urs) {
var text = u.text;
var data = u.data;
if (typeof text === "string") use(text, ent);
else if (Array.isArray(text)) for (var path of text) use(path, ent);
if (typeof data === "string") Object.merge(ent, json.decode(Resources.replstrs(data)));
else if (Array.isArray(data)) {
for (var path of data) {
if (typeof path === "string") Object.merge(ent, json.decode(Resources.replstrs(data)));
else if (path instanceof Object) Object.merge(ent, path);
}
}
}
}
var emptyur = {
name: "empty",
};
var getur = function (text, data) {
if (!text && !data) {
console.info("empty ur");
return {
name: "empty",
};
}
var urstr = text;
if (data) urstr += "+" + data;
if (!ur[urstr]) {
ur[urstr] = {
name: urstr,
text: text,
data: data,
};
}
return ur[urstr];
};
var ur_from_file = function (file) {
var urname = file.name();
if (ur[urname]) {
console.warn(`Tried to make another ur with the name ${urname} from ${file}, but it already exists.`);
return undefined;
}
var newur = {
name: urname,
};
ur[urname] = newur;
ur._list.push(urname);
return newur;
};
game.loadurs = function () {
return;
ur = {};
ur._list = [];
/* FIND ALL URS IN A PROJECT */
for (var file of io.glob("**.ur")) {
var newur = ur_from_file(file);
if (!newur) continue;
var uur = Resources.replstrs(file);
var urjson = json.decode(uur);
Object.assign(newur, urjson);
}
for (var file of io.glob("**.jso").filter(f => !ur[f.name()])) {
if (file[0] === "." || file[0] === "_") continue;
var newur = ur_from_file(file);
if (!newur) continue;
newur.text = file;
var data = file.set_ext(".json");
if (io.exists(data)) {
console.info(`Found matching json ${data} for ${file}`);
newur.data = data;
}
}
};
game.ur = {};
game.ur.load = function (str) {};
game.ur.add_data = function (str, data) {
var nur = ur[str];
if (!nur) {
console.warn(`Cannot add data to the ur ${str}.`);
return;
}
if (!Array.isArray(ur.data)) {
var arr = [];
if (ur.data) arr.push(ur.data);
ur.data = arr;
}
ur.data.push(data);
};
game.ur.save = function (str) {
var nur = ur[str];
if (!nur) {
console.warn(`Cannot save ur ${str}.`);
return;
}
};
return { entity };

View File

@@ -1,3 +1,5 @@
var geometry = os.use('geometry')
var shape = {}; var shape = {};
shape.box = {}; shape.box = {};
shape.box.points = function (ll, ur) { shape.box.points = function (ll, ur) {
@@ -67,4 +69,7 @@ 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 }; for (var i in geometry)
shape[i] = geometry[i]
return shape

26
scripts/gizmos.js Normal file
View File

@@ -0,0 +1,26 @@
var ex = {}
ex.pick_gameobject_points = function pick_gameobject_points(worldpos, gameobject, points) {
var idx = Math.grab_from_points(worldpos, points.map(gameobject.this2world, gameobject), 25);
if (idx === -1) return undefined;
return idx;
}
ex.normalizeSpacing = function normalizeSpacing(spacing) {
if (typeof spacing === 'number') {
return {l: spacing, r: spacing, t: spacing, b: spacing};
} else if (Array.isArray(spacing)) {
if (spacing.length === 2) {
return {l: spacing[0], r: spacing[0], t: spacing[1], b: spacing[1]};
} else if (spacing.length === 4) {
return {l: spacing[0], r: spacing[1], t: spacing[2], b: spacing[3]};
}
} else if (typeof spacing === 'object') {
return {l: spacing.l || 0, r: spacing.r || 0, t: spacing.t || 0, b: spacing.b || 0};
} else {
return {l:0, r:0, t:0, b:0};
}
}
return ex

197
scripts/graphics.js Normal file
View File

@@ -0,0 +1,197 @@
var graphics = {}
function calc_image_size(img)
{
if (!img.texture || !img.rect) return;
return [img.texture.width*img.rect.width, img.texture.height*img.rect.height];
}
function create_image(path)
{
var data = io.slurpbytes(path);
var newimg;
switch(path.ext()) {
case 'gif':
newimg = os.make_gif(data);
if (newimg.surface)
newimg.texture = prosperon.gpu.load_texture(newimg.surface);
else
for (var frame of newimg.frames)
frame.texture = prosperon.gpu.load_texture(frame.surface);
break;
case 'ase':
case 'aseprite':
newimg = os.make_aseprite(data);
if (newimg.surface)
newimg.texture = prosperon.gpu.load_texture(newimg.surface);
else {
for (var anim in newimg) {
var a = newimg[anim];
for (var frame of a.frames)
frame.texture = prosperon.gpu.load_texture(frame.surface);
}
}
break;
default:
newimg = {
surface: os.make_texture(data)
};
newimg.texture = prosperon.gpu.load_texture(newimg.surface);
break;
}
return newimg;
}
var image = {};
image.dimensions = function()
{
return [this.texture.width, this.texture.height].scale([this.rect[2], this.rect[3]]);
}
var spritesheet;
var sheet_frames = [];
var sheetsize = 1024;
function pack_into_sheet(images)
{
return;
if (!Array.isArray(images)) images = [images];
if (images[0].texture.width > 300 && images[0].texture.height > 300) return;
sheet_frames = sheet_frames.concat(images);
var sizes = sheet_frames.map(x => [x.rect.width*x.texture.width, x.rect.height*x.texture.height]);
var pos = os.rectpack(sheetsize, sheetsize, sizes);
if (!pos) {
console.error(`did not make spritesheet properly from images ${images}`);
console.info(sizes);
return;
}
var newsheet = os.make_tex_data(sheetsize,sheetsize);
for (var i = 0; i < pos.length; i++) {
// Copy the texture to the new sheet
newsheet.copy(sheet_frames[i].texture, pos[i], sheet_frames[i].rect);
// Update the frame's rect to the new position in normalized coordinates
sheet_frames[i].rect.x = pos[i][0] / newsheet.width;
sheet_frames[i].rect.y = pos[i][1] / newsheet.height;
sheet_frames[i].rect.width = sizes[i][0] / newsheet.width;
sheet_frames[i].rect.height = sizes[i][1] / newsheet.height;
sheet_frames[i].texture = newsheet;
}
newsheet.load_gpu();
spritesheet = newsheet;
return spritesheet;
}
graphics.is_image = function(obj)
{
if (obj.texture && obj.rect) return true;
}
// Any request to it returns an image, which is a texture and rect.
graphics.texture = function texture(path) {
if (typeof path !== 'string') {
return path;
throw new Error('need a string for graphics.texture')
}
var parts = path.split(':');
var ipath = Resources.find_image(parts[0]);
graphics.texture.cache[ipath] ??= create_image(ipath);
return graphics.texture.cache[ipath];
}
graphics.texture.cache = {};
graphics.texture.time_cache = {};
graphics.texture.total_size = function()
{
var size = 0;
// Object.values(graphics.texture.cache).forEach(x => size += x.texture.inram() ? x..texture.width*x.texture.height*4 : 0);
return size;
}
graphics.texture.total_vram = function()
{
var vram = 0;
// Object.values(graphics.texture.cache).forEach(x => vram += x.vram);
return vram;
}
graphics.tex_hotreload = function tex_hotreload(file) {
if (!(file in graphics.texture.cache)) return;
var img = create_image(file);
var oldimg = graphics.texture.cache[file];
console.log(json.encode(img))
merge_objects(oldimg,img, ['surface', 'texture', 'loop', 'time']);
graphics.texture.cache[file] = img;
};
function make_spritesheet(paths, width, height)
{
return
var textures = paths.map(path => graphics.texture(path));
var sizes = textures.map(tex => [tex.width, tex.height]);
var pos = os.rectpack(width, height, sizes);
if (!pos) return;
var sheet = os.make_tex_data(width,height);
var st = profile.now();
for (var i = 0; i < pos.length; i++)
sheet.copy(textures[i], pos[i].x, pos[i].y);
sheet.save("spritesheet.qoi");
sheet.load_gpu();
}
graphics.semver = {};
graphics.semver.valid = function (v, range) {
v = v.split(".");
range = range.split(".");
if (v.length !== 3) return undefined;
if (range.length !== 3) return undefined;
if (range[0][0] === "^") {
range[0] = range[0].slice(1);
if (parseInt(v[0]) >= parseInt(range[0])) return true;
return false;
}
if (range[0] === "~") {
range[0] = range[0].slice(1);
for (var i = 0; i < 2; i++) if (parseInt(v[i]) < parseInt(range[i])) return false;
return true;
}
return graphics.semver.cmp(v.join("."), range.join(".")) === 0;
};
graphics.semver.cmp = function (v1, v2) {
var ver1 = v1.split(".");
var ver2 = v2.split(".");
for (var i = 0; i < 3; i++) {
var n1 = parseInt(ver1[i]);
var n2 = parseInt(ver2[i]);
if (n1 > n2) return 1;
else if (n1 < n2) return -1;
}
return 0;
};
graphics.semver.cmp.doc = "Compare two semantic version numbers, given like X.X.X.";
graphics.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 ^.
~ means that MAJOR and MINOR must match exactly, but any PATCH greater or equal is valid.
^ means that MAJOR must match exactly, but any MINOR and PATCH greater or equal is valid.`;
return graphics

View File

@@ -1,3 +1,5 @@
var input = os.use('input')
var downkeys = {}; var downkeys = {};
function keyname(key) function keyname(key)
@@ -314,6 +316,7 @@ var Player = {
pawns: [], pawns: [],
control(pawn) { control(pawn) {
if (!pawn) return
if (!pawn.inputs) { if (!pawn.inputs) {
console.warn(`attempted to control a pawn without any input object.`); console.warn(`attempted to control a pawn without any input object.`);
return; return;
@@ -333,9 +336,8 @@ input.do_uncontrol = function input_do_uncontrol(pawn) {
}); });
}; };
for (var i = 0; i < 4; i++) { for (var i = 0; i < 4; i++)
Player.create(); Player.create();
}
Player.control.doc = "Control a provided object, if the object has an 'inputs' object."; Player.control.doc = "Control a provided object, if the object has an 'inputs' object.";
Player.uncontrol.doc = "Uncontrol a previously controlled object."; Player.uncontrol.doc = "Uncontrol a previously controlled object.";
@@ -345,6 +347,6 @@ Player.doc.players = "A list of current players.";
var player = Player; var player = Player;
return { input.player = Player
player,
}; return input

View File

@@ -1,6 +1,11 @@
// Layout code // Layout code
// Contain is for how it will treat its children. If they should be laid out as a row, or column, or in a flex style, etc. // Contain is for how it will treat its children. If they should be laid out as a row, or column, or in a flex style, etc.
var geometry = use('geometry')
var render = use('render')
var graphics = use('graphics')
var gizmo = use('gizmos')
var lay_ctx = layout.make_context(); var lay_ctx = layout.make_context();
var clay_base = { var clay_base = {
@@ -21,25 +26,9 @@ var clay_base = {
var root_item; var root_item;
var root_config; var root_config;
var boxes = []; var boxes = [];
globalThis.clay = {}; var clay = {}
clay.normalizeSpacing = function normalizeSpacing(spacing) { layout.draw = function draw(size, fn, config = {})
if (typeof spacing === 'number') {
return {l: spacing, r: spacing, t: spacing, b: spacing};
} else if (Array.isArray(spacing)) {
if (spacing.length === 2) {
return {l: spacing[0], r: spacing[0], t: spacing[1], b: spacing[1]};
} else if (spacing.length === 4) {
return {l: spacing[0], r: spacing[1], t: spacing[2], b: spacing[3]};
}
} else if (typeof spacing === 'object') {
return {l: spacing.l || 0, r: spacing.r || 0, t: spacing.t || 0, b: spacing.b || 0};
} else {
return {l:0, r:0, t:0, b:0};
}
}
clay.draw = function draw(size, fn, config = {})
{ {
lay_ctx.reset(); lay_ctx.reset();
boxes = []; boxes = [];
@@ -61,7 +50,7 @@ clay.draw = function draw(size, fn, config = {})
box.content = lay_ctx.get_rect(box.id); box.content = lay_ctx.get_rect(box.id);
box.boundingbox = Object.assign({}, box.content); box.boundingbox = Object.assign({}, box.content);
var padding = clay.normalizeSpacing(box.config.padding || 0); var padding = gizmo.normalizeSpacing(box.config.padding || 0);
if (padding.l || padding.r || padding.t || padding.b) { if (padding.l || padding.r || padding.t || padding.b) {
// Adjust the boundingbox to include the padding // Adjust the boundingbox to include the padding
box.boundingbox.x -= padding.l; box.boundingbox.x -= padding.l;
@@ -70,7 +59,7 @@ clay.draw = function draw(size, fn, config = {})
box.boundingbox.height += padding.t + padding.b; box.boundingbox.height += padding.t + padding.b;
} }
box.marginbox = Object.assign({}, box.content); box.marginbox = Object.assign({}, box.content);
var margin = clay.normalizeSpacing(box.config.margin || 0); var margin = gizmo.normalizeSpacing(box.config.margin || 0);
box.marginbox.x -= margin.l; box.marginbox.x -= margin.l;
box.marginbox.y -= margin.t; box.marginbox.y -= margin.t;
box.marginbox.width += margin.l+margin.r; box.marginbox.width += margin.l+margin.r;
@@ -104,15 +93,15 @@ function create_view_fn(base_config)
} }
} }
clay.vstack = create_view_fn({ layout.vstack = create_view_fn({
contain: layout.contain.column | layout.contain.start, contain: layout.contain.column | layout.contain.start,
}); });
clay.hstack = create_view_fn({ layout.hstack = create_view_fn({
contain: layout.contain.row | layout.contain.start, contain: layout.contain.row | layout.contain.start,
}); });
clay.spacer = create_view_fn({ layout.spacer = create_view_fn({
behave: layout.behave.hfill | layout.behave.vfill behave: layout.behave.hfill | layout.behave.vfill
}); });
@@ -124,8 +113,8 @@ function image_size(img)
function add_item(config) function add_item(config)
{ {
// Normalize the child's margin // Normalize the child's margin
var margin = clay.normalizeSpacing(config.margin || 0); var margin = gizmo.normalizeSpacing(config.margin || 0);
var padding = clay.normalizeSpacing(config.padding || 0); var padding = gizmo.normalizeSpacing(config.padding || 0);
var childGap = root_config.child_gap || 0; var childGap = root_config.child_gap || 0;
// Adjust for child_gap // Adjust for child_gap
@@ -180,16 +169,16 @@ function rectify_configs(config_array)
return cleanobj; return cleanobj;
} }
clay.image = function image(path, ...configs) layout.image = function image(path, ...configs)
{ {
var config = rectify_configs(configs); var config = rectify_configs(configs);
var image = game.texture(path); var image = graphics.texture(path);
config.image = image; config.image = image;
config.size ??= [image.texture.width, image.texture.height]; config.size ??= [image.texture.width, image.texture.height];
add_item(config); add_item(config);
} }
clay.text = function text(str, ...configs) layout.text = function text(str, ...configs)
{ {
var config = rectify_configs(configs); var config = rectify_configs(configs);
config.size ??= [0,0]; config.size ??= [0,0];
@@ -211,7 +200,7 @@ var button_base = Object.assign(Object.create(clay_base), {
hovered:{ hovered:{
} }
}); });
clay.button = function button(str, action, config = {}) layout.button = function button(str, action, config = {})
{ {
config.__proto__ = button_base; config.__proto__ = button_base;
config.size = render.text_size(str,config.font); config.size = render.text_size(str,config.font);
@@ -222,6 +211,7 @@ clay.button = function button(str, action, config = {})
var hovered = undefined; var hovered = undefined;
layout.newframe = function() { hovered = undefined; } layout.newframe = function() { hovered = undefined; }
// mousepos given in hud coordinates // mousepos given in hud coordinates
layout.draw_commands = function draw_commands(cmds, pos = [0,0], mousepos = prosperon.camera.screen2hud(input.mouse.screenpos())) layout.draw_commands = function draw_commands(cmds, pos = [0,0], mousepos = prosperon.camera.screen2hud(input.mouse.screenpos()))
{ {
@@ -277,5 +267,5 @@ layout.inputs.mouse.left = function()
layout.toString = _ => "layout" layout.toString = _ => "layout"
return layout; return layout

View File

@@ -1,7 +1,7 @@
var layout = use("layout.js"); var clay = use("layout.js");
this.hud = function () { this.hud = function () {
layout.draw_commands(clay.draw([], _ => { clay.draw_commands(clay.draw([], _ => {
clay.text("No game yet! Make main.js to get started!"); clay.text("No game yet! Make main.js to get started!");
})); }));
}; };

View File

@@ -1,3 +1,7 @@
var Color = use('color')
var ex = {}
var emitter = {}; var emitter = {};
emitter.life = 10; emitter.life = 10;
emitter.scale = 1; emitter.scale = 1;
@@ -85,7 +89,7 @@ emitter.burst = function (count, t) {
var emitters = []; var emitters = [];
var make_emitter = function () { ex.make = function make_emitter() {
var e = Object.create(emitter); var e = Object.create(emitter);
e.particles = []; e.particles = [];
e.dead = []; e.dead = [];
@@ -93,11 +97,11 @@ var make_emitter = function () {
return e; return e;
}; };
function update_emitters(dt) { ex.update = function update_emitters(dt) {
for (var e of emitters) e.step(dt); for (var e of emitters) e.step(dt);
} }
function stat_emitters() ex.stat = function stat_emitters()
{ {
var stat = {}; var stat = {};
stat.emitters = emitters.length; stat.emitters = emitters.length;
@@ -107,6 +111,6 @@ function stat_emitters()
return stat; return stat;
} }
function all_emitters() { return emitters; } ex.all = function all_emitters() { return emitters; }
return { make_emitter, update_emitters, stat_emitters, all_emitters }; return ex

View File

@@ -9,7 +9,9 @@ var HIT = {
}; };
*/ */
export function pos_query(pos, start = world, give = 10) { var phys = {};
phys.pos_query = function pos_query(pos, start = world, give = 10) {
var ret; var ret;
ret = physics.point_query_nearest(pos, 0); ret = physics.point_query_nearest(pos, 0);
@@ -21,12 +23,12 @@ export function pos_query(pos, start = world, give = 10) {
}); });
}; };
export function box_point_query(box, points) { phys.box_point_query = function box_point_query(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) if (bbox.pointin(bbox, points[i])) inside.push[i]; for (var i in points) if (bbox.pointin(bbox, points[i])) inside.push[i];
return inside; return inside;*/
}; };
Object.assign(physics, { Object.assign(physics, {

View File

@@ -1,3 +1,5 @@
var profile = os.use('profile')
/* /*
TYPES OF PROFILING TYPES OF PROFILING
report - can see specific events that happened. Includes inclusive vs noninclusive times. When used on top of each other, also generates a callstack. report - can see specific events that happened. Includes inclusive vs noninclusive times. When used on top of each other, also generates a callstack.
@@ -5,6 +7,8 @@
memory - can see how much memory is allocated and from where [not implemented yet] memory - can see how much memory is allocated and from where [not implemented yet]
*/ */
var graphics = use('graphics')
function calc_cpu(fn, times, diff = 0) { function calc_cpu(fn, times, diff = 0) {
var series = []; var series = [];
@@ -270,8 +274,8 @@ var get_snapshot = function()
} }
snap.actors = actor.__stats(); snap.actors = actor.__stats();
snap.memory.textures = game.texture.total_size(); snap.memory.textures = graphics.texture.total_size();
snap.memory.texture_vram = game.texture.total_vram(); snap.memory.texture_vram = graphics.texture.total_vram();
snap.particles = stat_emitters(); snap.particles = stat_emitters();
} }
@@ -383,4 +387,4 @@ profile.print_gc = function () {
profile.data.gc[profile.curframe] = gc; profile.data.gc[profile.curframe] = gc;
}; };
return { profile }; return profile

View File

@@ -1,556 +0,0 @@
globalThis.gamestate = {};
global.pull_registers = function(obj)
{
var reggies = [];
for (var reg in Register.registries) {
if (typeof obj[reg] === "function")
reggies.push(reg);
}
return reggies;
}
function register_obj(obj, reg)
{
var fn = obj[reg].bind(obj);
fn.layer = obj[reg].layer;
var name = obj.ur ? obj.ur.name : obj.toString();
obj.timers.push(Register.registries[reg].register(fn, name));
if (!obj[reg].name) Object.defineProperty(obj[reg], 'name', {value:`${obj._file}_${reg}`});
}
global.check_registers = function check_registers(obj) {
if (obj.__reggies) {
if (obj.__reggies.length == 0) return;
// fast path
for (var reg of obj.__reggies)
register_obj(obj,reg);
return;
}
for (var reg in Register.registries) {
if (typeof obj[reg] === "function")
register_obj(obj,reg);
}
/* for (var k in obj) {
if (!k.startsWith("on_")) continue;
var signal = k.fromfirst("on_");
Event.observe(signal, obj, obj[k]);
}*/
};
globalThis.timers = []
global.setTimeout = function(fn,seconds, ...args) {
return prosperon.add_timer(globalThis, _ => fn.apply(undefined,args), seconds);
}
global.clearTimeout = function(id) { id(); }
prosperon.delay = setTimeout;
global.obscure("global");
global.mixin("render");
global.mixin("debug");
global.mixin('layout')
globalThis.parseq = use('parseq');
globalThis.sim = {};
sim.mode = "play";
sim.play = function () {
this.mode = "play";
// os.reindex_static();
game.all_objects(o => {
if (!o._started) {
o._started = true;
o.start?.();
}
});
};
sim.playing = function () {
return this.mode === "play";
};
sim.pause = function () {
this.mode = "pause";
};
sim.paused = function () {
return this.mode === "pause";
};
sim.step = function () {
this.mode = "step";
};
sim.stepping = function () {
return this.mode === "step";
};
var frame_t = profile.now();
var physlag = 0;
prosperon.SIGABRT = function()
{
console.error(new Error('SIGABRT'));
os.exit(1);
}
prosperon.SIGSEGV = function()
{
console.error(new Error('SIGSEGV'));
os.exit(1);
}
prosperon.exit = function()
{
}
prosperon.init = function () {
render.init();
// imgui.init(render._main);
tracy.gpu_init();
globalThis.audio = use("sound.js");
world_start();
/* shape.quad = {
pos: os.make_buffer([
0, 0, 0,
0, 1, 0,
1, 0, 0,
1, 1, 0], 0),
verts: 4,
uv: os.make_buffer([
0, 1,
0, 0,
1, 1,
1, 0], 2),
index: os.make_buffer([0, 1, 2, 2, 1, 3], 1),
count: 6,
};
shape.triangle = {
pos: os.make_buffer([0, 0, 0, 0.5, 1, 0, 1, 0, 0], 0),
uv: os.make_buffer([0, 0, 0.5, 1, 1, 0], 2),
verts: 3,
count: 3,
index: os.make_buffer([0, 1, 2], 1),
};*/
if (io.exists("main.js")) global.app = actor.spawn("main.js");
else global.app = actor.spawn("nogame.js");
};
prosperon.release_mode = function () {
prosperon.debug = false;
debug.kill();
};
prosperon.debug = true;
game.timescale = 1;
var eachobj = function (obj, fn) {
var val = fn(obj);
if (val) return val;
for (var o in obj.objects) {
if (obj.objects[o] === obj) console.error(`Object ${obj.toString()} is referenced by itself.`);
val = eachobj(obj.objects[o], fn);
if (val) return val;
}
};
game.all_objects = function (fn, startobj = world) {
return eachobj(startobj, fn);
};
game.find_object = function (fn, startobj = world) {};
game.tags = {};
game.tag_add = function (tag, obj) {
game.tags[tag] ??= {};
game.tags[tag][obj.guid] = obj;
};
game.tag_rm = function (tag, obj) {
delete game.tags[tag][obj.guid];
};
game.tag_clear_guid = function (guid) {
for (var tag in game.tags) delete game.tags[tag][guid];
};
game.objects_with_tag = function (tag) {
if (!game.tags[tag]) return [];
return Object.values(game.tags[tag]);
};
game.doc = {};
game.doc.object = "Returns the entity belonging to a given id.";
game.doc.pause = "Pause game simulation.";
game.doc.play = "Resume or start game simulation.";
function calc_image_size(img)
{
if (!img.texture || !img.rect) return;
return [img.texture.width*img.rect.width, img.texture.height*img.rect.height];
}
function create_image(path)
{
var data = io.slurpbytes(path);
var newimg;
switch(path.ext()) {
case 'gif':
newimg = os.make_gif(data);
if (newimg.surface)
newimg.texture = render._main.load_texture(newimg.surface);
else
for (var frame of newimg.frames)
frame.texture = render._main.load_texture(frame.surface);
break;
case 'ase':
case 'aseprite':
newimg = os.make_aseprite(data);
if (newimg.surface)
newimg.texture = render._main.load_texture(newimg.surface);
else {
for (var anim in newimg) {
var a = newimg[anim];
for (var frame of a.frames)
frame.texture = render._main.load_texture(frame.surface);
}
}
break;
default:
newimg = {
surface: os.make_texture(data)
};
newimg.texture = render._main.load_texture(newimg.surface);
break;
}
return newimg;
}
function merge_objects(oldobj,newobj, properties) {
function recursive_merge(target,src) {
for (var key of Object.keys(src)) {
if (properties.includes(key)) {
target[key] = src[key];
continue;
}
if (src[key] && typeof src[key] === 'object' && target[key] && typeof target[key] === 'object')
recursive_merge(target[key],src[key])
}
}
recursive_merge(oldobj,newobj);
}
game.tex_hotreload = function tex_hotreload(file) {
if (!(file in game.texture.cache)) return;
var img = create_image(file);
var oldimg = game.texture.cache[file];
console.log(json.encode(img))
merge_objects(oldimg,img, ['surface', 'texture', 'loop', 'time']);
game.texture.cache[file] = img;
};
var image = {};
image.dimensions = function()
{
return [this.texture.width, this.texture.height].scale([this.rect[2], this.rect[3]]);
}
var spritesheet;
var sheet_frames = [];
var sheetsize = 1024;
function pack_into_sheet(images)
{
return;
if (!Array.isArray(images)) images = [images];
if (images[0].texture.width > 300 && images[0].texture.height > 300) return;
sheet_frames = sheet_frames.concat(images);
var sizes = sheet_frames.map(x => [x.rect.width*x.texture.width, x.rect.height*x.texture.height]);
var pos = os.rectpack(sheetsize, sheetsize, sizes);
if (!pos) {
console.error(`did not make spritesheet properly from images ${images}`);
console.info(sizes);
return;
}
var newsheet = os.make_tex_data(sheetsize,sheetsize);
for (var i = 0; i < pos.length; i++) {
// Copy the texture to the new sheet
newsheet.copy(sheet_frames[i].texture, pos[i], sheet_frames[i].rect);
// Update the frame's rect to the new position in normalized coordinates
sheet_frames[i].rect.x = pos[i][0] / newsheet.width;
sheet_frames[i].rect.y = pos[i][1] / newsheet.height;
sheet_frames[i].rect.width = sizes[i][0] / newsheet.width;
sheet_frames[i].rect.height = sizes[i][1] / newsheet.height;
sheet_frames[i].texture = newsheet;
}
newsheet.load_gpu();
spritesheet = newsheet;
return spritesheet;
}
game.is_image = function(obj)
{
if (obj.texture && obj.rect) return true;
}
// Any request to it returns an image, which is a texture and rect.
game.texture = function texture(path) {
if (typeof path !== 'string') {
return path;
throw new Error('need a string for game.texture')
}
var parts = path.split(':');
var ipath = Resources.find_image(parts[0]);
game.texture.cache[ipath] ??= create_image(ipath);
return game.texture.cache[ipath];
}
game.texture.cache = {};
game.texture.time_cache = {};
game.texture.total_size = function()
{
var size = 0;
// Object.values(game.texture.cache).forEach(x => size += x.texture.inram() ? x..texture.width*x.texture.height*4 : 0);
return size;
}
game.texture.total_vram = function()
{
var vram = 0;
// Object.values(game.texture.cache).forEach(x => vram += x.vram);
return vram;
}
prosperon.semver = {};
prosperon.semver.valid = function (v, range) {
v = v.split(".");
range = range.split(".");
if (v.length !== 3) return undefined;
if (range.length !== 3) return undefined;
if (range[0][0] === "^") {
range[0] = range[0].slice(1);
if (parseInt(v[0]) >= parseInt(range[0])) return true;
return false;
}
if (range[0] === "~") {
range[0] = range[0].slice(1);
for (var i = 0; i < 2; i++) if (parseInt(v[i]) < parseInt(range[i])) return false;
return true;
}
return prosperon.semver.cmp(v.join("."), range.join(".")) === 0;
};
prosperon.semver.cmp = function (v1, v2) {
var ver1 = v1.split(".");
var ver2 = v2.split(".");
for (var i = 0; i < 3; i++) {
var n1 = parseInt(ver1[i]);
var n2 = parseInt(ver2[i]);
if (n1 > n2) return 1;
else if (n1 < n2) return -1;
}
return 0;
};
prosperon.semver.cmp.doc = "Compare two semantic version numbers, given like X.X.X.";
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 ^.
~ means that MAJOR and MINOR must match exactly, but any PATCH greater or equal is valid.
^ means that MAJOR must match exactly, but any MINOR and PATCH greater or equal is valid.`;
global.mixin("input");
global.mixin("std");
global.mixin("diff");
global.mixin("color");
global.mixin("tween");
global.mixin("particle");
//global.mixin("physics");
global.mixin("geometry");
/*
Factory for creating registries. Register one with 'X.register',
which returns a function that, when invoked, cancels the registry.
*/
globalThis.Register = {
registries: [],
add_cb(name) {
var n = {};
var fns = [];
n.register = function (fn, oname) {
if (!(fn instanceof Function)) return;
var guid = prosperon.guid();
var dofn = function (...args) {
fn(...args);
};
Object.defineProperty(dofn, 'name', {value:`do_${oname}`});
var left = 0;
var right = fns.length - 1;
dofn.layer = fn.layer;
dofn.layer ??= 0;
while (left <= right) {
var mid = Math.floor((left + right) / 2);
if (fns[mid] === dofn.layer) {
left = mid;
break;
} else if (fns[mid].layer < dofn.layer) left = mid + 1;
else right = mid - 1;
}
fns.splice(left, 0, dofn);
return function () {
fns.remove(dofn);
};
};
prosperon[name] = function (...args) {
// tracy.fiber_enter(vector.fib);
fns.forEach(fn => fn(...args));
// tracy.fiber_leave(vector.fib);
};
Object.defineProperty(prosperon[name], 'name', {value:name});
prosperon[name].fns = fns;
n.clear = function () {
fns = [];
};
Register[name] = n;
Register.registries[name] = n;
return n;
},
};
Register.add_cb("appupdate");
Register.add_cb("update").doc = "Called once per frame.";
Register.add_cb("physupdate");
Register.add_cb("gui");
Register.add_cb("hud");
Register.add_cb("draw");
Register.add_cb("imgui");
Register.add_cb("app");
Register.add_cb("prerender");
global.mixin("components");
var Event = {
events: {},
observe(name, obj, fn) {
this.events[name] ??= [];
this.events[name].push([obj, fn]);
},
unobserve(name, obj) {
this.events[name] = this.events[name].filter(x => x[0] !== obj);
},
rm_obj(obj) {
Object.keys(this.events).forEach(name => Event.unobserve(name, obj));
},
notify(name, ...args) {
if (!this.events[name]) return;
this.events[name].forEach(function (x) {
x[1].call(x[0], ...args);
});
},
};
prosperon.add_timer = function(obj, fn, seconds)
{
var timers = obj.timers;
var stop = function () {
timers.remove(stop);
timer.fn = undefined;
timer = undefined;
};
function execute() {
if (fn)
timer.remain = fn(stop.seconds);
if (!timer.remain)
stop();
else
stop.seconds = timer.remain;
}
var timer = os.make_timer(execute);
timer.remain = seconds;
stop.remain = seconds;
stop.seconds = seconds;
timers.push(stop);
return stop;
}
global.mixin("spline");
global.mixin("actor");
global.mixin("entity");
function world_start() {
globalThis.world = Object.create(entity);
world.transform = os.make_transform();
world.objects = {};
world.toString = function () {
return "world";
};
world.ur = "world";
world.kill = function () {
this.clear();
};
world.phys = 2;
world.zoom = 1;
world._ed = { selectable: false };
world.ur = {};
world.ur.fresh = {};
}
function make_spritesheet(paths, width, height)
{
var textures = paths.map(path => game.texture(path));
var sizes = textures.map(tex => [tex.width, tex.height]);
var pos = os.rectpack(width, height, sizes);
if (!pos) return;
var sheet = os.make_tex_data(width,height);
var st = profile.now();
for (var i = 0; i < pos.length; i++)
sheet.copy(textures[i], pos[i].x, pos[i].y);
sheet.save("spritesheet.qoi");
gamestate.spritess = sheet;
sheet.load_gpu();
}
return {
sim,
frame_t,
physlag,
Event,
};

View File

@@ -1,3 +1,35 @@
var render = {}
var profile = use('profile')
var game = os.use('game')
var config = use('config.js')
var gizmo = use('gizmos')
game.timescale = 1
prosperon.window = game.engine_start(config);
var driver = "vulkan"
switch(os.sys()) {
case "Linux":
driver = "vulkan"
break
case "Windows":
// driver = "direct3d12"
driver = "vulkan"
break
case "macOS":
driver = "metal"
break
}
render._main = prosperon.window.make_gpu(false,driver)
prosperon.gpu = render._main
render._main.window = prosperon.window
render._main.claim_window(prosperon.window)
render._main.set_swapchain('sdr', 'vsync')
var graphics = use('graphics')
var unit_transform = os.make_transform(); var unit_transform = os.make_transform();
render.doc = { render.doc = {
@@ -469,6 +501,7 @@ function render_camera(cmds, camera)
{ {
var pass; var pass;
try{ try{
delete camera.target // TODO: HORRIBLE
if (!camera.target) { if (!camera.target) {
main_color.width = main_depth.width = camera.size.x; main_color.width = main_depth.width = camera.size.x;
main_color.height = main_depth.height = camera.size.y; main_color.height = main_depth.height = camera.size.y;
@@ -631,6 +664,18 @@ prosperon.camera = {};
// If camera viewport is defined, will draw to the screen // If camera viewport is defined, will draw to the screen
// If target is defined, will render to a target, too // If target is defined, will render to a target, too
prosperon.camera.transform = os.make_transform();
prosperon.camera.transform.unit();
prosperon.camera.zoom = 1;
prosperon.camera.size = [640,360];
prosperon.camera.mode = 'keep';
prosperon.camera.viewport = {x:0,y:0,width:1,height:1}
prosperon.camera.fov = 45;
prosperon.camera.type = 'ortho';
prosperon.camera.ortho = true
prosperon.camera.aspect = 16/9;
delete prosperon.camera.target;
prosperon.camera.draw_rect = function(size) prosperon.camera.draw_rect = function(size)
{ {
var mode = this.presentation || "letterbox" var mode = this.presentation || "letterbox"
@@ -716,30 +761,6 @@ pipeline_model = Object.create(base_pipeline);
pipeline_model.vertex = "model.vert" pipeline_model.vertex = "model.vert"
pipeline_model.fragment = "model.frag" pipeline_model.fragment = "model.frag"
var quad_model;
render.init = function () {
shader_type = render._main.shader_format()[0];
prosperon.font = render.get_font('fonts/c64.ttf', 8);
std_sampler = render._main.make_sampler({
min_filter: "nearest",
mag_filter: "nearest",
mipmap_mode: "nearest",
address_mode_u: "repeat",
address_mode_v: "repeat",
address_mode_w: "repeat"
});
quad_model = render._main.make_quad();
io.mount("core");
render._main.present = gpupresent;
var cmds = render._main.acquire_cmd_buffer();
cmds.__proto__.upload_model = upload_model;
cmds.upload_model(quad_model);
cmds.submit();
imgui.init(render._main, prosperon.window);
};
render.draw_sprites = true; render.draw_sprites = true;
render.draw_particles = true; render.draw_particles = true;
render.draw_hud = true; render.draw_hud = true;
@@ -758,6 +779,8 @@ function insertion_sort(arr, cmp)
return arr return arr
} }
var sprite = use('sprite')
function sprites_to_queue(ysort = false) function sprites_to_queue(ysort = false)
{ {
var pos = prosperon.camera.transform.pos; var pos = prosperon.camera.transform.pos;
@@ -768,13 +791,8 @@ function sprites_to_queue(ysort = false)
width:size.x, width:size.x,
height:size.y height:size.y
}; };
var culled = sprite_qt.query(camrect) var culled = sprite.tree.query(camrect)
if (globalThis.so_sprite_qt) {
var cull2 = so_sprite_qt.query(camrect)
culled = culled.concat(cull2)
}
if (culled.length == 0) return []; if (culled.length == 0) return [];
var cmd = render._main.make_sprite_queue(culled, prosperon.camera, sprite_pipeline, 1); var cmd = render._main.make_sprite_queue(culled, prosperon.camera, sprite_pipeline, 1);
return cmd; return cmd;
} }
@@ -912,12 +930,11 @@ render.text = function text(text, rect, font = prosperon.font, size = 0, color =
os.make_text_buffer(str, pos, size, color, wrap, font); // this puts text into buffer os.make_text_buffer(str, pos, size, color, wrap, font); // this puts text into buffer
}; };
var tttsize = render.text_size;
render.text_size = function(str, font, ...args) render.text_size = function(str, font, ...args)
{ {
if (typeof font === 'string') if (typeof font === 'string')
font = render.get_font(font); font = render.get_font(font);
return tttsize(str,font, ...args); return font.text_size(str, ...args);
} }
var stencil_write = { var stencil_write = {
@@ -964,7 +981,7 @@ var stencil_invert = {
render.mask = function mask(image, pos, scale, rotation = 0, ref = 1) render.mask = function mask(image, pos, scale, rotation = 0, ref = 1)
{ {
if (typeof image === 'string') if (typeof image === 'string')
image = game.texture(image); image = graphics.texture(image);
var tex = image.texture; var tex = image.texture;
@@ -994,7 +1011,7 @@ function tile(image, rect = [0,0], color = Color.white, repeat = {})
{ {
if (!image) throw Error ('Need an image to render.') if (!image) throw Error ('Need an image to render.')
if (typeof image === "string") if (typeof image === "string")
image = game.texture(image); image = graphics.texture(image);
render._main.tile(image, rect, undefined, 1); render._main.tile(image, rect, undefined, 1);
return; return;
@@ -1019,7 +1036,7 @@ var std_sprite_cmd = {
render.image = function image(image, rect = [0,0], rotation = 0, color, pipeline) { render.image = function image(image, rect = [0,0], rotation = 0, color, pipeline) {
if (!image) throw Error ('Need an image to render.') if (!image) throw Error ('Need an image to render.')
if (typeof image === "string") if (typeof image === "string")
image = game.texture(image); image = graphics.texture(image);
rect.width ??= image.texture.width; rect.width ??= image.texture.width;
rect.height ??= image.texture.height; rect.height ??= image.texture.height;
@@ -1039,7 +1056,7 @@ render.image = function image(image, rect = [0,0], rotation = 0, color, pipeline
render.images = function images(image, rects, config) render.images = function images(image, rects, config)
{ {
if (!image) throw Error ('Need an image to render.'); if (!image) throw Error ('Need an image to render.');
if (typeof image === "string") image = game.texture(image); if (typeof image === "string") image = graphics.texture(image);
var bb = []; var bb = [];
bb.width = image.texture.width; bb.width = image.texture.width;
@@ -1075,7 +1092,7 @@ render.tile = function(image, rect, color = Color.white, tile = tile_def, pipeli
{ {
if (!image) throw Error ('Need an image to render.') if (!image) throw Error ('Need an image to render.')
if (typeof image === "string") if (typeof image === "string")
image = game.texture(image); image = graphics.texture(image);
var mesh = render._main.tile(image.texture, {x:0,y:0,width:image.texture.width,height:image.texture.height}, rect, tile); var mesh = render._main.tile(image.texture, {x:0,y:0,width:image.texture.width,height:image.texture.height}, rect, tile);
current_queue.push({ current_queue.push({
@@ -1100,9 +1117,9 @@ var slice9_info = {
render.slice9 = function slice9(image, rect = [0,0], slice = 0, color = Color.white, info = slice9_info, pipeline = sprite_pipeline) { render.slice9 = function slice9(image, rect = [0,0], slice = 0, color = Color.white, info = slice9_info, pipeline = sprite_pipeline) {
if (!image) throw Error ('Need an image to render.') if (!image) throw Error ('Need an image to render.')
if (typeof image === "string") if (typeof image === "string")
image = game.texture(image); image = graphics.texture(image);
var mesh = render._main.slice9(image.texture, rect, clay.normalizeSpacing(slice), info); var mesh = render._main.slice9(image.texture, rect, gizmo.normalizeSpacing(slice), info);
current_queue.push({ current_queue.push({
type: 'geometry', type: 'geometry',
mesh, mesh,
@@ -1188,7 +1205,7 @@ screen2cam.doc = "Convert a screen space position in pixels to a normalized view
prosperon.gizmos = function gizmos() { prosperon.gizmos = function gizmos() {
game.all_objects(o => { game.all_objects(o => {
if (o.gizmo) render.image(game.texture(o.gizmo), o.pos); if (o.gizmo) render.image(graphics.texture(o.gizmo), o.pos);
}); });
}; };
@@ -1260,7 +1277,7 @@ var imgui_fn = function imgui_fn() {
prosperon.title = imgui.textinput("Title", prosperon.title); prosperon.title = imgui.textinput("Title", prosperon.title);
prosperon.icon = imgui.textinput("Icon", prosperon.icon); prosperon.icon = imgui.textinput("Icon", prosperon.icon);
imgui.button("Refresh window", _ => { imgui.button("Refresh window", _ => {
prosperon.set_icon(game.texture(prosperon.icon)); prosperon.set_icon(graphics.texture(prosperon.icon));
}); });
}); });
imgui.button("quit", os.exit); imgui.button("quit", os.exit);
@@ -1307,8 +1324,8 @@ var imgui_fn = function imgui_fn() {
} }
var texs = {}; var texs = {};
for (var path in game.texture.cache) { for (var path in graphics.texture.cache) {
var image = game.texture.cache[path]; var image = graphics.texture.cache[path];
if (image.texture && !texs[image.texture]) if (image.texture && !texs[image.texture])
texs[image.texture] = image.texture; texs[image.texture] = image.texture;
} }
@@ -1324,6 +1341,7 @@ var imgui_fn = function imgui_fn() {
// imgui.endframe(render._main); // imgui.endframe(render._main);
}; };
var dmon = use('dmon')
if (dmon) dmon.watch('.'); if (dmon) dmon.watch('.');
function dmon_cb(e) function dmon_cb(e)
@@ -1331,20 +1349,24 @@ function dmon_cb(e)
try { try {
io.invalidate(); io.invalidate();
if (e.file.startsWith('.')) return; if (e.file.startsWith('.')) return;
if (e.file.endsWith('.js')) // if (e.file.endsWith('.js'))
actor.hotreload(e.file); // actor.hotreload(e.file);
else if (e.file.endsWith('.hlsl')) if (e.file.endsWith('.hlsl'))
shader_hotreload(e.file); shader_hotreload(e.file);
else if (Resources.is_image(e.file)) else if (Resources.is_image(e.file))
game.tex_hotreload(e.file); game.tex_hotreload(e.file);
} catch(e) { console.error(e); } } catch(e) { console.error(e); }
} }
var sim = use('sim')
var emitters = use('particle')
var waittime = 1/240; var waittime = 1/240;
var last_frame_time = 0; var last_frame_time = 0;
var frame_t = 0;
// Ran once per frame // Ran once per frame
var fpses = []; var fpses = [];
prosperon.process = function process() { render.process = function process() {
var now = profile.now(); var now = profile.now();
var dt = now - last_frame_time; var dt = now - last_frame_time;
if (dt < waittime) os.sleep(waittime-dt); if (dt < waittime) os.sleep(waittime-dt);
@@ -1357,7 +1379,6 @@ prosperon.process = function process() {
try { try {
game.engine_input(e => { game.engine_input(e => {
prosperon[e.type]?.(e); prosperon[e.type]?.(e);
}); });
} catch(e) { console.error(e); } } catch(e) { console.error(e); }
@@ -1367,7 +1388,7 @@ try {
try { prosperon.appupdate(dt); } catch(e) { console.error(e) } try { prosperon.appupdate(dt); } catch(e) { console.error(e) }
input.procdown(); input.procdown();
try { try {
update_emitters(dt * game.timescale); emitters.update(dt * game.timescale);
os.update_timers(dt * game.timescale); os.update_timers(dt * game.timescale);
prosperon.update(dt*game.timescale); prosperon.update(dt*game.timescale);
} catch(e) { console.error(e) } } catch(e) { console.error(e) }
@@ -1387,7 +1408,7 @@ try {
current_queue = render_queue; current_queue = render_queue;
try { prosperon.draw(); } catch(e) { console.error(e) } try { prosperon.draw(); } catch(e) { console.error(e) }
for (var e of all_emitters()) for (var e of emitters.all())
render.particles(e); render.particles(e);
current_queue = hud_queue; current_queue = hud_queue;
try { prosperon.hud(); } catch(e) { console.error(e) } try { prosperon.hud(); } catch(e) { console.error(e) }
@@ -1398,4 +1419,22 @@ try {
tracy.end_frame(); tracy.end_frame();
}; };
return { render }; // Some initialization
shader_type = render._main.shader_format()[0];
prosperon.font = render.get_font('fonts/c64.ttf', 8);
std_sampler = render._main.make_sampler({
min_filter: "nearest",
mag_filter: "nearest",
mipmap_mode: "nearest",
address_mode_u: "repeat",
address_mode_v: "repeat",
address_mode_w: "repeat"
});
io.mount("core");
render._main.present = gpupresent;
imgui.init(render._main, prosperon.window);
tracy.gpu_init()
return render

37
scripts/search.js Normal file
View File

@@ -0,0 +1,37 @@
var ex = {}
var eachobj = function (obj, fn) {
var val = fn(obj);
if (val) return val;
for (var o in obj.objects) {
if (obj.objects[o] === obj) console.error(`Object ${obj.toString()} is referenced by itself.`);
val = eachobj(obj.objects[o], fn);
if (val) return val;
}
};
ex.all_objects = function (fn, startobj = world) {
return eachobj(startobj, fn);
};
ex.find_object = function (fn, startobj = world) {};
var gtags = {};
ex.tag_add = function (tag, obj) {
gtags[tag] ??= new Set();
gtags[tag].add(obj)
};
ex.tag_rm = function (tag, obj) {
delete gtags[tag].delete(obj)
};
ex.tag_clear_guid = function (obj) {
for (var tag in gtags) gtags[tag].delete(obj)
};
ex.objects_with_tag = function (tag) {
if (!gtags[tag]) return [];
return Array.from(gtags[tag])
};
return ex

29
scripts/sim.js Normal file
View File

@@ -0,0 +1,29 @@
var sim = {};
sim.mode = "play";
sim.play = function () {
this.mode = "play";
// os.reindex_static();
game.all_objects(o => {
if (!o._started) {
o._started = true;
o.start?.();
}
});
};
sim.playing = function () {
return this.mode === "play";
};
sim.pause = function () {
this.mode = "pause";
};
sim.paused = function () {
return this.mode === "pause";
};
sim.step = function () {
this.mode = "step";
};
sim.stepping = function () {
return this.mode === "step";
};
return sim

View File

@@ -1,3 +1,5 @@
//var soloud = use('soloud')
var tween = use('tween')
soloud.init(); soloud.init();
var audio = {}; var audio = {};
@@ -46,7 +48,7 @@ audio.music = function music(file, fade = 0.5) {
if (!song) { if (!song) {
song = audio.play(file); song = audio.play(file);
song.volume = 1; song.volume = 1;
// tween(song,'volume', 1, fade); // tween.tween(song,'volume', 1, fade);
return; return;
} }
@@ -55,8 +57,8 @@ audio.music = function music(file, fade = 0.5) {
temp.volume = 1; temp.volume = 1;
var temp2 = song; var temp2 = song;
// tween(temp, 'volume', 1, fade); // tween.tween(temp, 'volume', 1, fade);
// tween(temp2, 'volume', 0, fade); // tween.tween(temp2, 'volume', 0, fade);
song = temp; song = temp;
song.loop = true; song.loop = true;
}; };

View File

@@ -1,11 +1,8 @@
globalThis.so_sprite_qt = os.make_rtree(); var graphics = use('graphics')
var sprite = {
var sprite = {
image: undefined, image: undefined,
get diffuse() { return this.image; }, set color(x) { this._sprite.color = x; },
set diffuse(x) {},
set color(x) {
this._sprite.color = x;
},
get color() { return this._sprite.color; }, get color() { return this._sprite.color; },
anim_speed: 1, anim_speed: 1,
play(str, loop = true, reverse = false, fn) { play(str, loop = true, reverse = false, fn) {
@@ -27,9 +24,8 @@ globalThis.so_sprite_qt = os.make_rtree();
var stop; var stop;
this.del_anim?.(); this.del_anim?.();
self.del_anim = function () { this.del_anim = () => {
self.del_anim = undefined; this.del_anim = undefined;
self = undefined;
advance = undefined; advance = undefined;
stop?.(); stop?.();
}; };
@@ -37,9 +33,7 @@ globalThis.so_sprite_qt = os.make_rtree();
var f = 0; var f = 0;
if (reverse) f = playing.frames.length - 1; if (reverse) f = playing.frames.length - 1;
function advance(time) { var advance = (time) => {
if (!self) return;
var done = false; var done = false;
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;
@@ -49,27 +43,27 @@ globalThis.so_sprite_qt = os.make_rtree();
if (f === 0) done = true; if (f === 0) done = true;
} }
self.image = playing.frames[f]; this.image = playing.frames[f];
if (done) { if (done) {
// notify requestor // notify requestor
fn?.(); fn?.();
if (!loop) { if (!loop) {
self?.stop(); this?.stop();
return; return;
} }
} }
return playing.frames[f].time/self.anim_speed; return playing.frames[f].time/this.anim_speed;
} }
stop = self.delay(advance, playing.frames[f].time/self.anim_speed); stop = this.delay(advance, playing.frames[f].time/this.anim_speed);
advance(); advance();
}, },
stop() { stop() {
this.del_anim?.(); this.del_anim?.();
}, },
set path(p) { set path(p) {
var image = game.texture(p); var image = graphics.texture(p);
if (!image) { if (!image) {
console.warn(`Could not find image ${p}.`); console.warn(`Could not find image ${p}.`);
return; return;
@@ -106,10 +100,14 @@ globalThis.so_sprite_qt = os.make_rtree();
return this._p; return this._p;
}, },
garbage: function() { garbage: function() {
console.log("KILLING SPRITE")
this.del_anim?.(); this.del_anim?.();
this.anim = undefined; this.anim = undefined;
so_sprite_qt.remove(this._sprite) tree.delete(this._sprite)
this.transform.parent = undefined
for (var t of this.transform.children())
t.parent = undefined
delete this.transform
delete this._sprite
}, },
anchor: [0, 0], anchor: [0, 0],
set layer(v) { this._sprite.layer = v; }, set layer(v) { this._sprite.layer = v; },
@@ -118,9 +116,7 @@ globalThis.so_sprite_qt = os.make_rtree();
return this; return this;
}, },
boundingbox() { boundingbox() {
var dim = this.dimensions(); return Object.freeze(this._sprite.rect) // freeze so it can't be modified on the outside
var realpos = dim.scale(0.5).add(this.pos);
return bbox.fromcwh(realpos, dim);
} }
}; };
@@ -187,25 +183,34 @@ sprite.inputs.kp1 = function () {
this.setanchor("ul"); this.setanchor("ul");
}; };
var tree = os.make_rtree()
sprite.tree = tree;
sprite.t_hook = function() { sprite.t_hook = function() {
var msp = this.sprite; var msp = this.sprite;
so_sprite_qt.remove(msp); if (this.__in)
tree.delete(msp);
msp.rect = this.torect() msp.rect = this.torect()
msp.set_affine(this) msp.set_affine(this)
so_sprite_qt.insert(msp) tree.add(msp)
this.__in = true
} }
Object.mixin(sprite,use("transform"))
return sprite; return sprite;
--- ---
if (!this.overling.transform) throw new Error("Overling must have a transform to have a sprite") var Color = use('color')
this.transform = os.make_transform(); this.transform = os.make_transform();
this.transform.parent = this.overling.transform; if (this.overling.transform)
this.transform.parent = this.overling.transform;
this.transform.change_hook = $.t_hook; this.transform.change_hook = $.t_hook;
var msp = os.make_sprite(); var msp = os.make_sprite();
this._sprite = msp; this._sprite = msp;
msp.color = Color.white; msp.color = Color.white;
this.transform.sprite = msp this.transform.sprite = msp
so_sprite_qt.insert(msp)

View File

@@ -3,6 +3,9 @@ os.env.doc = "Return the value of the environment variable v.";
if (os.sys() === "windows") os.user = os.env("USERNAME"); if (os.sys() === "windows") os.user = os.env("USERNAME");
else os.user = os.env("USER"); else os.user = os.env("USER");
var sim = use('sim')
var io = use('io')
/*var ignore; /*var ignore;
if (ignore = io.slurp('.prosperonignore')) { if (ignore = io.slurp('.prosperonignore')) {
ignore = ignore.split('\n'); ignore = ignore.split('\n');
@@ -44,7 +47,9 @@ appy.inputs.f11.doc = "Toggle window fullscreen.";
appy.inputs.f11.title = "Toggle Fullscreen"; appy.inputs.f11.title = "Toggle Fullscreen";
appy.inputs["M-f4"] = os.exit; appy.inputs["M-f4"] = os.exit;
player[0].control(appy); var input = use('input')
input.player[0].control(appy);
os.home = os.env("HOME"); os.home = os.env("HOME");
@@ -70,7 +75,6 @@ os.openurl = function (url) {
else os.system(`open ${url}`); else os.system(`open ${url}`);
}; };
var projectfile = "project.prosperon";
io.dumpfolder = ".prosperon"; io.dumpfolder = ".prosperon";
Resources.texture = {}; Resources.texture = {};
@@ -164,16 +168,7 @@ Cmdline.register_order(
say("No game to edit. Try making one with 'prosperon init'."); say("No game to edit. Try making one with 'prosperon init'.");
return; return;
} }
sim.pause(); sim.pause();
game.engine_start(function () {
global.mixin("editor.js");
use("editorconfig.js");
use("config.js");
render.set_font("fonts/c64.ttf", 8);
editor.enter_editor();
});
}, },
"Edit the project in this folder. Give it the name of an UR to edit that specific object.", "Edit the project in this folder. Give it the name of an UR to edit that specific object.",
"?UR?", "?UR?",
@@ -217,65 +212,12 @@ Cmdline.register_order(
function (argv) { function (argv) {
if (argv[0]) io.chdir(argv[0]); if (argv[0]) io.chdir(argv[0]);
// game.loadurs(); if (io.exists("main.js")) app.spawn("main.js")
else app.spawn("nogame.js");
if (!io.exists(projectfile)) { var ren = use('render')
console.log("No game to play. Try making one with 'prosperon init'.");
return;
}
var project = json.decode(io.slurp(projectfile)); while(1) ren.process();
prosperon.title = project.title;
prosperon.width = 1280;
prosperon.height = 720;
prosperon.size = [1280, 720];
prosperon.icon = os.make_texture(io.slurpbytes('core/icons/moon.gif'));
prosperon.high_dpi = 0;
prosperon.alpha = 1;
prosperon.fullscreen = 0;
prosperon.sample_count = 1;
prosperon.enable_clipboard = true;
prosperon.enable_dragndrop=true;
prosperon.max_dropped_files=1;
prosperon.swap_interval = 1;
prosperon.title = "Prosperon";
prosperon.name = prosperon.title;
prosperon.version = "432r23a";
prosperon.identifier = "world.pockle.prosperon";
prosperon.creator = "Pockle World LLC"
prosperon.copyright = "Copyright Pockle World 2025"
prosperon.type = "application"
prosperon.url = "https://github.com/johnbrethauer/prosperon"
if (io.exists("config.js")) global.mixin("config.js");
else console.warn("No config.js file found. Starting with default parameters.");
prosperon.window = game.engine_start(prosperon);
var driver = "vulkan"
switch(os.sys()) {
case "Linux":
driver = "vulkan"
break
case "Windows":
// driver = "direct3d12"
driver = "vulkan"
break
case "macOS":
driver = "metal"
break
}
render._main = prosperon.window.make_gpu(false, driver);
render._main.window = prosperon.window;
render._main.claim_window(prosperon.window);
render._main.set_swapchain("sdr", "vsync");
var tt = game.texture('moon');
tt.texture.__proto__.toString = function() { return os.value_id(this); }
prosperon.init();
while(1) prosperon.process();
}, },
"Play the game present in this folder.", "Play the game present in this folder.",
); );
@@ -606,9 +548,4 @@ function convertYAMLtoJSON(yamlString) {
return jsonObj; return jsonObj;
} }
return { return {cmd_args}
Resources,
Cmdline,
cmd_args,
convertYAMLtoJSON,
};

32
scripts/transform.js Normal file
View File

@@ -0,0 +1,32 @@
var ret = {
get pos() {
return this.transform.pos;
},
set pos(x) {
this.transform.pos = x;
},
get angle() {
return this.transform.angle;
},
set angle(x) {
this.transform.angle = x;
},
get scale() {
return this.transform.scale;
},
set scale(x) {
this.transform.scale = x;
},
move(vec) {
this.pos = this.pos.add(vec);
},
rotate(x) {
this.transform.rotate([0, 0, -1],x);
},
grow(vec) {
if (typeof vec === "number") vec = [vec, vec];
this.scale = this.scale.map((x, i) => x * vec[i]);
},
}
return ret

View File

@@ -1,3 +1,5 @@
var profile = use('profile')
/* 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) { linear(t) {

View File

@@ -42,6 +42,8 @@ typedef struct rtree rtree;
#include <SDL3/SDL_gpu.h> #include <SDL3/SDL_gpu.h>
#include <SDL3/SDL_error.h> #include <SDL3/SDL_error.h>
#include <SDL3/SDL_properties.h> #include <SDL3/SDL_properties.h>
#include <SDL3/SDL_loadso.h>
#ifdef __APPLE__ #ifdef __APPLE__
#include <Accelerate/Accelerate.h> #include <Accelerate/Accelerate.h>
@@ -49,8 +51,6 @@ typedef struct rtree rtree;
//#include <cblas.h> //#include <cblas.h>
#endif #endif
#define RT_DEPTH SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT
static JSAtom width_atom; static JSAtom width_atom;
static JSAtom height_atom; static JSAtom height_atom;
static JSAtom l_atom; static JSAtom l_atom;
@@ -1126,8 +1126,6 @@ static void js_transform_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark
QJSCLASSMARK(transform) QJSCLASSMARK(transform)
QJSCLASS(font) QJSCLASS(font)
//QJSCLASS(warp_gravity)
//QJSCLASS(warp_damp)
QJSCLASS(datastream) QJSCLASS(datastream)
static JSClassID js_timer_id; static JSClassID js_timer_id;
@@ -1559,87 +1557,6 @@ int point2segindex(HMM_Vec2 p, HMM_Vec2 *segs, double slop) {
return best; return best;
} }
/*JSC_GETSET(warp_gravity, strength, number)
JSC_GETSET(warp_gravity, decay, number)
JSC_GETSET(warp_gravity, spherical, bool)
JSC_GETSET(warp_gravity, mask, bitmask)
JSC_GETSET(warp_gravity, planar_force, vec3)
static const JSCFunctionListEntry js_warp_gravity_funcs [] = {
CGETSET_ADD(warp_gravity, strength),
CGETSET_ADD(warp_gravity, decay),
CGETSET_ADD(warp_gravity, spherical),
CGETSET_ADD(warp_gravity, mask),
CGETSET_ADD(warp_gravity, planar_force),
};
JSC_GETSET(warp_damp, damp, vec3)
static const JSCFunctionListEntry js_warp_damp_funcs [] = {
CGETSET_ADD(warp_damp, damp)
};
*/
HMM_Mat4 transform2view(transform *t)
{
HMM_Vec3 look = HMM_AddV3(t->pos, transform_direction(t, vFWD));
HMM_Mat4 ret = HMM_LookAt_RH(t->pos, look, vUP);
ret = HMM_MulM4(ret, HMM_Scale(t->scale));
return ret;
}
JSC_CCALL(render_set_projection_ortho,
lrtb extents = js2lrtb(js, argv[0]);
float nearme = js2number(js,argv[1]);
float farme = js2number(js,argv[2]);
globalview.p = HMM_Orthographic_RH_ZO(
extents.l,
extents.r,
extents.b,
extents.t,
nearme,
farme
);
globalview.vp = HMM_MulM4(globalview.p, globalview.v);
)
JSC_CCALL(render_set_projection_perspective,
float fov = js2number(js,argv[0]);
float aspect = js2number(js,argv[1]);
float nearme = js2number(js,argv[2]);
float farme = js2number(js,argv[3]);
globalview.p = HMM_Perspective_RH_NO(fov, aspect, nearme, farme);
globalview.vp = HMM_MulM4(globalview.p, globalview.v);
)
JSC_CCALL(render_set_view,
globalview.v = transform2view(js2transform(js,argv[0]));
globalview.vp = HMM_MulM4(globalview.p, globalview.v);
)
JSC_SCALL(render_text_size,
font *f = js2font(js,argv[1]);
float size = js2number(js,argv[2]);
if (!size) size = f->height;
float letterSpacing = js2number(js,argv[3]);
float wrap = js2number(js,argv[4]);
ret = vec22js(js,measure_text(str, f, size, letterSpacing, wrap));
)
JSC_CCALL(render_draw_color,
SDL_Renderer *renderer = js2SDL_Renderer(js,self);
colorf rgba = js2color(js,argv[0]);
SDL_SetRenderDrawColorFloat(renderer, rgba.r, rgba.g, rgba.b, rgba.a);
)
static const JSCFunctionListEntry js_render_funcs[] = {
MIST_FUNC_DEF(render, text_size, 5),
MIST_FUNC_DEF(render, set_projection_ortho, 3),
MIST_FUNC_DEF(render, set_projection_perspective, 4),
MIST_FUNC_DEF(render, set_view, 1),
MIST_FUNC_DEF(render, draw_color, 1),
};
static JSValue idx_buffer = JS_UNDEFINED; static JSValue idx_buffer = JS_UNDEFINED;
static int idx_count = 0; static int idx_count = 0;
@@ -2738,8 +2655,21 @@ JSC_CCALL(game_engine_input,
JS_FreeValue(js,ret); JS_FreeValue(js,ret);
} }
) )
#include "wildmatch.h"
JSC_SSCALL(game_glob,
if (wildmatch(str, str2, WM_PATHNAME | WM_PERIOD | WM_WILDSTAR) == WM_MATCH)
ret = JS_NewBool(js,1);
else
ret = JS_NewBool(js, 0);
)
JSC_CCALL(game_cameras, static const JSCFunctionListEntry js_game_funcs[] = {
MIST_FUNC_DEF(game, engine_start, 1),
MIST_FUNC_DEF(game, engine_input,1),
MIST_FUNC_DEF(game, glob, 2),
};
JSC_CCALL(camera_list,
int num; int num;
SDL_CameraID *ids = SDL_GetCameras(&num); SDL_CameraID *ids = SDL_GetCameras(&num);
if (num == 0) return JS_UNDEFINED; if (num == 0) return JS_UNDEFINED;
@@ -2750,7 +2680,7 @@ JSC_CCALL(game_cameras,
return jsids; return jsids;
) )
JSC_CCALL(game_open_camera, JSC_CCALL(camera_open,
int id = js2number(js,argv[0]); int id = js2number(js,argv[0]);
SDL_Camera *cam = SDL_OpenCamera(id, NULL); SDL_Camera *cam = SDL_OpenCamera(id, NULL);
if (!cam) ret = JS_ThrowReferenceError(js, "Could not open camera %d: %s\n", id, SDL_GetError()); if (!cam) ret = JS_ThrowReferenceError(js, "Could not open camera %d: %s\n", id, SDL_GetError());
@@ -2758,22 +2688,14 @@ JSC_CCALL(game_open_camera,
ret = SDL_Camera2js(js,cam); ret = SDL_Camera2js(js,cam);
) )
#include "wildmatch.h" JSC_CCALL(camera_name,
JSC_SSCALL(game_glob,
if (wildmatch(str, str2, WM_PATHNAME | WM_PERIOD | WM_WILDSTAR) == WM_MATCH)
ret = JS_NewBool(js,1);
else
ret = JS_NewBool(js, 0);
)
JSC_CCALL(game_camera_name,
const char *name = SDL_GetCameraName(js2number(js,argv[0])); const char *name = SDL_GetCameraName(js2number(js,argv[0]));
if (!name) return JS_ThrowReferenceError(js, "Could not get camera name from id %d.", (int)js2number(js,argv[0])); if (!name) return JS_ThrowReferenceError(js, "Could not get camera name from id %d.", (int)js2number(js,argv[0]));
return JS_NewString(js, name); return JS_NewString(js, name);
) )
JSC_CCALL(game_camera_position, JSC_CCALL(camera_position,
SDL_CameraPosition pos = SDL_GetCameraPosition(js2number(js,argv[0])); SDL_CameraPosition pos = SDL_GetCameraPosition(js2number(js,argv[0]));
switch(pos) { switch(pos) {
case SDL_CAMERA_POSITION_UNKNOWN: return JS_NewString(js,"unknown"); case SDL_CAMERA_POSITION_UNKNOWN: return JS_NewString(js,"unknown");
@@ -2782,14 +2704,11 @@ JSC_CCALL(game_camera_position,
} }
) )
static const JSCFunctionListEntry js_game_funcs[] = { static const JSCFunctionListEntry js_camera_funcs[] = {
MIST_FUNC_DEF(game, engine_start, 1), MIST_FUNC_DEF(camera, list, 0),
MIST_FUNC_DEF(game, engine_input,1), MIST_FUNC_DEF(camera, open, 1),
MIST_FUNC_DEF(game, cameras, 0), MIST_FUNC_DEF(camera, name, 1),
MIST_FUNC_DEF(game, open_camera, 1), MIST_FUNC_DEF(camera, position, 1),
MIST_FUNC_DEF(game, camera_name,1),
MIST_FUNC_DEF(game, camera_position,1),
MIST_FUNC_DEF(game, glob, 2),
}; };
JSC_SCALL(SDL_Window_make_renderer, JSC_SCALL(SDL_Window_make_renderer,
@@ -5665,8 +5584,8 @@ static const JSCFunctionListEntry js_debug_funcs[] = {
MIST_FUNC_DEF(debug, build_backtrace, 0), MIST_FUNC_DEF(debug, build_backtrace, 0),
MIST_FUNC_DEF(debug, closure_vars, 1), MIST_FUNC_DEF(debug, closure_vars, 1),
MIST_FUNC_DEF(debug, local_vars, 1), MIST_FUNC_DEF(debug, local_vars, 1),
MIST_FUNC_DEF(debug,fn_info, 1), MIST_FUNC_DEF(debug, fn_info, 1),
MIST_FUNC_DEF(debug,backtrace_fns,0), MIST_FUNC_DEF(debug, backtrace_fns,0),
MIST_FUNC_DEF(debug, dump_obj, 1), MIST_FUNC_DEF(debug, dump_obj, 1),
}; };
@@ -5991,7 +5910,7 @@ static JSValue js_transform_get_parent(JSContext *js, JSValueConst self)
static JSValue js_transform_set_parent(JSContext *js, JSValueConst self, JSValue v) static JSValue js_transform_set_parent(JSContext *js, JSValueConst self, JSValue v)
{ {
transform *p = js2transform(js,v); transform *p = js2transform(js,v);
if (!p) if (!JS_IsUndefined(v) && !p)
return JS_ThrowReferenceError(js,"Parent must be another transform."); return JS_ThrowReferenceError(js,"Parent must be another transform.");
transform *t = js2transform(js,self); transform *t = js2transform(js,self);
@@ -6014,6 +5933,7 @@ static JSValue js_transform_set_parent(JSContext *js, JSValueConst self, JSValue
} }
} }
if (!p) return JS_UNDEFINED;
t->parent = p; t->parent = p;
t->jsparent = JS_DupValue(js,v); t->jsparent = JS_DupValue(js,v);
@@ -6031,6 +5951,13 @@ JSC_CCALL(transform_torect,
return rect2js(js,transform2rect(t)); return rect2js(js,transform2rect(t));
) )
JSC_CCALL(transform_children,
transform *t = js2transform(js,self);
ret = JS_NewArray(js);
for (int i = 0; i < arrlen(t->jschildren); i++)
JS_SetPropertyUint32(js,ret,i,JS_DupValue(js,t->jschildren[i]));
)
static const JSCFunctionListEntry js_transform_funcs[] = { static const JSCFunctionListEntry js_transform_funcs[] = {
CGETSET_ADD(transform, pos), CGETSET_ADD(transform, pos),
CGETSET_ADD(transform, scale), CGETSET_ADD(transform, scale),
@@ -6048,6 +5975,7 @@ static const JSCFunctionListEntry js_transform_funcs[] = {
MIST_FUNC_DEF(transform, rect, 1), MIST_FUNC_DEF(transform, rect, 1),
MIST_FUNC_DEF(transform, array, 0), MIST_FUNC_DEF(transform, array, 0),
MIST_FUNC_DEF(transform, torect, 0), MIST_FUNC_DEF(transform, torect, 0),
MIST_FUNC_DEF(transform, children, 0),
}; };
JSC_CCALL(datastream_time, return number2js(js,plm_get_time(js2datastream(js,self)->plm)); ) JSC_CCALL(datastream_time, return number2js(js,plm_get_time(js2datastream(js,self)->plm)); )
@@ -6080,11 +6008,21 @@ JSC_GET(font, height, number)
JSC_GET(font, ascent, number) JSC_GET(font, ascent, number)
JSC_GET(font, descent, number) JSC_GET(font, descent, number)
JSC_SCALL(font_text_size,
font *f = js2font(js,self);
float size = js2number(js,argv[0]);
if (!size) size = f->height;
float letterSpacing = js2number(js,argv[1]);
float wrap = js2number(js,argv[2]);
ret = vec22js(js,measure_text(str, f, size, letterSpacing, wrap));
)
static const JSCFunctionListEntry js_font_funcs[] = { static const JSCFunctionListEntry js_font_funcs[] = {
CGETSET_ADD(font, linegap), CGETSET_ADD(font, linegap),
MIST_GET(font, height), MIST_GET(font, height),
MIST_GET(font, ascent), MIST_GET(font, ascent),
MIST_GET(font, descent) MIST_GET(font, descent),
MIST_FUNC_DEF(font, text_size, 3),
}; };
const char *STRTEST = "TEST STRING"; const char *STRTEST = "TEST STRING";
@@ -7251,6 +7189,53 @@ JSC_CCALL(os_clean_transforms,
clean_all(); clean_all();
) )
typedef struct {
const char *name;
const JSCFunctionListEntry *fn;
size_t fn_count;
} ModuleEntry;
#define MISTLINE(NAME) { #NAME, js_##NAME##_funcs, countof(js_##NAME##_funcs) }
static ModuleEntry module_registry[] = {
MISTLINE(game),
MISTLINE(io),
MISTLINE(input),
MISTLINE(prosperon),
MISTLINE(time),
MISTLINE(console),
MISTLINE(profile),
MISTLINE(debug),
MISTLINE(vector),
MISTLINE(spline),
MISTLINE(performance),
MISTLINE(geometry),
MISTLINE(camera),
};
JSC_SCALL(os_use,
SDL_SharedObject *ptr = SDL_LoadObject(str);
if (!ptr) {
for (int i = 0; i < sizeof(module_registry)/sizeof(module_registry[0]); i++) {
if (strcmp(str,module_registry[i].name) == 0) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js,mod,module_registry[i].fn, module_registry[i].fn_count);
return mod;
}
}
return JS_ThrowReferenceError(js, "%s\nAnd could not find a static module.", SDL_GetError());
}
JSValue (*js_use)(JSContext*);
js_use = (JSValue (*)(JSContext*))SDL_LoadFunction(ptr, "use");
if (!js_use)
ret = JS_ThrowReferenceError(js, "Shared library %s has no use function", str);
else
ret = js_use(js);
SDL_UnloadObject(ptr);
)
static const JSCFunctionListEntry js_os_funcs[] = { static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, turbulence, 4), MIST_FUNC_DEF(os, turbulence, 4),
MIST_FUNC_DEF(os, model_buffer, 1), MIST_FUNC_DEF(os, model_buffer, 1),
@@ -7319,6 +7304,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, cull_sprites, 2), MIST_FUNC_DEF(os, cull_sprites, 2),
MIST_FUNC_DEF(os, rects_to_sprites,2), MIST_FUNC_DEF(os, rects_to_sprites,2),
MIST_FUNC_DEF(os, on, 2), MIST_FUNC_DEF(os, on, 2),
MIST_FUNC_DEF(os, use, 1),
}; };
JSC_CCALL(qtree_insert, JSC_CCALL(qtree_insert,
@@ -7356,7 +7342,7 @@ static const JSCFunctionListEntry js_qtree_funcs[] = {
MIST_FUNC_DEF(qtree, query, 2), MIST_FUNC_DEF(qtree, query, 2),
}; };
JSC_CCALL(rtree_insert, JSC_CCALL(rtree_add,
rtree *tree = js2rtree(js,self); rtree *tree = js2rtree(js,self);
JSValue v = argv[0]; JSValue v = argv[0];
rect r; rect r;
@@ -7386,7 +7372,7 @@ int rtree_cmp(const JSValue *a, const JSValue *b, JSContext *js)
return !same; return !same;
} }
JSC_CCALL(rtree_remove, JSC_CCALL(rtree_delete,
rtree *tree = js2rtree(js,self); rtree *tree = js2rtree(js,self);
JSValue v = argv[0]; JSValue v = argv[0];
rect r; rect r;
@@ -7451,16 +7437,68 @@ JSC_CCALL(rtree_query,
*/ */
) )
JSC_CCALL(rtree_count, struct rtree_each
{
JSValue fn;
JSContext *js;
};
int rtree_foreach(const NUMTYPE *min, const NUMTYPE *max, const JSValue *value, struct rtree_each *each)
{
JSValue ret = JS_Call(each->js, each->fn, JS_UNDEFINED, 0, NULL);
uncaught_exception(each->js, ret);
return 1;
}
JSC_CCALL(rtree_forEach,
rtree *tree = js2rtree(js,self); rtree *tree = js2rtree(js,self);
return number2js(js, rtree_count(tree)); struct rtree_each each;
each.fn = JS_DupValue(js,argv[0]);
each.js = js;
rtree_scan(tree, rtree_foreach, &each);
JS_FreeValue(js,each.fn);
) )
typedef struct {
JSContext *js;
JSValue v;
int has;
} rtree_has;
int rtree_hasfn(const NUMTYPE *min, const NUMTYPE *max, const JSValue *value, rtree_has *has)
{
if (JS_SameValue(has->js, has->v, *value)) {
has->has = 1;
return 0;
}
return 1;
}
JSC_CCALL(rtree_has,
rtree *tree = js2rtree(js,self);
rtree_has has;
has.js = js;
has.v = JS_DupValue(js,argv[0]);
has.has = 0;
rtree_scan(tree, rtree_hasfn, &has);
JS_FreeValue(js,argv[0]);
return JS_NewBool(js,has.has);
)
JSValue js_rtree_get_size(JSContext *js, JSValue self, int magic)
{
rtree *tree = js2rtree(js,self);
return number2js(js,rtree_count(tree));
}
static const JSCFunctionListEntry js_rtree_funcs[] = { static const JSCFunctionListEntry js_rtree_funcs[] = {
MIST_FUNC_DEF(rtree, insert, 1), MIST_FUNC_DEF(rtree, add, 1),
MIST_FUNC_DEF(rtree, remove, 1), MIST_FUNC_DEF(rtree, delete, 1),
MIST_FUNC_DEF(rtree, query, 1), MIST_FUNC_DEF(rtree, query, 1),
MIST_FUNC_DEF(rtree, count, 0), JS_CGETSET_DEF("size", js_rtree_get_size,NULL),
MIST_FUNC_DEF(rtree, forEach, 1),
MIST_FUNC_DEF(rtree, has, 1),
}; };
JSC_GETSET(sprite, layer, number) JSC_GETSET(sprite, layer, number)
@@ -7503,9 +7541,7 @@ JS_SetPropertyFunctionList(js, js_##NAME, js_##NAME##_funcs, countof(js_##NAME##
JS_SetPrototype(js, js_##NAME, PARENT); \ JS_SetPrototype(js, js_##NAME, PARENT); \
JSValue js_layout_use(JSContext *js); JSValue js_layout_use(JSContext *js);
JSValue js_miniz_use(JSContext *js);
JSValue js_soloud_use(JSContext *js); JSValue js_soloud_use(JSContext *js);
JSValue js_chipmunk2d_use(JSContext *js);
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
JSValue js_tracy_use(JSContext *js); JSValue js_tracy_use(JSContext *js);
@@ -7513,7 +7549,6 @@ JSValue js_tracy_use(JSContext *js);
#ifndef NEDITOR #ifndef NEDITOR
JSValue js_imgui(JSContext *js); JSValue js_imgui(JSContext *js);
JSValue js_dmon_use(JSContext *js);
#endif #endif
static void signal_handler(int sig) { static void signal_handler(int sig) {
@@ -7581,57 +7616,26 @@ void ffi_load(JSContext *js) {
// QJSCLASSPREP_FUNCS(SDL_GPUShader) // QJSCLASSPREP_FUNCS(SDL_GPUShader)
QJSCLASSPREP_FUNCS(SDL_GPUBuffer) QJSCLASSPREP_FUNCS(SDL_GPUBuffer)
// QJSCLASSPREP_FUNCS(SDL_GPUTransferBuffer) // QJSCLASSPREP_FUNCS(SDL_GPUTransferBuffer)
QJSCLASSPREP_FUNCS(PHYSFS_File) QJSCLASSPREP_FUNCS(PHYSFS_File)
QJSGLOBALCLASS(os);
QJSCLASSPREP_FUNCS(transform); QJSCLASSPREP_FUNCS(transform);
// QJSCLASSPREP_FUNCS(warp_gravity);
// QJSCLASSPREP_FUNCS(warp_damp);
QJSCLASSPREP_FUNCS(font); QJSCLASSPREP_FUNCS(font);
QJSCLASSPREP_FUNCS(datastream); QJSCLASSPREP_FUNCS(datastream);
QJSCLASSPREP_FUNCS(timer); QJSCLASSPREP_FUNCS(timer);
QJSGLOBALCLASS(input); QJSGLOBALCLASS(os);
QJSGLOBALCLASS(io);
QJSGLOBALCLASS(prosperon);
QJSGLOBALCLASS(time);
QJSGLOBALCLASS(console);
QJSGLOBALCLASS(profile);
QJSGLOBALCLASS(debug);
QJSGLOBALCLASS(game);
QJSGLOBALCLASS(render);
QJSGLOBALCLASS(vector);
QJSGLOBALCLASS(spline);
QJSGLOBALCLASS(performance);
QJSGLOBALCLASS(geometry);
JS_SetPropertyStr(js, prosperon, "version", JS_NewString(js,"ver"));
JS_SetPropertyStr(js, prosperon, "revision", JS_NewString(js,"com"));
JS_SetPropertyStr(js, prosperon, "date", JS_NewString(js,"date"));
JSValue array_proto = js_getpropertystr(js,globalThis, "Array"); JSValue array_proto = js_getpropertystr(js,globalThis, "Array");
array_proto = js_getpropertystr(js,array_proto, "prototype"); array_proto = js_getpropertystr(js,array_proto, "prototype");
JS_SetPropertyFunctionList(js, array_proto, js_array_funcs, countof(js_array_funcs)); JS_SetPropertyFunctionList(js, array_proto, js_array_funcs, countof(js_array_funcs));
JS_SetPropertyStr(js, globalThis, "layout", js_layout_use(js)); JS_SetPropertyStr(js, globalThis, "layout", js_layout_use(js));
JS_SetPropertyStr(js, globalThis, "miniz", js_miniz_use(js));
JS_SetPropertyStr(js, globalThis, "soloud", js_soloud_use(js)); JS_SetPropertyStr(js, globalThis, "soloud", js_soloud_use(js));
JS_SetPropertyStr(js, globalThis, "chipmunk2d", js_chipmunk2d_use(js));
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
JS_SetPropertyStr(js, globalThis, "tracy", js_tracy_use(js)); JS_SetPropertyStr(js, globalThis, "tracy", js_tracy_use(js));
#endif #endif
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGSEGV, signal_handler);
signal(SIGABRT, signal_handler);
atexit(exit_handler);
#ifndef NEDITOR #ifndef NEDITOR
JS_SetPropertyStr(js, globalThis, "dmon", js_dmon_use(js));
JS_SetPropertyStr(js, globalThis, "imgui", js_imgui(js)); JS_SetPropertyStr(js, globalThis, "imgui", js_imgui(js));
#endif #endif
@@ -7729,5 +7733,11 @@ void ffi_load(JSContext *js) {
global_js = js; global_js = js;
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGSEGV, signal_handler);
signal(SIGABRT, signal_handler);
atexit(exit_handler);
JS_FreeValue(js,globalThis); JS_FreeValue(js,globalThis);
} }

View File

@@ -125,7 +125,6 @@ JSValue TYPE##2js(JSContext *js, TYPE *n) { \
return j; }\ return j; }\
\ \
#define QJSGLOBALCLASS(NAME) \ #define QJSGLOBALCLASS(NAME) \
JSValue NAME = JS_NewObject(js); \ JSValue NAME = JS_NewObject(js); \
JS_SetPropertyFunctionList(js, NAME, js_##NAME##_funcs, countof(js_##NAME##_funcs)); \ JS_SetPropertyFunctionList(js, NAME, js_##NAME##_funcs, countof(js_##NAME##_funcs)); \

View File

@@ -16,7 +16,6 @@ static transform model = {
.jsparent = JS_UNDEFINED, .jsparent = JS_UNDEFINED,
.change_hook = JS_UNDEFINED .change_hook = JS_UNDEFINED
}; };
transform *make_transform() transform *make_transform()
{ {

View File

@@ -12,10 +12,11 @@ typedef struct transform {
HMM_Mat4 cache; HMM_Mat4 cache;
HMM_Mat4 gcache; HMM_Mat4 gcache;
int dirty; int dirty;
JSValue self;
struct transform *parent; struct transform *parent;
JSValue jsparent;
struct transform **children; struct transform **children;
JSValue self;
JSValue jsparent;
JSValue *jschildren; JSValue *jschildren;
JSValue change_hook; JSValue change_hook;
} transform; } transform;

View File

@@ -1,62 +0,0 @@
#include "warp.h"
#include "stb_ds.h"
static warp_gravity **warps = NULL;
warp_damp *warp_damp_make()
{
warp_damp *d = calloc(sizeof(*d),1);
return d;
}
void warp_damp_free(warp_damp *d) { free(d); }
warp_gravity *warp_gravity_make()
{
warp_gravity *n = calloc(sizeof(*n),1);
n->strength = 9.8;
n->t.scale = (HMM_Vec3){0,-1,0};
n->planar_force = (HMM_Vec3){0,-1,0};
arrput(warps, n);
return n;
}
void warp_gravity_free(warp_gravity *n) {
for (int i = 0; i < arrlen(warps); i++) {
if (warps[i] == n) {
arrdelswap(warps, i);
break;
}
}
free(n);
}
HMM_Vec3 warp_damp_force(warp_damp *d, HMM_Vec3 pos, HMM_Vec3 vel)
{
return HMM_MulV3(vel, d->damp);
}
HMM_Vec3 warp_gravity_force(warp_gravity *g, HMM_Vec3 pos)
{
HMM_Vec3 f = (HMM_Vec3){0,0,0};
if (g->strength == 0) return f;
if (g->spherical) {
HMM_Vec3 dir = HMM_SubV3(g->t.pos, pos);
float len = HMM_LenV3(dir);
if (len == 0) return f;
HMM_Vec3 norm = HMM_NormV3(HMM_SubV3(g->t.pos, pos));
return HMM_MulV3F(norm,g->strength);
} else {
return HMM_MulV3F(g->planar_force, g->strength);
}
}
HMM_Vec3 warp_force(HMM_Vec3 pos, warpmask mask)
{
HMM_Vec3 f = (HMM_Vec3){0,0,0};
for (int i = 0; i < arrlen(warps); i++) {
if (!(mask & warps[i]->mask)) continue;
f = HMM_AddV3(f, warp_gravity_force(warps[i], pos));
}
return f;
}

View File

@@ -1,47 +0,0 @@
#ifndef WARP_H
#define WARP_H
#include "stdint.h"
#include "transform.h"
typedef uint32_t warpmask;
#define gravmask 1U
typedef struct {
transform t;
float strength;
float decay;
int spherical;
HMM_Vec3 planar_force;
warpmask mask;
} warp_gravity;
typedef struct {
transform t;
int unlimited_range;
HMM_Vec3 range;
HMM_Vec3 falloff;
HMM_Vec3 damp;
warpmask mask;
} warp_damp;
typedef struct {
transform t;
float strength;
float decay;
float pulse; /* strength of random variance in the wind effect */
float frequency; /* when 0, pulse is smooth. Increase for very pulse over time */
float turbulence; /* When 0, pulsing is smooth and regular. Increase for more chaos. */
int spherical;
} warp_wind;
/* returns the total force for an object at pos */
HMM_Vec3 warp_force(HMM_Vec3 pos, warpmask mask);
warp_gravity *warp_gravity_make();
warp_damp *warp_damp_make();
void warp_gravity_free(warp_gravity *g);
void warp_damp_free(warp_damp *d);
#endif