From ac91e0742ab8830f65587497504e4df05bbaf318 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Mon, 2 Oct 2023 12:58:17 +0000 Subject: [PATCH] level object saving; transform and physics objects --- scripts/base.js | 24 +++---- scripts/components.js | 12 ++-- scripts/editor.js | 45 ++++-------- scripts/engine.js | 46 ------------- scripts/entity.js | 147 ++++++++++++++++++++++++++++------------ source/engine/ffi.c | 1 + source/engine/texture.c | 13 ++-- 7 files changed, 145 insertions(+), 143 deletions(-) diff --git a/scripts/base.js b/scripts/base.js index 5e92b14..2e942f2 100644 --- a/scripts/base.js +++ b/scripts/base.js @@ -240,22 +240,19 @@ Object.defineProperty(Object.prototype, 'push', { var str = val.toString(); str = str.replaceAll('.', '_'); var n = 1; - var t = str + n; + var t = str; while (Object.hasOwn(this,t)) { - n++; t = str + n; + n++; } this[t] = val; - return val; + return t; } }); Object.defineProperty(Object.prototype, 'remove', { value: function(val) { - for (var e of this.entries()) { - if (e.value === val) - delete this[e.key] - } + delete this[val.toString()]; } }); @@ -385,7 +382,10 @@ Object.defineProperty(String.prototype, 'dir', { Object.defineProperty(String.prototype, 'updir', { value: function() { - var dir = this.dir(); + if (this.lastIndexOf('/') === this.length-1) + return this.slice(0,this.length-1); + + var dir = (this + "/").dir(); return dir.dir(); } }); @@ -521,10 +521,8 @@ function add(x,y) { return x+y; }; function mult(x,y) { return x*y; }; Object.defineProperty(Array.prototype, 'mapc', { - value: function(fn, arr) { - return this.map(function(x, i) { - return fn(x, arr[i]); - }); + value: function(fn) { + return this.map(x => fn(x)); } }); @@ -671,7 +669,7 @@ Math.lerp = function(s,f,t) { return (f-s)*t + s; }; Number.prec = function(num) { - return num.toPrecision(3)/1; + return parseFloat(num.toFixed(2)); } Object.defineProperty(Object.prototype, 'lerp',{ diff --git a/scripts/components.js b/scripts/components.js index 502e48d..00398a1 100644 --- a/scripts/components.js +++ b/scripts/components.js @@ -47,10 +47,11 @@ var component = { var res = this.gameobject.ur.toString(); res = res.replaceAll('.', '/'); - if (!path.startsWith(res)) - return path; - - return path.replace(res, "").slice(1); + var dir = path.dir(); + if (res.startsWith(dir)) + return path.base(); + + return path; }, }; @@ -72,8 +73,7 @@ component.sprite = { component.sprite.maker = Object.copy(component, { set path(x) { - x = this.resani(x); - cmd(12,this.id,x,this.rect); + cmd(12,this.id,this.resani(x),this.rect); }, get path() { return this.resavi(cmd(116,this.id)); diff --git a/scripts/editor.js b/scripts/editor.js index a21478b..b618208 100644 --- a/scripts/editor.js +++ b/scripts/editor.js @@ -420,9 +420,9 @@ var editor = { else x.dirty = false; if (x.dirty) sname += "*"; - GUI.text(sname, world2screen(x.pos).add([0, 16]), 1, lvlcolor); - GUI.text(x.pos.map(function(x) { return Math.round(x); }), world2screen(x.pos), 1, Color.white); - Debug.arrow(world2screen(x.pos), world2screen(x.pos.add(x.up().scale(40))), Color.yellow, 1); + GUI.text(sname, world2screen(x.worldpos).add([0, 16]), 1, lvlcolor); + GUI.text(x.worldpos.map(function(x) { return Math.round(x); }), world2screen(x.worldpos), 1, Color.white); + Debug.arrow(world2screen(x.worldpos), world2screen(x.worldpos.add(x.up().scale(40))), Color.yellow, 1); if ('gizmo' in x && typeof x['gizmo'] === 'function' ) x.gizmo(); @@ -431,12 +431,12 @@ var editor = { if (this.selectlist.length === 0) for (var key in this.edit_level.objects) { var o = this.edit_level.objects[key]; - GUI.text(key, world2screen(o.pos), 1, lvlcolor); + GUI.text(key, world2screen(o.worldpos), 1, lvlcolor); } else this.selectlist.forEach(function(x) { Object.entries(x.objects).forEach(function(x) { - GUI.text(x[0], world2screen(x[1].pos), 1, lvlcolor); + GUI.text(x[0], world2screen(x[1].worldpos), 1, lvlcolor); }); }); @@ -450,7 +450,7 @@ var editor = { for (var key in this.selectlist[0].components) { var selected = this.sel_comp === this.selectlist[0].components[key]; var str = (selected ? ">" : " ") + key + " [" + this.selectlist[0].components[key].name + "]"; - GUI.text(str, world2screen(this.selectlist[0].pos).add([0,-16*(i++)])); + GUI.text(str, world2screen(this.selectlist[0].worldpos).add([0,-16*(i++)])); } if (this.sel_comp) { @@ -460,7 +460,7 @@ var editor = { editor.edit_level.objects.forEach(function(obj) { if (!obj.selectable) - GUI.image("icons/icons8-lock-16.png", world2screen(obj.pos)); + GUI.image("icons/icons8-lock-16.png", world2screen(obj.worldpos)); }); Debug.draw_grid(1, editor_config.grid_size, Color.Editor.grid.alpha(0.3)); @@ -686,8 +686,8 @@ editor.inputs['C-F'] = function() { editor.edit_level = editor.edit_level.level; editor.unselect(); editor.reset_undos(); - editor.curlvl = editor.edit_level.save(); - editor.edit_level.check_dirty(); +// editor.curlvl = editor.edit_level.save(); +// editor.edit_level.check_dirty(); }; editor.inputs['C-F'].doc = "Tunnel out of the level you are editing, saving it in the process."; @@ -696,7 +696,7 @@ editor.inputs['C-r'].doc = "Negate the selected's angle."; editor.inputs.r = function() { if (editor.sel_comp && 'angle' in editor.sel_comp) { - var relpos = Mouse.worldpos.sub(editor.sel_comp.gameobject.pos); + var relpos = Mouse.worldpos.sub(editor.sel_comp.gameobject.worldpos); editor.startoffset = Math.atan2(relpos.y, relpos.x); editor.startrot = editor.sel_comp.angle; @@ -774,34 +774,18 @@ editor.inputs.escape.doc = "Quit editor."; editor.inputs['C-s'] = function() { if (editor.selectlist.length === 0) { - saveaspanel.stem = editor.edit_level.toString(); + saveaspanel.stem = editor.edit_level.ur.toString(); saveaspanel.obj = editor.edit_level; editor.openpanel(saveaspanel); return; }; + if (editor.selectlist.length !== 1 || !editor.selectlist[0].dirty) return; - Log.warn(JSON.stringify(editor.selectlist[0],null,1)); - Log.warn(JSON.stringify(editor.selectlist[0].ur,null,1)); - Object.merge(editor.selectlist[0].ur, editor.selectlist[0].json_obj()); - Log.warn(JSON.stringify(editor.selectlist[0].ur,null,1)); - var path = editor.selectlist[0].toString(); + Object.merge(editor.selectlist[0].ur_obj(), editor.selectlist[0].level_obj()); + var path = editor.selectlist[0].ur.toString(); path = path.replaceAll('.','/'); path = path + "/" + path.name() + ".json"; IO.slurpwrite(JSON.stringify(editor.selectlist[0].ur,null,1), path); - return; - - if (editor.edit_level.level) { - if (!editor.edit_level.unique) - editor.save_current(); - - editor.selectlist = []; - editor.selectlist.push(editor.edit_level); - editor.edit_level = editor.edit_level.level; - - return; - } - - editor.save_current(); }; editor.inputs['C-s'].doc = "Save selected."; @@ -907,6 +891,7 @@ editor.inputs.lm.doc = "Selection box."; editor.inputs.lm.released = function() { Mouse.normal(); + editor.unselect(); if (!editor.sel_start) return; diff --git a/scripts/engine.js b/scripts/engine.js index 09fc9fa..6af862f 100644 --- a/scripts/engine.js +++ b/scripts/engine.js @@ -538,52 +538,6 @@ function Color(from) { }; */ -var ur_json = function() -{ - function objdiff(from, to) { - if (!to) return from; // Everything on from is unique - - var ret = {}; - - for (var key in from) { - if (typeof from[key] === 'undefined' || typeof to[key] === 'undefined') continue; - if (typeof from[key] === 'function') continue; - if (typeof to === 'object' && !(key in to)) continue; - - if (Array.isArray(from[key])) { - if (!Array.isArray(to[key])) - ret[key] = from[key].slice(); - - if (from[key].length !== to[key].length) - ret[key] = from[key].slice(); - - var diff = objdiff(from[key], to[key]); - if (diff && !diff.empty) - ret[key] = from[key]; - - continue; - } - - if (typeof from[key] === 'object') { - if (key === 'points') Log.warn("POINTS"); - var diff = objdiff(from[key], to[key]); - if (diff && !diff.empty) - ret[key] = diff; - continue; - } - - if (from[key] !== to[key]) - ret[key] = from[key]; - } - if (ret.empty) return undefined; - return ret; - } - - var ur = objdiff(this,this.ur); - - return ur ? ur : {}; -} - load("scripts/components.js"); function find_com(objects) diff --git a/scripts/entity.js b/scripts/entity.js index 76cabe0..3923d81 100644 --- a/scripts/entity.js +++ b/scripts/entity.js @@ -61,20 +61,6 @@ var gameobject = { } }, - set_relpos(x) { - if (!this.level) { - this.pos = x; - return; - } - - this.pos = Vector.rotate(x, Math.deg2rad(this.level.angle)).add(this.level.pos); - }, - - get_relpos() { - var offset = this.pos.sub(this.level.pos); - return Vector.rotate(offset, -Math.deg2rad(this.level.angle)); - }, - get_relangle() { if (!this.level) return this.angle; return this.angle - this.level.angle; @@ -146,8 +132,8 @@ var gameobject = { this.objects.forEach(function(obj) { obj.scale *= pct; - obj.set_relpos(obj.get_relpos().scale(pct)); - }, this); + obj.pos = obj.pos.scale(pct); + }); }, get flipx() { return cmd(104,this.body); }, @@ -156,8 +142,8 @@ var gameobject = { return; this.objects.forEach(function(obj) { obj.flipx = !obj.flipx; - var rp = obj.get_relpos(); - obj.pos = [-rp.x, rp.y].add(this.pos); + var rp = obj.pos; + obj.pos = [-rp.x, rp.y].add(this.worldpos); obj.angle = -obj.angle; },this); }, @@ -167,18 +153,25 @@ var gameobject = { cmd(56, this.body, x); return; this.objects.forEach(function(obj) { - var rp = obj.get_relpos(); - obj.pos = [rp.x, -rp.y].add(this.pos); + var rp = obj.pos; + obj.pos = [rp.x, -rp.y].add(this.worldpos); obj.angle = -obj.angle; },this); }, set pos(x) { - var diff = x.sub(this.pos); - this.objects.forEach(function(x) { x.pos = x.pos.add(diff); }); - set_body(2,this.body,x); + this.worldpos = Vector.rotate(x, Math.deg2rad(this.level.angle)).add(this.level.worldpos); }, + get pos() { + var offset = this.worldpos.sub(this.level.worldpos); + return Vector.rotate(offset, -Math.deg2rad(this.level.angle)); + }, + + get worldpos() { return q_body(1,this.body); }, + set worldpos(x) { + var diff = x.sub(this.worldpos); + this.objects.forEach(function(x) { x.worldpos = x.worldpos.add(diff); }); + set_body(2,this.body,x); }, - get pos() { return q_body(1,this.body); }, get angle() { return Math.rad2deg(q_body(2,this.body))%360; }, set angle(x) { @@ -234,9 +227,13 @@ var gameobject = { spawn(ur) { if (typeof ur === 'string') ur = prototypes.get_ur(ur); - if (!ur) Log.warn("Failed to make UR from " + ur); - - return gameobject.make(ur, this); + if (!ur) { + Log.warn("Failed to make UR from " + ur); + return undefined; + } + + var go = gameobject.make(ur, this); + return go; }, /* Bounding box of the object in world dimensions */ @@ -273,7 +270,52 @@ var gameobject = { }, json_obj() { - return JSON.parse(JSON.stringify(this)); + function objdiff(from, to) { + var ret = {}; + + for (var key in from) { + if (typeof from[key] === 'undefined' || typeof to[key] === 'undefined') continue; + if (typeof from[key] === 'function') continue; + if (typeof to === 'object' && !(key in to)) continue; + + if (Array.isArray(from[key])) { + if (!Array.isArray(to[key])) + ret[key] = Object.values(objdiff(from[key], [])); + + if (from[key].length !== to[key].length) + ret[key] = Object.values(objdiff(from[key], [])); + + var diff = objdiff(from[key], to[key]); + if (diff && !diff.empty) + ret[key] = Object.values(diff); + + continue; + } + + if (typeof from[key] === 'object') { + var diff = objdiff(from[key], to[key]); + if (diff && !diff.empty) + ret[key] = diff; + continue; + } + + if (typeof from[key] === 'number') { + var a = Number.prec(from[key]); + if (a !== to[key]) + ret[key] = a; + continue; + } + + if (from[key] !== to[key]) + ret[key] = from[key]; + } + if (ret.empty) return undefined; + return ret; + } + + var ur = objdiff(this,this.ur); + + return ur ? ur : {}; }, transform_obj() { @@ -286,10 +328,20 @@ var gameobject = { var json = this.json_obj(); Object.entries(this.objects).forEach(function(x) { json[x[0]] = x[1].transform_obj(); + Object.assign(json[x[0]], x[1].json_obj()); json[x[0]].ur = x[1].ur.toString(); }); return json; }, + + ur_obj() { + var ur = this.json_obj(); + for (var k in ur) + if (ur[k].ur) + delete ur[k]; + + return ur; + }, make_ur() { var thisur = this.json_obj(); @@ -300,13 +352,18 @@ var gameobject = { transform() { var t = {}; - t.pos = this.get_relpos(); - t.pos.x = Number.prec(t.pos.x); - t.pos.y = Number.prec(t.pos.y); - t.angle = Number.prec(this.get_relangle()); + t.pos = this.pos.map(Number.prec); + t.angle = Number.prec(this.angle); return t; }, + phys_obj() { + var phys = {}; + phys.velocity = this.velocity.map(Number.prec); + phys.angularvelocity = Number.prec(this.angularvelocity); + return phys; + }, + dup(diff) { var n = this.level.spawn(this.ur); Object.totalmerge(n, this.make_ur()); @@ -322,14 +379,11 @@ var gameobject = { Register.endofloop(() => { cmd(2, this.body); delete Game.objects[this.body]; - -// if (this.level) -// this.level.unregister(this); + this.level.remove_obj(this); Player.uncontrol(this); this.instances.remove(this); Register.unregister_obj(this); - // Signal.clear_obj(this); this.body = -1; for (var key in this.components) { @@ -343,13 +397,20 @@ var gameobject = { }); }, + remove_obj(obj) { + delete this[obj.toString()]; + this.objects.remove(obj); + }, + up() { return [0,1].rotate(Math.deg2rad(this.angle));}, down() { return [0,-1].rotate(Math.deg2rad(this.angle));}, right() { return [1,0].rotate(Math.deg2rad(this.angle));}, left() { return [-1,0].rotate(Math.deg2rad(this.angle));}, reparent(parent) { - if (this.level === parent) return; - parent.objects.push(this); + if (this.level === parent) + this.level.remove_obj(this); + var name = parent.objects.push(this); + this.toString = function() { return name; }; if (this.level) this.level.objects.remove(this); @@ -384,7 +445,9 @@ var gameobject = { } }; + var save_tostr = obj.toString; Object.totalmerge(obj,ur); + obj.toString = save_tostr; obj.objects.forEach(function(x) { x.pos = obj.pos.add(x.pos); }); obj.components.forEach(function(x) { if ('sync' in x) x.sync(); }); obj.check_registers(obj); @@ -409,7 +472,7 @@ var gameobject = { }, } -gameobject.toJSON = ur_json; +gameobject.toJSON = gameobject.level_obj; gameobject.ur = { // pos: [0,0], @@ -510,9 +573,9 @@ prototypes.list = []; prototypes.from_obj = function(name, obj) { - var newobj = Object.copy(gameobject.ur, obj); - prototypes.ur[name] = newobj; - newobj.toString = function() { return name; }; + var newur = Object.copy(gameobject.ur, obj); + prototypes.ur[name] = newur; + newur.toString = function() { return name; }; return prototypes.ur[name]; } diff --git a/source/engine/ffi.c b/source/engine/ffi.c index d71f9f1..92f7883 100644 --- a/source/engine/ffi.c +++ b/source/engine/ffi.c @@ -967,6 +967,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) case 102: eye = HMM_AddV3(eye,(HMM_Vec3){0,0,0.01}); break; + case 103: return num2js(js2go(argv[1])->scale); diff --git a/source/engine/texture.c b/source/engine/texture.c index 8d81c47..d5ff364 100644 --- a/source/engine/texture.c +++ b/source/engine/texture.c @@ -27,7 +27,7 @@ static struct { struct Texture *tex_default; struct Texture *texture_notex() { - return texture_pullfromfile("icons/no_tex.png"); + return texture_pullfromfile("icons/no_tex.gif"); } unsigned int next_pow2(unsigned int v) @@ -83,7 +83,12 @@ struct Texture *texture_pullfromfile(const char *path) { if (index != -1) return texhash[index].value; - YughInfo("Loading texture %s.", path); + long rawlen; + unsigned char *raw = slurp_file(path, &rawlen); + + if (!raw) return texture_notex(); + + unsigned char *data; struct Texture *tex = calloc(1, sizeof(*tex)); tex->opts.sprite = 1; @@ -94,10 +99,6 @@ struct Texture *texture_pullfromfile(const char *path) { int n; - long rawlen; - unsigned char *raw = slurp_file(path, &rawlen); - unsigned char *data; - char *ext = strrchr(path, '.'); if (!strcmp(ext, ".qoi")) {