Add guid; tags; physics point checking

This commit is contained in:
John Alanbrook 2024-04-03 00:44:08 -05:00
parent 3f73a808d8
commit 033b7c5109
14 changed files with 212 additions and 167 deletions

View file

@ -1,10 +1,23 @@
this.phys = physics.kinematic; this.phys = physics.kinematic;
this.dir_view2world = function(dir) { return dir.scale(this.realzoom()); }; this.dir_view2world = function(dir) { return dir.scale(this.realzoom()); };
this.view2world = function(pos) { return render.view2world(pos); }; this.view2world = function(pos) {
this.world2view = function(pos) { return render.world2view(pos); }; pos = pos.scale([window.rendersize.x/window.size.x, window.rendersize.y/window.size.y]);
pos = pos.sub(window.rendersize.scale(0.5));
pos = pos.scale(this.zoom);
pos = pos.add(this.pos);
return pos;
};
this.world2view = function(pos) {
pos = pos.sub(this.pos);
pos = pos.scale(1.0/this.zoom);
pos = pos.add(window.rendersize);
return pos;
};
this.realzoom = function() { return render.get_zoom(); }; this.realzoom = function() { return render.get_zoom(); };
this.screenright = function() { return this.pos.x + (window.rendersize.x/2); } this.screenright = function() {
return this.view2world(window.size).x;
}
this.screenleft = function() { return this.pos.x - (window.rendersize.x/2); } this.screenleft = function() { return this.pos.x - (window.rendersize.x/2); }
this.zoom = 1; this.zoom = 1;

View file

