diff --git a/scripts/base.js b/scripts/base.js index 1663ea0..7ce0be5 100644 --- a/scripts/base.js +++ b/scripts/base.js @@ -62,6 +62,18 @@ Object.dainty_assign = function(target, source) } } +Object.isObject = function(o) +{ + return (typeof o === 'object' && !Array.isArray(o)); +} + +Object.setter_assign = function(target, source) +{ + for (var key in target) + if (Object.isAccessor(target,key) && typeof source[key] !== 'undefined') + target[key] = source[key]; +} + Object.containingKey = function(obj, prop) { if (typeof obj !== 'object') return undefined; @@ -822,6 +834,16 @@ function bb_expand(oldbb, x) { return bb; }; +function bl2bb(bl, wh) +{ + return { + b: bl.y, + l: bl.x, + r: bl.x + wh.x, + t: bl.y + wh.y + }; +} + function bb_from_objects(objs) { var bb = objs[0].boundingbox; objs.forEach(function(obj) { bb = bb_expand(bb, obj.boundingbox); }); diff --git a/scripts/components.js b/scripts/components.js index 10f1f05..94640eb 100644 --- a/scripts/components.js +++ b/scripts/components.js @@ -22,40 +22,9 @@ var component = { extend(spec) { return Object.copy(this, spec); }, - - /* Given a relative path, return the full path */ - resani(path) { - if (!this.gameobject) return path; - if (path[0] === '/') return path.slice(1); - var res = this.gameobject.ur.toString(); - res = res.replaceAll('.', '/'); - var restry = res + "/" + path; - while (!IO.exists(restry)) { - res = res.updir() + "/"; - if (res === "/") - return path; - - restry = res + path; - } - return restry; - }, - - /* Given the full path, return the most relative path */ - resavi(path) { - if (!this.gameobject) return path; - if (path[0] === '/') return path; - - var res = this.gameobject.ur.toString(); - res = res.replaceAll('.', '/'); - var dir = path.dir(); - if (res.startsWith(dir)) - return path.base(); - - return path; - }, }; -component.sprite = { +component.sprite = Object.copy(component, { pos:[0,0], color:[1,1,1], layer:0, @@ -63,20 +32,25 @@ component.sprite = { path: "", toString() { return "sprite"; }, make(go) { - var nsprite = Object.create(component.sprite.maker); + var nsprite = Object.create(this); nsprite.gameobject = go; - Object.assign(nsprite, make_sprite(go.body)); - nsprite.ur = this; + Object.assign(nsprite, make_sprite(go.body)); + Object.complete_assign(nsprite, component.sprite.impl); + for (var p in component.sprite.impl) + if (typeof this[p] !== 'undefined') { + Log.warn(`setting ${p}`); + nsprite[p] = this[p]; + } return nsprite; }, -}; +}); -component.sprite.maker = Object.copy(component, { +component.sprite.impl = { set path(x) { - cmd(12,this.id,this.resani(x),this.rect); + cmd(12,this.id,prototypes.resani(this.gameobject.ur, x),this.rect); }, get path() { - return this.resavi(cmd(116,this.id)); + return prototypes.resavi(this.gameobject.ur, cmd(116,this.id)); }, hide() { this.enabled = false; }, show() { this.enabled = true; }, @@ -105,11 +79,11 @@ component.sprite.maker = Object.copy(component, { dimensions() { return cmd(64,this.path); }, width() { return cmd(64,this.path).x; }, height() { return cmd(64,this.path).y; }, -}); +}; Object.freeze(sprite); -var sprite = component.sprite.maker; +var sprite = component.sprite; sprite.inputs = {}; sprite.inputs.kp9 = function() { this.pos = [0,0]; }; @@ -707,15 +681,21 @@ bucket.inputs.rb.doc = "Rotate the points CW."; bucket.inputs.rb.rep = true; component.circle2d = Object.copy(collider2d, { - set radius(x) { cmd_circle2d(0,this.id,x); }, - get radius() { return cmd_circle2d(2,this.id); }, + impl: { + set radius(x) { cmd_circle2d(0,this.id,x); }, + get radius() { return cmd_circle2d(2,this.id); }, - set scale(x) { this.radius = x; }, - get scale() { return this.radius; }, - - set offset(x) { cmd_circle2d(1,this.id,x); }, - get offset() { return cmd_circle2d(3,this.id); }, + set scale(x) { Log.warn(x);this.radius = x; }, + get scale() { return this.radius; }, + set offset(x) { cmd_circle2d(1,this.id,x); }, + get offset() { return cmd_circle2d(3,this.id); }, + }, + + radius:10, + offset:[0,0], + toString() { return "circle2d"; }, + boundingbox() { var diameter = this.radius*2*this.gameobject.scale; return cwh2bb(this.offset.scale(this.gameobject.scale), [this.radius,this.radius]); @@ -725,22 +705,10 @@ component.circle2d = Object.copy(collider2d, { var circle = Object.create(this); circle.gameobject = go; Object.assign(circle, make_circle2d(go.body)); + Object.complete_assign(circle,this.impl); return circle; }, - - gui() { - Nuke.newline(); - Nuke.label("circle2d"); - this.radius = Nuke.pprop("Radius", this.radius); - this.offset = Nuke.pprop("offset", this.offset); - }, - - ur: { - radius:10, - offset:[0,0], - toString() { return "circle2d"; }, - }, }); /* ASSETS */ diff --git a/scripts/editor.js b/scripts/editor.js index f0dbeb2..8ead122 100644 --- a/scripts/editor.js +++ b/scripts/editor.js @@ -420,7 +420,7 @@ var editor = { GUI.text("$$$$$$", [0,ypos],1,lvlcolor); this.selectlist.forEach(function(x) { - var sname = x.ur.toString(); + var sname = x.__proto__.toString(); if (!x.level_obj().empty) x.dirty = true; else @@ -1231,6 +1231,7 @@ editor.inputs.s = function() { if (editor.sel_comp) { if (!('scale' in editor.sel_comp)) return; + Log.warn(`scaling ${editor.sel_comp.toString()}`); editor.scalelist.push({ obj: editor.sel_comp, scale: editor.sel_comp.scale, @@ -1257,19 +1258,23 @@ var inputpanel = { value: "", on: false, stolen: {}, - + pos:[100,Window.height-50], + wh:[350,600], + anchor: [0,1], + gui() { - this.guibody(); + var win = Mum.window({width:this.wh.x,height:this.wh.y, color:Color.black.alpha(0.1), anchor:this.anchor}); + var itms = this.guibody(); + if (!Array.isArray(itms)) itms = [itms]; + win.items = itms; + win.draw(this.pos.slice()); }, guibody() { - this.value = Nuke.textbox(this.value); - - Nuke.newline(2); - if (Nuke.button("submit")) { - this.submit(); - return true; - } + return [ + Mum.text({str:this.value, color:Color.green}), + Mum.button({str:"Submit", action:this.submit}) + ]; }, open(steal) { @@ -1313,6 +1318,8 @@ var inputpanel = { submit_check() { return true; }, keycb() {}, + + caret: 0, input_backspace_pressrep() { this.value = this.value.slice(0,-1); @@ -1321,13 +1328,24 @@ var inputpanel = { }; inputpanel.inputs = {}; -inputpanel.inputs.char = function(c) { this.value += c; this.keycb(); } +inputpanel.inputs.char = function(c) { + this.value = this.value.slice(0,this.caret) + c + this.value.slice(this.caret); + this.caret++; + Log.warn(this.caret); + this.keycb(); +} +inputpanel.inputs['C-d'] = function() { this.value = this.value.slice(0,this.caret) + this.value.slice(this.caret+1); }; inputpanel.inputs.tab = function() { this.value = tab_complete(this.value, this.assets); } inputpanel.inputs.escape = function() { this.close(); } -inputpanel.inputs.backspace = function() { this.value = this.value.slice(0,-1); this.keycb(); }; +inputpanel.inputs.backspace = function() { + this.value = this.value.slice(0,this.caret-1) + this.value.slice(this.caret); + this.caret--; + this.keycb(); +}; inputpanel.inputs.backspace.rep = true; inputpanel.inputs.enter = function() { this.submit(); } + function proto_count_lvls(name) { if (!this.occs) this.occs = {}; @@ -1376,21 +1394,25 @@ load("scripts/textedit.js"); var replpanel = Object.copy(inputpanel, { title: "REPL", closeonsubmit:false, + wh: [700,300], + pos: [50,50], + anchor: [0,0], + guibody() { - cmd(141); - var w = 700; - var h = 300; - var p = [50,50]; var log = cmd(84); - GUI.window(p, [w,h], Color.black.alpha(0.1)); - GUI.scissor(p.x,p.y,w,h); - GUI.text(log, p.add([0,10]), 1, Color.white, w, [0,0]); - GUI.text(this.value, p, 1, Color.green, w); - cmd(141); - GUI.scissor(0,0,Window.width, Window.height); + log = log.slice(-500); + + return [ + Mum.text({str:log, anchor:[0,0], offset:[0,-300]}), + Mum.text({str:this.value,color:Color.green, offset:[0,-290], caret: this.caret}) + ]; }, + prevmark:-1, + prevthis:[], action() { + if (!this.value) return; + this.prevthis.unshift(this.value); var ecode = ""; var repl_obj = (editor.selectlist.length === 1) ? editor.selectlist[0] : editor.edit_level; ecode += `var $ = repl_obj.objects;`; @@ -1400,11 +1422,31 @@ var replpanel = Object.copy(inputpanel, { ecode += this.value; Log.say(this.value); this.value = ""; + this.caret = 0; var ret = function() {return eval(ecode);}.call(repl_obj); - Log.say(ret); + if (ret) + Log.say(ret); }, }); +replpanel.inputs = Object.create(inputpanel.inputs); +replpanel.inputs['C-p'] = function() +{ + if (this.prevmark >= this.prevthis.length) return; + this.prevmark++; + this.value = this.prevthis[this.prevmark]; +} + +replpanel.inputs['C-n'] = function() +{ + this.prevmark--; + if (this.prevmark < 0) { + this.prevmark = -1; + this.value = ""; + } else + this.value = this.prevthis[this.prevmark]; +} + var objectexplorer = Object.copy(inputpanel, { title: "object explorer", obj: undefined, @@ -1601,7 +1643,9 @@ var openlevelpanel = Object.copy(inputpanel, { }, guibody() { - Mum.column({items:Object.values(this.mumlist)}).draw([100,100]); + var a = [Mum.text({str:this.value,color:Color.green})]; + var b = a.concat(Object.values(this.mumlist)); + return Mum.column({items:b}); }, }); @@ -1804,3 +1848,6 @@ Game.stop(); Game.editor_mode(true); load("editorconfig.js"); + +Log.warn(ur.ball); +Log.warn(ur['ball.big']); diff --git a/scripts/engine.js b/scripts/engine.js index b84aac3..8e95ebd 100644 --- a/scripts/engine.js +++ b/scripts/engine.js @@ -690,8 +690,8 @@ Register.update.register(Game.exec, Game); load("scripts/entity.js"); var preprimum = {}; -preprimum.objects = []; -var World = gameobject.make(gameobject.ur, preprimum); +preprimum.objects = {}; +var World = gameobject.make(preprimum); var Primum = World; Primum.level = undefined; Primum.toString = function() { return "Primum"; }; diff --git a/scripts/entity.js b/scripts/entity.js index 5ee170f..95aef7b 100644 --- a/scripts/entity.js +++ b/scripts/entity.js @@ -11,102 +11,7 @@ function grab_from_points(pos, points, slop) { }; var gameobject = { - - layer_nuke() { - Nuke.label("Collision layer"); - Nuke.newline(Collision.num); - for (var i = 0; i < Collision.num; i++) - this.layer = Nuke.radio(i, this.layer, i); - }, - - draw_layer: 1, - draw_layer_nuke() { - Nuke.label("Draw layer"); - Nuke.newline(5); - for (var i = 0; i < 5; i++) - this.draw_layer = Nuke.radio(i, this.draw_layer, i); - }, - - hide() { this.components.forEach(function(x) { x.hide(); }); this.objects.forEach(function(x) { x.hide(); }); }, - show() { this.components.forEach(function(x) { x.show(); }); this.objects.forEach(function(x) { x.show(); }); }, - - phys_nuke() { - Nuke.newline(1); - Nuke.label("phys"); - Nuke.newline(3); - this.phys = Nuke.radio("dynamic", this.phys, 0); - this.phys = Nuke.radio("kinematic", this.phys, 1); - this.phys = Nuke.radio("static", this.phys, 2); - }, - - set_center(pos) { - var change = pos.sub(this.pos); - this.pos = pos; - - for (var key in this.components) { - this.components[key].finish_center(change); - } - }, - - get_relangle() { - if (!this.level) return this.angle; - return this.angle - this.level.angle; - }, - - width() { - var bb = this.boundingbox(); - return bb.r - bb.l; - }, - - height() { - var bb = this.boundingbox(); - return bb.t-bb.b; - }, - - /* Make a unique object the same as its prototype */ - revert() { -// var t = this.transform(); - Object.totalmerge(this,this.ur); -// Object.merge(this,t); - }, - - gui() { - var go_guis = walk_up_get_prop(this, 'go_gui'); - Nuke.newline(); - - go_guis.forEach(function(x) { x.call(this); }, this); - - for (var key in this) { - if (typeof this[key] === 'object' && 'gui' in this[key]) this[key].gui(); - } - }, - - check_registers(obj) { - Register.unregister_obj(this); - - if (typeof obj.update === 'function') - Register.update.register(obj.update, obj); - - if (typeof obj.physupdate === 'function') - Register.physupdate.register(obj.physupdate, obj); - - if (typeof obj.collide === 'function') - obj.register_hit(obj.collide, obj); - - if (typeof obj.separate === 'function') - obj.register_separate(obj.separate, obj); - - if (typeof obj.draw === 'function') - Register.draw.register(obj.draw,obj); - - if (typeof obj.debug === 'function') - Register.debug.register(obj.debug, obj); - - obj.components.forEach(function(x) { - if (typeof x.collide === 'function') - register_collide(1, x.collide, x, obj.body, x.shape); - }); - }, + impl: { get scale() { return cmd(103, this.body); }, set scale(x) { var pct = x/this.scale; @@ -141,12 +46,34 @@ var gameobject = { },this); }, - set pos(x) { this.set_worldpos(Vector.rotate(x, Math.deg2rad(this.level.angle)).add(this.level.worldpos())); }, + set pos(x) { + this.set_worldpos(x); return; + this.set_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 elasticity() { return cmd(107,this.body); }, + set elasticity(x) { cmd(106,this.body,x); }, + get friction() { return cmd(109,this.body); }, + set friction(x) { cmd(108,this.body,x); }, + + set mass(x) { set_body(7,this.body,x); }, + get mass() { + if (!(this.phys === Physics.dynamic)) + return this.__proto__.mass; + + return q_body(5, this.body); + }, + + set phys(x) { set_body(1, this.body, x); }, + get phys() { return q_body(0,this.body); }, + get velocity() { return q_body(3, this.body); }, + set velocity(x) { set_body(9, this.body, x); }, + get angularvelocity() { return Math.rad2deg(q_body(4, this.body)); }, + set angularvelocity(x) { set_body(8, this.body, Math.deg2rad(x)); }, worldpos() { return q_body(1,this.body); }, set_worldpos(x) { var diff = x.sub(this.worldpos()); @@ -170,30 +97,10 @@ var gameobject = { set_body(0,this.body, Math.deg2rad(x)); }, - get elasticity() { return cmd(107,this.body); }, - set elasticity(x) { cmd(106,this.body,x); }, - - get friction() { return cmd(109,this.body); }, - set friction(x) { cmd(108,this.body,x); }, - - set mass(x) { set_body(7,this.body,x); }, - get mass() { - if (!(this.phys === Physics.dynamic)) - return this.ur.mass; - - return q_body(5, this.body); - }, - - set phys(x) { set_body(1, this.body, x); }, - get phys() { return q_body(0,this.body); }, - get velocity() { return q_body(3, this.body); }, - set velocity(x) { set_body(9, this.body, x); }, - get angularvelocity() { return Math.rad2deg(q_body(4, this.body)); }, - set angularvelocity(x) { set_body(8, this.body, Math.deg2rad(x)); }, pulse(vec) { set_body(4, this.body, vec);}, - push(vec) { set_body(12,this.body,vec);}, + shove(vec) { set_body(12,this.body,vec);}, world2this(pos) { return cmd(70, this.body, pos); }, this2world(pos) { return cmd(71, this.body,pos); }, set layer(x) { cmd(75,this.body,x); }, @@ -201,11 +108,6 @@ var gameobject = { alive() { return this.body >= 0; }, in_air() { return q_body(7, this.body);}, on_ground() { return !this.in_air(); }, - - disable() { this.components.forEach(function(x) { x.disable(); });}, - enable() { this.components.forEach(function(x) { x.enable(); });}, - sync() { }, - spawn(ur) { if (typeof ur === 'string') ur = prototypes.get_ur(ur); @@ -214,9 +116,103 @@ var gameobject = { return undefined; } - var go = gameobject.make(ur, this); + var go = ur.make(this); return go; }, + + reparent(parent) { + 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); + this.level = parent; + }, + remove_obj(obj) { + delete this[obj.toString()]; + this.objects.remove(obj); + }, + + }, + + draw_layer: 1, + components: [], + objects: [], + level: undefined, + + hide() { this.components.forEach(function(x) { x.hide(); }); this.objects.forEach(function(x) { x.hide(); }); }, + show() { this.components.forEach(function(x) { x.show(); }); this.objects.forEach(function(x) { x.show(); }); }, + + get_relangle() { + if (!this.level) return this.angle; + return this.angle - this.level.angle; + }, + + width() { + var bb = this.boundingbox(); + return bb.r - bb.l; + }, + + height() { + var bb = this.boundingbox(); + return bb.t-bb.b; + }, + + /* Make a unique object the same as its prototype */ + revert() { + Object.totalmerge(this,this.__proto__); + }, + + check_registers(obj) { + Register.unregister_obj(obj); + + if (typeof obj.update === 'function') + Register.update.register(obj.update, obj); + + if (typeof obj.physupdate === 'function') + Register.physupdate.register(obj.physupdate, obj); + + if (typeof obj.collide === 'function') + obj.register_hit(obj.collide, obj); + + if (typeof obj.separate === 'function') + obj.register_separate(obj.separate, obj); + + if (typeof obj.draw === 'function') + Register.draw.register(obj.draw,obj); + + if (typeof obj.debug === 'function') + Register.debug.register(obj.debug, obj); + + obj.components.forEach(function(x) { + if (typeof x.collide === 'function') + register_collide(1, x.collide, x, obj.body, x.shape); + }); + }, + pos: [0,0], + angle:0, + phys:1, + flipx:false, + flipy:false, + scale:1, + elasticity:0.5, + friction:1, + mass:1, + velocity:[0,0], + angularvelocity:0, + layer:0, + save:true, + selectable:true, + ed_locked:false, + + + disable() { this.components.forEach(function(x) { x.disable(); });}, + enable() { this.components.forEach(function(x) { x.enable(); });}, + sync() { }, + /* Bounding box of the object in world dimensions */ boundingbox() { @@ -294,9 +290,10 @@ var gameobject = { if (ret.empty) return undefined; return ret; }, - + json_obj() { - var ur = gameobject.diff(this,this.ur); + var ur = gameobject.diff(this,this.__proto__); + return ur ? ur : {}; }, @@ -309,8 +306,8 @@ var gameobject = { level_obj() { var json = this.json_obj(); var objects = {}; - this.ur.objects ??= {}; - if (!Object.keys(this.objects).equal(Object.keys(this.ur.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(); @@ -318,7 +315,7 @@ var gameobject = { } else { for (var o in this.objects) { var obj = this.objects[o].json_obj(); - Object.assign(obj, gameobject.diff(this.objects[o].transform(), this.ur.objects[o])); + Object.assign(obj, gameobject.diff(this.objects[o].transform(), this.__proto__.objects[o])); if (!obj.empty) objects[o] = obj; } @@ -361,7 +358,7 @@ var gameobject = { }, dup(diff) { - var n = this.level.spawn(this.ur); + var n = this.level.spawn(this.__proto__); Object.totalmerge(n, this.make_ur()); return n; }, @@ -380,6 +377,7 @@ var gameobject = { Player.uncontrol(this); Register.unregister_obj(this); + this.instances.remove(this); this.body = -1; for (var key in this.components) { @@ -395,76 +393,62 @@ 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) - this.level.remove_obj(this); - var name = parent.objects.push(this); - this.toString = function() { return name; }; - - if (this.level) - this.level.objects.remove(this); - this.level = parent; - }, - - make(ur, level) { + instances: [], + make(level) { level ??= Primum; - var obj = Object.create(gameobject); + var obj = Object.create(this); + this.instances.push(obj); obj.body = make_gameobject(); obj.components = {}; obj.objects = {}; + Object.complete_assign(obj, gameobject.impl); Object.hide(obj, 'components'); Object.hide(obj, 'objects'); - obj.toString = function() { return obj.ur.toString(); }; + obj.toJSON = gameobject.level_json; Game.register_obj(obj); cmd(113, obj.body, obj); // set the internal obj reference to this obj - obj.ur = ur; - obj.reparent(level); - for (var prop in ur) { - var p = ur[prop]; + for (var prop in this) { + var p = this[prop]; if (typeof p !== 'object') continue; if ('ur' in p) { obj[prop] = obj.spawn(prototypes.get_ur(p.ur)); obj.rename_obj(obj[prop].toString(), prop); } else if ('comp' in p) { - obj[prop] = component[p.comp].make(obj); + Log.warn(p); + obj[prop] = Object.assign(component[p.comp].make(obj), p); obj.components[prop] = obj[prop]; } }; - if (ur.objects) { - for (var prop in ur.objects) { - var o = ur.objects[prop]; + if (this.objects) { + for (var prop in this.objects) { + Log.warn(this.objects[prop]); + continue; + var o = this.objects[prop]; var newobj = obj.spawn(prototypes.get_ur(o.ur)); if (!newobj) continue; obj.rename_obj(newobj.toString(), prop); } } - var save_tostr = obj.toString; - Object.totalmerge(obj,ur); - obj.toString = save_tostr; - obj.components.forEach(function(x) { if ('sync' in x) x.sync(); }); - obj.check_registers(obj); + for (var p in this.impl) { + if (Object.isAccessor(this.impl, p)) + obj[p] = this[p]; + } + + obj.components.forEach(function(x) { if ('sync' in x) x.sync(); }); + gameobject.check_registers(obj); - obj.objects.forEach(function(x) { - x.ur = prototypes.get_ur(x.ur); - }); - if (typeof obj.start === 'function') obj.start(); return obj; @@ -501,26 +485,7 @@ var gameobject = { }, } -gameobject.toJSON = gameobject.level_obj; - -gameobject.spawn.doc = `Spawn an entity of type 'ur' on this entity. Returns the spawned entity.`; - -gameobject.ur = { -// pos: [0,0], - scale:1, - flipx:false, - flipy:false, -// angle:0, - elasticity:0.5, - friction:1, - mass:1, -// velocity:[0,0], -// angularvelocity:0, - layer: 0, - save: true, - selectable: true, - ed_locked: false, -}; +gameobject.impl.spawn.doc = `Spawn an entity of type 'ur' on this entity. Returns the spawned entity.`; /* Default objects */ var prototypes = {}; @@ -560,7 +525,7 @@ prototypes.from_file = function(file) return prototypes.get_ur(urpath); } - var upperur = gameobject.ur; + var upperur = gameobject; if (path.length > 1) { var upperpath = path.slice(0,-1); @@ -571,7 +536,8 @@ prototypes.from_file = function(file) } } - var newur = Object.create(upperur); + var newur = {};//Object.create(upperur); + file = file.replaceAll('.','/'); var jsfile = prototypes.get_ur_file(urpath, ".js"); @@ -594,8 +560,16 @@ prototypes.from_file = function(file) json ??= {}; Object.merge(newur,json); + for (var p in newur) + if (Object.isObject(newur[p]) && Object.isObject(upperur[p])) + newur[p].__proto__ = upperur[p]; + + newur.__proto__ = upperur; + newur.instances = []; + prototypes.list.push(urpath); newur.toString = function() { return urpath; }; + newur.ur = urpath; ur[urpath] = newur; return ur[urpath]; @@ -605,7 +579,7 @@ prototypes.list = []; prototypes.from_obj = function(name, obj) { - var newur = Object.copy(gameobject.ur, obj); + var newur = Object.copy(gameobject, obj); prototypes.ur[name] = newur; newur.toString = function() { return name; }; return prototypes.ur[name]; @@ -715,3 +689,36 @@ prototypes.from_obj("camera2d", { }); prototypes.from_obj("arena", {}); + +prototypes.resavi = function(ur, path) +{ + if (!ur) return path; + if (path[0] === '/') return path; + + var res = ur.replaceAll('.', '/'); + var dir = path.dir(); + if (res.startsWith(dir)) + return path.base(); + + return path; +} + +prototypes.resani = function(ur, path) +{ + if (!path) return ""; + Log.warn(`Sanitizing ${path} from ${ur}`); + if (!ur) return path; + if (path[0] === '/') return path.slice(1); + + var res = ur.replaceAll('.', '/'); + var restry = res + "/" + path; + while (!IO.exists(restry)) { + res = res.updir() + "/"; + if (res === "/") + return path; + + restry = res + path; + } + return restry; +} + diff --git a/scripts/gui.js b/scripts/gui.js index f7ca5a1..07deb39 100644 --- a/scripts/gui.js +++ b/scripts/gui.js @@ -1,10 +1,11 @@ var GUI = { - text(str, pos, size, color, wrap, anchor, frame) { + text(str, pos, size, color, wrap, anchor, cursor) { size ??= 1; color ??= Color.white; wrap ??= -1; anchor ??= [0,1]; - frame ??= Window.boundingbox(); + + cursor ??= -1; var bb = cmd(118, str, size, wrap); var w = bb.r*2; @@ -18,15 +19,11 @@ var GUI = { p.y += h * (1 - anchor.y); bb.t += h*(1-anchor.y); bb.b += h*(1-anchor.y); - ui_text(str, p, size, color, wrap, bb); + ui_text(str, p, size, color, wrap, cursor); return bb; }, - text_cursor(str, pos, size, cursor) { - cursor_text(str,pos,size,Color.white,cursor); - }, - scissor(x,y,w,h) { cmd(140,x,y,w,h); }, @@ -68,13 +65,14 @@ var GUI = { }; var Mum = { - padding:[2,2], /* Each element inset with this padding on all sides */ + padding:[0,0], /* Each element inset with this padding on all sides */ + offset:[0,0], font: "fonts/LessPerfectDOSVGA.ttf", font_size: 1, text_align: "left", scale: 1, angle: 0, - anchor: [0,0], + anchor: [0,1], text_shadow: { pos: [0,0], color: Color.white, @@ -93,6 +91,8 @@ var Mum = { return n; }, + start() {}, + extend(def) { var n = Object.create(this); Object.assign(n, def); @@ -103,22 +103,58 @@ var Mum = { Mum.text = Mum.extend({ draw(cursor) { if (this.hide) return; - this.calc_bb(cursor); - + this.caret ??= -1; + +/* if (!this.bb) + this.calc_bb(cursor); + else + this.update_bb(cursor); +*/ if (this.selected) { Object.assign(this,this.hovered); this.calc_bb(cursor); } - - var pos = cursor.sub(bb2wh(this.bb).scale(this.anchor)); - ui_text(this.str, pos, this.font_size, this.color, this.width); + this.calc_bb(cursor); + var aa = [0,1].sub(this.anchor); + var pos = cursor.add(this.wh.scale(aa)).add(this.offset); + if (this.caret > -1) Log.warn(`Drawing box at pos ${this.caret} over letter ${this.str[this.caret-1]}`); + ui_text(this.str, pos, this.font_size, this.color, -1, this.width, this.caret); }, + + update_bb(cursor) { + this.bb = movebb(this.bb, cursor.sub(this.wh.scale(this.anchor))); + }, + calc_bb(cursor) { var bb = cmd(118,this.str, this.font_size, this.width); - var wh = bb2wh(bb); - var pos = cursor.sub(wh.scale(this.anchor)); + this.wh = bb2wh(bb); + var pos = cursor.sub(this.wh.scale(this.anchor)); this.bb = movebb(bb,pos); }, + start() { + this.calc_bb([0,0]); + }, +}); + +Mum.window = Mum.extend({ + start() { + this.wh = [this.width, this.height]; + this.bb = cwh2bb([0,0], this.wh); + }, + draw(pos) { + var p = pos.sub(this.wh.scale(this.anchor)); + GUI.window(p,this.wh, this.color); + this.bb = bl2bb(p, this.wh); + var pos = [this.bb.l, this.bb.t]; + GUI.flush(); + GUI.scissor(p.x,p.y,this.wh.x,this.wh.y); + this.items.forEach(function(item) { + if (item.hide) return; + item.draw(pos.slice()); + }); + GUI.flush(); + GUI.scissor_win(); + }, }); Mum.image = Mum.extend({ @@ -130,9 +166,9 @@ Mum.image = Mum.extend({ } var tex_wh = cmd(64, this.path); - var wh = tex_wh.slice(); - if (this.width !== 0) wh.x = this.width; - if (this.height !== 0) wh.y = this.height; + this.wh = tex_wh.slice(); + if (this.width !== 0) this.wh.x = this.width; + if (this.height !== 0) this.wh.y = this.height; this.wh = wh.scale(this.scale); this.sendscale = [wh.x/tex_wh.x, wh.y/tex_wh.y]; @@ -156,9 +192,8 @@ Mum.column = Mum.extend({ this.items.forEach(function(item) { if (item.hide) return; item.draw(cursor); - var wh = bb2wh(item.bb); - cursor.y -= wh.y*2; - cursor.y -= this.padding.y*2; + cursor.y -= item.wh.y; + cursor.y -= this.padding.y; }, this); }, }); diff --git a/source/engine/ffi.c b/source/engine/ffi.c index 4fd32f5..60b4062 100644 --- a/source/engine/ffi.c +++ b/source/engine/ffi.c @@ -282,26 +282,12 @@ JSValue duk_ui_text(JSContext *js, JSValueConst this, int argc, JSValueConst *ar float size = js2number(argv[2]); struct rgba c = js2color(argv[3]); int wrap = js2int(argv[4]); - struct boundingbox bb = js2bb(argv[5]); - JSValue ret = JS_NewInt64(js, renderText(s, pos, size, c, wrap, -1, 1.0, bb)); + int cursor = js2int(argv[5]); + JSValue ret = JS_NewInt64(js, renderText(s, pos, size, c, wrap, cursor, 1.0)); JS_FreeCString(js, s); return ret; } -JSValue duk_cursor_text(JSContext *js, JSValueConst this, int argc, JSValueConst *argv) { - const char *s = JS_ToCString(js, argv[0]); - HMM_Vec2 pos = js2hmmv2(argv[1]); - - float size = js2number(argv[2]); - struct rgba c = js2color(argv[3]); - int wrap = js2int(argv[5]); - int cursor = js2int(argv[4]); - struct boundingbox bb = js2bb(argv[6]); - renderText(s, pos, size, c, wrap, cursor, 1.0,bb); - JS_FreeCString(js, s); - return JS_NULL; -} - 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])); @@ -1636,8 +1622,7 @@ void ffi_load() { DUK_FUNC(register, 3) DUK_FUNC(register_collide, 6) - DUK_FUNC(ui_text, 5) - DUK_FUNC(cursor_text, 5) + DUK_FUNC(ui_text, 6) DUK_FUNC(gui_img, 10) DUK_FUNC(inflate_cpv, 3) diff --git a/source/engine/font.c b/source/engine/font.c index 8677ccb..d97f1c8 100644 --- a/source/engine/font.c +++ b/source/engine/font.c @@ -191,6 +191,7 @@ void draw_char_box(struct Character c, HMM_Vec2 cursor, float scale, struct rgba cpVect b; b.x = cursor.X; b.y = cursor.Y; + color.a = 30; draw_box(b, wh, color); } @@ -210,10 +211,8 @@ void text_flush(HMM_Mat4 *proj) { curchar = 0; } -static int drawcaret = 0; - -void sdrawCharacter(struct Character c, HMM_Vec2 cursor, float scale, struct rgba color, struct boundingbox frame) { - if (curchar == max_chars) +void sdrawCharacter(struct Character c, HMM_Vec2 cursor, float scale, struct rgba color) { + if (curchar+1 >= max_chars) return; struct text_vert vert; @@ -278,14 +277,13 @@ struct boundingbox text_bb(const char *text, float scale, float lw, float tracki } } } - - return cwh2bb((HMM_Vec2){0,0}, (HMM_Vec2){cursor.X,-cursor.Y}); + + return cwh2bb((HMM_Vec2){0,0}, (HMM_Vec2){cursor.X,font->linegap-cursor.Y}); } /* pos given in screen coordinates */ -int renderText(const char *text, HMM_Vec2 pos, float scale, struct rgba color, float lw, int caret, float tracking, struct boundingbox frame) { +int renderText(const char *text, HMM_Vec2 pos, float scale, struct rgba color, float lw, int caret, float tracking) { int len = strlen(text); - drawcaret = caret; HMM_Vec2 cursor = pos; @@ -295,15 +293,15 @@ int renderText(const char *text, HMM_Vec2 pos, float scale, struct rgba color, f struct rgba usecolor = color; while (*line != '\0') { - if (caret == curchar) + if (caret >= 0 && caret == curchar) draw_char_box(font->Characters[69], cursor, scale, color); if (isblank(*line)) { - sdrawCharacter(font->Characters[*line], cursor, scale, usecolor, frame); + sdrawCharacter(font->Characters[*line], cursor, scale, usecolor); cursor.X += font->Characters[*line].Advance * tracking * scale; line++; } else if (isspace(*line)) { - sdrawCharacter(font->Characters[*line], cursor, scale, usecolor, frame); + sdrawCharacter(font->Characters[*line], cursor, scale, usecolor); cursor.Y -= scale * font->linegap; cursor.X = pos.X; line++; @@ -322,7 +320,7 @@ int renderText(const char *text, HMM_Vec2 pos, float scale, struct rgba color, f } while (wordstart < line) { - sdrawCharacter(font->Characters[*wordstart], cursor, scale, usecolor, frame); + sdrawCharacter(font->Characters[*wordstart], cursor, scale, usecolor); cursor.X += font->Characters[*wordstart].Advance * tracking * scale; wordstart++; } diff --git a/source/engine/font.h b/source/engine/font.h index 32b3296..c37fab0 100644 --- a/source/engine/font.h +++ b/source/engine/font.h @@ -31,10 +31,10 @@ struct sFont { void font_init(); struct sFont *MakeFont(const char *fontfile, int height); -void sdrawCharacter(struct Character c, HMM_Vec2 cursor, float scale, struct rgba color, struct boundingbox frame); +void sdrawCharacter(struct Character c, HMM_Vec2 cursor, float scale, struct rgba color); void text_settype(struct sFont *font); struct boundingbox text_bb(const char *text, float scale, float lw, float tracking); -int renderText(const char *text, HMM_Vec2 pos, float scale, struct rgba color, float lw, int caret, float tracking, struct boundingbox frame); +int renderText(const char *text, HMM_Vec2 pos, float scale, struct rgba color, float lw, int caret, float tracking); // void text_frame(); void text_flush(HMM_Mat4 *proj);