diff --git a/scripts/base.js b/scripts/base.js index 1da9d57..e9992f8 100644 --- a/scripts/base.js +++ b/scripts/base.js @@ -320,6 +320,7 @@ json.encode = function(value, space, replacer, whitelist) json.decode = function(text, reviver) { + if (!text) return undefined; return JSON.parse(text,reviver); } @@ -859,7 +860,7 @@ Object.defineProperty(String.prototype, 'dir', { }); Object.defineProperty(String.prototype, 'splice', { - value: function(index, str, ) { + value: function(index, str) { return this.slice(0,index) + str + this.slice(index); } }); @@ -881,6 +882,28 @@ Object.defineProperty(String.prototype, 'updir', { } }); +Object.defineProperty(String.prototype, 'trimchr', { + value: function(chars) { + var start = this.length; + var end = 0; + for (var i = 0; i < this.length; i++) { + if (!chars.includes(this[i])) { + start = i; + break; + } + } + + for (var i = this.length-1; i >= 0; i--) { + if (!chars.includes(this[i])) { + end = i+1; + break; + } + } + + return this.substring(start,end); + } +}); + Object.defineProperty(String.prototype, 'startswith', { value: function(val) { if (!val) return false; @@ -1573,4 +1596,3 @@ return { Vector, bbox }; - diff --git a/scripts/engine.js b/scripts/engine.js index 2977704..74f6392 100644 --- a/scripts/engine.js +++ b/scripts/engine.js @@ -4,7 +4,7 @@ globalThis.global = globalThis; function use(file) { if (use.files[file]) return use.files[file]; - + var c = io.slurp(file); var script = `(function() { ${c} })();`; @@ -52,7 +52,85 @@ var load = use; Object.assign(global, use("scripts/base.js")); global.obscure('global'); +global.mixin("scripts/render.js"); + +global.Game = { + engine_start(fn) { + cmd(257, fn); + }, + + native: render.device.pc, + + object_count() { + return cmd(214); + }, + + all_objects(fn) { + /* Wind down from Primum */ + }, + + /* Returns a list of objects by name */ + find(name) { + + }, + + /* Return a list of objects derived from a specific prototype */ + find_proto(proto) { + + }, + + /* List of all objects spawned that have a specific tag */ + find_tag(tag){ + + }, + + quit() { + sys_cmd(0); + return; + }, + + pause() { sys_cmd(3); }, + stop() { Game.pause(); }, + step() { sys_cmd(4);}, + editor_mode(m) { sys_cmd(10, m); }, + playing() { return sys_cmd(5); }, + paused() { return sys_cmd(6); }, + stepping() { return cmd(79); }, + play() { sys_cmd(1); }, + + wait_fns: [], + + wait_exec(fn) { + if (!phys_stepping()) + fn(); + else + this.wait_fns.push(fn); + }, + + exec() { + this.wait_fns.forEach(function(x) { x(); }); + + this.wait_fns = []; + }, +}; + +Game.gc = function() { cmd(259); } +Game.gc.doc = "Force the garbage collector to run."; + +Game.doc = {}; +Game.doc.object = "Returns the entity belonging to a given id."; +Game.doc.quit = "Immediately quit the game."; +Game.doc.pause = "Pause game simulation."; +Game.doc.stop = "Stop game simulation. This does the same thing as 'pause', and if the game is a debug build, starts its editor."; +Game.doc.play = "Resume or start game simulation."; +Game.doc.editor_mode = "Set to true for the game to only update on input; otherwise the game updates every frame."; +Game.doc.dt = "Current frame dt."; +Game.doc.view_camera = "Set the camera for the current view."; +Game.doc.camera = "Current camera."; + +global.mixin("scripts/input.js"); global.mixin("scripts/std.js"); + global.mixin("scripts/diff.js"); console.level = 1; @@ -140,9 +218,9 @@ var timer = { }; global.mixin("scripts/tween.js"); -global.mixin("scripts/render.js"); + global.mixin("scripts/physics.js"); -global.mixin("scripts/input.js"); + global.mixin("scripts/ai.js"); global.mixin("scripts/geometry.js"); @@ -297,78 +375,6 @@ global.mixin("scripts/debug.js"); global.mixin("scripts/spline.js"); global.mixin("scripts/components.js"); -var Game = { - engine_start(fn) { - cmd(257, fn); - }, - - native: render.device.pc, - - object_count() { - return cmd(214); - }, - - all_objects(fn) { - /* Wind down from Primum */ - }, - - /* Returns a list of objects by name */ - find(name) { - - }, - - /* Return a list of objects derived from a specific prototype */ - find_proto(proto) { - - }, - - /* List of all objects spawned that have a specific tag */ - find_tag(tag){ - - }, - - quit() { - sys_cmd(0); - return; - }, - pause() { sys_cmd(3); }, - stop() { Game.pause(); }, - step() { sys_cmd(4);}, - editor_mode(m) { sys_cmd(10, m); }, - playing() { return sys_cmd(5); }, - paused() { return sys_cmd(6); }, - stepping() { return cmd(79); }, - play() { sys_cmd(1); }, - - wait_fns: [], - - wait_exec(fn) { - if (!phys_stepping()) - fn(); - else - this.wait_fns.push(fn); - }, - - exec() { - this.wait_fns.forEach(function(x) { x(); }); - - this.wait_fns = []; - }, -}; - -Game.gc = function() { cmd(259); } -Game.gc.doc = "Force the garbage collector to run."; - -Game.doc = {}; -Game.doc.object = "Returns the entity belonging to a given id."; -Game.doc.quit = "Immediately quit the game."; -Game.doc.pause = "Pause game simulation."; -Game.doc.stop = "Stop game simulation. This does the same thing as 'pause', and if the game is a debug build, starts its editor."; -Game.doc.play = "Resume or start game simulation."; -Game.doc.editor_mode = "Set to true for the game to only update on input; otherwise the game updates every frame."; -Game.doc.dt = "Current frame dt."; -Game.doc.view_camera = "Set the camera for the current view."; -Game.doc.camera = "Current camera."; Window.doc = {}; Window.doc.width = "Width of the game window."; diff --git a/scripts/entity.js b/scripts/entity.js index 7762256..d30dcad 100644 --- a/scripts/entity.js +++ b/scripts/entity.js @@ -461,8 +461,8 @@ var gameobject = { alive() { return this.body >= 0; }, in_air() { return q_body(7, this.body);}, - hide() { this.components.forEach(x=>x.hide()); this.objects.forEach(x=>x.hide());}, - show() { this.components.forEach(function(x) { x.show(); }); this.objects.forEach(function(x) { x.show(); }); }, + hide() { this.components.forEach(x=>x.hide?.()); this.objects.forEach(x=>x.hide?.());}, + show() { this.components.forEach(function(x) { x.show?.(); }); this.objects.forEach(function(x) { x.show?.(); }); }, width() { var bb = this.boundingbox(); @@ -817,11 +817,15 @@ function apply_ur(u, e) return; } - if (topur.text) - feval_env(topur.text, e); + if (topur.text) { + var script = Resources.replstrs(topur.text); + eval_env(script, e, topur.text); + } - if (topur.data) - Object.merge(config, json.decode(io.slurp(topur.data))); + if (topur.data) { + var jss = Resources.replstrs(topur.data); + Object.merge(config, json.decode(jss)); + } } Object.merge(e, config); diff --git a/scripts/std.js b/scripts/std.js index ccabd4f..62fdf28 100644 --- a/scripts/std.js +++ b/scripts/std.js @@ -1,5 +1,32 @@ os.cwd.doc = "Get the absolute path of the current working directory."; os.env.doc = "Return the value of the environment variable v."; +os.platform = "steam"; +if (os.sys === 'windows') + os.user = os.env("USERNAME"); +else + os.user = os.env("USER"); + +var steam = {}; +steam.appid = 480; +steam.userid = 8437843; + +os.home = os.env("HOME"); + +steam.path = { + windows: `C:/Program Files (x86)/Steam/userdata/${steam.userid}/${steam.appid}`, + macos: `${os.home}/Library/Application Support/Steam/userdata/${steam.userid}/${steam.appid}`, + linux: `${os.home}/.local/share/Steam/userdata/${steam.userid}/${steam.appid}` +}; + +var otherpath = { + windows:`C:/Users/${os.user}/Saved Games`, + macos: `${os.home}/Library/Application Support`, + linux: `${os.home}/.local/share` +} + +os.prefpath = function() { + return otherpath[os.sys()] + "/" + (Game.title ? Game.title : "Untitled Prosperon Game"); +} var projectfile = ".prosperon/project.json"; @@ -25,12 +52,37 @@ Resources.is_animation = function(path) return false; } +Resources.is_path = function(str) +{ + return !/[\\\/:*?"<>|]/.test(str); +} + Resources.texture = {}; Resources.texture.dimensions = function(path) { return cmd(64,path); } Resources.gif = {}; Resources.gif.frames = function(path) { return cmd(139,path); } +Resources.replpath = function(str, path) +{ + if (str[0] === "/") + return str.rm(0); + + if (str[0] === "@") + return os.prefpath() + "/" + str.rm(0); + + if (!path) return str; + + var stem = path.dir(); + while (stem) { + var tr = stem + "/" +str; + if (io.exists(tr)) return tr; + stem = steam.updir(); + } + + return str; +} + Resources.replstrs = function(path) { var script = io.slurp(path); @@ -38,13 +90,8 @@ Resources.replstrs = function(path) var stem = path.dir(); script = script.replace(regexp,function(str) { - if (str[1] === "/") - return str.rm(1); - - if (str[1] === "@") - return str.rm(1).splice(1, "playerpath/"); - - return str.splice(1, stem + "/"); + var newstr = Resources.replpath(str.trimchr('"'), path); + return `"${newstr}"`; }); return script; @@ -146,14 +193,65 @@ console.doc = { /* io path rules. Starts with, meaning: "@": user path - "/": game path + "/": root game path + "" : relative game path */ -var tmp = io.chmod; +var tmpchm = io.chmod; io.chmod = function(file,mode) { - return tmp(file,parseInt(mode,8)); + return tmpchm(file,parseInt(mode,8)); } - + + +var tmpslurp = io.slurp; +io.slurp = function(path) +{ + path = Resources.replpath(path); + return tmpslurp(path); +} + +var tmpslurpb = io.slurpbytes; +io.slurpbytes = function(path) +{ + path = Resources.replpath(path); + return tmpslurpb(path); +} + +io.mkpath = function(dir) +{ + var mkstack = []; + while (!io.exists(dir)) { + mkstack.push(dir.fromlast('/')); + dir = dir.dir(); + } + for (var d of mkstack) { + dir = dir + "/" + d; + say(`making ${dir}`); + io.mkdir(dir); + } +} + +var tmpslurpw = io.slurpwrite; +io.slurpwrite = function(path, c) +{ + path = Resources.replpath(path); + io.mkpath(path.dir()); + return tmpslurpw(path, c); +} + +var tmpcp = io.cp; +io.cp = function(f1,f2) +{ + io.mkpath(f2.dir()); + tmpcp(f1,f2); +} + +var tmprm = io.rm; +io.rm = function(f) +{ + tmprm(Resources.replpath(f)); +} + io.mixin({ extensions(ext) { var paths = io.ls(); @@ -226,7 +324,7 @@ Cmdline.register_order("edit", function() { }, "Edit the project in this folder. Give it the name of an UR to edit that specific object.", "?UR?"); Cmdline.register_order("init", function() { - if (io.exists(".prosperon")) { + if (io.exists(projectfile)) { say("Already a game here."); return; } @@ -255,6 +353,7 @@ Cmdline.register_order("play", function() { } var project = json.decode(io.slurp(projectfile)); + Game.title = project.title; global.mixin("config.js"); if (project.title) Window.title(project.title); @@ -490,7 +589,7 @@ return { console, Resources, say, - io, Cmdline, - cmd_args + cmd_args, + steam }; diff --git a/source/engine/jsffi.c b/source/engine/jsffi.c index 8df78a7..4da9e43 100644 --- a/source/engine/jsffi.c +++ b/source/engine/jsffi.c @@ -1909,9 +1909,22 @@ JSValue js_os_env(JSContext *js, JSValueConst this, int argc, JSValue *argv) return ret; } +JSValue js_os_sys(JSContext *js, JSValueConst this) +{ + #ifdef __linux__ + return str2js("linux"); + #elif defined(_WIN32) || defined(_WIN64) + return str2js("windows"); + #elif defined(__APPLE__) + return str2js("macos"); + #endif + return JS_UNDEFINED; +} + static const JSCFunctionListEntry js_os_funcs[] = { MIST_CFUNC_DEF("cwd", 0, js_os_cwd), MIST_CFUNC_DEF("env", 1, js_os_env), + MIST_CFUNC_DEF("sys", 0, js_os_sys), }; JSValue js_io_exists(JSContext *js, JSValueConst this, int argc, JSValue *argv) @@ -1952,6 +1965,7 @@ JSValue js_io_mv(JSContext *js, JSValueConst this, int argc, JSValue *argv) JSValue js_io_rm(JSContext *js, JSValueConst this, int argc, JSValue *argv) { char *file = JS_ToCString(js, argv[0]); + JSValue ret = int2js(remove(file)); JS_FreeCString(js,file); return JS_UNDEFINED; } @@ -1979,6 +1993,7 @@ JSValue js_io_slurp(JSContext *js, JSValueConst this, int argc, JSValue *argv) { char *f = js2str(argv[0]); size_t len; + char *s = slurp_text(f,&len); JS_FreeCString(js,f); diff --git a/source/engine/resources.c b/source/engine/resources.c index cafec78..b6a26de 100644 --- a/source/engine/resources.c +++ b/source/engine/resources.c @@ -207,7 +207,7 @@ int cp(const char *p1, const char *p2) size_t len; void *data = slurp_file(p1, &len); - FILE *f = fopen_mkdir(p2, "w"); + FILE *f = fopen(p2, "w"); if (!f) return 1; fwrite(data, len, 1, f); free(data); @@ -215,34 +215,23 @@ int cp(const char *p1, const char *p2) return 0; } -void rek_mkdir(char *path) { - char *sep = strrchr(path, '/'); - if(sep != NULL) { - *sep = 0; - rek_mkdir(path); - *sep = '/'; - } -#if defined __WIN32 - if(mkdir(path) && errno != EEXIST) -#else - if (mkdir(path, 0777) && errno != EEXIST) -#endif - printf("error while trying to create '%s'\n%m\n", path); -} +int mkpath(char *dir, mode_t mode) +{ + if (!dir) { + errno = EINVAL; + return 1; + } -FILE *fopen_mkdir(const char *path, const char *mode) { - char *sep = strrchr(path, '/'); - if(sep) { - char *path0 = strdup(path); - path0[ sep - path ] = 0; - rek_mkdir(path0); - free(path0); - } - return fopen(path,mode); + if (strlen(dir) == 1 && dir[0] == '/') + return 0; + +// mkpath(dirname(strdupa(dir)), mode); + + return mkdir(dir, mode); } int slurp_write(const char *txt, const char *filename, size_t len) { - FILE *f = fopen_mkdir(filename, "w"); + FILE *f = fopen(filename, "w"); if (!f) return 1; if (len < 0) len = strlen(txt); diff --git a/source/engine/resources.h b/source/engine/resources.h index 1c40432..c7cff9f 100644 --- a/source/engine/resources.h +++ b/source/engine/resources.h @@ -16,7 +16,6 @@ FILE *path_open(const char *tag, const char *fmt, ...); char **ls(const char *path); int cp(const char *p1, const char *p2); int fexists(const char *path); -FILE *fopen_mkdir(const char *path, const char *mode); char *dirname(const char *path);