made engine into a small bootstrap script
This commit is contained in:
parent
6047452b62
commit
1142bfb896
|
@ -824,10 +824,6 @@ Object.defineProperty(String.prototype, 'sub', {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.defineProperty(String.prototype, 'rm', {
|
|
||||||
value: function(index, endidx = index+1) { return this.slice(0,index) + this.slice(endidx); }
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.defineProperty(String.prototype, 'updir', {
|
Object.defineProperty(String.prototype, 'updir', {
|
||||||
value: function() {
|
value: function() {
|
||||||
if (this.lastIndexOf('/') === this.length-1)
|
if (this.lastIndexOf('/') === this.length-1)
|
||||||
|
@ -1647,7 +1643,6 @@ Math.sign = function(n) { return n >= 0 ? 1 : -1; }
|
||||||
return {
|
return {
|
||||||
convert,
|
convert,
|
||||||
time,
|
time,
|
||||||
json,
|
|
||||||
Vector,
|
Vector,
|
||||||
bbox,
|
bbox,
|
||||||
yaml
|
yaml
|
||||||
|
|
|
@ -50,7 +50,7 @@ debug.draw = function() {
|
||||||
"EDIT", [0, 0], 1);
|
"EDIT", [0, 0], 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function assert(op, str = `assertion failed [value '${op}']`)
|
var assert = function(op, str = `assertion failed [value '${op}']`)
|
||||||
{
|
{
|
||||||
if (!op)
|
if (!op)
|
||||||
console.panic(str);
|
console.panic(str);
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
"use math";
|
"use math";
|
||||||
|
|
||||||
|
Object.defineProperty(String.prototype, 'rm', {
|
||||||
|
value: function(index, endidx = index+1) { return this.slice(0,index) + this.slice(endidx); }
|
||||||
|
});
|
||||||
|
|
||||||
Object.defineProperty(String.prototype, "tolast", {
|
Object.defineProperty(String.prototype, "tolast", {
|
||||||
value: function (val) {
|
value: function (val) {
|
||||||
var idx = this.lastIndexOf(val);
|
var idx = this.lastIndexOf(val);
|
||||||
|
@ -25,12 +29,23 @@ Object.defineProperty(String.prototype, "folder", {
|
||||||
|
|
||||||
globalThis.Resources = {};
|
globalThis.Resources = {};
|
||||||
|
|
||||||
Resources.rm_fn = function(fnstr, text)
|
Resources.rm_fn = function(fn, text)
|
||||||
{
|
{
|
||||||
while (text.match(fnstr)) {
|
var reg = new RegExp(fn.source + "\\s*\\(");
|
||||||
|
var match;
|
||||||
|
while (match = text.match(reg)) {
|
||||||
|
var last = match.index+match[0].length;
|
||||||
|
var par = 1;
|
||||||
|
while (par !== 0) {
|
||||||
|
if (text[last] === '(') par++;
|
||||||
|
if (text[last] === ')') par--;
|
||||||
|
last++;
|
||||||
|
}
|
||||||
|
text = text.rm(match.index, last);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
Resources.rm_fn.doc = "Remove calls to a given function from a given text script.";
|
Resources.rm_fn.doc = "Remove calls to a given function from a given text script.";
|
||||||
|
|
||||||
Resources.replpath = function (str, path) {
|
Resources.replpath = function (str, path) {
|
||||||
|
@ -58,6 +73,9 @@ Resources.replstrs = function (path) {
|
||||||
var stem = path.dir();
|
var stem = path.dir();
|
||||||
|
|
||||||
// remove console statements
|
// remove console statements
|
||||||
|
script = Resources.rm_fn(/console\.(spam|info|warn|error)/, script);
|
||||||
|
script = Resources.rm_fn(/profile\.(cache|frame|endcache|endframe)/, script);
|
||||||
|
script = Resources.rm_fn(/assert/, script);
|
||||||
//script = script.replace(/console\.(.*?)\(.*?\)/g, '');
|
//script = script.replace(/console\.(.*?)\(.*?\)/g, '');
|
||||||
//script = script.replace(/assert\(.*?\)/g, '');
|
//script = script.replace(/assert\(.*?\)/g, '');
|
||||||
|
|
||||||
|
@ -122,8 +140,6 @@ Resources.find_script = function (file) {
|
||||||
return find_ext(file, Resources.scripts);
|
return find_ext(file, Resources.scripts);
|
||||||
};
|
};
|
||||||
|
|
||||||
var t_units = ["ns", "us", "ms", "s", "m", "h"];
|
|
||||||
|
|
||||||
console.transcript = "";
|
console.transcript = "";
|
||||||
console.say = function (msg) {
|
console.say = function (msg) {
|
||||||
msg += "\n";
|
msg += "\n";
|
||||||
|
@ -200,638 +216,55 @@ console.doc = {
|
||||||
|
|
||||||
globalThis.global = globalThis;
|
globalThis.global = globalThis;
|
||||||
|
|
||||||
var use_prof = "USE";
|
var use_cache = {};
|
||||||
|
|
||||||
profile.addreport = function(){};
|
globalThis.use = function(file, env = {}, script) {
|
||||||
|
|
||||||
function use(file, env = {}, script) {
|
|
||||||
file = Resources.find_script(file);
|
file = Resources.find_script(file);
|
||||||
var st = profile.now();
|
profile.cache("USE", file);
|
||||||
|
|
||||||
if (use.cache[file]) {
|
if (use_cache[file]) {
|
||||||
var ret = use.cache[file].call(env);
|
var ret = use_cache[file].call(env);
|
||||||
profile.addreport(use_prof, file, st);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
script ??= Resources.replstrs(file);
|
script ??= Resources.replstrs(file);
|
||||||
|
|
||||||
script = `(function() { var self = this; ${script}; })`;
|
script = `(function() { var self = this; ${script}; })`;
|
||||||
var fn = os.eval(file, script);
|
var fn = os.eval(file, script);
|
||||||
use.cache[file] = fn;
|
use_cache[file] = fn;
|
||||||
var ret = fn.call(env);
|
var ret = fn.call(env);
|
||||||
profile.addreport(use_prof, file, st);
|
profile.endcache();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
use.cache = {};
|
function stripped_use (file, env = {}, script) {
|
||||||
|
file = Resources.find_script(file);
|
||||||
|
|
||||||
global.check_registers = function (obj) {
|
if (use_cache[file]) {
|
||||||
for (var reg in Register.registries) {
|
var ret = use_cache[file].call(env);
|
||||||
if (typeof obj[reg] === 'function') {
|
return;
|
||||||
var fn = obj[reg].bind(obj);
|
|
||||||
var name = obj.ur ? obj.ur.name : obj.toString();
|
|
||||||
obj.timers.push(Register.registries[reg].register(fn, name));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
script ??= Resources.replstrs(file);
|
||||||
|
|
||||||
for (var k in obj) {
|
script = `(function() { var self = this; ${script}; })`;
|
||||||
if (!k.startsWith("on_")) continue;
|
var fn = os.eval(file, script);
|
||||||
var signal = k.fromfirst("on_");
|
var ret = fn.call(env);
|
||||||
Event.observe(signal, obj, obj[k]);
|
profile.endcache();
|
||||||
}
|
return ret;
|
||||||
};
|
}
|
||||||
|
|
||||||
Object.assign(global, use("scripts/base"));
|
function bare_use(file)
|
||||||
global.obscure("global");
|
|
||||||
global.mixin("scripts/profile");
|
|
||||||
global.mixin("scripts/render");
|
|
||||||
global.mixin("scripts/debug");
|
|
||||||
|
|
||||||
var frame_t = profile.secs(profile.now());
|
|
||||||
|
|
||||||
var sim = {};
|
|
||||||
sim.mode = "play";
|
|
||||||
sim.play = function () {
|
|
||||||
this.mode = "play";
|
|
||||||
os.reindex_static();
|
|
||||||
};
|
|
||||||
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 physlag = 0;
|
|
||||||
|
|
||||||
var gggstart = game.engine_start;
|
|
||||||
game.engine_start = function (s) {
|
|
||||||
game.startengine = 1;
|
|
||||||
gggstart(
|
|
||||||
function () {
|
|
||||||
global.mixin("scripts/sound.js");
|
|
||||||
world_start();
|
|
||||||
window.set_icon(os.make_texture("icons/moon.gif"));
|
|
||||||
Object.readonly(window.__proto__, "vsync");
|
|
||||||
Object.readonly(window.__proto__, "enable_dragndrop");
|
|
||||||
Object.readonly(window.__proto__, "enable_clipboard");
|
|
||||||
Object.readonly(window.__proto__, "high_dpi");
|
|
||||||
Object.readonly(window.__proto__, "sample_count");
|
|
||||||
s();
|
|
||||||
|
|
||||||
shape.quad = {
|
|
||||||
pos: os.make_buffer([0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0], 0),
|
|
||||||
verts: 4,
|
|
||||||
uv: os.make_buffer([0, 1, 1, 1, 0, 0, 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, 2, 1], 1),
|
|
||||||
};
|
|
||||||
|
|
||||||
shape.centered_quad = {
|
|
||||||
pos: os.make_buffer([-0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5], 0),
|
|
||||||
verts: 4,
|
|
||||||
uv: os.make_buffer([0,1,1,1,0,0,1,0],2),
|
|
||||||
index: os.make_buffer([0,1,2,2,1,3],1),
|
|
||||||
count: 6
|
|
||||||
};
|
|
||||||
|
|
||||||
render.init();
|
|
||||||
|
|
||||||
camera = make_camera();
|
|
||||||
camera.transform.pos = [0,0,-100];
|
|
||||||
camera.mode = "keep";
|
|
||||||
camera.break = "fit";
|
|
||||||
camera.size = game.size;
|
|
||||||
gamestate.camera = camera;
|
|
||||||
|
|
||||||
hudcam = make_camera();
|
|
||||||
hudcam.near = 0;
|
|
||||||
hudcam.size = camera.size;
|
|
||||||
hudcam.mode = "keep";
|
|
||||||
hudcam.break = "fit";
|
|
||||||
|
|
||||||
appcam = make_camera();
|
|
||||||
appcam.near = 0;
|
|
||||||
appcam.size = window.size;
|
|
||||||
appcam.transform.pos = [window.size.x,window.size.y,-100];
|
|
||||||
screencolor = render.screencolor();
|
|
||||||
},
|
|
||||||
process,
|
|
||||||
window.size.x,
|
|
||||||
window.size.y,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
game.startengine = 0;
|
|
||||||
var frames = [];
|
|
||||||
|
|
||||||
prosperon.release_mode = function()
|
|
||||||
{
|
{
|
||||||
prosperon.debug = false;
|
var script = io.slurp(file);
|
||||||
mum.debug = false;
|
script = `(function() { var self = this; ${script}; })`;
|
||||||
debug.kill();
|
Object.assign(globalThis, os.eval(file, script).call(globalThis));
|
||||||
}
|
|
||||||
prosperon.debug = true;
|
|
||||||
|
|
||||||
// Returns an array in the form of [left, bottom, right, top] in pixels of the camera to render to
|
|
||||||
// Camera viewport is [left,bottom,right,top] in relative values
|
|
||||||
function camviewport()
|
|
||||||
{
|
|
||||||
var aspect = (this.viewport[2]-this.viewport[0])/(this.viewport[3]-this.viewport[1])*window.size.x/window.size.y;
|
|
||||||
var raspect = this.size.x/this.size.y;
|
|
||||||
|
|
||||||
var left = this.viewport[0]*window.size.x;
|
|
||||||
var bottom = this.viewport[1]*window.size.y;
|
|
||||||
|
|
||||||
var usemode = this.mode;
|
|
||||||
|
|
||||||
if (this.break && this.size.x > window.size.x && this.size.y > window.size.y)
|
|
||||||
usemode = this.break;
|
|
||||||
|
|
||||||
if (usemode === "fit")
|
|
||||||
if (raspect < aspect) usemode = "height";
|
|
||||||
else usemode = "width";
|
|
||||||
|
|
||||||
switch(usemode) {
|
|
||||||
case "stretch":
|
|
||||||
case "expand":
|
|
||||||
return [0, 0, window.size.x, window.size.y];
|
|
||||||
case "keep":
|
|
||||||
return [left, bottom, left+this.size.x, bottom+this.size.y];
|
|
||||||
case "height":
|
|
||||||
var ret = [left, 0, this.size.x*(window.size.y/this.size.y), window.size.y];
|
|
||||||
ret[0] = (window.size.x-(ret[2]-ret[0]))/2;
|
|
||||||
return ret;
|
|
||||||
case "width":
|
|
||||||
var ret = [0, bottom, window.size.x, this.size.y*(window.size.x/this.size.x)];
|
|
||||||
ret[1] = (window.size.y-(ret[3]-ret[1]))/2;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [0, 0, window.size.x, window.size.y];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// pos is pixels on the screen, lower left[0,0]
|
profile.enabled = false;
|
||||||
function camscreen2world(pos)
|
|
||||||
{
|
|
||||||
var view = this.screen2cam(pos);
|
|
||||||
view.x *= this.size.x;
|
|
||||||
view.y *= this.size.y;
|
|
||||||
view = view.sub([this.size.x/2, this.size.y/2]);
|
|
||||||
view = view.add(this.pos.xy);
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
camscreen2world.doc = "Convert a view position for a camera to world."
|
bare_use("scripts/base.js");
|
||||||
|
bare_use("scripts/profile.js");
|
||||||
|
|
||||||
function screen2cam(pos)
|
if (!profile.enabled)
|
||||||
{
|
use = stripped_use;
|
||||||
var viewport = this.view();
|
|
||||||
var width = viewport[2]-viewport[0];
|
|
||||||
var height = viewport[3]-viewport[1];
|
|
||||||
var left = pos.x-viewport[0];
|
|
||||||
var bottom = pos.y-viewport[1];
|
|
||||||
var p = [left/width, bottom/height];
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
screen2cam.doc = "Convert a screen space position in pixels to a normalized viewport position in a camera."
|
Object.assign(globalThis, use("scripts/prosperon.js"));
|
||||||
|
|
||||||
function make_camera()
|
|
||||||
{
|
|
||||||
var cam = world.spawn();
|
|
||||||
cam.near = 0.1;
|
|
||||||
cam.far = 1000;
|
|
||||||
cam.ortho = true;
|
|
||||||
cam.viewport = [0,0,1,1];
|
|
||||||
cam.size = window.size.slice(); // The render size of this camera in pixels
|
|
||||||
// In ortho mode, this determines how many pixels it will see
|
|
||||||
cam.mode = "stretch";
|
|
||||||
cam.screen2world = camscreen2world;
|
|
||||||
cam.screen2cam = screen2cam;
|
|
||||||
|
|
||||||
cam.mousepos = function() { return this.screen2world(input.mouse.screenpos()); }
|
|
||||||
cam.view = camviewport;
|
|
||||||
cam.offscreen = false;
|
|
||||||
return cam;
|
|
||||||
}
|
|
||||||
|
|
||||||
var camera;
|
|
||||||
var hudcam;
|
|
||||||
var appcam;
|
|
||||||
var screencolor;
|
|
||||||
|
|
||||||
prosperon.render = function()
|
|
||||||
{
|
|
||||||
profile.frame("world");
|
|
||||||
render.set_camera(camera);
|
|
||||||
profile.frame("sprites");
|
|
||||||
render.sprites();
|
|
||||||
profile.endframe();
|
|
||||||
profile.frame("draws");
|
|
||||||
prosperon.draw();
|
|
||||||
profile.endframe();
|
|
||||||
hudcam.size = camera.size;
|
|
||||||
hudcam.transform.pos = [hudcam.size.x/2, hudcam.size.y/2, -100];
|
|
||||||
render.set_camera(hudcam);
|
|
||||||
|
|
||||||
profile.endframe();
|
|
||||||
profile.frame("hud");
|
|
||||||
|
|
||||||
prosperon.hud();
|
|
||||||
render.flush_text();
|
|
||||||
|
|
||||||
render.end_pass();
|
|
||||||
|
|
||||||
profile.endframe();
|
|
||||||
|
|
||||||
profile.frame("post process");
|
|
||||||
/* draw the image of the game world first */
|
|
||||||
render.glue_pass();
|
|
||||||
render.viewport(...camera.view());
|
|
||||||
render.use_shader(render.postshader);
|
|
||||||
render.use_mat({diffuse:screencolor});
|
|
||||||
render.draw(shape.quad);
|
|
||||||
|
|
||||||
profile.endframe();
|
|
||||||
|
|
||||||
profile.frame("app");
|
|
||||||
|
|
||||||
// Flush & render
|
|
||||||
appcam.transform.pos = [window.size.x/2, window.size.y/2, -100];
|
|
||||||
appcam.size = window.size.slice();
|
|
||||||
if (os.sys() !== 'macos')
|
|
||||||
appcam.size.y *= -1;
|
|
||||||
|
|
||||||
render.set_camera(appcam);
|
|
||||||
render.viewport(...appcam.view());
|
|
||||||
|
|
||||||
// Call gui functions
|
|
||||||
mum.style = mum.dbg_style;
|
|
||||||
prosperon.gui();
|
|
||||||
if (mum.drawinput) mum.drawinput();
|
|
||||||
prosperon.gui_dbg();
|
|
||||||
render.flush_text();
|
|
||||||
mum.style = mum.base;
|
|
||||||
|
|
||||||
profile.endframe();
|
|
||||||
|
|
||||||
profile.frame("imgui");
|
|
||||||
|
|
||||||
render.imgui_new(window.size.x, window.size.y, 0.01);
|
|
||||||
prosperon.imgui();
|
|
||||||
render.imgui_end();
|
|
||||||
|
|
||||||
profile.endframe();
|
|
||||||
|
|
||||||
render.end_pass();
|
|
||||||
render.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
function process() {
|
|
||||||
profile.frame("frame");
|
|
||||||
var dt = profile.secs(profile.now()) - frame_t;
|
|
||||||
frame_t = profile.secs(profile.now());
|
|
||||||
|
|
||||||
profile.frame("app update");
|
|
||||||
prosperon.appupdate(dt);
|
|
||||||
profile.endframe();
|
|
||||||
|
|
||||||
profile.frame("input");
|
|
||||||
input.procdown();
|
|
||||||
profile.endframe();
|
|
||||||
|
|
||||||
if (sim.mode === "play" || sim.mode === "step") {
|
|
||||||
profile.frame("update");
|
|
||||||
prosperon.update(dt * game.timescale);
|
|
||||||
profile.endframe();
|
|
||||||
if (sim.mode === "step") sim.pause();
|
|
||||||
|
|
||||||
profile.frame("physics");
|
|
||||||
physlag += dt;
|
|
||||||
|
|
||||||
while (physlag > physics.delta) {
|
|
||||||
physlag -= physics.delta;
|
|
||||||
prosperon.phys2d_step(physics.delta * game.timescale);
|
|
||||||
prosperon.physupdate(physics.delta * game.timescale);
|
|
||||||
}
|
|
||||||
profile.endframe();
|
|
||||||
}
|
|
||||||
|
|
||||||
profile.frame("render");
|
|
||||||
prosperon.window_render(window.size);
|
|
||||||
prosperon.render();
|
|
||||||
profile.endframe();
|
|
||||||
|
|
||||||
profile.endframe();
|
|
||||||
}
|
|
||||||
|
|
||||||
globalThis.fps = function () {
|
|
||||||
return 0;
|
|
||||||
// var sum = 0;
|
|
||||||
// for (var i = 0; i < frames.length; i++) sum += frames[i];
|
|
||||||
// return frames.length / sum;
|
|
||||||
};
|
|
||||||
|
|
||||||
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.";
|
|
||||||
game.doc.camera = "Current camera.";
|
|
||||||
|
|
||||||
game.texture = function (path, force = false) {
|
|
||||||
if (force && game.texture.cache[path]) return game.texture.cache[path];
|
|
||||||
|
|
||||||
if (!io.exists(path)) {
|
|
||||||
console.warn(`Missing texture: ${path}`);
|
|
||||||
game.texture.cache[path] = game.texture("icons/no_tex.gif");
|
|
||||||
} else game.texture.cache[path] ??= os.make_texture(path);
|
|
||||||
|
|
||||||
return game.texture.cache[path];
|
|
||||||
};
|
|
||||||
game.texture.cache = {};
|
|
||||||
|
|
||||||
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.doc =
|
|
||||||
"Functions for semantic versioning numbers. Semantic versioning is given as a triple digit number, as MAJOR.MINOR.PATCH.";
|
|
||||||
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.`;
|
|
||||||
|
|
||||||
prosperon.iconified = function (icon) {};
|
|
||||||
prosperon.focus = function (focus) {};
|
|
||||||
prosperon.resize = function (dimensions) {
|
|
||||||
window.size.x = dimensions.x;
|
|
||||||
window.size.y = dimensions.y;
|
|
||||||
};
|
|
||||||
prosperon.suspended = function (sus) {};
|
|
||||||
prosperon.mouseenter = function () {};
|
|
||||||
prosperon.mouseleave = function () {};
|
|
||||||
prosperon.touchpress = function (touches) {};
|
|
||||||
prosperon.touchrelease = function (touches) {};
|
|
||||||
prosperon.touchmove = function (touches) {};
|
|
||||||
prosperon.clipboardpaste = function (str) {};
|
|
||||||
prosperon.quit = function () {
|
|
||||||
if (profile.disabled) return;
|
|
||||||
|
|
||||||
say("===START CACHE REPORTS===\n");
|
|
||||||
for (var i in profile.report_cache) {
|
|
||||||
say(profile.printreport(profile.report_cache[i],i));
|
|
||||||
}
|
|
||||||
|
|
||||||
say("===FRAME AVERAGES===\n");
|
|
||||||
say(profile.print_frame_avg());
|
|
||||||
say("\n");
|
|
||||||
|
|
||||||
profile.print_cpu_instr();
|
|
||||||
};
|
|
||||||
|
|
||||||
window.size = [640, 480];
|
|
||||||
window.mode = "keep";
|
|
||||||
window.toggle_fullscreen = function() { window.fullscreen = !window.fullscreen; }
|
|
||||||
|
|
||||||
window.set_icon.doc = "Set the icon of the window using the PNG image at path.";
|
|
||||||
|
|
||||||
window.doc = {};
|
|
||||||
window.doc.dimensions = "Window width and height packaged in an array [width,height]";
|
|
||||||
window.doc.title = "Name in the title bar of the window.";
|
|
||||||
window.doc.boundingbox = "Boundingbox of the window, with top and right being its height and width.";
|
|
||||||
|
|
||||||
global.mixin("scripts/input");
|
|
||||||
global.mixin("scripts/std");
|
|
||||||
global.mixin("scripts/diff");
|
|
||||||
global.mixin("scripts/color");
|
|
||||||
global.mixin("scripts/gui");
|
|
||||||
global.mixin("scripts/tween");
|
|
||||||
global.mixin("scripts/ai");
|
|
||||||
global.mixin("scripts/particle");
|
|
||||||
|
|
||||||
var timer = {
|
|
||||||
update(dt) {
|
|
||||||
this.remain -= dt;
|
|
||||||
if (this.remain <= 0) {
|
|
||||||
this.fn();
|
|
||||||
this.kill();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
kill() {
|
|
||||||
this.end();
|
|
||||||
delete this.fn;
|
|
||||||
},
|
|
||||||
|
|
||||||
delay(fn, secs) {
|
|
||||||
var t = Object.create(this);
|
|
||||||
t.time = secs;
|
|
||||||
t.remain = secs;
|
|
||||||
t.fn = fn;
|
|
||||||
t.end = Register.update.register(timer.update.bind(t));
|
|
||||||
var returnfn = timer.kill.bind(t);
|
|
||||||
returnfn.remain = secs;
|
|
||||||
return returnfn;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
global.mixin("scripts/physics");
|
|
||||||
global.mixin("scripts/geometry");
|
|
||||||
|
|
||||||
/*
|
|
||||||
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, e_event = false) {
|
|
||||||
var n = {};
|
|
||||||
var fns = [];
|
|
||||||
|
|
||||||
n.register = function (fn, oname) {
|
|
||||||
if (!(fn instanceof Function)) return;
|
|
||||||
|
|
||||||
var dofn = function(...args) {
|
|
||||||
var st = profile.now();
|
|
||||||
fn(...args);
|
|
||||||
profile.addreport(name, oname, st);
|
|
||||||
}
|
|
||||||
|
|
||||||
fns.push(dofn);
|
|
||||||
return function () {
|
|
||||||
fns.remove(dofn);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
prosperon[name] = function (...args) {
|
|
||||||
fns.forEach(x => x(...args));
|
|
||||||
};
|
|
||||||
|
|
||||||
prosperon[name].fns = fns;
|
|
||||||
n.clear = function () {
|
|
||||||
fns = [];
|
|
||||||
};
|
|
||||||
|
|
||||||
Register[name] = n;
|
|
||||||
Register.registries[name] = n;
|
|
||||||
|
|
||||||
return n;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Register.add_cb("appupdate", true);
|
|
||||||
Register.add_cb("update", true).doc = "Called once per frame.";
|
|
||||||
Register.add_cb("physupdate", true);
|
|
||||||
Register.add_cb("gui", true);
|
|
||||||
Register.add_cb("hud", true);
|
|
||||||
Register.add_cb("draw_dbg", true);
|
|
||||||
Register.add_cb("gui_dbg", true);
|
|
||||||
Register.add_cb("hud_dbg", true);
|
|
||||||
Register.add_cb("draw", true);
|
|
||||||
Register.add_cb("imgui", true);
|
|
||||||
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
global.mixin("scripts/spline");
|
|
||||||
global.mixin("scripts/components");
|
|
||||||
global.mixin("scripts/actor");
|
|
||||||
global.mixin("scripts/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 = {};
|
|
||||||
game.cam = world;
|
|
||||||
}
|
|
||||||
|
|
||||||
global.mixin("scripts/physics");
|
|
||||||
global.mixin("scripts/widget");
|
|
||||||
global.mixin("scripts/mum");
|
|
||||||
|
|
||||||
window.title = `Prosperon v${prosperon.version}`;
|
|
||||||
window.size = [500, 500];
|
|
||||||
|
|
|
@ -138,7 +138,7 @@ var entity = {
|
||||||
},
|
},
|
||||||
|
|
||||||
spawn(text, config, callback) {
|
spawn(text, config, callback) {
|
||||||
var st = profile.now();
|
|
||||||
var ent = Object.create(entity);
|
var ent = Object.create(entity);
|
||||||
ent.transform = os.make_transform();
|
ent.transform = os.make_transform();
|
||||||
|
|
||||||
|
@ -165,7 +165,8 @@ var entity = {
|
||||||
use(text, ent);
|
use(text, ent);
|
||||||
else if (Array.isArray(text))
|
else if (Array.isArray(text))
|
||||||
for (var path of text) use(path,ent);
|
for (var path of text) use(path,ent);
|
||||||
|
profile.cache("ENTITY TIME", ent.ur.name);
|
||||||
|
var st = profile.now();
|
||||||
if (typeof config === 'string')
|
if (typeof config === 'string')
|
||||||
Object.merge(ent, json.decode(Resources.replstrs(config)));
|
Object.merge(ent, json.decode(Resources.replstrs(config)));
|
||||||
else if (Array.isArray(config))
|
else if (Array.isArray(config))
|
||||||
|
@ -230,7 +231,7 @@ var entity = {
|
||||||
for (var i in ent.objects)
|
for (var i in ent.objects)
|
||||||
ent.ur.fresh.objects[i] = ent.objects[i].instance_obj();
|
ent.ur.fresh.objects[i] = ent.objects[i].instance_obj();
|
||||||
|
|
||||||
profile.addreport("ENTITY TIME", ent.ur.name, st);
|
profile.endcache();
|
||||||
|
|
||||||
return ent;
|
return ent;
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
var t_units = ["ns", "us", "ms", "s", "m", "h"];
|
||||||
|
|
||||||
profile.cpu = function(fn, times = 1, q = "unnamed") {
|
profile.cpu = function(fn, times = 1, q = "unnamed") {
|
||||||
var start = profile.now();
|
var start = profile.now();
|
||||||
for (var i = 0; i < times; i++)
|
for (var i = 0; i < times; i++)
|
||||||
|
@ -33,6 +35,7 @@ function add_callgraph(fn, line, time) {
|
||||||
var hittar = 500;
|
var hittar = 500;
|
||||||
var hitpct = 0.2;
|
var hitpct = 0.2;
|
||||||
var start_gather = profile.now();
|
var start_gather = profile.now();
|
||||||
|
if (profile.enabled)
|
||||||
profile.gather(hittar, function() {
|
profile.gather(hittar, function() {
|
||||||
var time = profile.now()-st;
|
var time = profile.now()-st;
|
||||||
|
|
||||||
|
@ -141,19 +144,43 @@ profile.print_frame_avg = function()
|
||||||
print_frame(profile_frames[i], "", 'frame');
|
print_frame(profile_frames[i], "", 'frame');
|
||||||
}
|
}
|
||||||
|
|
||||||
profile.report_cache = {};
|
var report_cache = {};
|
||||||
|
|
||||||
profile.addreport = function (group, line, start) {
|
var cachest = 0;
|
||||||
|
var cachegroup;
|
||||||
|
var cachetitle;
|
||||||
|
profile.cache = function(group, title)
|
||||||
|
{
|
||||||
|
cachest = profile.now();
|
||||||
|
cachegroup = group;
|
||||||
|
cachetitle = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.endcache = function(tag = "")
|
||||||
|
{
|
||||||
|
addreport(cachegroup, cachetitle + tag, cachest);
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.print_cache_report = function()
|
||||||
|
{
|
||||||
|
var str = "===START CACHE REPORTS===\n";
|
||||||
|
for (var i in report_cache)
|
||||||
|
str += printreport(report_cache[i], i) + "\n";
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addreport(group, line, start) {
|
||||||
if (typeof group !== 'string') group = 'UNGROUPED';
|
if (typeof group !== 'string') group = 'UNGROUPED';
|
||||||
profile.report_cache[group] ??= {};
|
report_cache[group] ??= {};
|
||||||
var cache = profile.report_cache[group];
|
var cache = report_cache[group];
|
||||||
cache[line] ??= [];
|
cache[line] ??= [];
|
||||||
var t = profile.now();
|
var t = profile.now();
|
||||||
cache[line].push(t - start);
|
cache[line].push(t - start);
|
||||||
return t;
|
return t;
|
||||||
};
|
};
|
||||||
|
|
||||||
profile.printreport = function (cache, name) {
|
function printreport(cache, name) {
|
||||||
var report = `==${name}==` + "\n";
|
var report = `==${name}==` + "\n";
|
||||||
|
|
||||||
var reports = [];
|
var reports = [];
|
||||||
|
@ -176,12 +203,3 @@ profile.printreport = function (cache, name) {
|
||||||
|
|
||||||
return report;
|
return report;
|
||||||
};
|
};
|
||||||
|
|
||||||
var null_fn = function(){};
|
|
||||||
profile.disable = function()
|
|
||||||
{
|
|
||||||
profile.gather_stop();
|
|
||||||
profile.frame = null_fn;
|
|
||||||
profile.endframe = null_fn;
|
|
||||||
profile.disabled = true;
|
|
||||||
}
|
|
425
scripts/prosperon.js
Normal file
425
scripts/prosperon.js
Normal file
|
@ -0,0 +1,425 @@
|
||||||
|
global.check_registers = function (obj) {
|
||||||
|
for (var reg in Register.registries) {
|
||||||
|
if (typeof obj[reg] === 'function') {
|
||||||
|
var fn = obj[reg].bind(obj);
|
||||||
|
var name = obj.ur ? obj.ur.name : obj.toString();
|
||||||
|
obj.timers.push(Register.registries[reg].register(fn, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var k in obj) {
|
||||||
|
if (!k.startsWith("on_")) continue;
|
||||||
|
var signal = k.fromfirst("on_");
|
||||||
|
Event.observe(signal, obj, obj[k]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
global.obscure("global");
|
||||||
|
global.mixin("scripts/render");
|
||||||
|
global.mixin("scripts/debug");
|
||||||
|
|
||||||
|
var frame_t = profile.secs(profile.now());
|
||||||
|
|
||||||
|
var sim = {};
|
||||||
|
sim.mode = "play";
|
||||||
|
sim.play = function () {
|
||||||
|
this.mode = "play";
|
||||||
|
os.reindex_static();
|
||||||
|
};
|
||||||
|
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 physlag = 0;
|
||||||
|
|
||||||
|
var gggstart = game.engine_start;
|
||||||
|
game.engine_start = function (s) {
|
||||||
|
game.startengine = 1;
|
||||||
|
gggstart(
|
||||||
|
function () {
|
||||||
|
global.mixin("scripts/sound.js");
|
||||||
|
world_start();
|
||||||
|
window.set_icon(os.make_texture("icons/moon.gif"));
|
||||||
|
Object.readonly(window.__proto__, "vsync");
|
||||||
|
Object.readonly(window.__proto__, "enable_dragndrop");
|
||||||
|
Object.readonly(window.__proto__, "enable_clipboard");
|
||||||
|
Object.readonly(window.__proto__, "high_dpi");
|
||||||
|
Object.readonly(window.__proto__, "sample_count");
|
||||||
|
s();
|
||||||
|
|
||||||
|
shape.quad = {
|
||||||
|
pos: os.make_buffer([0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0], 0),
|
||||||
|
verts: 4,
|
||||||
|
uv: os.make_buffer([0, 1, 1, 1, 0, 0, 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, 2, 1], 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
shape.centered_quad = {
|
||||||
|
pos: os.make_buffer([-0.5, -0.5, -0.5, 0.5, -0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5], 0),
|
||||||
|
verts: 4,
|
||||||
|
uv: os.make_buffer([0,1,1,1,0,0,1,0],2),
|
||||||
|
index: os.make_buffer([0,1,2,2,1,3],1),
|
||||||
|
count: 6
|
||||||
|
};
|
||||||
|
|
||||||
|
render.init();
|
||||||
|
|
||||||
|
prosperon.camera = prosperon.make_camera();
|
||||||
|
var camera = prosperon.camera;
|
||||||
|
camera.transform.pos = [0,0,-100];
|
||||||
|
camera.mode = "keep";
|
||||||
|
camera.break = "fit";
|
||||||
|
camera.size = game.size;
|
||||||
|
gamestate.camera = camera;
|
||||||
|
|
||||||
|
prosperon.hudcam = prosperon.make_camera();
|
||||||
|
var hudcam = prosperon.hudcam;
|
||||||
|
hudcam.near = 0;
|
||||||
|
hudcam.size = camera.size;
|
||||||
|
hudcam.mode = "keep";
|
||||||
|
hudcam.break = "fit";
|
||||||
|
|
||||||
|
prosperon.appcam = prosperon.make_camera();
|
||||||
|
var appcam = prosperon.appcam;
|
||||||
|
appcam.near = 0;
|
||||||
|
appcam.size = window.size;
|
||||||
|
appcam.transform.pos = [window.size.x,window.size.y,-100];
|
||||||
|
prosperon.screencolor = render.screencolor();
|
||||||
|
},
|
||||||
|
prosperon.process,
|
||||||
|
window.size.x,
|
||||||
|
window.size.y,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
game.startengine = 0;
|
||||||
|
var frames = [];
|
||||||
|
|
||||||
|
prosperon.release_mode = function()
|
||||||
|
{
|
||||||
|
prosperon.debug = false;
|
||||||
|
mum.debug = false;
|
||||||
|
debug.kill();
|
||||||
|
}
|
||||||
|
prosperon.debug = true;
|
||||||
|
|
||||||
|
globalThis.fps = function () {
|
||||||
|
return 0;
|
||||||
|
// var sum = 0;
|
||||||
|
// for (var i = 0; i < frames.length; i++) sum += frames[i];
|
||||||
|
// return frames.length / sum;
|
||||||
|
};
|
||||||
|
|
||||||
|
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.";
|
||||||
|
game.doc.camera = "Current camera.";
|
||||||
|
|
||||||
|
game.texture = function (path, force = false) {
|
||||||
|
if (force && game.texture.cache[path]) return game.texture.cache[path];
|
||||||
|
|
||||||
|
if (!io.exists(path)) {
|
||||||
|
console.warn(`Missing texture: ${path}`);
|
||||||
|
game.texture.cache[path] = game.texture("icons/no_tex.gif");
|
||||||
|
} else game.texture.cache[path] ??= os.make_texture(path);
|
||||||
|
|
||||||
|
return game.texture.cache[path];
|
||||||
|
};
|
||||||
|
game.texture.cache = {};
|
||||||
|
|
||||||
|
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.doc =
|
||||||
|
"Functions for semantic versioning numbers. Semantic versioning is given as a triple digit number, as MAJOR.MINOR.PATCH.";
|
||||||
|
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.`;
|
||||||
|
|
||||||
|
prosperon.iconified = function (icon) {};
|
||||||
|
prosperon.focus = function (focus) {};
|
||||||
|
prosperon.resize = function (dimensions) {
|
||||||
|
window.size.x = dimensions.x;
|
||||||
|
window.size.y = dimensions.y;
|
||||||
|
};
|
||||||
|
prosperon.suspended = function (sus) {};
|
||||||
|
prosperon.mouseenter = function () {};
|
||||||
|
prosperon.mouseleave = function () {};
|
||||||
|
prosperon.touchpress = function (touches) {};
|
||||||
|
prosperon.touchrelease = function (touches) {};
|
||||||
|
prosperon.touchmove = function (touches) {};
|
||||||
|
prosperon.clipboardpaste = function (str) {};
|
||||||
|
prosperon.quit = function () {
|
||||||
|
if (!profile.enabled) return;
|
||||||
|
|
||||||
|
say(profile.print_cache_report());
|
||||||
|
|
||||||
|
say("===FRAME AVERAGES===\n");
|
||||||
|
say(profile.print_frame_avg());
|
||||||
|
say("\n");
|
||||||
|
|
||||||
|
profile.print_cpu_instr();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.size = [640, 480];
|
||||||
|
window.mode = "keep";
|
||||||
|
window.toggle_fullscreen = function() { window.fullscreen = !window.fullscreen; }
|
||||||
|
|
||||||
|
window.set_icon.doc = "Set the icon of the window using the PNG image at path.";
|
||||||
|
|
||||||
|
window.doc = {};
|
||||||
|
window.doc.dimensions = "Window width and height packaged in an array [width,height]";
|
||||||
|
window.doc.title = "Name in the title bar of the window.";
|
||||||
|
window.doc.boundingbox = "Boundingbox of the window, with top and right being its height and width.";
|
||||||
|
|
||||||
|
global.mixin("scripts/input");
|
||||||
|
global.mixin("scripts/std");
|
||||||
|
global.mixin("scripts/diff");
|
||||||
|
global.mixin("scripts/color");
|
||||||
|
global.mixin("scripts/gui");
|
||||||
|
global.mixin("scripts/tween");
|
||||||
|
global.mixin("scripts/ai");
|
||||||
|
global.mixin("scripts/particle");
|
||||||
|
|
||||||
|
var timer = {
|
||||||
|
update(dt) {
|
||||||
|
this.remain -= dt;
|
||||||
|
if (this.remain <= 0) {
|
||||||
|
this.fn();
|
||||||
|
this.kill();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
kill() {
|
||||||
|
this.end();
|
||||||
|
delete this.fn;
|
||||||
|
},
|
||||||
|
|
||||||
|
delay(fn, secs) {
|
||||||
|
var t = Object.create(this);
|
||||||
|
t.time = secs;
|
||||||
|
t.remain = secs;
|
||||||
|
t.fn = fn;
|
||||||
|
t.end = Register.update.register(timer.update.bind(t));
|
||||||
|
var returnfn = timer.kill.bind(t);
|
||||||
|
returnfn.remain = secs;
|
||||||
|
return returnfn;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
global.mixin("scripts/physics");
|
||||||
|
global.mixin("scripts/geometry");
|
||||||
|
|
||||||
|
/*
|
||||||
|
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, e_event = false) {
|
||||||
|
var n = {};
|
||||||
|
var fns = [];
|
||||||
|
|
||||||
|
n.register = function (fn, oname) {
|
||||||
|
if (!(fn instanceof Function)) return;
|
||||||
|
|
||||||
|
var dofn = function(...args) {
|
||||||
|
profile.cache(name,oname);
|
||||||
|
var st = profile.now();
|
||||||
|
fn(...args);
|
||||||
|
profile.endcache();
|
||||||
|
}
|
||||||
|
|
||||||
|
fns.push(dofn);
|
||||||
|
return function () {
|
||||||
|
fns.remove(dofn);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
prosperon[name] = function (...args) {
|
||||||
|
fns.forEach(x => x(...args));
|
||||||
|
};
|
||||||
|
|
||||||
|
prosperon[name].fns = fns;
|
||||||
|
n.clear = function () {
|
||||||
|
fns = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
Register[name] = n;
|
||||||
|
Register.registries[name] = n;
|
||||||
|
|
||||||
|
return n;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Register.add_cb("appupdate", true);
|
||||||
|
Register.add_cb("update", true).doc = "Called once per frame.";
|
||||||
|
Register.add_cb("physupdate", true);
|
||||||
|
Register.add_cb("gui", true);
|
||||||
|
Register.add_cb("hud", true);
|
||||||
|
Register.add_cb("draw_dbg", true);
|
||||||
|
Register.add_cb("gui_dbg", true);
|
||||||
|
Register.add_cb("hud_dbg", true);
|
||||||
|
Register.add_cb("draw", true);
|
||||||
|
Register.add_cb("imgui", true);
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
global.mixin("scripts/spline");
|
||||||
|
global.mixin("scripts/components");
|
||||||
|
global.mixin("scripts/actor");
|
||||||
|
global.mixin("scripts/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 = {};
|
||||||
|
game.cam = world;
|
||||||
|
}
|
||||||
|
|
||||||
|
global.mixin("scripts/physics");
|
||||||
|
global.mixin("scripts/widget");
|
||||||
|
global.mixin("scripts/mum");
|
||||||
|
|
||||||
|
window.title = `Prosperon v${prosperon.version}`;
|
||||||
|
window.size = [500, 500];
|
||||||
|
|
||||||
|
return {
|
||||||
|
Register,
|
||||||
|
sim,
|
||||||
|
frame_t,
|
||||||
|
physlag,
|
||||||
|
Event
|
||||||
|
}
|
|
@ -182,7 +182,8 @@ render.make_shader = function(shader)
|
||||||
shader = io.slurp(`shaders/${file}`);
|
shader = io.slurp(`shaders/${file}`);
|
||||||
}
|
}
|
||||||
var writejson = `.prosperon/${file.name()}.shader.json`;
|
var writejson = `.prosperon/${file.name()}.shader.json`;
|
||||||
var st = profile.now();
|
|
||||||
|
profile.cache("shader", file);
|
||||||
|
|
||||||
breakme: if (io.exists(writejson)) {
|
breakme: if (io.exists(writejson)) {
|
||||||
var data = json.decode(io.slurp(writejson));
|
var data = json.decode(io.slurp(writejson));
|
||||||
|
@ -194,7 +195,7 @@ render.make_shader = function(shader)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
profile.addreport("shader [cached]", file, st);
|
profile.endcache(" [cached]");
|
||||||
var shaderobj = json.decode(io.slurp(writejson));
|
var shaderobj = json.decode(io.slurp(writejson));
|
||||||
var obj = shaderobj[os.sys()];
|
var obj = shaderobj[os.sys()];
|
||||||
obj.pipe = render.pipeline(obj);
|
obj.pipe = render.pipeline(obj);
|
||||||
|
@ -310,7 +311,7 @@ render.make_shader = function(shader)
|
||||||
compiled.files = files;
|
compiled.files = files;
|
||||||
|
|
||||||
io.slurpwrite(writejson, json.encode(compiled));
|
io.slurpwrite(writejson, json.encode(compiled));
|
||||||
profile.addreport('shader', file, st);
|
profile.endcache();
|
||||||
|
|
||||||
var obj = compiled[os.sys()];
|
var obj = compiled[os.sys()];
|
||||||
obj.pipe = render.pipeline(obj);
|
obj.pipe = render.pipeline(obj);
|
||||||
|
@ -791,4 +792,199 @@ render.draw = function(mesh, ssbo, inst = 1)
|
||||||
render.spdraw(cur.bind.count, inst);
|
render.spdraw(cur.bind.count, inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Returns an array in the form of [left, bottom, right, top] in pixels of the camera to render to
|
||||||
|
// Camera viewport is [left,bottom,right,top] in relative values
|
||||||
|
function camviewport()
|
||||||
|
{
|
||||||
|
var aspect = (this.viewport[2]-this.viewport[0])/(this.viewport[3]-this.viewport[1])*window.size.x/window.size.y;
|
||||||
|
var raspect = this.size.x/this.size.y;
|
||||||
|
|
||||||
|
var left = this.viewport[0]*window.size.x;
|
||||||
|
var bottom = this.viewport[1]*window.size.y;
|
||||||
|
|
||||||
|
var usemode = this.mode;
|
||||||
|
|
||||||
|
if (this.break && this.size.x > window.size.x && this.size.y > window.size.y)
|
||||||
|
usemode = this.break;
|
||||||
|
|
||||||
|
if (usemode === "fit")
|
||||||
|
if (raspect < aspect) usemode = "height";
|
||||||
|
else usemode = "width";
|
||||||
|
|
||||||
|
switch(usemode) {
|
||||||
|
case "stretch":
|
||||||
|
case "expand":
|
||||||
|
return [0, 0, window.size.x, window.size.y];
|
||||||
|
case "keep":
|
||||||
|
return [left, bottom, left+this.size.x, bottom+this.size.y];
|
||||||
|
case "height":
|
||||||
|
var ret = [left, 0, this.size.x*(window.size.y/this.size.y), window.size.y];
|
||||||
|
ret[0] = (window.size.x-(ret[2]-ret[0]))/2;
|
||||||
|
return ret;
|
||||||
|
case "width":
|
||||||
|
var ret = [0, bottom, window.size.x, this.size.y*(window.size.x/this.size.x)];
|
||||||
|
ret[1] = (window.size.y-(ret[3]-ret[1]))/2;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [0, 0, window.size.x, window.size.y];
|
||||||
|
}
|
||||||
|
|
||||||
|
// pos is pixels on the screen, lower left[0,0]
|
||||||
|
function camscreen2world(pos)
|
||||||
|
{
|
||||||
|
var view = this.screen2cam(pos);
|
||||||
|
view.x *= this.size.x;
|
||||||
|
view.y *= this.size.y;
|
||||||
|
view = view.sub([this.size.x/2, this.size.y/2]);
|
||||||
|
view = view.add(this.pos.xy);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
camscreen2world.doc = "Convert a view position for a camera to world."
|
||||||
|
|
||||||
|
function screen2cam(pos)
|
||||||
|
{
|
||||||
|
var viewport = this.view();
|
||||||
|
var width = viewport[2]-viewport[0];
|
||||||
|
var height = viewport[3]-viewport[1];
|
||||||
|
var left = pos.x-viewport[0];
|
||||||
|
var bottom = pos.y-viewport[1];
|
||||||
|
var p = [left/width, bottom/height];
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
screen2cam.doc = "Convert a screen space position in pixels to a normalized viewport position in a camera."
|
||||||
|
|
||||||
|
prosperon.make_camera = function()
|
||||||
|
{
|
||||||
|
var cam = world.spawn();
|
||||||
|
cam.near = 0.1;
|
||||||
|
cam.far = 1000;
|
||||||
|
cam.ortho = true;
|
||||||
|
cam.viewport = [0,0,1,1];
|
||||||
|
cam.size = window.size.slice(); // The render size of this camera in pixels
|
||||||
|
// In ortho mode, this determines how many pixels it will see
|
||||||
|
cam.mode = "stretch";
|
||||||
|
cam.screen2world = camscreen2world;
|
||||||
|
cam.screen2cam = screen2cam;
|
||||||
|
|
||||||
|
cam.mousepos = function() { return this.screen2world(input.mouse.screenpos()); }
|
||||||
|
cam.view = camviewport;
|
||||||
|
cam.offscreen = false;
|
||||||
|
return cam;
|
||||||
|
}
|
||||||
|
|
||||||
|
var screencolor;
|
||||||
|
|
||||||
|
prosperon.render = function()
|
||||||
|
{
|
||||||
|
profile.frame("world");
|
||||||
|
render.set_camera(prosperon.camera);
|
||||||
|
profile.frame("sprites");
|
||||||
|
render.sprites();
|
||||||
|
profile.endframe();
|
||||||
|
profile.frame("draws");
|
||||||
|
prosperon.draw();
|
||||||
|
profile.endframe();
|
||||||
|
prosperon.hudcam.size = prosperon.camera.size;
|
||||||
|
prosperon.hudcam.transform.pos = [prosperon.hudcam.size.x/2, prosperon.hudcam.size.y/2, -100];
|
||||||
|
render.set_camera(prosperon.hudcam);
|
||||||
|
|
||||||
|
profile.endframe();
|
||||||
|
profile.frame("hud");
|
||||||
|
|
||||||
|
prosperon.hud();
|
||||||
|
render.flush_text();
|
||||||
|
|
||||||
|
render.end_pass();
|
||||||
|
|
||||||
|
profile.endframe();
|
||||||
|
|
||||||
|
profile.frame("post process");
|
||||||
|
/* draw the image of the game world first */
|
||||||
|
render.glue_pass();
|
||||||
|
render.viewport(...prosperon.camera.view());
|
||||||
|
render.use_shader(render.postshader);
|
||||||
|
render.use_mat({diffuse:prosperon.screencolor});
|
||||||
|
render.draw(shape.quad);
|
||||||
|
|
||||||
|
profile.endframe();
|
||||||
|
|
||||||
|
profile.frame("app");
|
||||||
|
|
||||||
|
// Flush & render
|
||||||
|
prosperon.appcam.transform.pos = [window.size.x/2, window.size.y/2, -100];
|
||||||
|
prosperon.appcam.size = window.size.slice();
|
||||||
|
if (os.sys() !== 'macos')
|
||||||
|
prosperon.appcam.size.y *= -1;
|
||||||
|
|
||||||
|
render.set_camera(prosperon.appcam);
|
||||||
|
render.viewport(...prosperon.appcam.view());
|
||||||
|
|
||||||
|
// Call gui functions
|
||||||
|
mum.style = mum.dbg_style;
|
||||||
|
prosperon.gui();
|
||||||
|
if (mum.drawinput) mum.drawinput();
|
||||||
|
prosperon.gui_dbg();
|
||||||
|
render.flush_text();
|
||||||
|
mum.style = mum.base;
|
||||||
|
|
||||||
|
profile.endframe();
|
||||||
|
|
||||||
|
profile.frame("imgui");
|
||||||
|
|
||||||
|
render.imgui_new(window.size.x, window.size.y, 0.01);
|
||||||
|
prosperon.imgui();
|
||||||
|
render.imgui_end();
|
||||||
|
|
||||||
|
profile.endframe();
|
||||||
|
|
||||||
|
render.end_pass();
|
||||||
|
render.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
prosperon.process = function() {
|
||||||
|
profile.frame("frame");
|
||||||
|
var dt = profile.secs(profile.now()) - frame_t;
|
||||||
|
frame_t = profile.secs(profile.now());
|
||||||
|
|
||||||
|
profile.frame("app update");
|
||||||
|
prosperon.appupdate(dt);
|
||||||
|
profile.endframe();
|
||||||
|
|
||||||
|
profile.frame("input");
|
||||||
|
input.procdown();
|
||||||
|
profile.endframe();
|
||||||
|
|
||||||
|
if (sim.mode === "play" || sim.mode === "step") {
|
||||||
|
profile.frame("update");
|
||||||
|
prosperon.update(dt * game.timescale);
|
||||||
|
profile.endframe();
|
||||||
|
if (sim.mode === "step") sim.pause();
|
||||||
|
|
||||||
|
profile.frame("physics");
|
||||||
|
physlag += dt;
|
||||||
|
|
||||||
|
while (physlag > physics.delta) {
|
||||||
|
physlag -= physics.delta;
|
||||||
|
prosperon.phys2d_step(physics.delta * game.timescale);
|
||||||
|
prosperon.physupdate(physics.delta * game.timescale);
|
||||||
|
}
|
||||||
|
profile.endframe();
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.frame("render");
|
||||||
|
prosperon.window_render(window.size);
|
||||||
|
prosperon.render();
|
||||||
|
profile.endframe();
|
||||||
|
|
||||||
|
profile.endframe();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return {render};
|
return {render};
|
||||||
|
|
Loading…
Reference in a new issue