@ -76,6 +76,7 @@ function json_from_whitelist(whitelist)
} }
Object.mixin(os.sprite(true), { Object.mixin(os.sprite(true), {
loop: true,
toJSON:json_from_whitelist([ toJSON:json_from_whitelist([
"path", "path",
"pos", "pos",
@ -96,7 +97,7 @@ Object.mixin(os.sprite(true), {
self.del_anim = undefined; self.del_anim = undefined;
self = undefined; self = undefined;
advance = undefined; advance = undefined;
stop(); stop?.();
} }
str ??= 0; str ??= 0;
var playing = self.anim[str]; var playing = self.anim[str];
@ -112,8 +113,7 @@ Object.mixin(os.sprite(true), {
f = (f+1)%playing.frames.length; f = (f+1)%playing.frames.length;
if (f === 0) { if (f === 0) {
self.anim_done?.(); self.anim_done?.();
self.anim_done = undefined; if (!self.loop) { self.stop(); return; }
// if (!self.loop) { self.stop(); return; }
} }
stop = self.gameobject.delay(advance, playing.frames[f].time); stop = self.gameobject.delay(advance, playing.frames[f].time);
} }
@ -149,6 +149,7 @@ Object.mixin(os.sprite(true), {
this.del_anim?.(); this.del_anim?.();
this.anim = undefined; this.anim = undefined;
this.gameobject = undefined; this.gameobject = undefined;
this.anim_done = undefined;
}, },
toString() { return "sprite"; }, toString() { return "sprite"; },
move(d) { this.pos = this.pos.add(d); }, move(d) { this.pos = this.pos.add(d); },
@ -461,14 +462,14 @@ polygon2d.inputs.f10 = function() {
polygon2d.inputs.f10.doc = "Sort all points to be CCW order."; polygon2d.inputs.f10.doc = "Sort all points to be CCW order.";
polygon2d.inputs['C-lm'] = function() { polygon2d.inputs['C-lm'] = function() {
this.points.push(this.gameobject.world2this(Mouse.worldpos())); this.points.push(this.gameobject.world2this(input.mouse.worldpos()));
}; };
polygon2d.inputs['C-lm'].doc = "Add a point to location of mouse."; polygon2d.inputs['C-lm'].doc = "Add a point to location of mouse.";
polygon2d.inputs.lm = function(){}; polygon2d.inputs.lm = function(){};
polygon2d.inputs.lm.released = function(){}; polygon2d.inputs.lm.released = function(){};
polygon2d.inputs['C-M-lm'] = function() { polygon2d.inputs['C-M-lm'] = function() {
var idx = Math.grab_from_points(Mouse.worldpos(), this.points.map(p => this.gameobject.this2world(p)), 25); var idx = Math.grab_from_points(input.mouse.worldpos(), this.points.map(p => this.gameobject.this2world(p)), 25);
if (idx === -1) return; if (idx === -1) return;
this.points.splice(idx, 1); this.points.splice(idx, 1);
}; };
@ -777,7 +778,7 @@ bucket.inputs['C-o'].doc = "Set spline to linear.";
bucket.inputs['C-M-lm'] = function() { bucket.inputs['C-M-lm'] = function() {
if (Spline.is_catmull(this.type)) { if (Spline.is_catmull(this.type)) {
var idx = Math.grab_from_points(Mouse.worldpos(), this.points.map(p => this.gameobject.this2world(p)), 25); var idx = Math.grab_from_points(input.mouse.worldpos(), this.points.map(p => this.gameobject.this2world(p)), 25);
if (idx === -1) return; if (idx === -1) return;
} else { } else {
@ -787,16 +788,16 @@ bucket.inputs['C-M-lm'] = function() {
}; };
bucket.inputs['C-M-lm'].doc = "Select the given point as the '0' of this spline."; bucket.inputs['C-M-lm'].doc = "Select the given point as the '0' of this spline.";
bucket.inputs['C-lm'] = function() { this.add_node(Mouse.worldpos()); } bucket.inputs['C-lm'] = function() { this.add_node(input.mouse.worldpos()); }
bucket.inputs['C-lm'].doc = "Add a point to the spline at the mouse position."; bucket.inputs['C-lm'].doc = "Add a point to the spline at the mouse position.";
bucket.inputs['C-M-lm'] = function() { bucket.inputs['C-M-lm'] = function() {
var idx = -1; var idx = -1;
if (Spline.is_catmull(this.type)) if (Spline.is_catmull(this.type))
idx = Math.grab_from_points(Mouse.worldpos(), this.points.map(p => this.gameobject.this2world(p)), 25); idx = Math.grab_from_points(input.mouse.worldpos(), this.points.map(p => this.gameobject.this2world(p)), 25);
else { else {
var nodes = Spline.bezier_nodes(this.points); var nodes = Spline.bezier_nodes(this.points);
idx = Math.grab_from_points(Mouse.worldpos(), nodes.map(p => this.gameobject.this2world(p)), 25); idx = Math.grab_from_points(input.mouse.worldpos(), nodes.map(p => this.gameobject.this2world(p)), 25);
idx *= 3; idx *= 3;
} }

View file

@ -22,12 +22,12 @@ debug.draw = function() {
if (this.draw_gizmos) if (this.draw_gizmos)
game.all_objects(function(x) { game.all_objects(function(x) {
if (!x.icon) return; if (!x.icon) return;
GUI.image(x.icon, window.world2screen(x.pos)); GUI.image(x.icon, game.camera.world2view(x.pos));
}); });
if (this.draw_names) if (this.draw_names)
game.all_objects(function(x) { game.all_objects(function(x) {
render.text(x, window.world2screen(x.pos).add([0,32]), 1, Color.debug.names); render.text(x, game.camera.view2screen(x.pos).add([0,32]), 1, Color.debug.names);
}); });
if (debug.gif.rec) { if (debug.gif.rec) {

View file

@ -24,7 +24,7 @@ var editor = {
desktop: undefined, /* The editor desktop, where all editing objects live */ desktop: undefined, /* The editor desktop, where all editing objects live */
working_layer: 0, working_layer: 0,
get cursor() { get cursor() {
if (this.selectlist.length === 0 ) return Mouse.worldpos(); if (this.selectlist.length === 0 ) return input.mouse.worldpos();
return physics.com(this.selectlist.map(x => x.pos)); return physics.com(this.selectlist.map(x => x.pos));
}, },
edit_mode: "basic", edit_mode: "basic",
@ -32,7 +32,7 @@ var editor = {
get_this() { return this.edit_level; }, get_this() { return this.edit_level; },
try_select() { /* nullify true if it should set selected to null if it doesn't find an object */ 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()); var go = physics.pos_query(input.mouse.worldpos());
return this.do_select(go); return this.do_select(go);
}, },
@ -82,7 +82,7 @@ var editor = {
return function(go) { go.pos = go.pos.add(amt)}; return function(go) { go.pos = go.pos.add(amt)};
}, },
step_amt() { return Keys.shift() ? 10 : 1; }, step_amt() { return input.keyboard.down("shift") ? 10 : 1; },
on_grid(pos) { on_grid(pos) {
return pos.every(function(x) { return x % editor.grid_size === 0; }); return pos.every(function(x) { return x % editor.grid_size === 0; });
@ -97,7 +97,7 @@ var editor = {
key_move(dir) { key_move(dir) {
if (!editor.grabselect) return; if (!editor.grabselect) return;
if (Keys.ctrl()) if (input.keyboard.down('ctrl'))
this.selectlist.forEach(this.snapper(dir.scale(1.01), editor.grid_size)); this.selectlist.forEach(this.snapper(dir.scale(1.01), editor.grid_size));
else else
this.selectlist.forEach(this.mover(dir.scale(this.step_amt()))); this.selectlist.forEach(this.mover(dir.scale(this.step_amt())));
@ -154,7 +154,7 @@ var editor = {
}, },
input_num_pressed(num) { input_num_pressed(num) {
if (Keys.ctrl()) { if (input.keyboard.down('ctrl')) {
this.camera_recalls[num] = { this.camera_recalls[num] = {
pos:this.camera.pos, pos:this.camera.pos,
zoom:this.camera.zoom zoom:this.camera.zoom
@ -375,7 +375,7 @@ var editor = {
/* Draw selection box */ /* Draw selection box */
if (this.sel_start) { if (this.sel_start) {
var endpos = Mouse.worldpos(); var endpos = input.mouse.worldpos();
var c = []; var c = [];
c[0] = (endpos[0] - this.sel_start[0]) / 2; c[0] = (endpos[0] - this.sel_start[0]) / 2;
c[0] += this.sel_start[0]; c[0] += this.sel_start[0];
@ -393,7 +393,7 @@ var editor = {
gui() { gui() {
/* Clean out killed objects */ /* Clean out killed objects */
this.selectlist = this.selectlist.filter(function(x) { return x.alive; }); this.selectlist = this.selectlist.filter(function(x) { return x.alive; });
render.text([0,0], window.world2screen([0,0])); render.text([0,0], game.camera.world2view([0,0]));
render.text("WORKING LAYER: " + this.working_layer, [0,520]); render.text("WORKING LAYER: " + this.working_layer, [0,520]);
render.text("MODE: " + this.edit_mode, [0,500]); render.text("MODE: " + this.edit_mode, [0,500]);
@ -455,15 +455,15 @@ var editor = {
render.circle(x[1].screenpos(),10,Color.blue.alpha(0.3)); render.circle(x[1].screenpos(),10,Color.blue.alpha(0.3));
}); });
var mg = physics.pos_query(Mouse.worldpos(),10); var mg = physics.pos_query(input.mouse.worldpos(),10);
if (mg) { if (mg) {
var p = mg.path_from(thiso); var p = mg.path_from(thiso);
render.text(p, Mouse.screenpos(),1,Color.teal); render.text(p, input.mouse.screenpos(),1,Color.teal);
} }
if (this.rotlist.length === 1) if (this.rotlist.length === 1)
render.text(Math.trunc(this.rotlist[0].obj.angle), Mouse.screenpos(), 1, Color.teal); render.text(Math.trunc(this.rotlist[0].obj.angle), input.mouse.screenpos(), 1, Color.teal);
if (this.selectlist.length === 1) { if (this.selectlist.length === 1) {
var i = 1; var i = 1;
@ -484,8 +484,8 @@ var editor = {
}); });
render.grid(1, editor.grid_size, Color.Editor.grid.alpha(0.3)); render.grid(1, editor.grid_size, Color.Editor.grid.alpha(0.3));
var startgrid = window.screen2world([-20,0]).map(function(x) { return Math.snap(x, editor.grid_size); }); var startgrid = game.camera.view2world([-20,0]).map(function(x) { return Math.snap(x, editor.grid_size); });
var endgrid = window.screen2world([window.width, window.height]); var endgrid = game.camera.view2world([window.width, window.height]);
var w_step = Math.round(editor.ruler_mark_px/window.width * (endgrid.x-startgrid.x)/editor.grid_size)*editor.grid_size; var w_step = Math.round(editor.ruler_mark_px/window.width * (endgrid.x-startgrid.x)/editor.grid_size)*editor.grid_size;
if (w_step === 0) w_step = editor.grid_size; if (w_step === 0) w_step = editor.grid_size;
@ -494,12 +494,12 @@ var editor = {
if (h_step === 0) h_step = editor.grid_size; if (h_step === 0) h_step = editor.grid_size;
while(startgrid[0] <= endgrid[0]) { while(startgrid[0] <= endgrid[0]) {
render.text(startgrid[0], [window.world2screen([startgrid[0], 0])[0],0]); render.text(startgrid[0], [game.camera.world2view([startgrid[0], 0])[0],0]);
startgrid[0] += w_step; startgrid[0] += w_step;
} }
while(startgrid[1] <= endgrid[1]) { while(startgrid[1] <= endgrid[1]) {
render.text(startgrid[1], [0, window.world2screen([0, startgrid[1]])[1]]); render.text(startgrid[1], [0, game.camera.world2view([0, startgrid[1]])[1]]);
startgrid[1] += h_step; startgrid[1] += h_step;
} }
@ -523,7 +523,7 @@ var editor = {
load(urstr) { load(urstr) {
var obj = editor.edit_level.spawn(urstr); var obj = editor.edit_level.spawn(urstr);
obj.set_worldpos(Mouse.worldpos()); obj.set_worldpos(input.mouse.worldpos());
this.selectlist = [obj]; this.selectlist = [obj];
}, },
@ -604,7 +604,7 @@ var editor = {
editor.new_object = function() editor.new_object = function()
{ {
var obj = editor.edit_level.spawn(); var obj = editor.edit_level.spawn();
obj.set_worldpos(Mouse.worldpos()); obj.set_worldpos(input.mouse.worldpos());
this.selectlist = [obj]; this.selectlist = [obj];
return obj; return obj;
} }
@ -650,7 +650,7 @@ editor.inputs.drop = function(str) {
return; return;
} }
var mg = physics.pos_query(Mouse.worldpos(),10); var mg = physics.pos_query(input.mouse.worldpos(),10);
if (!mg) return; if (!mg) return;
var img = mg.get_comp_by_name('sprite'); var img = mg.get_comp_by_name('sprite');
if (!img) return; if (!img) return;
@ -794,7 +794,7 @@ editor.inputs['C-r'].doc = "Negate the selected's angle.";
editor.inputs.r = function() { editor.inputs.r = function() {
if (editor.sel_comp && 'angle' in editor.sel_comp) { if (editor.sel_comp && 'angle' in editor.sel_comp) {
var relpos = Mouse.worldpos().sub(editor.sel_comp.gameobject.worldpos()); var relpos = input.mouse.worldpos().sub(editor.sel_comp.gameobject.worldpos());
editor.startoffset = Math.atan2(relpos.y, relpos.x); editor.startoffset = Math.atan2(relpos.y, relpos.x);
editor.startrot = editor.sel_comp.angle; editor.startrot = editor.sel_comp.angle;
@ -803,7 +803,7 @@ editor.inputs.r = function() {
editor.rotlist = []; editor.rotlist = [];
editor.selectlist.forEach(function(x) { editor.selectlist.forEach(function(x) {
var relpos = Mouse.worldpos().sub(editor.cursor); var relpos = input.mouse.worldpos().sub(editor.cursor);
editor.rotlist.push({ editor.rotlist.push({
obj: x, obj: x,
angle: x.angle, angle: x.angle,
@ -1016,11 +1016,11 @@ editor.inputs.f3 = function() {
this.openpanel(componentexplorer); this.openpanel(componentexplorer);
}; };
editor.inputs.lm = function() { editor.sel_start = Mouse.worldpos(); }; editor.inputs.lm = function() { editor.sel_start = input.mouse.worldpos(); };
editor.inputs.lm.doc = "Selection box."; editor.inputs.lm.doc = "Selection box.";
editor.inputs.lm.released = function() { editor.inputs.lm.released = function() {
Mouse.normal(); input.mouse.normal();
editor.unselect(); editor.unselect();
if (!editor.sel_start) return; if (!editor.sel_start) return;
@ -1033,11 +1033,11 @@ editor.inputs.lm.released = function() {
var selects = []; var selects = [];
/* TODO: selects somehow gets undefined objects in here */ /* TODO: selects somehow gets undefined objects in here */
if (Vector.equal(Mouse.worldpos(), editor.sel_start, 5)) { if (Vector.equal(input.mouse.worldpos(), editor.sel_start, 5)) {
var sel = editor.try_select(); var sel = editor.try_select();
if (sel) selects.push(sel); if (sel) selects.push(sel);
} else { } else {
var box = bbox.frompoints([editor.sel_start, Mouse.worldpos()]); var box = bbox.frompoints([editor.sel_start, input.mouse.worldpos()]);
physics.box_query(bbox.tocwh(box), function(entity) { physics.box_query(bbox.tocwh(box), function(entity) {
var obj = editor.do_select(entity); var obj = editor.do_select(entity);
@ -1051,7 +1051,7 @@ editor.inputs.lm.released = function() {
if (Object.empty(selects)) return; if (Object.empty(selects)) return;
if (Keys.shift()) { if (input.keyboard.down('shift')) {
selects.forEach(function(x) { selects.forEach(function(x) {
this.selectlist.push_unique(x); this.selectlist.push_unique(x);
}, this); }, this);
@ -1059,7 +1059,7 @@ editor.inputs.lm.released = function() {
return; return;
} }
if (Keys.ctrl()) { if (input.keyboard.down('ctrl')) {
selects.forEach(function(x) { selects.forEach(function(x) {
delete this.selectlist[x.toString()]; delete this.selectlist[x.toString()];
}, this); }, this);
@ -1091,7 +1091,7 @@ editor.try_pick = function()
editor.grabselect = []; editor.grabselect = [];
if (editor.sel_comp && 'pick' in editor.sel_comp) if (editor.sel_comp && 'pick' in editor.sel_comp)
return editor.sel_comp.pick(Mouse.worldpos()); return editor.sel_comp.pick(input.mouse.worldpos());
return editor.try_select(); return editor.try_select();
} }
@ -1099,7 +1099,7 @@ editor.try_pick = function()
editor.inputs.mm = function() { editor.inputs.mm = function() {
if (editor.brush_obj) { if (editor.brush_obj) {
editor.selectlist = editor.dup_objects([editor.brush_obj]); editor.selectlist = editor.dup_objects([editor.brush_obj]);
editor.selectlist[0].pos = Mouse.worldpos(); editor.selectlist[0].pos = input.mouse.worldpos();
editor.grabselect = editor.selectlist[0]; editor.grabselect = editor.selectlist[0];
return; return;
} }
@ -1113,30 +1113,30 @@ editor.inputs['C-mm'] = editor.inputs.mm;
editor.inputs['C-M-lm'] = function() editor.inputs['C-M-lm'] = function()
{ {
var go = physics.pos_query(Mouse.worldpos()); var go = physics.pos_query(input.mouse.worldpos());
if (!go) return; if (!go) return;
editor.edit_level = go.master; editor.edit_level = go.master;
} }
editor.inputs['C-M-mm'] = function() { editor.inputs['C-M-mm'] = function() {
editor.mousejoy = Mouse.screenpos(); editor.mousejoy = input.mouse.screenpos();
editor.joystart = editor.camera.pos; editor.joystart = editor.camera.pos;
}; };
editor.inputs['C-M-rm'] = function() { editor.inputs['C-M-rm'] = function() {
editor.mousejoy = Mouse.screenpos(); editor.mousejoy = input.mouse.screenpos();
editor.z_start = editor.camera.zoom; editor.z_start = editor.camera.zoom;
Mouse.disabled(); input.mouse.disabled();
}; };
editor.inputs.rm.released = function() { editor.inputs.rm.released = function() {
editor.mousejoy = undefined; editor.mousejoy = undefined;
editor.z_start = undefined; editor.z_start = undefined;
Mouse.normal(); input.mouse.normal();
}; };
editor.inputs.mm.released = function () { editor.inputs.mm.released = function () {
Mouse.normal(); input.mouse.normal();
this.grabselect = []; this.grabselect = [];
editor.mousejoy = undefined; editor.mousejoy = undefined;
editor.joystart = undefined; editor.joystart = undefined;
@ -1157,7 +1157,7 @@ editor.inputs.mouse.move = function(pos, dpos)
x.sync(); x.sync();
}); });
var relpos = Mouse.worldpos().sub(editor.cursor); var relpos = input.mouse.worldpos().sub(editor.cursor);
var dist = Vector.length(relpos); var dist = Vector.length(relpos);
editor.scalelist?.forEach(function(x) { editor.scalelist?.forEach(function(x) {
@ -1174,7 +1174,7 @@ editor.inputs.mouse.move = function(pos, dpos)
editor.rotlist?.forEach(function(x) { editor.rotlist?.forEach(function(x) {
var anglediff = Math.atan2(relpos.y, relpos.x) - x.rotoffset; var anglediff = Math.atan2(relpos.y, relpos.x) - x.rotoffset;
x.obj.angle = x.angle + Math.rad2turn(anglediff); x.obj.angle = x.angle + Math.rad2turn(anglediff);
if (Keys.shift()) if (input.keyboard.down('shift'))
x.obj.angle = Math.nearest(x.obj.angle, (1/24)); x.obj.angle = Math.nearest(x.obj.angle, (1/24));
if (x.pos) if (x.pos)
x.obj.pos = x.pos.sub(x.offset).add(x.offset.rotate(anglediff)); x.obj.pos = x.pos.sub(x.offset).add(x.offset.rotate(anglediff));
@ -1192,7 +1192,7 @@ editor.inputs.mouse['C-scroll'] = function(scroll)
editor.camera.zoom += scroll.y/100; editor.camera.zoom += scroll.y/100;
} }
editor.inputs['C-M-S-lm'] = function() { editor.selectlist[0].set_center(Mouse.worldpos()); }; editor.inputs['C-M-S-lm'] = function() { editor.selectlist[0].set_center(input.mouse.worldpos()); };
editor.inputs['C-M-S-lm'].doc = "Set world center to mouse position."; editor.inputs['C-M-S-lm'].doc = "Set world center to mouse position.";
editor.inputs.delete = function() { editor.inputs.delete = function() {
@ -1229,7 +1229,7 @@ editor.inputs.g = function() {
if (editor.sel_comp) { if (editor.sel_comp) {
if ('pick' in editor.sel_comp) { if ('pick' in editor.sel_comp) {
editor.grabselect = [editor.sel_comp.pick(Mouse.worldpos())]; editor.grabselect = [editor.sel_comp.pick(input.mouse.worldpos())];
return; return;
} }
@ -1246,7 +1246,7 @@ editor.inputs.g = function() {
} }
if (editor.sel_comp && 'pick' in editor.sel_comp) { if (editor.sel_comp && 'pick' in editor.sel_comp) {
var o = editor.sel_comp.pick(Mouse.worldpos()); var o = editor.sel_comp.pick(input.mouse.worldpos());
if (o) editor.grabselect = [o]; if (o) editor.grabselect = [o];
return; return;
} }
@ -1255,7 +1255,7 @@ editor.inputs.g = function() {
}; };
editor.inputs.g.doc = "Move selected objects."; editor.inputs.g.doc = "Move selected objects.";
editor.inputs.g.released = function() { editor.grabselect = []; Mouse.normal(); }; editor.inputs.g.released = function() { editor.grabselect = []; input.mouse.normal(); };
editor.inputs.up = function() { this.key_move([0,1]); }; editor.inputs.up = function() { this.key_move([0,1]); };
editor.inputs.up.rep = true; editor.inputs.up.rep = true;
@ -1302,13 +1302,13 @@ editor.inputs['M-g'] = function()
editor.inputs['M-g'].doc = "Move all."; editor.inputs['M-g'].doc = "Move all.";
editor.inputs['C-lb'] = function() { editor.inputs['C-lb'] = function() {
editor.grid_size -= Keys.shift() ? 10 : 1; editor.grid_size -= input.keyboard.down('shift') ? 10 : 1;
if (editor.grid_size <= 0) editor.grid_size = 1; if (editor.grid_size <= 0) editor.grid_size = 1;
}; };
editor.inputs['C-lb'].doc = "Decrease grid size. Hold shift to decrease it more."; editor.inputs['C-lb'].doc = "Decrease grid size. Hold shift to decrease it more.";
editor.inputs['C-lb'].rep = true; editor.inputs['C-lb'].rep = true;
editor.inputs['C-rb'] = function() { editor.grid_size += Keys.shift() ? 10 : 1; }; editor.inputs['C-rb'] = function() { editor.grid_size += input.keyboard.down('shift') ? 10 : 1; };
editor.inputs['C-rb'].doc = "Increase grid size. Hold shift to increase it more."; editor.inputs['C-rb'].doc = "Increase grid size. Hold shift to increase it more.";
editor.inputs['C-rb'].rep = true; editor.inputs['C-rb'].rep = true;
@ -1373,7 +1373,7 @@ compmode.inputs['C-x'] = function() {};
editor.scalelist = []; editor.scalelist = [];
editor.inputs.s = function() { editor.inputs.s = function() {
var scaleoffset = Vector.length(Mouse.worldpos().sub(editor.cursor)); var scaleoffset = Vector.length(input.mouse.worldpos().sub(editor.cursor));
editor.scalelist = []; editor.scalelist = [];
if (editor.sel_comp) { if (editor.sel_comp) {
@ -1580,7 +1580,7 @@ replpanel.inputs = Object.create(inputpanel.inputs);
replpanel.inputs.block = true; replpanel.inputs.block = true;
replpanel.inputs.lm = function() replpanel.inputs.lm = function()
{ {
var mg = physics.pos_query(Mouse.worldpos()); var mg = physics.pos_query(input.mouse.worldpos());
if (!mg) return; if (!mg) return;
var p = mg.path_from(editor.get_this()); var p = mg.path_from(editor.get_this());
this.value = p; this.value = p;

View file

@ -254,7 +254,6 @@ var sim = {
} }
var physlag = 0; var physlag = 0;
var timescale = 1;
var gggstart = game.engine_start; var gggstart = game.engine_start;
game.engine_start = function(s) { game.engine_start = function(s) {
@ -288,8 +287,8 @@ function process()
while (physlag > phys_step) { while (physlag > phys_step) {
physlag -= phys_step; physlag -= phys_step;
prosperon.phys2d_step(phys_step*timescale); prosperon.phys2d_step(phys_step*game.timescale);
prosperon.physupdate(phys_step*timescale); prosperon.physupdate(phys_step*game.timescale);
} }
if (!game.camera) if (!game.camera)
@ -314,6 +313,7 @@ function process()
game.timescale = 1; game.timescale = 1;
game.all_objects = function(fn) {
var eachobj = function(obj,fn) var eachobj = function(obj,fn)
{ {
fn(obj); fn(obj);
@ -321,13 +321,35 @@ var eachobj = function(obj,fn)
eachobj(obj.objects[o],fn); eachobj(obj.objects[o],fn);
} }
game.all_objects = function(fn) { eachobj(world,fn); }; eachobj(world,fn);
};
game.tags = {};
game.tag_add = function(tag, obj) {
game.tags[tag] ??= {};
game.tags[tag][obj.guid] = obj;
}
game.tag_rm = function(tag, obj) {
delete game.tags[tag][obj.guid];
}
game.tag_clear_guid = function(guid)
{
for (var tag in game.tags)
delete game.tags[tag][guid];
}
game.objects_with_tag = function(tag)
{
if (!game.tags[tag]) return;
return Object.values(game.tags[tag]);
}
game.doc = {}; game.doc = {};
game.doc.object = "Returns the entity belonging to a given id."; game.doc.object = "Returns the entity belonging to a given id.";
game.doc.pause = "Pause game simulation."; game.doc.pause = "Pause game simulation.";
game.doc.play = "Resume or start game simulation."; game.doc.play = "Resume or start game simulation.";
game.doc.dt = "Current frame dt.";
game.doc.camera = "Current camera."; game.doc.camera = "Current camera.";
game.texture = function(path) game.texture = function(path)
@ -344,7 +366,6 @@ game.texture = function(path)
} }
game.texture.cache = {}; game.texture.cache = {};
prosperon.semver = {}; prosperon.semver = {};
prosperon.semver.valid = function(v, range) prosperon.semver.valid = function(v, range)
{ {
@ -519,17 +540,6 @@ window.modetypes = {
window.size = [640, 480]; window.size = [640, 480];
window.screen2world = function(screenpos) {
if (game.camera)
return game.camera.view2world(screenpos);
return screenpos;
}
window.world2screen = function(worldpos) {
return game.camera.world2view(worldpos);
}
window.set_icon.doc = "Set the icon of the window using the PNG image at path."; window.set_icon.doc = "Set the icon of the window using the PNG image at path.";
global.mixin("scripts/spline"); global.mixin("scripts/spline");

View file

@ -239,7 +239,7 @@ var gameobject = {
this.pos = x; this.pos = x;
this.objects.forEach((o, i) => o.set_worldpos(this.this2world(poses[i]))); this.objects.forEach((o, i) => o.set_worldpos(this.this2world(poses[i])));
}, },
screenpos() { return window.world2screen(this.worldpos()); }, screenpos() { return game.camera.world2view(this.worldpos()); },
worldangle() { return this.angle; }, worldangle() { return this.angle; },
sworldangle(x) { this.angle = x; }, sworldangle(x) { this.angle = x; },
@ -254,10 +254,19 @@ var gameobject = {
*/ */
spawn(text, config, callback) { spawn(text, config, callback) {
var ent = os.make_gameobject(); var ent = os.make_gameobject();
ent.guid = prosperon.guid();
ent.setref(ent); ent.setref(ent);
ent.components = {}; ent.components = {};
ent.objects = {}; ent.objects = {};
ent.timers = []; ent.timers = [];
ent.reparent(this);
ent._ed = {
selectable: true,
dirty: false,
inst: false,
urdiff: {},
};
if (typeof text === 'object') // assume it's an ur if (typeof text === 'object') // assume it's an ur
{ {
config = text.data; config = text.data;
@ -269,15 +278,6 @@ var gameobject = {
if (config) if (config)
Object.assign(ent, json.decode(io.slurp(config))); Object.assign(ent, json.decode(io.slurp(config)));
ent.reparent(this);
ent._ed = {
selectable: true,
dirty: false,
inst: false,
urdiff: {},
};
ent.ur = text + "+" + config; ent.ur = text + "+" + config;
for (var [prop, p] of Object.entries(ent)) { for (var [prop, p] of Object.entries(ent)) {
@ -321,6 +321,8 @@ var gameobject = {
} }
} }
if (ent.tag) game.tag_add(ent.tag, ent);
return ent; return ent;
}, },
@ -368,8 +370,8 @@ var gameobject = {
objects: {}, objects: {},
master: undefined, master: undefined,
this2screen(pos) { return window.world2screen(this.this2world(pos)); }, this2screen(pos) { return game.camera.world2view(this.this2world(pos)); },
screen2this(pos) { return this.world2this(window.screen2world(pos)); }, screen2this(pos) { return this.world2this(game.camera.view2world(pos)); },
in_air() { return this.in_air(); }, in_air() { return this.in_air(); },
@ -544,6 +546,8 @@ var gameobject = {
this.clear(); this.clear();
if (typeof this.stop === 'function') this.stop(); if (typeof this.stop === 'function') this.stop();
game.tag_clear_guid(this.guid);
for (var i in this) { for (var i in this) {
if (typeof this[i] === 'object') delete this[i]; if (typeof this[i] === 'object') delete this[i];
if (typeof this[i] === 'function') delete this[i]; if (typeof this[i] === 'function') delete this[i];

View file

@ -69,7 +69,7 @@ GUI.controls.set_mum = function(mum)
} }
GUI.controls.check_bb = function(mum) GUI.controls.check_bb = function(mum)
{ {
if (bbox.pointin(mum.bb, Mouse.screenpos())) if (bbox.pointin(mum.bb, input.mouse.screenpos()))
GUI.controls.set_mum(mum); GUI.controls.set_mum(mum);
} }
GUI.controls.inputs = {}; GUI.controls.inputs = {};

View file

@ -1,4 +1,4 @@
var keycodes = { input.keycodes = {
259: "back", 259: "back",
258: "tab", 258: "tab",
257: "enter", 257: "enter",
@ -17,9 +17,9 @@ var keycodes = {
45: "minus", 45: "minus",
}; };
var codekeys = {}; input.codekeys = {};
for (var code in keycodes) for (var code in input.keycodes)
codekeys[keycodes[code]] = code; input.codekeys[input.keycodes[code]] = code;
var mod = { var mod = {
shift: 0, shift: 0,
@ -48,7 +48,7 @@ function keyname_extd(key)
return `kp${num}`; return `kp${num}`;
} }
if (keycodes[key]) return keycodes[key]; if (input.keycodes[key]) return input.keycodes[key];
if (key >= 32 && key <= 126) return String.fromCharCode(key).lc(); if (key >= 32 && key <= 126) return String.fromCharCode(key).lc();
return undefined; return undefined;
@ -120,32 +120,32 @@ prosperon.mousescroll = function(dx){
player[0].mouse_input(modstr() + "scroll", dx); player[0].mouse_input(modstr() + "scroll", dx);
}; };
prosperon.mousedown = function(b){ prosperon.mousedown = function(b){
player[0].raw_input(modstr() + Mouse.button[b], "pressed"); player[0].raw_input(modstr() + input.mouse.button[b], "pressed");
}; };
prosperon.mouseup = function(b){ prosperon.mouseup = function(b){
player[0].raw_input(modstr() + Mouse.button[b], "released"); player[0].raw_input(modstr() + input.mouse.button[b], "released");
}; };
var Mouse = { input.mouse = {
screenpos() { return mousepos.slice(); }, screenpos() { return mousepos.slice(); },
worldpos() { return window.screen2world(mousepos); }, worldpos() { return game.camera.view2world(mousepos); },
disabled() { input.mouse_mode(1); }, disabled() { input.mouse_mode(1); },
normal() { input.mouse_mode(0); }, normal() { input.mouse_mode(0); },
mode(m) { mode(m) {
if (Mouse.custom[m]) if (input.mouse.custom[m])
input.cursor_img(Mouse.custom[m]); input.cursor_img(input.mouse.custom[m]);
else else
input.mouse_cursor(m); input.mouse_cursor(m);
}, },
set_custom_cursor(img, mode) { set_custom_cursor(img, mode) {
mode ??= Mouse.cursor.default; mode ??= input.mouse.cursor.default;
if (!img) if (!img)
delete Mouse.custom[mode]; delete input.mouse.custom[mode];
else { else {
input.cursor_img(img); input.cursor_img(img);
Mouse.custom[mode] = img; input.mouse.custom[mode] = img;
} }
}, },
@ -170,17 +170,18 @@ var Mouse = {
}, },
}; };
Mouse.doc = {}; input.mouse.doc = {};
Mouse.doc.pos = "The screen position of the mouse."; input.mouse.doc.pos = "The screen position of the mouse.";
Mouse.doc.worldpos = "The position in the game world of the mouse."; input.mouse.doc.worldpos = "The position in the game world of the mouse.";
Mouse.disabled.doc = "Set the mouse to hidden. This locks it to the game and hides it, but still provides movement and click events."; input.mouse.disabled.doc = "Set the mouse to hidden. This locks it to the game and hides it, but still provides movement and click events.";
Mouse.normal.doc = "Set the mouse to show again after hiding."; input.mouse.normal.doc = "Set the mouse to show again after hiding.";
var Keys = { input.keyboard = {};
down(code) { input.keyboard.down = function(code) {
return prosperon.keys[code]; if (typeof code === 'number') return prosperon.keys[code];
}, if (typeof code === 'string') return prosperon.keys[keyname_extd(code)];
}; return undefined;
}
input.state2str = function(state) { input.state2str = function(state) {
if (typeof state === 'string') return state; if (typeof state === 'string') return state;
@ -370,10 +371,5 @@ Player.doc.players = "A list of current players.";
var player = Player; var player = Player;
return { return {
Mouse, player
Keys,
Player,
player,
keycodes,
codekeys
}; };

View file

@ -246,6 +246,7 @@ Cmdline.register_order("play", function(argv) {
global.mixin("scripts/sound.js"); global.mixin("scripts/sound.js");
global.app = actor.spawn("game.js"); global.app = actor.spawn("game.js");
if (project.icon) window.set_icon(game.texture(project.icon)); if (project.icon) window.set_icon(game.texture(project.icon));
game.camera = world.spawn("scripts/camera2d");
}); });
}, "Play the game present in this folder."); }, "Play the game present in this folder.");

View file

@ -582,9 +582,9 @@ JSValue arb2js(cpArbiter *arb)
void phys_run_post(cpSpace *space, JSValue *fn, JSValue *hit) void phys_run_post(cpSpace *space, JSValue *fn, JSValue *hit)
{ {
JSValue hh = *hit; script_call_sym(*fn, 1, hit);
script_call_sym(*fn, 1, &hh); JS_FreeValue(js, *hit);
JS_FreeValue(js, hh); JS_FreeValue(js, *fn);
} }
/* TODO: Limitation, cannot handle multiple collision same frame */ /* TODO: Limitation, cannot handle multiple collision same frame */
@ -595,6 +595,7 @@ int script_phys_cb_begin(cpArbiter *arb, cpSpace *space, gameobject *go)
if (!JS_IsUndefined(go->cbs.begin) && cpSpaceAddPostStepCallback(space, phys_run_post, &go->cbs.begin, &go->cbs.bhit)) { if (!JS_IsUndefined(go->cbs.begin) && cpSpaceAddPostStepCallback(space, phys_run_post, &go->cbs.begin, &go->cbs.bhit)) {
YughSpam("Added begin for %p", &go->cbs.begin); YughSpam("Added begin for %p", &go->cbs.begin);
JS_DupValue(js, go->cbs.begin);
go->cbs.bhit = arb2js(arb); go->cbs.bhit = arb2js(arb);
} }

View file

@ -714,8 +714,6 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, make_texture, 1), MIST_FUNC_DEF(os, make_texture, 1),
}; };
JSC_CCALL(render_normal, opengl_rendermode(LIT))
JSC_CCALL(render_wireframe, opengl_rendermode(WIREFRAME))
JSC_CCALL(render_grid, draw_grid(js2number(argv[0]), js2number(argv[1]), js2color(argv[2]));) JSC_CCALL(render_grid, draw_grid(js2number(argv[0]), js2number(argv[1]), js2color(argv[2]));)
JSC_CCALL(render_point, draw_cppoint(js2vec2(argv[0]), js2number(argv[1]), js2color(argv[2]))) JSC_CCALL(render_point, draw_cppoint(js2vec2(argv[0]), js2number(argv[1]), js2color(argv[2])))
JSC_CCALL(render_circle, draw_circle(js2vec2(argv[0]), js2number(argv[1]), js2number(argv[2]), js2color(argv[3]), -1);) JSC_CCALL(render_circle, draw_circle(js2vec2(argv[0]), js2number(argv[1]), js2number(argv[2]), js2color(argv[3]), -1);)
@ -743,16 +741,10 @@ JSC_CCALL(render_end_pass,
sprite_flush(); sprite_flush();
) )
JSC_SCALL(render_text_size, ret = bb2js(text_bb(str, js2number(argv[1]), js2number(argv[2]), 1))) JSC_SCALL(render_text_size, ret = bb2js(text_bb(str, js2number(argv[1]), js2number(argv[2]), 1)))
JSC_CCALL(render_world2screen, return vec22js(world2screen(js2vec2(argv[0]))))
JSC_CCALL(render_screen2world, return vec22js(screen2world(js2vec2(argv[0]))))
JSC_CCALL(render_set_camera, useproj = projection) JSC_CCALL(render_set_camera, useproj = projection)
JSC_CCALL(render_set_window, useproj = hudproj) JSC_CCALL(render_set_window, useproj = hudproj)
static const JSCFunctionListEntry js_render_funcs[] = { static const JSCFunctionListEntry js_render_funcs[] = {
MIST_FUNC_DEF(render,world2screen,1),
MIST_FUNC_DEF(render,screen2world,1),
MIST_FUNC_DEF(render, normal, 0),
MIST_FUNC_DEF(render, wireframe, 0),
MIST_FUNC_DEF(render, grid, 3), MIST_FUNC_DEF(render, grid, 3),
MIST_FUNC_DEF(render, point, 3), MIST_FUNC_DEF(render, point, 3),
MIST_FUNC_DEF(render, circle, 3), MIST_FUNC_DEF(render, circle, 3),
@ -873,11 +865,21 @@ static const JSCFunctionListEntry js_input_funcs[] = {
JSC_CCALL(prosperon_emitters_step, emitters_step(js2number(argv[0]))) JSC_CCALL(prosperon_emitters_step, emitters_step(js2number(argv[0])))
JSC_CCALL(prosperon_phys2d_step, phys2d_update(js2number(argv[0]))) JSC_CCALL(prosperon_phys2d_step, phys2d_update(js2number(argv[0])))
JSC_CCALL(prosperon_window_render, openglRender(&mainwin, js2gameobject(argv[0]), js2number(argv[1]))) JSC_CCALL(prosperon_window_render, openglRender(&mainwin, js2gameobject(argv[0]), js2number(argv[1])))
JSC_CCALL(prosperon_guid,
uint8_t bytes[16];
for (int i = 0; i < 16; i++) bytes[i] = rand()%256;
char uuid[37];
snprintf(uuid, 37, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]);
return str2js(uuid);
)
static const JSCFunctionListEntry js_prosperon_funcs[] = { static const JSCFunctionListEntry js_prosperon_funcs[] = {
MIST_FUNC_DEF(prosperon, emitters_step, 1), MIST_FUNC_DEF(prosperon, emitters_step, 1),
MIST_FUNC_DEF(prosperon, phys2d_step, 1), MIST_FUNC_DEF(prosperon, phys2d_step, 1),
MIST_FUNC_DEF(prosperon, window_render, 0) MIST_FUNC_DEF(prosperon, window_render, 0),
MIST_FUNC_DEF(prosperon, guid, 0),
}; };
JSC_CCALL(time_now, JSC_CCALL(time_now,
@ -1085,18 +1087,50 @@ static void ray_query_fn(cpShape *shape, float t, cpVect n, float a, JSValue *cb
number2js(a) number2js(a)
}; };
script_call_sym(*cb, 3, argv); script_call_sym(*cb, 3, argv);
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++) JS_FreeValue(js, argv[i]);
JS_FreeValue(js, argv[i]);
} }
JSC_CCALL(physics_ray_query, JSC_CCALL(physics_ray_query,
cpSpaceSegmentQuery(space, js2vec2(argv[0]).cp, js2vec2(argv[1]).cp, js2number(argv[2]), allfilter, ray_query_fn, &argv[3]); cpSpaceSegmentQuery(space, js2vec2(argv[0]).cp, js2vec2(argv[1]).cp, js2number(argv[2]), allfilter, ray_query_fn, &argv[3]);
); );
static void point_query_fn(cpShape *shape, float dist, cpVect point, JSValue *cb)
{
JSValue argv[3] = {
JS_DupValue(js, shape2go(shape)->ref),
vec22js((HMM_Vec2)point),
number2js(dist)
};
script_call_sym(*cb, 3, argv);
for (int i = 0; i < 3; i++) JS_FreeValue(js, argv[i]);
}
JSC_CCALL(physics_point_query,
cpSpacePointQuery(space, js2vec2(argv[0]).cp, js2number(argv[1]), allfilter, point_query_fn, &argv[2]);
);
JSValue pointinfo2js(cpPointQueryInfo info)
{
JSValue o = JS_NewObject(js);
JS_SetPropertyStr(js, o, "distance", number2js(info.distance));
JS_SetPropertyStr(js, o, "point", vec22js((HMM_Vec2)info.point));
JS_SetPropertyStr(js, o, "entity", JS_DupValue(js, shape2go(info.shape)->ref));
return o;
}
JSC_CCALL(physics_point_query_nearest,
cpPointQueryInfo info;
cpShape *sh = cpSpacePointQueryNearest(space, js2vec2(argv[0]).cp, js2number(argv[1]), allfilter, &info);
if (!sh) return JS_UNDEFINED;
return pointinfo2js(info);
)
static const JSCFunctionListEntry js_physics_funcs[] = { static const JSCFunctionListEntry js_physics_funcs[] = {
MIST_FUNC_DEF(physics, sgscale, 2), MIST_FUNC_DEF(physics, sgscale, 2),
MIST_FUNC_DEF(physics, set_cat_mask, 2), MIST_FUNC_DEF(physics, set_cat_mask, 2),
MIST_FUNC_DEF(physics, pos_query, 2), MIST_FUNC_DEF(physics, pos_query, 2),
MIST_FUNC_DEF(physics, point_query, 3),
MIST_FUNC_DEF(physics, point_query_nearest, 3),
MIST_FUNC_DEF(physics, ray_query, 2), MIST_FUNC_DEF(physics, ray_query, 2),
MIST_FUNC_DEF(physics, box_query, 2), MIST_FUNC_DEF(physics, box_query, 2),
MIST_FUNC_DEF(physics, shape_query, 1), MIST_FUNC_DEF(physics, shape_query, 1),
@ -1200,7 +1234,13 @@ static const JSCFunctionListEntry js_window_funcs[] = {
MIST_FUNC_DEF(window, set_icon, 1) MIST_FUNC_DEF(window, set_icon, 1)
}; };
JSC_GETSET_BODY(pos, Position, cvec2) JSValue js_gameobject_set_pos(JSContext *js, JSValue this, JSValue val) {
gameobject *go = js2gameobject(this);
cpBodySetPosition(go->body, js2vec2(val).cp);
if (go->phys == CP_BODY_TYPE_STATIC)
cpSpaceReindexShapesForBody(space, go->body);
}
JSValue js_gameobject_get_pos(JSContext *js, JSValue this) { return vec22js((HMM_Vec2)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_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_angle (JSContext *js, JSValue this) { return number2js(HMM_RadToTurn*cpBodyGetAngle(js2gameobject(this)->body)); }
JSC_GETSET_BODY(velocity, Velocity, cvec2) JSC_GETSET_BODY(velocity, Velocity, cvec2)

View file

@ -121,18 +121,13 @@ void capture_screen(int x, int y, int w, int h, const char *path)
#include "HandmadeMath.h" #include "HandmadeMath.h"
int renderMode = LIT;
struct rgba editorClearColor = {35,60,92,255}; struct rgba editorClearColor = {35,60,92,255};
void opengl_rendermode(enum RenderMode r) {
renderMode = r;
}
sg_pass_action pass_action = {0}; sg_pass_action pass_action = {0};
static struct { static struct {
sg_pass_action pass_action; sg_pass_action pass_action;
sg_pass pass; sg_pass pass;
sg_pipeline pipe; sg_pipeline pipe;
sg_shader shader; sg_shader shader;
@ -283,23 +278,6 @@ void render_init() {
}); });
} }
HMM_Vec2 world2screen(HMM_Vec2 pos)
{
pos = HMM_SubV2(pos, campos);
pos = HMM_ScaleV2(pos, 1.0/camzoom);
pos = HMM_AddV2(pos, HMM_ScaleV2(mainwin.size,0.5));
return pos;
}
HMM_Vec2 screen2world(HMM_Vec2 pos)
{
pos = HMM_ScaleV2(pos, 1/mainwin.dpi);
pos = HMM_SubV2(pos, HMM_ScaleV2(mainwin.size, 0.5));
pos = HMM_ScaleV2(pos, camzoom);
pos = HMM_AddV2(pos, campos);
return pos;
}
HMM_Mat4 projection = {0.f}; HMM_Mat4 projection = {0.f};
HMM_Mat4 hudproj = {0.f}; HMM_Mat4 hudproj = {0.f};
HMM_Mat4 useproj = {0}; HMM_Mat4 useproj = {0};

View file

@ -85,8 +85,9 @@ int sprite_sort(sprite **sa, sprite **sb)
if (!goa && !gob) return 0; if (!goa && !gob) return 0;
if (!goa) return -1; if (!goa) return -1;
if (!gob) return 1; if (!gob) return 1;
if (goa->drawlayer == gob->drawlayer) return 0;
if (goa->drawlayer > gob->drawlayer) return 1; if (goa->drawlayer > gob->drawlayer) return 1;
if (gob->drawlayer > goa->drawlayer) return -1;
if (*sa > *sb) return 1;
return -1; return -1;
} }