From 0fcc2286fab69900b37a6af7208e501e4d7181b2 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Thu, 26 Oct 2023 16:48:02 +0000 Subject: [PATCH] instance saving --- Makefile | 2 +- docs/editor.md | 192 +++++------------------------------- scripts/base.js | 15 ++- scripts/components.js | 7 +- scripts/debug.js | 7 +- scripts/diff.js | 117 +++++++++++++++++++++- scripts/editor.js | 64 +++++++++--- scripts/engine.js | 1 + scripts/entity.js | 82 ++++++++++----- scripts/gui.js | 6 +- scripts/std.js | 33 ++++++- source/engine/2dphysics.c | 2 +- source/engine/debug/log.c | 31 ++++-- source/engine/ffi.c | 5 +- source/engine/render.c | 3 +- source/engine/script.c | 2 +- source/shaders/sprite.sglsl | 8 +- 17 files changed, 340 insertions(+), 237 deletions(-) diff --git a/Makefile b/Makefile index a2d70de..a2fed6a 100755 --- a/Makefile +++ b/Makefile @@ -179,7 +179,7 @@ api.md: $(DOCMD) @(echo "# API"; cat $^) > $@ @rm $^ -INPUT = editor component.sprite component.polygon2d component.edge2d component.circle2d +INPUT = editor DebugControls component.sprite component.polygon2d component.edge2d component.circle2d INPUTMD := $(addsuffix .input.md, $(INPUT)) input.md: $(INPUTMD) @(echo "# Input"; cat $^) > $@ diff --git a/docs/editor.md b/docs/editor.md index cc19c78..ba3b1da 100644 --- a/docs/editor.md +++ b/docs/editor.md @@ -1,185 +1,43 @@ -# Yugine Editor +# Primum Editor -The main editor view is made up of objects. Each object can have a -number of components attached to it. When an object is selected, its -name, position, and list of components are listed. +The main editor view is made up of objects. Each object can have a number of components attached to it. When an object is selected, its name, position, and list of components are listed. -The editor edits either entities or components. When it is started, there is no edited level. +In addition, a window showing each entity underneath that entity are shown. -## Basic controls -|Ctrl-Z|Undo| -|Ctrl-Shift-Z|Redo| -|Ctrl-A|Select all| -|Ctrl-S|Save| -|Ctrl-N|New| -|Ctrl-O|Open level| -|Ctrl-X|Cut| -|Ctrl-C|Copy| -|Ctrl-V|Paste| -|Alt-O|Add level to current level| -|Alt-A|or Alt-P Add a prefab| -|Ctrl-I|Objects on the level| -|Ctrl-E|Asset viewer. When on a component like a sprite, serves to select that sprite's texture| -|Ctrl-[|Downsize grid| -|Ctrl-]|Upsize grid| -|Backtick|REPL| -|Ctrl-[1-9]|to set camera positions| -|[1-9]|to recall camera positions| -|0|Set camera to home view| -|ESC|quit| -|Alt-1|Normal view| -|Alt-2|Wireframe view| -|Shift-Middle|Set editor cursor to mouse position (Cursor affects how objects rotate)| -|Shift-Ctrl-Middle|Set cursor to object selection| -|Shift-Right|Remove cursor| +## The desktop -## Editor Mode select +The desktop is the topmost object that exists in the editor. Instead of editing specific files, you simply load them into your desktop, and go from there. This makes it easier to see two different entities simultaneously so you can ensure changes to one are congruous with the vision for the others. -|Alt-F1|Basic mode| -|Alt-F2|Brush mode| - - Clicking will place what is on clipboard +## Ur-types -## Object controls +Ur-types are what are loaded into the game world. They are templates for creating entities. -|G|Translate| -|Alt-G|Snap objects to cursor| -|S|Scale| -|R|Rotate| -|Ctrl-P|Save object changes to prefab| -|Ctrl-shift-P|Save object changes as a unique prefab ("Parent")| -|Ctrl-shift-T|Save object changes to a side prefab ("Type")| -|Ctrl-J|Bake name to expose to level script| -|Alt-J|Remove baked name| -|Ctrl-Y|Show obj chain| -|Alt-Y|Start prototype explorer| -|Ctrl-U|Revert object or component to match prototype| -|Alt-U|Make object unique. If a level, allows setting of internal object position and rotation.| -|Ctrl-shift-G|Save group as a level| -|Arrows|Translate 1 px| -|Shift-Arrows|Translate 10 px| -|Tab|Select component| -|F|Zoom to object(s)| -|Ctrl-F|Focus on a selected sublevel. Edit and save it in place.| -|Ctrl-Shift-F|Go up one level in the editing chain.| -|M|Flip horizontally| -|Ctrl-M|Flip vertically| -|Ctrl-D|Duplicate| -|H|Hide| -|Ctrl-H|Unhide all| -|T|Lock| -|Alt-T|Unlock all| -|Q|Toggle component help| -|Ctrl-Shift-Alt-Click|Set object center| +## *'s and %'s -## Mouse controls +When a '*' is seen next to an entity's name, that means it is altered compared to its ur-type and is unsaved. There are a number of ways to take care of a '*'. If you do not do one of the below, something on the entity will be lost. -|Left|Select| -|Middle|Quick grab| -|Right|Unselect| +- Changes can be saved to the ur-type. This makes all other entities derived from the ur-type change. +- Changes can be saved as a sub ur-type. This creates a brand new type that can then be reused over and over again. +- Changes can be saved by saving the containing ur-type. Ur-types remember variances in the entities it 'owns'. -## Level controls -|Ctrl-L|Open level script| +When an entity is different from its ur-type, but the variance is saved due to its container, its name is preceded by a '%'. -## Game controls -|F1|Debug draw| -|F2|Config menu| -|F3|Show bounding boxes| -|F4|Show gizmos| -|F5|Start| -|F6|Pause| -|F7|Stop| -|F10|Toggle slow motion| +The function 'revert()' can be called on any entity to make it like its ur-type again. -== Components -Components all have their own set of controls. Many act similar to -objects. If a component has a position attribute, it will react as -expected to object grabbing; same with scaling, rotation, and so on. +## Levels? -If a component uses an asset, the asset viewer will serve to pick new -assets for it. +The concept of a 'level', a collection of spawned entities, is handled simply by creating sub ur-types of an empty entity. -## Spline controls -|Ctrl-click|Add a point| -|Shift-click|remove a point| -|+,-|Increase or decrease spline segments| -|Ctrl-+,-|Increase or decrease spline degrees. Put this to 1 for the spline to go point to point| -|Alt-B,V|Increase or decrease spline thickness| +## Editing level, ur-types, parents, children, etc -.Collider controls -|Alt-S|Toggle sensor| +lvl1 + tablebase + %flipper -## Yugine Programming +In this case, tablebase is saving a modified flipper. -### Object functions +lvl1 + %tablebase + %flipper -* start(): Called when the object is created, before the first update is ran -* update(dt): Called once per frame -* physupdate(dt): Called once per physics calculation -* stop(): Called when the object is killed -* collide(hit): Called when this object collides with another. If on a collider, specific to that collider - - hit.hit: Gameobject ID of what's being hit - - hit.velocity: Velocity of impact - - hit.normal: Normal of impact - -### Colliders -Colliders visually look different based on their status. Objects can -be in one of three states - -- Dynamic: respond to physics -- Kinematic: modeled with infinite momentum. An "unstoppable force" -controlled by a user. -- Static: modeled with infinite mass. An "immovable object" that -shouldn't move. - -Objects can then have colliders on them, each collider being a sensor, -or solid. Sensors respond to collision signals, while solid ones also -do not let objects through. - - - -### Input -Input works by adding functions to an object, and then "controlling" -them. The format for a function is "input_[key]_[action]". [Action] -can be any of - -- down: Called once per frame the key is down -- pressed: Called when the key is pressed -- released: called when the key is released - -For example, "input_p_pressed()" will be called when p is pressed, and not again -until it is released and pressed again. - -### Your game - -When the engine runs, it executes config.js, and then game.js. A -window should be created in config.js, and custom code for prototypes -should be executed. - -game.js is the place to open your first level. - -### Levels - -A level is a collection of objects. A level has a script associated -with it. The script is ran when the level is loaded. - -Levels can be added to other levels. Each is independent and unique. -In this way, complicated behavior can easily be added up. For example, -a world might have a door that opens with a lever. The door and lever -can be saved off as their own level, and the level script can contain -the code that causes the door to open when the lever is thrown. Then, -as many door-lever levels can be added to your game as you want. - -The two primary ways to add objects to the game are World.spawn, and -Level.add. World.spawn creates a single object in the world, Level.add -adds an entire level, along with its script. - -Levels also can be checked for "completion". A level can be loaded -over many frames, and only have all of its contents appear once it's -finished loading. World.spawn is immediate. - -Level.clear removes the level from the game world. - -.Level scripting -Each level has a script which is ran when the level is loaded, or the -game is played. A single object declared in it called "scene" can be -used to expose itself to the rest of the game. +This is ambiguous. lvl1 could be storing the flipper's diff, or tablebase could be. Additionally, tablebase could have a unique flipper, and then lvl1 also alters it. diff --git a/scripts/base.js b/scripts/base.js index b1a0e47..9bfe8d7 100644 --- a/scripts/base.js +++ b/scripts/base.js @@ -213,6 +213,19 @@ Object.hide = function(obj,...props) } } +Object.unhide = function(obj, ...props) +{ + for (var prop of props) { + var p = Object.getOwnPropertyDescriptor(obj,prop); + if (!p) { + Log.warn(`No property of name ${prop}.`); + return; + } + p.enumerable = true; + Object.defineProperty(obj, prop, p); + } +} + Object.defineProperty(Object.prototype, 'obscure', { value: function(name) { Object.defineProperty(this, name, { enumerable: false }); @@ -999,7 +1012,7 @@ var Vector = { var p = Vector.angle(v) + angle; return [r*Math.cos(p), r*Math.sin(p)]; }, - + equal(v1, v2, tol) { if (!tol) return v1.equal(v2); diff --git a/scripts/components.js b/scripts/components.js index 7421165..ea78264 100644 --- a/scripts/components.js +++ b/scripts/components.js @@ -7,6 +7,8 @@ function assign_impl(obj, impl) Object.mixin(obj, impl); + if (obj.sync) obj.sync(); + for (var key in tmp) obj[key] = tmp[key]; } @@ -31,6 +33,7 @@ var component = { var nc = Object.create(this); nc.gameobject = go; Object.assign(nc, this._enghook(go.body)); + nc.sync(); assign_impl(nc,this.impl); Object.hide(nc, ...this.hides); return nc; @@ -580,8 +583,8 @@ component.edge2d = Object.copy(collider2d, { }); component.edge2d.impl = { - set sensor(x) { cmd(18,this.shape,x); }, - get sensor() { return cmd(21,this.shape); }, + set sensor(x) { cmd(18,this.shape,x); }, + get sensor() { return cmd(21,this.shape); }, set thickness(x) { cmd_edge2d(1,this.id,x); diff --git a/scripts/debug.js b/scripts/debug.js index 085d7a4..d5e1709 100644 --- a/scripts/debug.js +++ b/scripts/debug.js @@ -92,7 +92,7 @@ var Debug = { if (this.draw_gizmos) Game.objects.forEach(function(x) { if (!x.icon) return; - gui_img(x.icon, world2screen(x.pos)); + GUI.image(x.icon, world2screen(x.pos)); }); if (this.draw_names) @@ -101,8 +101,8 @@ var Debug = { }); if (Debug.Options.gif.rec) { - gui_text("REC", [0,40], 1); - gui_text(Time.seconds_to_timecode(Time.time - Debug.Options.gif.start_time, Debug.Options.gif.fps), [0,30], 1); + GUI.text("REC", [0,40], 1); + GUI.text(Time.seconds_to_timecode(Time.time - Debug.Options.gif.start_time, Debug.Options.gif.fps), [0,30], 1); } GUI.text(Game.playing() ? "PLAYING" @@ -323,6 +323,7 @@ API.print_doc = function(name) else if (typeof obj.doc === 'string') mdoc += obj.doc + "\n"; for (var key in obj) { if (key === 'doc') continue; + if (key === 'toString') continue; mdoc += API.doc_entry(obj, key); } diff --git a/scripts/diff.js b/scripts/diff.js index 6afd95e..68b640f 100644 --- a/scripts/diff.js +++ b/scripts/diff.js @@ -83,7 +83,12 @@ function diffassign(target, from) { } }; -function ediff(from,to) +function diffkey(from,to,key) +{ + +} + +function objdiff(from,to) { var ret = {}; @@ -126,3 +131,113 @@ function ediff(from,to) return ret; } + +function valdiff(from,to) +{ + if (typeof from !== typeof to) return from; + if (typeof from === 'function') return undefined; + if (typeof from === 'undefined') return undefined; + + if (typeof from === 'number') { + if (Number.prec(from) !== Number.prec(to)) + return to; + + return undefined; + } + + if (typeof from === 'object') + return ediff(from,to); + + if (from !== to) return to; + + return undefined; +} + + + +function ediff(from,to) +{ + var ret = {}; + + if (!to) +// return ediff(from, {}); + return deep_copy(from); + + Object.entries(from).forEach(function([key,v]) { + if (typeof v === 'function') return; + if (typeof v === 'undefined') return; + + if (Array.isArray(v)) { + if (!Array.isArray(to[key]) || v.length !== to[key].length) + ret[key] = Object.values(ediff(v, [])); + + var diff = ediff(from[key], to[key]); + if (diff && !diff.empty) + ret[key] = Object.values(ediff(v,[])); + + return; + } + + if (typeof v === 'object') { + var diff = ediff(v, to[key]); + if (diff && !diff.empty) + ret[key] = diff; + return; + } + + if (typeof v === 'number') { + var a = Number.prec(v); + if (!to || a !== to[key]) + ret[key] = a; + return; + } + + if (!to || v !== to[key]) + ret[key] = v; + }); + if (ret.empty) return undefined; + + return ret; +} + +ediff.doc = "Given a from and to object, returns an object that, if applied to from, will make it the same as to. Does not include deletion; it is only additive."; + +function subdiff(from,to) +{ + +} + +subdiff.doc = "Given a from and to object, returns a list of properties that must be deleted from the 'from' object to make it like the 'to' object."; + +function samediff(from, to) +{ + var same = []; + if (!to) return same; + if (typeof to !== 'object') { + Log.warn("'To' must be an object. Got " + to); + return same; + } + Object.keys(from).forEach(function(k) { + if (Object.isObject(from[k])) { + samediff(from[k], to[k]); + return; + } + +// if (Array.isArray(from[k])) { +// var d = valdiff(from[k], to[k]); +// if (!d) +// } + + var d = valdiff(from[k], to[k]); + if (!d) + delete from[k]; + }); + + return same; +} + +samediff.doc = "Given a from and to object, returns an array of keys that are the same on from as on to."; + +function cleandiff(from, to) +{ +} diff --git a/scripts/editor.js b/scripts/editor.js index 1b61d0e..f1bb871 100644 --- a/scripts/editor.js +++ b/scripts/editor.js @@ -39,6 +39,10 @@ var editor = { return this.edit_level; }, + get_that() { + return this.selectlist.length === 1 ? this.selectlist[0] : this.get_this(); + }, + try_select() { /* nullify true if it should set selected to null if it doesn't find an object */ var go = physics.pos_query(Mouse.worldpos); return this.do_select(go); @@ -437,13 +441,19 @@ var editor = { var ypos = 200; var depth = 0; + var alldirty = false; + for (var lvl of lvlchain) { + if (alldirty) + lvl._ed.dirty = true; + else { + lvl._ed.check_dirty(); + if (lvl._ed.dirty) alldirty = true; + } + } lvlchain.reverse(); lvlchain.forEach(function(x,i) { depth = i; - var lvlstr = x.toString(); - x._ed.check_dirty(); - if (x._ed.dirty) - lvlstr += "*"; + var lvlstr = x._ed.namestr(); if (i === lvlchain.length-1) lvlstr += "[this]"; GUI.text(lvlstr, [0, ypos], 1, editor.color_depths[depth]); @@ -468,8 +478,7 @@ var editor = { }); Object.entries(thiso.objects).forEach(function(x) { - var p = x[0]; - if (x[1]._ed.dirty) p += "*"; + var p = x[1]._ed.namestr(); GUI.text(p, world2screen(x[1].worldpos()),1,editor.color_depths[depth]); }); @@ -542,6 +551,23 @@ var editor = { this.curpanels.forEach(function(x) { if (x.on) x.gui(); }); +/* + var o = editor.get_that(); + + var pos = [6,600]; + var offset = [0,0]; + var os = Object.entries(o.objects); + + GUI.text(o.toString(), pos.add(offset)); + offset = offset.add([5,-16]); + + os.forEach(function(x) { + GUI.text(x.toString(), pos.add(offset)); + offset = offset.add([0,-16]); + }); + + GUI.text(JSON.stringify(o._ed.urdiff,null,1), [500,500]); +*/ }, ed_debug() { @@ -830,18 +856,20 @@ editor.inputs['C-s'] = function() { } else if (editor.selectlist.length === 1) saveobj = editor.selectlist[0]; - saveobj._ed.check_dirty(); - if (!saveobj._ed.dirty) return; +// saveobj._ed.check_dirty(); +// if (!saveobj._ed.dirty) return; var savejs = saveobj.json_obj(); Object.merge(saveobj.__proto__, savejs); - saveobj.__proto__.objects = savejs.objects; + if (savejs.objects) saveobj.__proto__.objects = savejs.objects; var path = saveobj.ur.toString(); path = path.replaceAll('.','/'); path = path + "/" + path.name() + ".json"; IO.slurpwrite(JSON.stringify(saveobj.__proto__,null,1), path); Log.warn(`Wrote to file ${path}`); + + Object.values(saveobj.objects).forEach(function(x) { x._ed.check_dirty(); }); }; editor.inputs['C-s'].doc = "Save selected."; @@ -1431,6 +1459,11 @@ inputpanel.inputs['C-k'] = function() { this.value = this.value.slice(0,this.caret); }; +inputpanel.inputs.lm = function() +{ + gui_controls.check_submit(); +} + load("scripts/textedit.js"); var replpanel = Object.copy(inputpanel, { @@ -1633,7 +1666,8 @@ var objectexplorer = Object.copy(inputpanel, { guibody() { var items = []; - items.push(Mum.text({str:"Examining " + this.obj})); + items.push(Mum.text({str:"Examining " + this.obj.toString()})); + return items; var n = 0; var curobj = this.obj; @@ -1779,7 +1813,10 @@ var openlevelpanel = Object.copy(inputpanel, { }, keycb() { - this.assets = this.allassets.filter(x => x.search(this.value) !== -1); + if(this.value) + this.assets = this.allassets.filter(x => x.startswith(this.value)); + else + this.assets = this.allassets.slice(); for (var m in this.mumlist) this.mumlist[m].hide = true; this.assets.forEach(function(x) { @@ -1956,11 +1993,6 @@ limited_editor.inputs['C-q'] = function() editor.enter_editor(); } -limited_editor.inputs['M-q'] = function() -{ - editor.enter_editor(); -} - /* This is used for editing during a paused game */ var limited_editing = {}; limited_editing.inputs = {}; diff --git a/scripts/engine.js b/scripts/engine.js index 1823bb6..992a16e 100644 --- a/scripts/engine.js +++ b/scripts/engine.js @@ -787,6 +787,7 @@ Primum.level = undefined; Primum.toString = function() { return "Primum"; }; Primum._ed.selectable = false; Primum._ed.check_dirty = function() { }; +Primum._ed.dirty = false; globalThis.World.reparent = function(parent) { Log.warn("Cannot reparent the Primum."); } Game.view_camera(Primum.spawn(ur.camera2d)); } diff --git a/scripts/entity.js b/scripts/entity.js index ad5312f..e6072e9 100644 --- a/scripts/entity.js +++ b/scripts/entity.js @@ -35,6 +35,7 @@ var gameobject = { this.objects = {}; }, gscale() { return cmd(103,this.body); }, + sgscale(x) { cmd(36,this.body,x) }, get scale() { if (!this.level) return this.gscale(); return this.gscale()/this.level.gscale(); @@ -42,11 +43,11 @@ var gameobject = { set scale(x) { if (this.level) x *= this.level.gscale(); - var pct = x/this.scale; + var pct = x/this.gscale(); cmd(36, this.body, x); this.objects?.forEach(function(obj) { - obj.scale *= pct; + obj.sgscale(obj.gscale()*pct); obj.pos = obj.pos.scale(pct); }); }, @@ -75,12 +76,15 @@ var gameobject = { }, set pos(x) { - this.set_worldpos(Vector.rotate(x,Math.deg2rad(this.level.angle)).add(this.level.worldpos())); + this.set_worldpos(Vector.rotate(x.scale(this.level.gscale()),Math.deg2rad(this.level.worldangle())).add(this.level.worldpos())); }, get pos() { + if (!this.level) return this.worldpos(); var offset = this.worldpos().sub(this.level.worldpos()); - return Vector.rotate(offset, -Math.deg2rad(this.level.angle)); + offset = Vector.rotate(offset, -Math.deg2rad(this.level.angle)); + offset = offset.scale(1/this.level.gscale()); + return offset; }, get elasticity() { return cmd(107,this.body); }, set elasticity(x) { cmd(106,this.body,x); }, @@ -110,6 +114,7 @@ var gameobject = { }, worldangle() { return Math.rad2deg(q_body(2,this.body))%360; }, + sworldangle(x) { set_body(0,this.body,Math.deg2rad(x)); }, get angle() { if (!this.level) return this.worldangle(); return this.worldangle() - this.level.worldangle(); @@ -126,26 +131,16 @@ var gameobject = { p += diff; p = Math.deg2rad(p); x.pos = [r*Math.cos(p), r*Math.sin(p)]; - }, this); + }); set_body(0,this.body, Math.deg2rad(x - this.level.worldangle())); }, rotate(x) { - this.angle = this.angle + x; + this.sworldangle(this.worldangle()+x); }, - pulse(vec) { set_body(4, 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); }, - get layer() { return 0; }, - alive() { return this.body >= 0; }, - in_air() { return q_body(7, this.body);}, - on_ground() { return !this.in_air(); }, spawn_from_instance(inst) { return this.spawn(inst.ur, inst); }, @@ -159,6 +154,7 @@ var gameobject = { } var go = ur.make(this, data); + Object.hide(this, go.toString()); return go; }, @@ -204,6 +200,16 @@ var gameobject = { objects: {}, level: undefined, + pulse(vec) { set_body(4, 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); }, + get layer() { return 0; }, + alive() { return this.body >= 0; }, + in_air() { return q_body(7, this.body);}, + on_ground() { return !this.in_air(); }, + 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(); }); }, @@ -262,8 +268,6 @@ var gameobject = { register_collide(1, x.collide, x, obj.body, x.shape); }); }, - pos: [0,0], - angle:0, phys:1, flipx:false, flipy:false, @@ -271,8 +275,6 @@ var gameobject = { elasticity:0.5, friction:1, mass:1, - velocity:[0,0], - angularvelocity:0, layer:0, worldpos() { return [0,0]; }, @@ -342,6 +344,10 @@ var gameobject = { delete d.angularvelocity; return d; }, + + full_obj() { + + }, transform_obj() { var t = this.json_obj(); @@ -439,6 +445,7 @@ var gameobject = { var obj = Object.create(this); obj.make = undefined; obj.level = level; +// obj.toJSON = obj.transform_obj; // this.instances.push(obj); // Log.warn(`Made an object from ${this.toString()}`); // Log.warn(this.instances.length); @@ -448,8 +455,32 @@ var gameobject = { assign_impl(obj, gameobject.impl); obj._ed = { selectable: true, - check_dirty() { this.dirty = !obj.json_obj().empty; }, + check_dirty() { + this.urdiff = obj.json_obj(); + this.dirty = !this.urdiff.empty; + if (!obj.level) return; + var lur = ur[obj.level.ur]; + if (!lur) return; + var lur = lur.objects[obj.toString()]; + var d = ediff(this.urdiff,lur); + if (!d || d.empty) + this.inst = true; + else + this.inst = false; + }, + dirty: false, + inst: false, + model: Object.create(this), + urdiff: {}, + namestr() { + var s = obj.toString(); + if (this.dirty) + if (this.inst) s += "#"; + else s += "*"; + + return s; + }, }; obj.ur = this.toString(); @@ -466,11 +497,9 @@ var gameobject = { } }; - if (this.objects) { + if (this.objects) obj.make_objs(this.objects); - } - obj.level = undefined; obj.reparent(level); @@ -503,12 +532,17 @@ var gameobject = { Log.warn(`No object with name ${name}. Could not rename to ${newname}.`); return; } + if (name === newname) { + Object.hide(this, name); + return; + } if (this.objects[newname]) return; this.objects[newname] = this.objects[name]; this[newname] = this[name]; this[newname].toString = function() { return newname; }; + Object.hide(this, newname); delete this.objects[name]; delete this[name]; return this.objects[newname]; diff --git a/scripts/gui.js b/scripts/gui.js index 9632682..aeee627 100644 --- a/scripts/gui.js +++ b/scripts/gui.js @@ -66,7 +66,7 @@ var GUI = { }; var gui_controls = {}; -//gui_controls.toString = function() { return "GUI controls"; }; +gui_controls.toString = function() { return "GUI controls"; }; gui_controls.update = function() { }; gui_controls.set_mum = function(mum) @@ -93,10 +93,10 @@ gui_controls.inputs.mouse.scroll = function(scroll) { } -gui_controls.inputs.lm = function() { +gui_controls.check_submit = function() { if (this.selected && this.selected.action) this.selected.action(this.selected); -}; +} var Mum = { padding:[0,0], /* Each element inset with this padding on all sides */ diff --git a/scripts/std.js b/scripts/std.js index 6f9004c..f84af87 100644 --- a/scripts/std.js +++ b/scripts/std.js @@ -211,7 +211,7 @@ Cmdline.register_cmd("b", function(str) { }, "Pack the game into the given name."); Cmdline.register_cmd("e", function(pawn) { - run("scripts/editor.js"); + load("scripts/editor.js"); Log.write(`## Input for ${pawn}\n`); eval(`Log.write(Input.print_md_kbm(${pawn}));`); STD.exit(0); @@ -223,7 +223,36 @@ Cmdline.register_cmd("t", function() { }, "Test suite."); Cmdline.register_cmd("d", function(obj) { - run("scripts/editor.js"); + load("scripts/editor.js"); Log.say(API.print_doc(obj[0])); STD.exit(0); }, "Print documentation for an object."); + +Cmdline.register_cmd("cjson", function(json) { + var f = json[0]; + if (!IO.exists(f)) { + Log.warn(`File ${f} does not exist.`); + STD.exit(1); + } + + prototypes.generate_ur(); + + var j = JSON.parse(IO.slurp(f)); + + for (var k in j) { + if (k in j.objects) + delete j[k]; + } + + Log.warn(j); + + for (var k in j.objects) { + var o = j.objects[k]; + samediff(o, ur[o.ur]); + } + + Log.say(j); +// IO.slurpwrite(JSON.stringify(j,undefined,2), f); + + STD.exit(0); +}, "Clean up a jso file."); diff --git a/source/engine/2dphysics.c b/source/engine/2dphysics.c index 22f09ad..1a3b68b 100644 --- a/source/engine/2dphysics.c +++ b/source/engine/2dphysics.c @@ -588,7 +588,7 @@ int shape_get_sensor(struct phys2d_shape *shape) { if (!shape->shape) { struct phys2d_edge *edge = shape->data; if (arrlen(edge->shapes) > 0) return cpShapeGetSensor(edge->shapes[0]); - + YughError("Attempted to get the sensor of an edge with no shapes. It has %d points.", arrlen(edge->points)); return 0; } diff --git a/source/engine/debug/log.c b/source/engine/debug/log.c index 0f5fa7e..9abd0b3 100644 --- a/source/engine/debug/log.c +++ b/source/engine/debug/log.c @@ -31,12 +31,17 @@ FILE *logfile = NULL; char *consolelog; FILE *consolefp; +static FILE *sout; + void log_init() { consolelog = malloc(CONSOLE_BUF+1); consolefp = fmemopen(consolelog, CONSOLE_BUF+1,"w"); + sout = fdopen(dup(stdout),"w"); + sout = stdout; } +const char *logfmt = "%s:%d: %s, %s: %s\n"; void mYughLog(int category, int priority, int line, const char *file, const char *message, ...) { #ifndef NDEBUG @@ -46,18 +51,24 @@ void mYughLog(int category, int priority, int line, const char *file, const char double ticks = (double)clock()/CLOCKS_PER_SEC; - va_list args; + va_list args, arg2; va_start(args, message); - char msgbuffer[ERROR_BUFFER] = { '\0' }; - vsnprintf(msgbuffer, ERROR_BUFFER, message, args); + va_copy(arg2, args); + int len = vsnprintf(NULL, 0, message, args)+1; + char *msg = malloc(len); va_end(args); - - - char buffer[ERROR_BUFFER] = { '\0' }; - snprintf(buffer, ERROR_BUFFER, "%s:%d: %s, %s: %s\n", file, line, logstr[priority], catstr[category], msgbuffer); + vsprintf(msg, message, arg2); + va_end(arg2); + + len = snprintf(NULL, 0, logfmt, file,line,logstr[priority], catstr[category], msg)+1; + char *buffer = malloc(len); + sprintf(buffer, logfmt, file, line, logstr[priority], catstr[category], msg); fprintf(stderr, buffer); fflush(stderr); + + free(msg); + free(buffer); } #endif @@ -65,8 +76,8 @@ void mYughLog(int category, int priority, int line, const char *file, const char void log_print(const char *str) { - fprintf(stdout, "%s", str); - fflush(stdout); + fprintf(sout, "%s", str); + fflush(sout); #ifndef NDEBUG strncat(consolelog, str, CONSOLE_BUF); @@ -106,5 +117,5 @@ void log_cat(FILE *f) { void sg_logging(const char *tag, uint32_t lvl, uint32_t id, const char *msg, uint32_t line, const char *file, void *data) { lvl = 3-lvl; - mYughLog(2, lvl, line, file, "tag: %s, msg: %s", tag, msg); + mYughLog(2, lvl, line, file, "tag: %s, id: %d, msg: %s", tag, id, msg); } diff --git a/source/engine/ffi.c b/source/engine/ffi.c index d445bc8..3a40d59 100644 --- a/source/engine/ffi.c +++ b/source/engine/ffi.c @@ -1564,7 +1564,10 @@ JSValue duk_cmd_edge2d(JSContext *js, JSValueConst this, int argc, JSValueConst int cmd = js2int(argv[0]); struct phys2d_edge *edge = js2ptr(argv[1]); - if (!edge) return JS_NULL; + if (!edge) { + YughError("Attempted to do a cmd on edge %p. Not found.", edge); + return JS_NULL; + } switch (cmd) { case 0: diff --git a/source/engine/render.c b/source/engine/render.c index 094943a..deba60b 100644 --- a/source/engine/render.c +++ b/source/engine/render.c @@ -191,6 +191,7 @@ void trace_init_image(sg_image id, const sg_image_desc *d, void *data) void trace_make_shader(const sg_shader_desc *d, sg_shader result, void *data) { + YughInfo("Making shader %s", d->label); if (sg_query_shader_state(result) == SG_RESOURCESTATE_FAILED) YughError("FAILED MAKING A SHADER: %s\n%s\n%s", d->label); } @@ -218,7 +219,7 @@ void trace_make_pipeline(const sg_pipeline_desc *d, sg_pipeline result, void *da void trace_apply_pipeline(sg_pipeline pip, void *data) { - YughInfo("Applying pipeline %d", pip); +// YughInfo("Applying pipeline %d", pip); } void trace_fail_pipeline(sg_pipeline pip, void *data) diff --git a/source/engine/script.c b/source/engine/script.c index 8324cda..d1641b6 100644 --- a/source/engine/script.c +++ b/source/engine/script.c @@ -24,7 +24,7 @@ JSRuntime *rt = NULL; #ifndef NDEBUG #define JS_EVAL_FLAGS JS_EVAL_FLAG_STRICT #else -#define JS_EVAL_FLAGS JS_EVAL_FLAG_STRICT | JS_EVAL_FLAG_STRIP +#define JS_EVAL_FLAGS JS_EVAL_FLAG_STRICT// | JS_EVAL_FLAG_STRIP #endif static struct { diff --git a/source/shaders/sprite.sglsl b/source/shaders/sprite.sglsl index a304534..40f743e 100644 --- a/source/shaders/sprite.sglsl +++ b/source/shaders/sprite.sglsl @@ -1,7 +1,7 @@ @vs vs in vec2 vertex; in vec2 uv; -in vec4 vColor; +in vec4 vc; out vec2 texcoords; out vec4 fcolor; @@ -10,7 +10,7 @@ uniform vs_p { mat4 proj; }; void main() { - fcolor = vColor; + fcolor = vc; texcoords = uv; gl_Position = proj * vec4(vertex, 0.0, 1.0); } @@ -27,9 +27,11 @@ uniform sampler smp; void main() { color = texture(sampler2D(image,smp), texcoords); - + if (color.a <= 0.1f) discard; + + color = mix(color, fcolor, 0.01); } @end