diff --git a/scripts/base.js b/scripts/base.js index 9b7323a..e6a5908 100644 --- a/scripts/base.js +++ b/scripts/base.js @@ -62,9 +62,24 @@ Object.dainty_assign = function(target, source) } } +Object.containingKey = function(obj, prop) +{ + if (typeof obj !== 'object') return undefined; + if (!(prop in obj)) return undefined; + + var o = obj; + while (o.__proto__ && !Object.hasOwn(o, prop)) + o = o.__proto__; + + return o; +} + Object.isAccessor = function(obj, prop) { - var desc = Object.getOwnPropertyDescriptor(obj,prop); + var o = Object.containingKey(obj,prop); + if (!o) return false; + + var desc = Object.getOwnPropertyDescriptor(o,prop); if (!desc) return false; if (desc.get || desc.set) return true; return false; @@ -78,6 +93,7 @@ Object.mergekey = function(o1,o2,k) if (Array.isArray(o2[k])) o1[k] = o2[k].slice(); else { + if (!o1[k]) o1[k] = {}; Object.merge(o1[k], o2[k]); } } else diff --git a/scripts/components.js b/scripts/components.js index 1a98ca8..e752511 100644 --- a/scripts/components.js +++ b/scripts/components.js @@ -34,7 +34,8 @@ component.sprite = { toString() { return "sprite"; }, make(go) { var nsprite = Object.create(component.sprite.maker); - Object.assign(nsprite, make_sprite(go)); + nsprite.gameobject = go; + Object.assign(nsprite, make_sprite(go.body)); nsprite.ur = this; return nsprite; }, @@ -108,7 +109,7 @@ var aseframeset2anim = function(frameset, meta) }; frameset.forEach(ase_make_frame); - + anim.dim = [frameset[0].sourceSize.x, frameset[0].sourceSize.y]; anim.loop = true; return anim; } @@ -153,6 +154,9 @@ var gif2anim = function(gif) anim.frames.push(frame); } anim.loop = true; + var dim = cmd(64,gif); + dim.y /= frames; + anim.dim = dim; return anim; } @@ -169,6 +173,8 @@ var strip2anim = function(strip) frame.time = 0.05; anim.frames.push(frame); } + anim.dim = cmd(64,strip); + anim.dim.x /= frames; return anim; } @@ -183,13 +189,11 @@ component.char2d = Object.copy(sprite, { get layer() { return this.gameobject.draw_layer; }, boundingbox() { - var dim = cmd(64,this.path); + var dim = this.curplaying.dim.slice(); dim = dim.scale(this.gameobject.scale); - var realpos = [0,0]; - // var realpos = this.pos.slice(); - - // realpos.x = realpos.x * dim.x + (dim.x/2); - // realpos.y = realpos.y * dim.y + (dim.y/2); + var realpos = this.pos.slice(); + realpos.x = realpos.x * dim.x + (dim.x/2); + realpos.y = realpos.y * dim.y + (dim.y/2); return cwh2bb(realpos,dim); }, @@ -205,7 +209,8 @@ component.char2d = Object.copy(sprite, { make(go) { var char = Object.create(this); - Object.assign(char, make_sprite(go)); + char.gameobject = go; + Object.assign(char, make_sprite(go.body)); char.frame = 0; char.timer = timer.make(char.advance.bind(char), 1); char.timer.loop = true; @@ -329,7 +334,8 @@ component.polygon2d = Object.copy(collider2d, { make(go) { var poly = Object.create(this); - Object.assign(poly, make_poly2d(go, this.points)); + poly.gameobject = go; + Object.assign(poly, make_poly2d(go.body, this.points)); poly.defn('points', this.points.copy()); poly.sync(); @@ -502,7 +508,8 @@ component.bucket = Object.copy(collider2d, { make(go) { var edge = Object.create(this); - Object.assign(edge, make_edge2d(go, this.points, this.thickness)); + edge.gameobject = go; + Object.assign(edge, make_edge2d(go.body, this.points, this.thickness)); Object.assign(edge, { set thickness(x) { cmd_edge2d(1,this.id,x); @@ -682,7 +689,8 @@ component.circle2d = Object.copy(collider2d, { make(go) { var circle = Object.create(this); - Object.assign(circle, make_circle2d(go, circle.radius, circle.offset)); + circle.gameobject = go; + Object.assign(circle, make_circle2d(go.body, circle.radius, circle.offset)); return circle; }, diff --git a/scripts/editor.js b/scripts/editor.js index cdd7a1b..7b5aeb9 100644 --- a/scripts/editor.js +++ b/scripts/editor.js @@ -498,6 +498,7 @@ return; GUI.text(lvlstr, [0, ypos], 1, ColorMap.Inferno.sample(lvlcolorsample)); lvlcolorsample -= 0.1; + if (!clvl.level) break; clvl = clvl.level; if (clvl) { GUI.text("^^^^^^", [0,ypos-15],1); @@ -990,6 +991,7 @@ editor.inputs['C-f1'].doc = "Enter basic edit mode."; editor.inputs['C-f2'] = function() { editor.edit_mode = "brush"; }; editor.inputs['C-f2'].doc = "Enter brush mode."; + editor.inputs.f2 = function() { objectexplorer.on_close = save_configs; objectexplorer.obj = configs; diff --git a/scripts/engine.js b/scripts/engine.js index d005a8c..d6c2aaf 100644 --- a/scripts/engine.js +++ b/scripts/engine.js @@ -567,7 +567,9 @@ var ur_json = function() return ret; } - return objdiff(this, this.ur); + var ur = objdiff(this,this.ur); + + return ur ? ur : {}; } @@ -744,13 +746,13 @@ Register.update.register(Game.exec, Game); load("scripts/entity.js"); var preprimum = {}; -preprimum.add_child = function() {}; +preprimum.objects = []; var World = gameobject.make(gameobject.ur, preprimum); var Primum = World; +Primum.level = undefined; Primum.toString = function() { return "Primum"; }; Primum.selectable = false; World.reparent = function(parent) { Log.warn("Cannot reparent the Primum."); } -World.unparent = function() { Log.warn("The Primum has no parent, always."); } /* Load configs */ function load_configs(file) { diff --git a/scripts/entity.js b/scripts/entity.js index 382c852..a16d486 100644 --- a/scripts/entity.js +++ b/scripts/entity.js @@ -187,7 +187,8 @@ var gameobject = { 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); }, + 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; }, @@ -244,7 +245,6 @@ var gameobject = { return gameobject.make(ur, this); }, - /* Bounding box of the object in world dimensions */ boundingbox() { var boxes = []; @@ -254,6 +254,8 @@ var gameobject = { if ('boundingbox' in this.components[key]) boxes.push(this.components[key].boundingbox()); } + for (var key in this.$) + boxes.push(this.$[key].boundingbox()); if (boxes.empty) return cwh2bb([0,0], [0,0]); @@ -276,9 +278,45 @@ var gameobject = { return bb ? bb : cwh2bb([0,0], [0,0]); }, + json_obj() { + function objdiff(from, to) { + if (!to) return from; // Everything on from is unique + var ret = {}; + + for (var key in from) { + if (!from[key] || !to[key]) continue; + if (typeof from[key] === 'function') continue; + if (typeof to === 'object' && !(key in to)) continue; + + if (typeof from[key] === 'object') { + if ('ur' in from[key]) { + var urdiff = objdiff(from[key],from[key].ur); + if (urdiff && !urdiff.empty) ret[key] = urdiff; + continue; + } + 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 : {}; + }, + dup(diff) { - var dup = Primum.spawn(this.__proto__); - Object.assign(dup, this); + var dup = this.level.spawn(this.ur); + var thisur = this.json_obj(); + thisur.pos = this.pos; + thisur.angle = this.angle; + Object.totalmerge(dup, thisur); return dup; }, @@ -307,31 +345,40 @@ var gameobject = { } this.objects.forEach(x => x.kill()); - - this.stop(); + if (typeof this.stop === 'function') + this.stop(); }); }, 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));}, + 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) + this.level.objects.remove(this); + this.level = parent; + }, make(ur, level) { level ??= Primum; var obj = Object.create(gameobject); - obj.defn('body', make_gameobject()); - obj.defn('components', {}); + obj.body = make_gameobject(); + obj.components = {}; + obj.objects = []; Game.register_obj(obj); - gameobject.make_parentable(obj); cmd(113, obj.body, obj); // set the internal obj reference to this obj obj.$ = {}; obj.ur = ur; - level.add_child(obj); + obj.reparent(level); + for (var prop in ur) { var p = ur[prop]; if (typeof p !== 'object') continue; @@ -340,16 +387,16 @@ var gameobject = { obj[prop] = obj.spawn(prototypes.get_ur(p.ur)); obj.$[prop] = obj[prop]; } else if ('make' in p) { - obj[prop] = p.make(obj.body); + obj[prop] = p.make(obj); obj.components[prop] = obj[prop]; } else if ('comp' in p) { - obj[prop] = component[p.comp].make(obj.body); + obj[prop] = component[p.comp].make(obj); obj.components[prop] = obj[prop]; } }; Object.totalmerge(obj,ur); - obj.check_registers(obj); + obj.check_registers(obj); if (typeof obj.start === 'function') obj.start(); @@ -387,37 +434,6 @@ gameobject.ur = { layer: 0, }; -gameobject.make_parentable = function(obj) { - var objects = []; - Object.defHidden(obj, 'level'); - - obj.remove_child = function(child) { - objects.remove(child); - } - - obj.add_child = function(child) { - child.unparent(); - objects.push(child); - child.level = obj; - } - - /* Reparent this object to a new one */ - obj.reparent = function(parent) { - if (parent === obj.level) - return; - - parent.add_child(obj); - obj.level = parent; - } - - obj.unparent = function() { - if (!obj.level) return; - obj.level.remove_child(obj); - obj.parent = undefined; - } - obj.objects = objects; -} - gameobject.entity = {}; /* Default objects */ diff --git a/source/engine/debug/log.c b/source/engine/debug/log.c index 5f6f650..f801ebe 100644 --- a/source/engine/debug/log.c +++ b/source/engine/debug/log.c @@ -25,6 +25,7 @@ char *catstr[] = {"engine", "script", "render"}; FILE *logfile = NULL; +#define ERROR_BUFFER 1024 #define CONSOLE_BUF 1024*1024*5 /* 5MB */ char *lastlog; diff --git a/source/engine/debug/log.h b/source/engine/debug/log.h index 73a62a1..6c73dd4 100644 --- a/source/engine/debug/log.h +++ b/source/engine/debug/log.h @@ -4,8 +4,6 @@ #include #include -#define ERROR_BUFFER 1024 - #define LOG_INFO 0 #define LOG_WARN 1 #define LOG_ERROR 2