From cff54833250e7b62a404b37b6791aacfd8804c0b Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Tue, 10 Oct 2023 22:37:58 +0000 Subject: [PATCH] proper saving --- scripts/base.js | 34 ++++++--- scripts/components.js | 86 +++++++++------------- scripts/diff.js | 1 + scripts/editor.js | 13 ++-- scripts/engine.js | 1 + scripts/entity.js | 137 +++++++++++++++++------------------- source/engine/ffi.c | 33 ++++++++- source/engine/ffi.h | 1 + source/engine/script.c | 6 +- source/engine/sprite.c | 1 + source/shaders/sprite.sglsl | 2 +- 11 files changed, 171 insertions(+), 144 deletions(-) diff --git a/scripts/base.js b/scripts/base.js index 12e7989..c242997 100644 --- a/scripts/base.js +++ b/scripts/base.js @@ -67,21 +67,33 @@ Object.deepfreeze = function(obj) } /* Goes through each key and overwrites if it's present */ +/* Object.dainty_assign = function(target, source) { - Object.keys(source).forEach(function(key) { - if (!(key in target)) return; - if (typeof source[key] === 'function') return; +Object.keys(target).forEach(function(key) { + if (!(key in source)) return; + if (typeof target[key] === 'function') return; - if (Array.isArray(source[key])) + if (Array.isArray(target[key])) target[key] = deep_copy(source[key]); - else if (typeof source[key] === 'object') + else if (typeof target[key] === 'object') Object.dainty_assign(target[key], source[key]); - else { - Log.warn(`set key ${key}`); + else target[key] = source[key]; - } + }); +} +*/ +Object.dainty_assign = function(target, source) +{ + Object.keys(source).forEach(function(k) { + if (!(k in target)) return; + if (Array.isArray(source[k])) + target[k] = deep_copy(source[k]); + else if (Object.isObject(source[k])) + Object.dainty_assign(target[k], source[k]); + else + target[k] = source[k]; }); } @@ -421,7 +433,11 @@ Object.defineProperty(String.prototype, 'tofirst', { }); Object.defineProperty(String.prototype, 'name', { - value: function() { return this.fromlast('/').tolast('.'); } + value: function() { + var idx = this.indexOf('/'); + if (idx === -1) + return this.tolast('.'); + return this.fromlast('/').tolast('.'); } }); Object.defineProperty(String.prototype, 'base', { diff --git a/scripts/components.js b/scripts/components.js index 69e9643..8071d8f 100644 --- a/scripts/components.js +++ b/scripts/components.js @@ -1,3 +1,16 @@ +function assign_impl(obj, impl) +{ + var tmp = {}; + for (var key in impl) + if (typeof obj[key] !== 'undefined' && typeof obj[key] !== 'function') + tmp[key] = obj[key]; + + Object.mixin(obj, impl); + + for (var key in tmp) + obj[key] = tmp[key]; +} + var component = { components: [], toString() { @@ -11,9 +24,20 @@ var component = { enabled: true, enable() { this.enabled = true; }, disable() { this.enabled = false; }, + + hides: ['gameobject', 'id'], + + make(go) { + var nc = Object.create(this); + nc.gameobject = go; + Object.assign(nc, this._enghook(go.body)); + assign_impl(nc,this.impl); + Object.hide(nc, ...this.hides); + return nc; + }, - make(go) { }, kill() { Log.info("Kill not created for this component yet"); }, + sync() {}, gui() { }, gizmo() { }, @@ -24,13 +48,6 @@ var component = { }, }; -component.assign_impl = function(o) -{ - for (var key in o.impl) - if (typeof o[key] !== 'undefined' && typeof o[key] !== 'function') - o[key] = o.impl[key]; -} - component.sprite = Object.copy(component, { pos:[0,0], color:[1,1,1], @@ -39,30 +56,17 @@ component.sprite = Object.copy(component, { path: "", rect: {s0:0, s1: 1, t0: 0, t1: 1}, toString() { return "sprite"; }, - make(go) { - var nsprite = Object.create(this); - nsprite.gameobject = go; - Object.assign(nsprite, make_sprite(go.body)); - Object.mixin(nsprite, component.sprite.impl); - nsprite.kill = component.sprite.impl.kill; - - Object.hide(nsprite, 'gameobject', 'id'); - for (var p in component.sprite.impl) - if (typeof this[p] !== 'undefined' && typeof this[p] !== 'function') - nsprite[p] = this[p]; - - return nsprite; - }, + _enghook: make_sprite, }); component.sprite.impl = { set path(x) { - Log.warn(x); cmd(12,this.id,prototypes.resani(this.gameobject.__proto__.toString(), x),this.rect); }, get path() { return prototypes.resavi(this.gameobject.__proto__.toString(), cmd(116,this.id)); }, + toString() { return "sprite"; }, hide() { this.enabled = false; }, show() { this.enabled = true; }, asset(str) { this.path = str; }, @@ -336,23 +340,15 @@ collider2d.inputs['M-t'].doc = "Toggle if this collider is enabled."; component.polygon2d = Object.copy(collider2d, { toString() { return "poly2d"; }, + flipx: false, + flipy: false, boundingbox() { return points2bb(this.spoints); }, - - make(go) { - var poly = Object.create(this); - poly.gameobject = go; - poly.points = []; - poly.flipx = false; - poly.flipy = false; - Object.assign(poly, make_poly2d(go.body, poly.points)); - Object.mixin(poly, poly.impl); - Object.hide(poly, 'id', 'shape', 'gameobject', 'flipx', 'flipy'); - return poly; - }, - + + hides: ['id', 'shape', 'gameobject'], + _enghook: make_poly2d, points:[], /* EDITOR */ @@ -726,21 +722,9 @@ component.circle2d = Object.copy(collider2d, { var diameter = this.radius*2*this.gameobject.scale; return cwh2bb(this.offset.scale(this.gameobject.scale), [this.radius,this.radius]); }, - - make(go) { - var circle = Object.create(this); - - circle.gameobject = go; - Object.assign(circle, make_circle2d(go.body)); - Object.mixin(circle,this.impl); - Object.hide(circle, 'gameobject', 'id', 'shape', 'scale'); - component.assign_impl(circle); - for (var key in this.impl) - if (typeof this[key] !== 'undefined' && typeof this[key] !== 'function') - if (this[key]) circle[key] = this[key]; - - return circle; - }, + + hides: ['gameobject', 'id', 'shape', 'scale'], + _enghook: make_circle2d, }); /* ASSETS */ diff --git a/scripts/diff.js b/scripts/diff.js index d37b7ca..6afd95e 100644 --- a/scripts/diff.js +++ b/scripts/diff.js @@ -122,6 +122,7 @@ function ediff(from,to) if (!to || v !== to[key]) ret[key] = v; }); + if (ret.empty) return undefined; return ret; } diff --git a/scripts/editor.js b/scripts/editor.js index 2a2a772..9de23b6 100644 --- a/scripts/editor.js +++ b/scripts/editor.js @@ -197,7 +197,7 @@ var editor = { stash: undefined, start_play_ed() { - this.stash = this.edit_level.level_obj(); + this.stash = this.edit_level.json_obj(); this.edit_level.kill(); // load_configs("game.config"); Game.play(); @@ -444,7 +444,7 @@ var editor = { this.selectlist.forEach(function(x) { var sname = x.__proto__.toString(); - if (!x.level_obj().empty) + if (!x.json_obj().empty) x._ed.dirty = true; else x._ed.dirty = false; @@ -480,7 +480,7 @@ var editor = { var i = 1; 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 + "]"; + var str = (selected ? ">" : " ") + key + " [" + this.selectlist[0].components[key].toString() + "]"; GUI.text(str, world2screen(this.selectlist[0].worldpos()).add([0,-16*(i++)])); } @@ -610,7 +610,7 @@ var editor = { this.openpanel(gen_notify("Entity already exists with that name. Delete first.")); } else { var path = sub.replaceAll('.', '/') + ".json"; - var saveobj = obj.level_obj(); + var saveobj = obj.json_obj(); IO.slurpwrite(JSON.stringify(saveobj,null,1), path); @@ -822,12 +822,15 @@ editor.inputs['C-s'] = function() { }; if (editor.selectlist.length !== 1 || !editor.selectlist[0]._ed.dirty) return; - Object.merge(editor.selectlist[0].__proto__, editor.selectlist[0].level_obj()); + var saveobj = editor.selectlist[0].json_obj(); + Object.merge(editor.selectlist[0].__proto__, saveobj); + editor.selectlist[0].__proto__.objects = saveobj.objects; var path = editor.selectlist[0].ur.toString(); path = path.replaceAll('.','/'); path = path + "/" + path.name() + ".json"; IO.slurpwrite(JSON.stringify(editor.selectlist[0].__proto__,null,1), path); + Log.warn(`Wrote to file ${path}`); }; editor.inputs['C-s'].doc = "Save selected."; diff --git a/scripts/engine.js b/scripts/engine.js index 0434c25..c523a73 100644 --- a/scripts/engine.js +++ b/scripts/engine.js @@ -716,6 +716,7 @@ preprimum.scale = 1; preprimum.gscale = function() { return 1; }; preprimum.pos = [0,0]; preprimum.angle = 0; +preprimum.remove_obj = function() {}; var World = preprimum.make(preprimum); var Primum = World; Primum.level = undefined; diff --git a/scripts/entity.js b/scripts/entity.js index bc34aef..9bffd26 100644 --- a/scripts/entity.js +++ b/scripts/entity.js @@ -130,7 +130,11 @@ var gameobject = { alive() { return this.body >= 0; }, in_air() { return q_body(7, this.body);}, on_ground() { return !this.in_air(); }, - spawn(ur) { + spawn_from_instance(inst) { + var ur = prototypes.get_ur(inst.ur); + + }, + spawn(ur, data) { if (typeof ur === 'string') ur = prototypes.get_ur(ur); if (!ur) { @@ -138,31 +142,47 @@ var gameobject = { return undefined; } - var go = ur.make(this); + var go = ur.make(this, data); return go; }, - + + /* Reparent 'this' to be 'parent's child */ reparent(parent) { if (this.level === parent) - this.level.remove_obj(this); - - var name = parent.objects.push(this); - this.toString = function() { return name; }; + return; + + this.level?.remove_obj(this); - if (this.level) - this.level.objects.remove(this); this.level = parent; + + function unique_name(list, obj) { + var str = obj.toString().replaceAll('.', '_'); + var n = 1; + var t = str; + while (Object.hasOwn(list, t)) { + t = str + n; + n++; + } + return t; + }; + + var name = unique_name(parent.objects, this.ur); + parent.objects[name] = this; + this.toString = function() { return name; }; }, + remove_obj(obj) { - delete this[obj.toString()]; - this.objects.remove(obj); + if (this[obj.toString()] === this.objects[obj.toString()]) + delete this[obj.toString()]; + + delete this.objects[obj.toString()]; }, }, draw_layer: 1, - components: [], - objects: [], + components: {}, + objects: {}, level: undefined, hide() { this.components.forEach(function(x) { x.hide(); }); this.objects.forEach(function(x) { x.hide(); }); }, @@ -194,6 +214,7 @@ var gameobject = { /* Make a unique object the same as its prototype */ revert() { Object.merge(this,this.__proto__); + this.sync(); }, check_registers(obj) { @@ -243,7 +264,10 @@ var gameobject = { disable() { this.components.forEach(function(x) { x.disable(); });}, enable() { this.components.forEach(function(x) { x.enable(); });}, - sync() { }, + sync() { + this.components.forEach(function(x) { x.sync(); }); + this.objects.forEach(function(x) { x.sync(); }); + }, /* Bounding box of the object in world dimensions */ @@ -285,20 +309,13 @@ var gameobject = { var objects = {}; this.__proto__.objects ??= {}; - if (!Object.keys(this.objects).equal(Object.keys(this.__proto__.objects))) { - for (var o in this.objects) { - objects[o] = this.objects[o].transform_obj(); - objects[o].ur = this.objects[o].ur.toString(); - } - } else { - for (var o in this.objects) { - var obj = ediff(this.objects[o].transform_obj(), - this.__proto__.objects[o]); - if (obj) objects[o] = obj; - } - } - if (!objects.empty) - d.objects = objects; + var curobjs = {}; + for (var o in this.objects) + curobjs[o] = this.objects[o].instance_obj(); + + var odiff = ediff(curobjs, this.__proto__.objects); + if (odiff) + d.objects = curobjs; delete d.pos; delete d.angle; @@ -307,16 +324,18 @@ var gameobject = { return d; }, + instance_obj() { + var t = this.transform_obj(); + t.ur = this.ur; + return t; + }, + transform_obj() { var t = this.json_obj(); Object.assign(t, this.transform()); return t; }, - level_obj() { - return this.json_obj(); - }, - ur_obj() { var ur = this.json_obj(); for (var k in ur) @@ -389,32 +408,29 @@ var gameobject = { left() { return [-1,0].rotate(Math.deg2rad(this.angle));}, instances: [], - make(level) { + make(level, data) { level ??= Primum; var obj = Object.create(this); + obj.level = level; this.instances.push(obj); obj.body = make_gameobject(); - Object.hide(obj, 'body'); obj.components = {}; obj.objects = {}; - Object.mixin(obj, gameobject.impl); - Object.hide(obj, 'components'); - Object.hide(obj, 'objects'); + assign_impl(obj, gameobject.impl); obj._ed = { selectable: true, dirty: false, }; - Object.hide(obj, '_ed'); obj.ur = this.toString(); - Object.hide(obj,'ur'); Game.register_obj(obj); cmd(113, obj.body, obj); // set the internal obj reference to this obj + obj.level = undefined; obj.reparent(level); - Object.hide(obj, 'level') + Object.hide(obj, 'ur','body', 'components', 'objects', '_ed', 'level'); for (var prop in this) { var p = this[prop]; @@ -423,20 +439,19 @@ var gameobject = { obj[prop] = p.make(obj); obj.components[prop] = obj[prop]; } - }; - + }; + if (this.objects) { for (var prop in this.objects) { var o = this.objects[prop]; - var newobj = obj.spawn(o.ur); + var newobj = obj.spawn(o.ur, o); if (!newobj) continue; obj.rename_obj(newobj.toString(), prop); } } Object.dainty_assign(obj, this); - - obj.components.forEach(function(x) { if ('sync' in x) x.sync(); }); + obj.sync(); gameobject.check_registers(obj); @@ -448,10 +463,11 @@ var gameobject = { make_objs(objs) { for (var prop in objs) { var o = objs[prop]; - var newobj = this.spawn(o.ur); + var newobj = this.spawn(o.ur, o); if (!newobj) continue; this.rename_obj(newobj.toString(), prop); - Object.assign(newobj,o); + Log.warn(`setting object ${prop} to ${JSON.stringify(o)}`); +// Object.assign(newobj,o); } }, @@ -488,29 +504,6 @@ gameobject.impl.spawn.doc = `Spawn an entity of type 'ur' on this entity. Return /* Default objects */ var prototypes = {}; prototypes.ur = {}; -prototypes.load_all = function() -{ -if (IO.exists("proto.json")) - prototypes = JSON.parse(IO.slurp("proto.json")); - -for (var key in prototypes) { - if (key in gameobjects) - Object.dainty_assign(gameobjects[key], prototypes[key]); - else { - /* Create this gameobject fresh */ - Log.info("Making new prototype: " + key + " from " + prototypes[key].from); - var newproto = gameobjects[prototypes[key].from].clone(key); - gameobjects[key] = newproto; - - for (var pkey in newproto) - if (typeof newproto[pkey] === 'object' && newproto[pkey] && 'clone' in newproto[pkey]) - newproto[pkey] = newproto[pkey].clone(); - - Object.dainty_assign(gameobjects[key], prototypes[key]); - } -} -} - prototypes.save_gameobjects = function() { slurpwrite(JSON.stringify(gameobjects,null,2), "proto.json"); }; /* Makes a new ur-type from disk. If the ur doesn't exist, it searches on the disk to create it. */ @@ -534,7 +527,7 @@ prototypes.from_file = function(file) } } - var newur = {};//Object.create(upperur); + var newur = {}; file = file.replaceAll('.','/'); @@ -548,7 +541,7 @@ prototypes.from_file = function(file) try { if (jsonfile) json = JSON.parse(IO.slurp(jsonfile)); } catch(e) { - Log.warn(e); + Log.warn(`Unable to create json from ${jsonfile}`); } if (!json && !script) { diff --git a/source/engine/ffi.c b/source/engine/ffi.c index b998851..6466678 100644 --- a/source/engine/ffi.c +++ b/source/engine/ffi.c @@ -92,6 +92,11 @@ JSValue str2js(const char *c) { return JS_NewString(js, c); } +const char *js2str(JSValue v) +{ + return JS_ToCString(js, v); +} + JSValue strarr2js(const char **c) { JSValue arr = JS_NewArray(js); @@ -289,6 +294,29 @@ JSValue duk_ui_text(JSContext *js, JSValueConst this, int argc, JSValueConst *ar return ret; } +int js_print_exception(JSValue v) +{ +#ifndef NDEBUG + if (!JS_IsException(v)) + return 0; + + JSValue ex = JS_GetException(js); + + const char *name = JS_ToCString(js, js_getpropstr(ex, "name")); + const char *msg = js2str(js_getpropstr(ex, "message")); + const char *stack = js2str(js_getpropstr(ex, "stack")); + int line = 0; + mYughLog(LOG_SCRIPT, LOG_ERROR, line, "script", "%s :: %s\n%s", name, msg, stack); + + JS_FreeCString(js, name); + JS_FreeCString(js, msg); + JS_FreeCString(js, stack); + JS_FreeValue(js,ex); + return 1; +#endif + return 0; +} + JSValue duk_gui_img(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) { const char *img = JS_ToCString(js, argv[0]); gui_draw_img(img, js2hmmv2(argv[1]), js2hmmv2(argv[2]), js2number(argv[3]), js2bool(argv[4]), js2hmmv2(argv[5]), 1.0, js2color(argv[6])); @@ -1469,8 +1497,7 @@ JSValue duk_cmd_circle2d(JSContext *js, JSValueConst this, int argc, JSValueCons JSValue duk_make_poly2d(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) { int go = js2int(argv[0]); struct phys2d_poly *poly = Make2DPoly(go); - phys2d_poly_setverts(poly, js2cpvec2arr(argv[1])); - + phys2d_poly_setverts(poly, NULL); JSValue polyval = JS_NewObject(js); js_setprop_str(polyval, "id", ptr2js(poly)); js_setprop_str(polyval, "shape", ptr2js(&poly->shape)); @@ -1625,7 +1652,7 @@ void ffi_load() { DUK_FUNC(cmd_box2d, 6) DUK_FUNC(make_circle2d, 1) DUK_FUNC(cmd_circle2d, 6) - DUK_FUNC(make_poly2d, 2) + DUK_FUNC(make_poly2d, 1) DUK_FUNC(cmd_poly2d, 6) DUK_FUNC(make_edge2d, 3) DUK_FUNC(cmd_edge2d, 6) diff --git a/source/engine/ffi.h b/source/engine/ffi.h index 7ef1d62..0742d29 100644 --- a/source/engine/ffi.h +++ b/source/engine/ffi.h @@ -12,6 +12,7 @@ cpVect js2vec2(JSValue v); JSValue bitmask2js(cpBitmask mask); cpBitmask js2bitmask(JSValue v); +int js_print_exception(JSValue v); struct rgba js2color(JSValue v); double js2number(JSValue v); diff --git a/source/engine/script.c b/source/engine/script.c index fd8779b..21964f7 100644 --- a/source/engine/script.c +++ b/source/engine/script.c @@ -63,7 +63,7 @@ void script_gc() JSValue num_cache[100] = {0}; -int js_print_exception(JSValue v) { +/*int js_print_exception(JSValue v) { #ifndef NDEBUG if (JS_IsException(v)) { JSValue exception = JS_GetException(js); @@ -73,7 +73,7 @@ int js_print_exception(JSValue v) { return 0; } - JSValue val = JS_GetPropertyStr(js, exception, "stack"); + JSValue val = JS_ToCStringJS_GetPropertyStr(js, exception, "stack"); const char *name = JS_ToCString(js, JS_GetPropertyStr(js, exception, "name")); const char *msg = JS_ToCString(js, JS_GetPropertyStr(js, exception, "message")); const char *stack = JS_ToCString(js, val); @@ -90,7 +90,7 @@ int js_print_exception(JSValue v) { #endif return 0; } - +*/ void script_run(const char *script, const char *file) { JSValue obj = JS_Eval(js, script, strlen(script), file, JS_EVAL_FLAGS); js_print_exception(obj); diff --git a/source/engine/sprite.c b/source/engine/sprite.c index a024d33..c23ed34 100644 --- a/source/engine/sprite.c +++ b/source/engine/sprite.c @@ -236,6 +236,7 @@ void tex_draw(struct Texture *tex, HMM_Vec2 pos, float angle, HMM_Vec2 size, HMM } bind_sprite.fs.images[0] = tex->id; +// YughWarn("Draw sprite %s at %g, %g", tex_get_path(tex), sposes[0].X, sposes[0].Y); sg_append_buffer(bind_sprite.vertex_buffers[0], SG_RANGE_REF(verts)); sg_apply_bindings(&bind_sprite); diff --git a/source/shaders/sprite.sglsl b/source/shaders/sprite.sglsl index 1120e65..a304534 100644 --- a/source/shaders/sprite.sglsl +++ b/source/shaders/sprite.sglsl @@ -26,7 +26,7 @@ uniform sampler smp; void main() { - color = fcolor * texture(sampler2D(image,smp), texcoords); + color = texture(sampler2D(image,smp), texcoords); if (color.a <= 0.1f) discard;