diff --git a/scripts/editor.js b/scripts/editor.js index ad958be..45d0abb 100644 --- a/scripts/editor.js +++ b/scripts/editor.js @@ -447,6 +447,7 @@ var editor = { depth++; render.text("$$$$$$", [0,ypos],1,editor.color_depths[depth]); + this.selectlist.forEach(function(x) { render.text(x.urstr(), x.screenpos().add([0, 32]), 1, Color.editor.ur); render.text(x.pos.map(function(x) { return Math.round(x); }), x.screenpos(), 1, Color.white); @@ -468,7 +469,7 @@ var editor = { } if (this.rotlist.length === 1) - render.text(Math.trunc(this.rotlist[0].obj.angle), input.mouse.screenpos(), 1, Color.teal); + render.text(Math.places(this.rotlist[0].angle, 3), input.mouse.screenpos(), 1, Color.teal); if (this.selectlist.length === 1) { var i = 1; @@ -639,7 +640,7 @@ editor.inputs['C-b'] = function() { c.grow(obj.scale); c.sync?.(); }); - obj.scale = [1,1,1]; + obj.set_scale([1,1,1]); } editor.inputs.drop = function(str) { @@ -763,7 +764,7 @@ editor.inputs.m = function() { return; } - editor.selectlist.forEach(obj => obj.scale = [-obj.scale[0], obj.scale[1]]); + editor.selectlist.forEach(obj => obj.grow([-1,1,1])); }; editor.inputs.m.doc = "Mirror selected objects on the X axis."; @@ -798,29 +799,16 @@ editor.inputs['C-F'] = function() { }; editor.inputs['C-F'].doc = "Tunnel out of the level you are editing, saving it in the process."; -editor.inputs['C-r'] = function() { editor.selectlist.forEach(function(x) { x.angle = -x.angle; }); }; +editor.inputs['C-r'] = function() { editor.selectlist.forEach(function(x) { x.rotate(-x.angle*2); }); } 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 = input.mouse.worldpos().sub(editor.sel_comp.gameobject.pos); - editor.startoffset = Math.atan2(relpos.y, relpos.x); - editor.startrot = editor.sel_comp.angle; - return; } - editor.rotlist = []; - editor.selectlist.forEach(function(x) { - var relpos = input.mouse.worldpos().sub(editor.cursor); - editor.rotlist.push({ - obj: x, - angle: x.angle, - pos: x.pos, - offset: x.pos.sub(editor.cursor), - rotoffset: Math.atan2(relpos.y, relpos.x), - }); - }); + editor.rotlist = editor.selectlist; }; editor.inputs.r.doc = "Rotate selected using the mouse while held down."; editor.inputs.r.released = function() { editor.rotlist = []; } @@ -1160,32 +1148,25 @@ editor.inputs.mouse.move = function(pos, dpos) } editor.grabselect?.forEach(function(x) { - if (!x) return; x.move(game.camera.dir_view2world(dpos)); x.sync(); }); var relpos = input.mouse.worldpos().sub(editor.cursor); - var dist = Vector.length(relpos); - - editor.scalelist?.forEach(function(x) { - var scalediff = dist / x.scaleoffset; - if (typeof x.obj.scale === 'number') - x.obj.scale = x.scale * scalediff; - else { - x.obj.scale = x.scale.map(x=> x * scalediff); - if (x.offset) - x.obj.pos = editor.cursor.add(x.offset.scale(scalediff)); - } - }); + var lastpos = relpos.sub(dpos); + + var dist = Vector.length(relpos.add(dpos)) - Vector.length(relpos); + var scalediff = 1+(dist/editor.scaleoffset); + + editor.scalelist?.forEach(function(x) { x.grow(scalediff); }); + var anglediff = Math.atan2(relpos.y, relpos.x) - Math.atan2(lastpos.y, lastpos.x); editor.rotlist?.forEach(function(x) { - var anglediff = Math.atan2(relpos.y, relpos.x) - x.rotoffset; - x.obj.angle = x.angle + Math.rad2turn(anglediff); - if (input.keyboard.down('shift')) - x.obj.angle = Math.nearest(x.obj.angle, (1/24)); - if (x.pos) - x.obj.pos = x.pos.sub(x.offset).add(x.offset.rotate(anglediff)); + x.rotate(anglediff/(2*Math.PI)); + if (input.keyboard.down('shift')) { + var rotate = Math.nearest(x.angle, 1/24) - x.angle; + x.rotate(rotate) + } }); } @@ -1211,11 +1192,7 @@ editor.inputs.delete.doc = "Delete selected objects."; editor.inputs['S-d'] = editor.inputs.delete; editor.inputs['C-k'] = editor.inputs.delete; -editor.inputs['C-u'] = function() { - this.selectlist.forEach(function(x) { - x.revert(); - }); -}; +editor.inputs['C-u'] = function() { this.selectlist.forEach(x => x.revert()); }; editor.inputs['C-u'].doc = "Revert selected objects back to their prefab form."; editor.inputs['M-u'] = function() { @@ -1381,27 +1358,16 @@ compmode.inputs['C-x'] = function() {}; editor.scalelist = []; editor.inputs.s = function() { - var scaleoffset = Vector.length(input.mouse.worldpos().sub(editor.cursor)); + editor.scaleoffset = Vector.length(input.mouse.worldpos().sub(editor.cursor)); editor.scalelist = []; if (editor.sel_comp) { if (!('scale' in editor.sel_comp)) return; - editor.scalelist.push({ - obj: editor.sel_comp, - scale: editor.sel_comp.scale, - scaleoffset: scaleoffset, - }); + editor.scalelist.push(editor.sel_comp); return; } - editor.selectlist.forEach(function(x) { - editor.scalelist.push({ - obj: x, - scale: x.scale, - offset: x.pos.sub(editor.cursor), - scaleoffset: scaleoffset, - }); - }); + editor.scalelist = editor.selectlist; }; editor.inputs.s.doc = "Scale selected."; diff --git a/scripts/entity.js b/scripts/entity.js index 58edb39..b8fd6cc 100644 --- a/scripts/entity.js +++ b/scripts/entity.js @@ -143,29 +143,39 @@ var gameobject = { cry(file) { return audio.cry(file); }, + + set pos(x) { this.set_pos(x); }, + get pos() { return this.rpos; }, + set angle(x) { this.set_angle(x); }, + get angle() { return this.rangle; }, + set scale(x) { this.set_scale(x); }, + get scale() { return this.rscale; }, set_pos(x, relative = world) { - var move = x.sub(this.pos); - this.pos = x; + var newpos = x.add(relative.pos); + var move = newpos.sub(this.pos); + this.rpos = newpos; this.objects.forEach(x => x.move(move)); }, set_angle(x, relative = world) { - var diff = x - this.angle; - this.angle = x; + var newangle = relative.angle + x; + var diff = newangle - this.angle; + this.rangle = newangle; this.objects.forEach(obj => { obj.rotate(diff); - obj.set_pos(Vector.rotate(obj.pos, diff)); + obj.set_pos(Vector.rotate(obj.get_pos(obj.master), diff), obj.master); }); }, set_scale(x, relative = world) { if (typeof x === 'number') x = [x,x,x]; - var pct = this.scale.map((s,i) => x[i]/s); - this.scale = x; + var newscale = relative.scale.map((s,i) => x[i]*s); + var pct = this.scale.map((s,i) => newscale[i]/s); + this.rscale = newscale; this.objects.forEach(obj => { obj.grow(pct); - obj.set_pos(obj.pos.map((x,i) => x*pct[i])); + obj.set_pos(obj.get_pos(obj.master).map((x,i) => x*pct[i]), obj.master); }); }, @@ -176,19 +186,22 @@ var gameobject = { get_angle(relative = world) { if (relative === world) return this.angle; - return this.master.angle - this.angle; + return this.angle - relative.angle; }, get_scale(relative = world) { if (relative === world) return this.scale; - var masterscale = this.master.scale; + var masterscale = relative.scale; return this.scale.map((x,i) => x/masterscale[i]); }, /* Moving, rotating, scaling functions, world relative */ move(vec) { this.set_pos(this.pos.add(vec)); }, rotate(x) { this.set_angle(this.angle + x); }, - grow(vec) { this.set_scale(this.scale.map((x, i) => x * vec[i])); }, + grow(vec) { + if (typeof vec === 'number') vec = [vec,vec,vec]; + this.set_scale(this.scale.map((x, i) => x * vec[i])); + }, screenpos() { return game.camera.world2view(this.pos); }, @@ -277,6 +290,9 @@ var gameobject = { if (callback) callback(ent); ent.ur.fresh ??= json.decode(json.encode(ent)); + ent.ur.fresh.objects = {}; + for (var i in ent.objects) + ent.ur.fresh.objects[i] = ent.objects[i].instance_obj(); return ent; }, @@ -430,11 +446,12 @@ var gameobject = { transform() { var t = {}; - t.pos = this.pos; + t.pos = this.get_pos(this.master); if (t.pos.every(x => x === 0)) delete t.pos; - t.angle = Math.places(this.angle, 4); + t.angle = Math.places(this.get_angle(this.master), 4); if (t.angle === 0) delete t.angle; - t.scale = this.scale; + return t; + t.scale = this.get_scale(this.master); t.scale = t.scale.map((x, i) => x / this.ur.fresh.scale[i]); t.scale = t.scale.map(x => Math.places(x, 3)); if (t.scale.every(x => x === 1)) delete t.scale; diff --git a/source/engine/jsffi.c b/source/engine/jsffi.c index 87f29d2..26b5223 100644 --- a/source/engine/jsffi.c +++ b/source/engine/jsffi.c @@ -1119,16 +1119,18 @@ static const JSCFunctionListEntry js_window_funcs[] = { MIST_FUNC_DEF(window, set_icon, 1) }; -JSValue js_gameobject_set_pos(JSContext *js, JSValue this, JSValue val) { +JSValue js_gameobject_set_rpos(JSContext *js, JSValue this, JSValue val) { cpBody *b = js2gameobject(this)->body; cpBodySetPosition(b, js2cvec2(val)); if (cpBodyGetType(b) == CP_BODY_TYPE_STATIC) cpSpaceReindexShapesForBody(space, b); return JS_UNDEFINED; } -JSValue js_gameobject_get_pos(JSContext *js, JSValue this) { return cvec22js(cpBodyGetPosition(js2gameobject(this)->body)); } -JSValue js_gameobject_set_angle (JSContext *js, JSValue this, JSValue val) { cpBodySetAngle(js2gameobject(this)->body, HMM_TurnToRad*js2number(val)); } -JSValue js_gameobject_get_angle (JSContext *js, JSValue this) { return number2js(HMM_RadToTurn*cpBodyGetAngle(js2gameobject(this)->body)); } +JSValue js_gameobject_get_rpos(JSContext *js, JSValue this) { return cvec22js(cpBodyGetPosition(js2gameobject(this)->body)); } +JSValue js_gameobject_set_rangle (JSContext *js, JSValue this, JSValue val) { cpBodySetAngle(js2gameobject(this)->body, HMM_TurnToRad*js2number(val)); } +JSValue js_gameobject_get_rangle (JSContext *js, JSValue this) { return number2js(HMM_RadToTurn*cpBodyGetAngle(js2gameobject(this)->body)); } +JSValue js_gameobject_get_rscale(JSContext *js, JSValue this) { return vec32js(js2gameobject(this)->scale); } +JSValue js_gameobject_set_rscale(JSContext *js, JSValue this, JSValue val) { js2gameobject(this)->scale = js2vec3(val); } JSC_GETSET_BODY(velocity, Velocity, cvec2) JSValue js_gameobject_set_angularvelocity (JSContext *js, JSValue this, JSValue val) { cpBodySetAngularVelocity(js2gameobject(this)->body, HMM_TurnToRad*js2number(val)); } JSValue js_gameobject_get_angularvelocity (JSContext *js, JSValue this) { return number2js(HMM_RadToTurn*cpBodyGetAngularVelocity(js2gameobject(this)->body)); } @@ -1142,7 +1144,6 @@ JSC_GETSET_APPLY(gameobject, elasticity, number) JSC_GETSET_APPLY(gameobject, mass, number) JSC_GETSET_APPLY(gameobject, phys, number) JSC_GETSET_APPLY(gameobject, layer, number) -JSC_GETSET(gameobject, scale, vec3) JSC_GETSET(gameobject, damping, number) JSC_GETSET(gameobject, timescale, number) JSC_GETSET(gameobject, maxvelocity, number) @@ -1160,16 +1161,15 @@ static const JSCFunctionListEntry js_gameobject_funcs[] = { CGETSET_ADD(gameobject, elasticity), CGETSET_ADD(gameobject,mass), CGETSET_ADD(gameobject,damping), - CGETSET_ADD(gameobject, scale), CGETSET_ADD(gameobject,timescale), CGETSET_ADD(gameobject,maxvelocity), CGETSET_ADD(gameobject,maxangularvelocity), CGETSET_ADD(gameobject,layer), CGETSET_ADD(gameobject,warp_filter), - CGETSET_ADD(gameobject,scale), CGETSET_ADD(gameobject,drawlayer), - CGETSET_ADD(gameobject, pos), - CGETSET_ADD(gameobject, angle), + CGETSET_ADD(gameobject, rpos), + CGETSET_ADD(gameobject, rangle), + CGETSET_ADD(gameobject, rscale), CGETSET_ADD(gameobject, velocity), CGETSET_ADD(gameobject, angularvelocity), CGETSET_ADD(gameobject, moi),