refactor
This commit is contained in:
13
meson.build
13
meson.build
@@ -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(
|
||||||
|
|||||||
198
scripts/actor.js
198
scripts/actor.js
@@ -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 };
|
|
||||||
229
scripts/base.js
229
scripts/base.js
@@ -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,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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 };
|
|
||||||
@@ -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,
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -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,
|
|
||||||
};
|
|
||||||
@@ -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"));
|
|
||||||
|
|
||||||
|
|||||||
@@ -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 };
|
|
||||||
@@ -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
26
scripts/gizmos.js
Normal 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
197
scripts/graphics.js
Normal 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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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!");
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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, {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
|
||||||
};
|
|
||||||
@@ -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
37
scripts/search.js
Normal 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
29
scripts/sim.js
Normal 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
|
||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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)
|
|
||||||
|
|||||||
@@ -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
32
scripts/transform.js
Normal 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
|
||||||
@@ -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) {
|
||||||
|
|||||||
312
source/jsffi.c
312
source/jsffi.c
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)); \
|
||||||
|
|||||||
@@ -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()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
Reference in New Issue
Block a user