instance saving

This commit is contained in:
John Alanbrook 2023-10-26 16:48:02 +00:00
parent 264a365a1a
commit 0fcc2286fa
17 changed files with 340 additions and 237 deletions

View file

@ -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 $^) > $@

View file

@ -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.

View file

@ -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 });

View file

@ -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;

View file

@ -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);
}

View file

@ -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)
{
}

View file

@ -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 = {};

View file

@ -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));
}

View file

@ -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]; },
@ -343,6 +345,10 @@ var gameobject = {
return d;
},
full_obj() {
},
transform_obj() {
var t = this.json_obj();
Object.assign(t, this.transform());
@ -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];

View file

@ -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 */

View file

@ -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.");

View file

@ -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;
}

View file

@ -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);
vsprintf(msg, message, arg2);
va_end(arg2);
char buffer[ERROR_BUFFER] = { '\0' };
snprintf(buffer, ERROR_BUFFER, "%s:%d: %s, %s: %s\n", file, line, logstr[priority], catstr[category], msgbuffer);
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);
}

View file

@ -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:

View file

@ -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)

View file

@ -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 {

View file

@ -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);
}
@ -30,6 +30,8 @@ void main()
if (color.a <= 0.1f)
discard;
color = mix(color, fcolor, 0.01);
}
@end